## 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 (因特网工程任务组) 组织的作品。这意味着除此之外还有其他的方式可以发布一份 RFC，例如，[独立发布流](https://link.juejin.cn?target=https%3A%2F%2Fwww.rfc-editor.org%2Fabout%2Findependent%2F)。

其实还有许多的“发布流”。**只有 IETF 的发布流才表示 IETF 组织对该协议的规范进行了审核并达成了共识**。

年代较远的文档（RFC5705之前的那些）写着的是“Network Working Group”，所以你需要更一步发掘看看是否他们代表了 IETF 的共识；关于这一点，我们可以把“Status of this Memo”作为切入点，还有 [RFC Editor 网站](https://link.juejin.cn?target=https%3A%2F%2Fwww.rfc-editor.org%2F)。

再接着往下则是“Request for Comments”编号。**如果这一栏写着“Internet-Draft”，则表示这个文档并不是一个 RFC 文档**，它只是一份提议而已，[任何人都可以写一份](https://link.juejin.cn?target=https%3A%2F%2Fdatatracker.ietf.org%2Fsubmit%2F)，因为 Internet-Draft 并不意味着它已经被 IETF 所采用。

**Category**（目录）分为“Standard Track”（标准）、“Informational”（信息性的）、“Experimental”（实验性的）、“Best Current Practice”（当前最佳实践）。它们之间的区别有时候比较模糊，但可以确信的是，如果是由 IETF 制作的，肯定经过了大量的审核。不过要注意，Informational 和 Experiment 并不属于标准，即便是经过 IETF 批准发布的。

最后，列在“横幅”右边的是这个文档的**作者**。和学术文章不一样，这个列表列出的并不是对这份文档做出了贡献的人员列表，在 RFC 中，这块信息位于文档尾部的“Acknowledgets (鸣谢)”一节；在 RFC 中，表示的只是“文档是谁写的”。

## 如何确定是否为最新版本？

RFC 是一种存档性质的系列文档，确定了就不能变动，哪怕只是一个字符。

所以，知道你现在正在查看的是否找对了是非常重要的。这一点从文档的顶部所包含的一些元信息

- Obsoletes

  列出了被这个文档所彻底取代的 RFC 。它说的是，你应该使用当前的这个文档，而不是这些。注意：老版本的协议不一定就会被新版本的取代；例如，HTTP/2 并没有淘汰 HTTP/1.1，因为它仍然是合法的（且是必要的），不过 RFC7230 淘汰了 RFC2616，因为他是那个协议的一个引用。

- Updates

  列出了被这个文档实质性更新到的 RFC。意思就是说，如果你阅读这些文档，那么同时也应该阅读这个。

不过遗憾的时，RFC 的纯文本展示版本（如 RFC Editor 网站上的文档）不会给出哪些文档对你正在阅读的这个文档造成了更新或者淘汰。这也是为什么许多人宁愿使用 [tools.ietf.org](https://link.juejin.cn?target=http%3A%2F%2Ftools.ietf.org) 网站上的 RFC 文档库，因为它会在文档顶部给出这块信息，
```
[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 文档也常存在错漏，所以在这个工具栏的右侧你还可以看到一个“Errata Exsit (存在勘误)”的字样，在其上面则是关于勘误信息的链接。

- Errata
  

   是一些关于这个文档的纠正和澄清，但还没到值得重新发布一个新的 RFC 的程度。不过有个时候他们会对 RFC 规范的实现造成一定影响（例如，这个规范中存在一个 bug，就会产生严重的误解），所以这还是值得一览的。举个例子，[这是 RFC7230 的勘误列表](https://link.juejin.cn/?target=https%3A%2F%2Fwww.rfc-editor.org%2Ferrata_search.php%3Frfc%3D7230)。在阅读时要留意他们的状态，许多的没被采纳的其实是因为人们的误解。

## 理解上下文

开发人员在查看了 RFC 中的某条语句后，在实现他们所看到的时，违背规范作者的用意的情况，会比你想象的还要普遍。

这是因为要写出一份在选择性阅读的情况下不会产生误解的规范是极端困难的（就像任何圣经中的语句一样）。

因此，不只是要阅读直接相关的那些文本，还要阅读它所引用的文本，不管引用的是规范之内的，还是其他规范的。在比较赶的情况下，如果不能把整个文档读一遍，那么把那些可能相关的段落都阅读一遍会有极大的帮助。

例如，HTTP 的消息头[定义](https://link.juejin.cn?target=https%3A%2F%2Fhttpwg.org%2Fspecs%2Frfc7230.html%23http.message)中使用的是以 CRLF 进行分割，但如果往下看到[这](https://link.juejin.cn?target=https%3A%2F%2Fhttpwg.org%2Fspecs%2Frfc7230.html%23message.robustness)的话，你会看到“a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR.（接收方可以用单个 LF  字符作为行结束符并忽略紧接在它之前的 CR）”这样一句话，显然这也是对的？

另外一个也比较重要的点是，许多协议都设有 IANA 注册表用于管理他们本身的可扩展点，这些才是实际的事实，而不是规范文档。例如，HTTP 方法列表的权威位于[这个注册表](https://link.juejin.cn?target=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fhttp-methods%2Fhttp-methods.xhtml)中，而不是什么 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](https://link.juejin.cn?target=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc2119) 定义的这些关键字有助于交流，但有时候也会给开发人员带来困惑。经常会在规范中看到下面这样的话：

```
The Foo message MUST NOT contain a Bar header.
Foo 消息**一定**不能包含 Bar 消息头。
```

这个要求描述的是协议中的一条消息，即“Foo 消息”。如果你发要出这样一条消息，它说的很明确，不需要包含 Bar 消息头；如果你包含了，那就不是一条符合规范的消息。

不过，对于消息的接收方来说，它就没有明确说明；假如你收到了一条带有 Bar 消息头的 Foo 消息，你该怎么做呢？

某些开发人员会选择拒绝这样一条消息——虽然规范文档中没有对该采取什么动作有明确规定；另外一些开发人员则会选择继续处理这条消息，但会先把 Bar 消息头剔除掉，或者忽视掉——即便规范文档明确说明了所有的消息头都需要进行处理。

以上所述的这些就可能造成预料之外的消息交流问题。正确的做法应该是遵循正常的消息头处理规则，除非规范中有特别说明。

这是因为，规范定义的比较宽泛的话，行为就会比较宽泛；言外之意就是说，所有没有明确说明不允许做的事情都是允许的。因此，越是对规范解读的过多就越可能发生意外的影响，因为这样你会倾向于引入更多的行为，而这样其他人就需要想办法避开这些行为。

理想而情况是，规范为文档在做定义时都是以处理这些消息的人的行为角度来的，就像这样：

```
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.
Foo 消息的发送者**一定**不能包含 Bar 消息头；接收到一条包含 Bar 消息头的 Foo 消息的接受者**一定**要忽略掉 Bar 消息头，但**一定**不能剔除它。
```



如果不是这样描述的的话，最好是查看一下规范中关于错误处理的更宽泛的建议（例如，HTTP 规范的“[Conformance and Error Handling](https://link.juejin.cn?target=https%3A%2F%2Fhttpwg.org%2Fspecs%2Frfc7230.html%23conformance) 行为规范及异常处理”一节）



另外，始终要记住规则所规定的要求；许多规范都有一套精心制作的术语，这些术语可以很好地界定协议中不同角色的职责。



例如 HTTP 的[代理](https://link.juejin.cn?target=https%3A%2F%2Fhttpwg.org%2Fspecs%2Frfc7230.html%23intermediaries)，这是一种中间层，既充当客户端又充当服务端（但并不真的是一个 User-Agent 

同样，HTTP 协议是一些规则中视情况使用“generating (生成)” 一条消息或者“forwarding (转发)”消息，对它们做出清晰的界定。对这类特定的术语多关注点可以节省你许多的猜测时间。

## 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.
```



实际情况是，作者们偏爱使用 SHOULD 和 SHOULD NOT 的来表述“我们希望你这样做，但我们知道我们不能总要求你这样做”这样的意思。

SHOULD 也并不表示如果服务器不喜欢就可以随意忽视规则的要求。

## 示例

因为有一个不好的事实就是，示例通常是作者给予关注度最小的，因为它们需要与协议的每次变动保持同步。

这样的结果就是，它们成为了规范中最不可靠的部分。对，规范的作者是应该在发布前对示例进行复核，但这并不代表不会存在漏洞。

另外，即便某个示例比较完美，它所要展示的关于协议的某个方面也可能并不是你所要寻找的；这些示例通常为了简洁都会都被精简过，或者是展示的只是解码步骤之后的。

所以，即便会耗费更多时间，也一定要看看文字部分——示例并不是规范。

## On ABNF

协议中的内容通常使用的是增强的NBF范式来表示，例如：

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

ABNF 为消息提供了一种理想的表达形式，你生成一条消息时需要满足它的规则，但他并没有指定当收到一条不满足规则的消息时如何处理的过程。其实，许多规范都没能说清 ABNF 的角色其实是描述规则要求的处理过程的。

如果你严格执行 ABNF 的规则，许多协议会达不到要求，但有时候这样反而是好事。在上面的这个例子中，空白字符是不允许出现在分号前后的，但你可以确信有些人就是会在这里放置空白字符，而有些规则的实现方也会接受这种情况。

所以，考虑到上下文及额外的规则要求，一定要看看 ABNF 前后的文本，并形成一种意识——你可能得在解析消息时放松 ABNF 的要求。

有些规范文档会更大强度地采取 ABNF 的强大性，并显式地指明解析的算法和发生错误时的处理。当出现这样的情况，你应该严格遵守，以确保交流性。

## 安全性

所以，你需要付出精力阅读一下“Security Considerations”一节——不管是在实现还是部署这个协议；如果你不这样做，很可能你会因此而“翻车”。

跟随它里面的引用（如果有的话）也是一个好习惯。如果没有，去尝试找一些用来理解正在被讨论的问题的术语。

如果 RFC 没能解答你的问题，或者你不确定它的文本所要表达的意思就是你所理解的，那么最好的方法就是找到相关的[工作组](https://link.juejin.cn?target=https%3A%2F%2Fdatatracker.ietf.org%2Fwg%2F)，然后在他们的邮件列表中进行提问。如果没有关于这个问题所属话题的活跃工作组，可以尝试到对应[领域](https://link.juejin.cn?target=https%3A%2F%2Fietf.org%2Ftopics%2Fareas%2F)的邮件列表提问。

我们不应该一开始就提交一条勘误——应该先跟其他人进行交流。

许多工作组现在都选择使用 Github 来管理他们的规范了；如果你有任何问题，可以提交一条问题反馈。如果规范已经成为 RFC 了，那么最好就是到该 RFC 的邮件列表中来反馈。

