如何阅读RFC

本文系翻译文章,【英语渣】来源: How to Read an RFC
作者是Mark Nottingham,同时担任IETF HTTP工作组和QUIC工作组的主席,也是IAB的成员。【大佬】


无论好坏,我们通过Requests For Comments(RFCs)定义了互联网使用的很多协议。这些RFC文档被开发人员始位圣经,他们会试着解析其背后的含义,同时避开他们不能理解的部分,他们认为这无关紧要。这通常会导致挫败感,更重要的是导致操作性和安全性问题。

然而,如果知悉了RFCs是如何构建和发布的,将会使您更容易理解RFC。下面是一些我在HTTP上和其它事情上的经验。

在哪里读RFC?

经典的阅读RFC的地方是RFC Editor Web Site。到那时,如我们下面将会看到的,RFC编辑器缺少一些关键信息,所以大多数人选择tools.ietf.org

即使是找到正确的RFC也是很困难的,因为现在已经有近9000条RFC了。显然,您可以通过Web搜索引擎来查找RFC,RFC编辑器网站也有一个非常好用的搜索功能。

另一个选择是使用EveryRFC,我将它们放在一起,以便于通过标题、关键字来查找RFC,并通过标签来进行探索。

毫无疑问,纯文本的RFC非常难以阅读,这种情况正在改善;RFC编辑器正在把RFC包装成一种新的RFC格式,这种格式有更令人满意的展示和自定义选项。同时,如果您需要更多有用的RFCs,您可以使用第三方的库存储中的RFC。【应该是指同类的RFC】例如,greenbytes保存了WebDAV相关的RFC,HTTP工作组维护了HTTP相关的RFC。

RFC是什么类型?

所有的RFC都有一个顶部横幅,就像下面这样:

Internet Engineering Task Force (IETF)                  R. Fielding, Ed.
Request for Comments: 7230                                         Adobe
Obsoletes: 2145, 2616                                    J. Reschke, Ed.
Updates: 2817, 2818                                           greenbytes
Category: Standards Track                                      June 2014
ISSN: 2070-1721

在左上方,显示**“Internet Engineering Task Force(IETF)【互联网工程任务组】”**。这表明这是IETF的产品;尽管这并不常见,但是也有其它不需要IETF共识而发布得RFC;例如:independent stream

实际上,有大量地“源”可用于发布RFC,只有IETF源表明整个IETF已经复核过并就协议规范达成了共识

老的文档(RFC5705之前的)这里显示的是**“Network Working Group【网络工作组】”**,所以您需要深挖一下,以看看它们是否是代表了IETF共识;查看RFC编辑器站点“Status of this Memo【备忘录状态】”部分可以了解标识情况。

之下是**“Request for Comments【RFC号】”**。如果是“Internet-Draft”则表明这不是RFC;这只是一个提案,每个人都可以写一个。因为每不是每一个Internet-Draft都会成为一个RFC。

**Category【类别】“是以下其中之一:“Standards Track”, “Informational”, “Experimental”或“Best Current Practice”。它们之间的区别有时是模糊的,但是如果是由IETF出品的,那它就有了合理的审查。然而,Informational【信息类型】和Experimental【实验类型】不是标准,尽管是由IETF达成共识并公布。

最后,文档的**author【作者】**列在了右边。不像学术界标准,这不是一份详尽的贡献列表;通常,那一部分在底部“致谢”部分完成。在RFC中,这代指是“谁写了这份文档”。通常,您会看到加了“Ed.”,这表明他们充当了编辑角色,因为RFC或者草案是早就存在的,就像RFC修订一样。

RFC是最新的吗?

RFCs是一系列的文档;他们不能改变,一个字符也不行(见RFC7158和RFC7159的区别,这是一个极端的例子,他们把年份搞错了)。

请确保您阅读的是正确的RFC文档。RFC文档头部有一些帮助元信息。

  • Obsoletes:列出了本文档废弃的RFC;即您应该用本文而不是那些被废弃的。请注意,并非是一个协议新的版本发布了,就使得老版本被废弃了;例如,HTTP/2并没有取代HTTP/1.1,因为它对于实现老的协议是合法且必须的。然而,RFC7230确实废弃了RFC2616,因为这(RFC7230)是该协议的参考。
  • Updates:列出了本文档实质性更新了的RFC;换句话说,如果您读了其它文档,那么您就也该阅读本文档。

不幸的是,ASCII文本RFC(例:在RFC编辑器上的)不会告知您哪些文档更新或废弃了您在读的文档。这就是为什么用户使用tools.ietf.org查看RFC,它在横幅(banner)中带了类似下面的信息:

[Docs] [txt|pdf] [draft-ietf-http...] [Tracker] [Diff1] [Diff2] [Errata]

Obsoleted by: 7230, 7231, 7232, 7233, 7234, 7235          DRAFT STANDARD
Updated by: 2817, 5785, 6266, 6585                          Errata Exist

页面上的每一个数字都是一个链接,连接到对应的RFC文档。

即使是最新的RFC也经常出现问题。在工具横幅中,您还会在右侧看到“Errata Exist”上面的勘误表链接警告。

Errata是对当前RFC的纠错和澄清,这些勘误表不值得重新发布一个新的RFC。但有时这会对RFC的实现有实质性的影响(例如,如果规范中有Bug,将会导致严重错误),那么他们就值得去做新的RFC了。

例如,这是RFC7230的勘误表。读勘误表的时候,请记住他们的状态;很多提案被拒绝,因为他们误解了规范。

理解上下文

开发者看了RFC,实现了他们所看的,但是这个实现和作者的意图相去甚远,而这件事超乎想象的常见。

这是因为,以不被选择性阅读【跳读】而误解的方式写一个规范是极度困难的(任何圣经一样的东西都有这种问题)。

因此,不仅需要直接阅读RFC,(至少)还需要阅读RFC引用的文档,不论引用的是同一个规范还是其它文档。如果您不能阅读整个文档,那么一定程度上阅读任何可能相关的部分也是有很大助益的。

例如,HTTP消息头的分隔定义为使用CRLF,但是如果您跳过了这里,那么您会看到“接收方可能(MAY)将单个LF识别为行终止符并忽略前面的任何CR。”非常清楚,不是吗?

同样需要记住的是,很多协议设置了IANA registries【IANA注册机构】来管理它们的扩展点;这不是规范,而是事实来源。例如,典型的HTTP方法列表在这个注册机构,而不是任何一个HTTP规范中。

语法要求

几乎所有的RFC都在顶部有类似于下面的样板:

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.

这些RFC2119中定义的关键字有助于定义互操作性,但是它们有时也使开发人员感到困惑。规范中经常可以看到类似下方的表述:

The Foo message MUST NOT contain a Bar header.

该要求放在伪协议“Foo message"之上。如果您发送一个,很明显不需要包含Bar header;如果您包含了Bar header,那么它将不符合信息要求。

然而,接收方的行为使不清晰的;如果您看到了带有Bar header的Foo message,您会怎么做?

一些开发者会拒绝包含了Bar header的Foo message,尽管规范没有说要这么做。其他人会继续处理这个message,但是去除Bar header,或者忽略它 —— 尽管规范明确指出要处理所有header。

上述所有事情都可能在无意间导致一些互操作性问题。正确的做法是:按照header正常的流程处理,除非明确指出不该这么做。

这是因为通常会写规范以便明确指定行为; 换句话说,允许所有未明确禁止的内容。因此,过度解读规范会无意间产生危害,因为您会带来其他人会绕过的新的行为。

理想世界中,规范将会根据处理信息角色的行为来定义,如下所示:

Senders of the Foo message MUST NOT include a Bar header. Recipients
of a Foo message that includes a Bar header MUST ignore the Bar header,
but MUST NOT remove it.

如果没有这个,最好找一下规范中处理错误的一般性建议(例如,HTTP的一致性和错误处理章节。)

同时,请记住要求的目标;大部分规范有一套高度发展的术语集合,用于区分协议中不同角色。

例如,HTTP有代理,代理是一种中介,它实现了客户端和服务器(但不是User-Agent或源服务器);它们需要关注所有角色的需求。

同样,HTTP根据具体情况,区分”产生“信息还是仅在某些要求中“转发”信息。关注这种类型的确定的术语有助于您节省很多猜测。

SHOULD

是的,SHOULD值得一节来讲述。尽管努力消除它,但这个多余的术语困扰着许多RFC。RFC2119将其描述为:

SHOULD  This word, or the adjective "RECOMMENDED", mean that there
        may exist valid reasons in particular circumstances to ignore a
        particular item, but the full implications must be understood and
        carefully weighed before choosing a different course.

实践中,作者通常使用SHOULDSHOULD NOT表明“我们希望您这样做,但是我们知道我们不能总是如此要求。”

例如,在HTTP方法概览中,我们看到:

When a request method is received that is unrecognized or not
implemented by an origin server, the origin server SHOULD respond
with the 501 (Not Implemented) status code. When a request method
is received that is known by an origin server but not allowed for
the target resource, the origin server SHOULD respond with the 405
(Method Not Allowed) status code.

使用SHOULD而不是MUST是因为服务器可能会合理地采取另一种行动;如果是从被认为是攻击者的客户端发来的请求,服务器可能会终止连接,或者如果请求的资源需要HTTP认证,服务器可能在使用405之前强制使用401(Not Authented【未认证】)。

SHOULD不意味着服务器可以自由地忽略一个请求,这看起来对RFC不太尊重。

又是,我们看到如下所示格式的SHOULD:

A sender that generates a message containing a payload body SHOULD
generate a Content-Type header field in that message unless the
intended media type of the enclosed representation is unknown to
the sender.

注意“unless”,它指定了SHOULD允许的“特殊情况”。可以说这可以指定为MUST,因为unless语句仍然适用,但这种规范风格更为普遍。

阅读示例

另外一个非常常见的陷阱是浏览规范的示例,并按照它们的功能来实现。

不幸的是,作者通常很少关注示例,因为它们必须随着协议每次的更新而更新。

结果就是,示例通常是规范最不可靠的部分。是的,作者应该在发布前绝对仔细检查这些示例,但是还是会出现纰漏。

此外,即使是一个完美的例子也可能无法说明是和您正在寻找的协议相关; 为简洁起见,它们经常被截断,或者在解码发生后显示。

还是要花费更多的时间阅读RFC实际文本;示例不是规范。

ABNF

增强型BNF通常用于定义伪协议,例如:

FooHeader = 1#foo
foo       = 1*9DIGIT [ ";" "bar" ]

一旦您习惯了,ABNF提供了一个简单且易于理解的协议元素应该是什么样子的草图。

然而,ABNF是“有抱负的” —— 它确定了一个理想的消息形式,您生成的信息确实需要与之相匹配。它没有指定如何处理未能匹配的已接收消息。实际上,很多规范在处理要求上很难说清楚和ABNF的关系。

如果您要严格执行ABNF,大多数协议将会严重失效,但有时它很重要。上述例子中,空格不允许出现在分号周围,但是您可以打赌有人会把它放在那里,而且有些实现也会接受。

所以,请确保阅读ABNF周围的文本,获取额外信息或上下文,并意识到如果没有直接要求,您可能必须将解析调整为比ABNF提示的更容易接受的输入。

一些规范开始承认ABNF的期望性质,并指定包含错误处理的显式解析算法。如果指定ABNF,应严格遵循这些,以确保互操作性。

安全注意事项

自从RFC3552开始,RFC样板中包含了“安全注意事项”一节。

结果就是,发布RFC中很少没有关于安全性的实质性部分;审核流程不允许草案只是说“此协议没有考虑安全因素”。

所以,无论您是在实施还是部署协议,都必须阅读并确保您了解“安全注意事项”部分; 如果您不这样做,很可能会有一些东西会让您不知所措。

遵循其引用(如果有的话)也是一个好主意。如果没有,请尝试查找用于理解所讨论问题的一些术语。

更多

如果RFC没有解答您的问题,或者您不确定其意图,最好的方式是寻找相关的工作组,并在其邮件列表提问。如果没有涉及相关主题的活跃的工作组,请尝试相应区域,的邮件列表。

填写一个勘误表通常不是您该做的第一步 —— 先去找人交流。

很多工作组现在使用GitHub来管理规范;如果您对活跃的规范有问题,请前去GitHub并提出一个issue。如果它已经是规范了,通常最好是使用邮件列表,除非您找到截然不同的指示。

我确信还有很多有关如何阅读RFC,并且一些和我写的是有争议的,但是这是我的意见。我希望它是有帮助的。

参考

  1. How to Read an RFC
  2. 如何阅读RFC
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章