如何閱讀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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章