2021-05-12 14:32:11
WebSocket協定中文版
WebSocket協定中文版
摘要
WebSocket協定實現在受控環境中執行不受信任程式碼的一個使用者端到一個從該程式碼已經選擇加入通訊的遠端主機之間的全雙工通訊。用於這個安全模型是通常由web瀏覽器使用的基於來源的安全模型。該協定包括一個開啟階段握手、接著是基本訊息幀、TCP之上的分層(layered over TCP)。該技術的目標是為需要與伺服器全雙工通訊且不需要依賴開啟多個HTTP連線(例如,使用 XMLHttpRequest或者 <iframe>和長查詢)的基於瀏覽器應用提供的一種機制。
本備忘錄狀態
這是一個Internet標準跟蹤檔案。
本檔案是網際網路工程任務組(IETF)的一個產物。它代表了IETF社群的共識。它已接受公共審查和已經被網際網路工程指導委員會(IESG)認可釋出。Interent標準的更多資訊可以在RFC 5741的第而節找到。
本檔案的當前狀態資訊、任何勘誤表以及如何提供它的反饋,可於http://www.rfc-editor.org/info/rfc6455獲得。
版權宣告
版權所有(C)2011 IETF信託和確認為檔案作者的人。保留所有權利。
本檔案遵守BCP 78和涉及IETF檔案(http://trustee.ietf.org/license-info)的在本檔案釋出之日起生效的IETF信託的法律條文。請仔細閱讀這些檔案,因為他們描述了關於本檔案的你的權利和限制。從本檔案中提取的程式碼元件必須包括描述在第四章和簡體BSD License檔案。e的信託法律條文並提供,不保證描述在簡體BSD許可協定中。
目錄
1 引言
1.1 背景
1.2 協定概述
1.3 開啟階段握手
1.4 關閉階段握手
1.5 設計理念
1.6 安全模型
1.7 與TCP和HTTP的關係
1.8 建立連線
1.9 使用WebSocket協定的子協定
2 一致性要求
2.1 術語和其他約定
3 WebSocket URI
4 開啟階段握手
4.1 使用者端要求
4.2 伺服器端要求
4.2.1 讀取使用者端的開啟階段握手
4.2.2 傳送伺服器的開啟階段握手
4.3 為握手中使用的新的頭欄位整理的ABNF
4.4 支援多個版本的WebSocket協定
5 資料框
5.1 概述
5.2 基本幀協定
5.3 使用者端到伺服器掩碼
5.4 分片
5.5 控制幀
5.5.1 Close
5.5.2 Ping
5.5.3 Pong
5.6 資料框
5.7 範例
5.8 可拓展性
6 傳送和接收資料
6.1 傳送資料
6.2 接收資料
7 關閉連線
7.1 定義
7.1.1 關閉WebSocket連線
7.1.2 啟動WebSocket關閉階段握手
7.1.3 關閉階段握手已啟動
7.1.4 WebSocket已關閉
7.1.5 WebSocket連線關閉程式碼
7.1.6 WebSocket連線關閉原因
7.1.7 失敗WebSocket連線
7.2 異常關閉
7.2.1 使用者端發起的關閉
7.2.2 伺服器端發起的關閉
7.2.3 從異常關閉中恢復
7.3 正常連線關閉
7.4 狀態碼
7.4.1 定義的狀態碼
7.4.2 保留的狀態碼範圍
8 錯誤處理
8.1 處理UTF-8編碼資料的錯誤
9 拓展
9.1 協商拓展
9.2 已知拓展
10 安全注意事項
10.1 非瀏覽器使用者端
10.2 Origin注意事項
10.3 攻擊基礎設施(掩碼)
10.4 實現特定限制
10.5 WebSocket使用者端驗證
10.6 連線的保密性和完整性
10.7 處理無效資料
10.8 使用SHA-1的WebSocket握手
11 IANA考慮
11.1 註冊新的URI模式
11.1.1 註冊「ws」模式
11.1.2 註冊「wss」模式
11.2 註冊「WebSocket」HTTP Upgrade關鍵字
11.3 註冊新的HTTP頭欄位
11.3.1 Sec-WebSocket-Key
11.3.2 Sec-WebSocket-Extensions
11.3.3 Sec-WebSocket-Accept
11.3.4 Sec-WebSocket-Protocol
11.3.5 Sec-WebSocket-Version
11.4 WebSocket拓展名註冊
11.5 WebSocket子協定名註冊
11.6 WebSocket版本號註冊
11.7 WebSocket關閉程式碼註冊
11.8 WebSocket操作嗎註冊
11.9 WebSocket幀頭位註冊
12 其他規範使用WebSocket協定
13 致謝
14 參考資料
14.1 參考標準
14.2 參考資料
1 引言
1.1 背景
本節是非規範的。
過去,建立需要需要在使用者端和服務之間的雙向通訊(例如,即時訊息和遊戲應用)的web應用,需要一個濫用的HTTP來輪詢伺服器進行更新但以不同的HTTP呼叫發生上行通知(RFC6202)。
這將導致各種各樣的問題:
1、伺服器被迫為每個使用者端使用一些不同的底層TCP連線:一個用於傳送資訊到使用者端和一個新的用於每個傳入訊息。
2、 線路層協定有較高的開銷,因為每個使用者端-伺服器訊息都有一個HTTP頭資訊。
3、使用者端指令碼被迫維護一個傳出的連線到傳入的連線的對映來跟蹤回覆。
一個簡單的辦法是使用單個TCP連線雙向傳輸。這是為什麼提供WebSocket協定。與WebSocket API結合[WSAPI],它提供了一個HTTP輪詢的替代來進行從web頁面到遠端伺服器的雙向通訊。
同樣的技術可以用於各種各樣的web應用:
遊戲、股票行情、同時編輯的多使用者應用、伺服器端服務以實時暴露的使用者介面等等。
WebSocket協定被設計來取代現有的使用HTTP作為傳輸層的雙向通訊技術,並受益於現有的基礎設施(代理、過濾、身份驗證)。這樣的技術被實現來在效率和可靠性之間權衡,因為HTTP最初的目的不是用於雙向通訊(參見[RFC6202]的進一步討論)。WebSocket協定試圖在現有的HTTP基礎設施上下文中解決現有的雙向HTTP技術目標;同樣,它被設計工作在HTTP埠80和443,也支援HTTP代理和中介軟體,即使這具體到當前環境意味著一些複雜性。但是,這種設計不限制WebSocket到HTTP,且未來的實現可以在一個專用的埠上使用一個更簡單的握手,且沒有再創造整個協定。最後一點是很重要的,因為互動訊息的傳輸模式不精確地匹配標準HTTP傳輸並可能在相同元件上包含不常見的負載。
1.2 協定概述
本節是非規範的。
本協定有兩部分:握手和資料傳輸。
來自使用者端的握手看起來像如下形式:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
來自伺服器的握手看起來像如下形式:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
來自使用者端的首行遵照Request-Line格式。來自伺服器的首行遵照Status-Line格式。Request-Line和Status-Line在[RFC2616]中定義。
在這兩種情況中一個無序的頭欄位集合出現在首行之後。這些頭欄位的意思指定在本檔案的第4章。另外的頭欄位也可能出現,例如cookie[RFC6265]。頭的格式和解析定義在[RFC2616]
一旦使用者端和伺服器都傳送了它們的握手,且如果握手成功,接著開始資料傳輸部分。這是一個每一段都可以的雙向通訊通道,彼此獨立,隨意發生資料。
一個成功握手之後,使用者端和伺服器來回第傳輸資料,在本規範中提到的概念單位為「訊息」。線上路上,一個訊息是由一個或者多個幀組成。WebSocket的訊息並不一定對應於一個特定的網路層幀,可以作為一個可以被一箇中介軟體合併或分解的片段訊息。
一個幀有一個相應的型別。屬於相同訊息的每一幀包含相同型別的資料。從廣義上講,有文字資料型別(它被解釋為UTF-8[RFC3629]文字)、二進位制資料型別(它的解釋是留給應用)、和控制幀型別(它是不準備包含用於應用的資料,而是協定級的訊號,例如應關閉連線的訊號)。這個版本的協定定義了六個幀型別並保留10個以備將來使用。
1.3 開啟階段握手
本節是非規範的。
開啟階段握手的目的是相容基於HTTP的伺服器軟體和中介軟體,以便單個埠可以用於與伺服器交流的HTTP使用者端和與伺服器交流的WebSocket使用者端。最後,WebSocket使用者端的握手是一個HTTP Upgrade請求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
依照[RC2616],握手中的頭欄位可能由使用者端按照任意順序傳送,因此在接收的不同頭欄位中的順序是不重要的。
"Request-URI"的GET方法[RFC2616]用於識別WebSocket連線的端點,即允許從一個IP地址服務的多個域名,也允許由單臺伺服器的多個WebSocket端點。
使用者端按照[RFC2616]在它的握手的|Host|頭欄位中包含主機名,以便使用者端和伺服器都能驗證他們同意哪一個正在使用的主機。
在WebSocket協定中另外的頭欄位可以用於選擇選項。典型的選項在這個版本中可用的是子協定選擇器(|Sec-WebSocket-Protocol|)、使用者端支援的擴充套件列表(|Sec-WebSocket-Extensions|)、|Origin|頭欄位等。|Sec-WebSocket-Protocol|請求頭欄位可以用來表示使用者端接受的子協定(WebSocket協定上的應用級協定層)。伺服器選擇一個可接受的協定,或不在乎它的握手中迴應該值表示它已經選擇了那個協定。
Sec-WebSocket-Protocol: chat
|Origin| 頭欄位 [RFC6454]是用於保護防止未授權的被瀏覽器中使用WebSocket API的指令碼跨域使用WebSocket伺服器。伺服器收到WebSocket連線請求生成的指令碼來源。如果伺服器不想接受來自此源的連線,它可以選擇通過傳送一個適當的HTTP錯誤碼拒絕該連線。這個頭欄位由瀏覽器使用者端傳送,對於非瀏覽器使用者端,如果它在這些使用者端上下文中有意義,這個頭欄位可以被傳送。
最後,伺服器要證明收到使用者端WebSocket握手的資訊,以便伺服器不接受不是WebSocket連線的連線。這可以防止一個通過使用XMLHttpRequest [XMLHttpRequest]或一個表單提交傳送它精心製作的包欺騙WebSocket伺服器的攻擊者。
為了證明收到的握手,伺服器必須攜帶兩條資訊並組合它們並形成一個響應。第一天資訊源自使用者端握手中的|Sec-WebSocket-Key|頭資訊:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
對於這個頭欄位,伺服器必須攜帶其值(出現在頭欄位上,如,減去開頭和結尾空格的base64編碼 [RFC4648]的版本)並將這個與字串形成的全域性唯一識別符號(GUID, [RFC4122]) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"連線起來,其不太可能被不理解WebSocket協定的網路端點使用。SHA-1雜湊(160位元)[FIPS.180-3],base64編碼(參見[RFC4648]第4章)、用於這個的一系列相關事務接著再伺服器握手過程中返回。
具體而言,如果在上面例子中,|Sec-WebSocket-Key|頭欄位的值為"dGhlIHNhbXBsZSBub25jZQ==",伺服器將連線字串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"形成字串"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"。伺服器使用SHA-1雜湊這個,併產生值0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。這個值接著使用base64編碼(參見[RFC4648]第4章),產生值 "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="。這個值將接著在|Sec-WebSocket-Accept|頭欄位中迴應。
來自伺服器的握手比使用者端握手更簡單。首行是一個HTTP Status-Line,具有狀態碼101:
HTTP/1.1 101 Switching Protocols:
101以外的任何狀態碼錶示WebSocket握手沒有完成且HTTP語意仍適用。頭資訊遵照該狀態碼。
|Connection| 和 |Upgrade|頭欄位完成HTTP升級。|Sec-WebSocket-Accept|頭欄位表示伺服器是否將接受該連線。如果存在,這個頭欄位必須包括使用者端在|Sec-WebSocket-Key|中現時傳送的與預定義的GUID的雜湊。任何其他值不能被解釋為一個伺服器可接受的連線。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
這些欄位由WebSocket使用者端為指令碼頁面做檢查。如果|Sec-WebSocket-Accept|不能匹配盼望的值、如果頭欄位缺失或者HTTP狀態碼不是101,則連線不能建立,且WebSocket幀將不發生。
可選的欄位也可以包含在內。在這合適版本的協定中,主要可選欄位是|Sec-WebSocket-Protocol|,其表示伺服器選擇的子協定。WebSocket使用者端驗證伺服器包含的在WebSocket使用者端握手中指定的一個值。宣告多個子協定的伺服器必須確保它選擇第一個,基於使用者端握手並指定它在其握手中。
Sec-WebSocket-Protocol: chat
伺服器也可以設定cookie相關的可選欄位為_set_ cookies,描述在[RFC6265]。
1.4 關閉階段握手
本節是非規範的。
關閉階段握手比開啟階段握手簡單得多。
兩個節點中的任一個都能傳送一個控制幀與包含一個指定控制序列的資料來開始關閉階段的握手(詳見5.5.1節)。在收到這樣一個幀時,另一個節點在響應中傳送一個Close幀,如果還沒有傳送一個。在收到那個控制幀時,第一個節點接著關閉連線,安全地知道沒有更多的資料到來。
傳送一個控制幀之後,表示連線將被關閉,一個節點不會傳送任何更多的資料;在接收到一個控制幀之後,表示連線將被關閉,一個節點會丟棄收到的任何更多的資料。
對於兩個節點同時地初始化這個握手是安全的。
關閉階段握手的目的是完成TCP關閉握手(FIN/ACK),基於TCP關閉階段握手不總是可靠的端到端,尤其存在攔截代理和中介軟體。
通過傳送一個Close幀並等待響應中的Close幀,某些情況下可避免資料不必要的丟失。例如,在某些平臺上,如果一個socket關閉了,且接收佇列中有資料,一個RST包被傳送了,這樣會導致接受RST的一方的recv()失敗,即使有資料等待讀取。
1.5 設計理念
本節是非規範的。
WebSocket協定應該以最小幀的原則設計(唯一存在的框架是使協定基於幀而不是基於流且支援區分Unicode文字和二進位制)。期望通過應用層將後設資料分層在WebSocket之上,通用的,通過應用層將後設資料分層在TCP之上(例如HTTP)。
從概念上講,WebSocket只是TCP之上的一層,執行以下操作。
1、為瀏覽器新增一個web基於來源的安全模型
2、新增一個定址和協定命名機制來支援在一個IP地址的一個埠的多個主機名的多個服務。
3、在TCP之上分一個幀機制層已回到TCP基於的IP包機制,但沒有長度限制。
4、包括一個額外的帶內(in-band)關閉階段握手,其被設計來工作在現存的代理和其他中介軟體。
除此之外,WebSocket沒有新增任何東西。基本上,它的目的是儘可能接近僅暴露原始TCP到指令碼,儘可能考慮到Web的約束。它也被設計為它的伺服器能與HTTP伺服器共用一個埠的這樣一種方式,通過持有它的握手是一個有效的HTTP Upgrade請求。一個可以在概念上使用其他協定來建立使用者端-伺服器訊息,但WebSocket的意圖是提供一個相對簡單的協定,可以與現有的HTTP和部署的HTTP基礎設施(例如代理)同時存在,並儘可能接近TCP,且對於使用考慮到安全考慮的這樣的基礎設施同樣是安全的,有針對性的補充以簡化使用並保持簡單的事情簡單(如增加的訊息語意)。
協定的目的是為了可拓展性:未來版本將可能引入額外的概念,如複用(multiplexing)。
1.6 安全模型
本節是非規範的。
WebSocket協定使用瀏覽器使用的來源模型限制web頁面可以與WebSocket伺服器通訊,當WebSocket協定是從一個web頁面使用。當然,當WebSocket協定被一個獨立的使用者端直接使用時(也就是,不是從瀏覽器中的一個web頁面),來源模型不再有用,因為使用者端可以提供任意隨意的來源字串。
該協定的目的是無法與現有的協定,如SMTP [RFC5321]和HTTP建立一個連線,同時允許HTTP伺服器來選擇支援該協定如果想要。這是通過具有嚴格的和詳盡的握手和通過限制在握手完成之前能被插入到連線的資料(因此限制多少伺服器可以被應用)實現的。
當資料時來自其他協定時,同樣的目的是無法建立連線的,尤其傳送到一個WebSocket伺服器的HTTP,例如,如果一個HTML表單提交到WebSocket伺服器可能會發生。這主要通過要求伺服器驗證它讀取的握手來實現,它只能做如果握手包含適當的部分,只能通過一個WebSocket使用者端傳送。尤其是,在寫本規範的時候,|Sec-|開頭的欄位不能又web瀏覽器的攻擊者設定,僅能使用HTML和JavaScript APIs例如XMLHttpRequest [XMLHttpRequest]。
1.7 與TCP和HTTP的關係
本節是非規範的。
WebSocket協定是一個獨立的基於TCP的協定。它與HTTP唯一的關係是它的握手是由HTTP伺服器解釋為一個Upgrade請求。
預設情況下,WebSocket協定使用埠80用於常規的WebSocket連線和埠443用於WebSocket連線的在傳輸層安全(TLS) [RFC2818]之上的隧道化。
1.8 建立連線
本節是非規範的
當一個連線到一個HTTP伺服器共用的埠時(這種情況是很有可能在傳輸資訊到埠80和443出現),連線將出現在HTTP伺服器,是一個正常的具有一個Upgrade提議的GET請求。在相對簡單的安裝,只用一個IP地址和單臺伺服器用於所有的資料傳輸到單個主機名,這可能允許一個切實可行的辦法對基於WebSocket協定的系統進行部署。在更復雜的安裝(例如,負載均衡和多伺服器),一組獨立的用於WebSocket連線的主機從HTTP伺服器分離出來可能更容易管理。在寫該規範的時候,應該指出的是,在埠80和443上的連線有明顯不同的成功率,對於在埠443上的連線時明顯更有可能成功,儘管這可能會隨著時間改變。
1.9 使用WebSocket協定的子協定
本節是非規範的。
使用者端可能通過包含|Sec-WebSocket-Protocol|欄位在它的握手中使用一個特定的子協定請求伺服器。如果它被指定,伺服器需要在它的響應中包含同樣的欄位和一個選擇的子協定值用於建立連線。
這些子協定名字應該按照11.5節被註冊。為了避免潛在的碰撞,推薦使用包含ASCII版本的子協定發明人的域名的名字。例如,如果Example公司要建立一個Chat子協定,由Web上的很多伺服器實現,它們可能命名它為"chat.example.com"。如果Example組織命名它們的競爭子協定為"chat.example.org",那麼兩個子協定可能由伺服器同時實現,因為伺服器根據使用者端傳送的值動態地選擇使用哪一個子協定。
通過改變子協定的名字,子協定可以以向後不相容方式版本化,例如,要從"bookings.example.net" 到 "v2.bookings.example.net"。就WebSocket使用者端而言,這些子協定被視為是完全不同的。向後相容的版本可以通過重用相同的子協定字串實現,但要仔細設定實際的子協定以支援這種可拓展性。
2 一致性要求
在本規範中所有的圖表、範例和註釋是非規範的,以及所有章節明確地標記為非規範的。除此之外,在本規範中的一切是規範的。
該檔案中的關鍵字必須 "MUST", 不能"MUST NOT", 需要"REQUIRED",應當 "SHALL", 不得"SHALL NOT",應該 "SHOULD", 不應該"SHOULD NOT", 推薦"RECOMMENDED", 可能 "MAY", 和可選的"OPTIONAL" 由 [RFC2119]中的描述解釋。
作為演演算法的一部分的祈使句中的要求措辭(例如「去掉任何前導空格字元」或「返回false並終止這些步驟」)解釋為引入演演算法中使用的關鍵字("MUST","SHOULD","MAY"等)的意思。
作為演演算法或特定的步驟的一致性要求措辭可以以任何形式實現,只要最終結果是相等的。(尤其是,定義在本規範中的演演算法目的是容易遵循而不必是高效能的)。
2.1 術語和其他約定
_ASCII_指定義在[ANSI.X3-4.1986]中的字元編碼方案。
此文中提到的UTF-8值和使用 UTF-8標記法格式定義在 STD 63 [RFC3629]。
關鍵術語例如命名演演算法或定義是表示像_this_。
頭欄位名字或變數表示像|this|。
變數值表示像/this/。
本檔案提及的程式_失敗WebSocket連線_。該程式定義在7.1.7節。
_將字串轉換為ASCII小寫_意思是替換U+0041到 U+005A(也就是,拉丁文,大寫字母A到拉丁文大寫字母Z)範圍的所有字元為U+0061到 U+007A(也就是,拉丁文,小寫字母A到拉丁文小寫字母Z)範圍對應的字元。
以一個_ASCII不區分大小寫_方式比較兩個字串意思是精確地比較它們,程式碼點對程式碼點,除了U+0041 到 U+005A(也就是,拉丁文,大寫字母A到拉丁文大寫字母Z)範圍中的字元,U+0061到 U+007A(也就是,拉丁文,小寫字母A到拉丁文小寫字母Z)範圍對應的字元被認為也匹配。
用於本檔案的術語「URI」定義在[RFC3986]。
當一個實現需要_傳送_作為WebSocket一部分的資料,實現可能任意地推遲實際的傳輸,例如,緩衝資料為了傳送更少的IP包。
注意,該檔案同時使用[RFC5234] 和 [RFC2616]的ABNF變體在不同章節。
3 WebSocket URIs
本規範定義了兩個URI方案,使用定義在RFC 5234 [RFC5234]中的ABNF句法、和術語和由URI規範RFC 3986 [RFC3986]定義的ABNF製品。
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
host = <host, defined in [RFC3986], Section 3.2.2>
port = <port, defined in [RFC3986], Section 3.2.3>
path = <path-abempty, defined in [RFC3986], Section 3.3>
query = <query, defined in [RFC3986], Section 3.4>
埠元件是可選的;用於「WS」的預設埠試80,而用於「WSS」預設埠時443。
如果方案元件不區分大寫匹配「wss」,URI被稱為「安全的」(它是說,「設定了安全標記」)。
"resource-name"(在4.1節也稱為/resource name/)可以通過連線一下來構造:
1、"/"如果路徑元件是空
2、 "/" if the path component is empty
3、路徑元件
4、the path component
5、 "?"如果查詢元件是空
6、"?" if the query component is non-empty
7、查詢元件
8、 the query component
片段識別符號在WebSocket URI中是無意義的且必須不用在這些URI上。任何URI方法,字元「#」,當不表示片段開始時,必須被跳脫為%23.
4 開啟階段握手
4.1 客戶單需求
要_建立WebSocket連線_,使用者端開啟一個連線並行送一個握手,就像本節中定義的那樣。一個連線最初被定義為一個CONNECTING狀態。使用者端將需要提供一個/host/, /port/,/resource name/, 和 /secure/標記,它們都是在第三章討論的WebSocket URI的元件,連同一起使用的一個/protocols/ 和 /extensions/列表。此外,如果使用者端是一個web瀏覽器,它提供/origin/。使用者端執行在一個受控環境,例如繫結到特定運營商的手機上的瀏覽器,可以下移(offload)連線管理到網路上的另一個代理。在這種情況下,用於本規範目的的使用者端被認為包括手機軟體和任何這樣的代理。
當用戶端要_建立一個WebSocket連線_,給定一組(/host/, /port/, /resource name/,和/secure/ 標記)、連同一起使用的一個/protocols/ 和 /extensions/列表、和在web瀏覽器情況下的一個/origin/,它必須開啟一個連線、傳送一個開啟階段握手、並讀取伺服器響應中的握手。應如何開啟連線的確切要求、在開啟階段握手應傳送什麼、以及應如何解釋伺服器響應,在本節如下所述。在下面的文字中,我們將使用第三章中的術語,如定義在那章中的"/host/" 和 "/secure/」標記。
1. 傳入該演演算法的WebSocket URI元件(/host/, /port/, /resource name/,和 /secure/ 標記)根據指定在第三章的WebSocket URI規範,必須是有效的。如果任何元件是無效的,使用者端必須_失敗WebSocket連線_並終止這些步驟。
2. 如果客戶已經有一個倒通過主機/host/和埠/port/對標識的遠端主機(IP地址)的WebSocket連線,即使遠端主機是已知的另一個名字,使用者端必須等待直到連線已經建立或由於連線已經失敗。必須不超過一個連線處於CONNECTING狀態。如果同時多個連線到同一個IP地址,使用者端必須序列化它們,以致一次不多於連線在以下步驟中執行。
如果使用者端不能決定遠端主機的IP地址(例如,因為所有通訊是通過代理伺服器本身進行DNS查詢),那麼使用者端必須假定這步的目的是每一個主機名參照一個不同遠端主機,且相反,使用者端應該限制同時掛起的連線總數為一個適當低的數(例如,使用者端可能允許到a.example.com 和 b.example.com同時掛起連線,但如果30個同時連線到同一個請求的主機,那可能是不允許的)。例如,在一個web瀏覽器上下文中,使用者端需要考慮使用者已經開啟的標籤數量,在設定同時掛起的連線數量的限制時。
注意:這使得它很難僅通過開啟大量的WebSocket連線到遠端主機為指令碼執行一個拒絕服務攻擊。當攻擊在關閉連線之前被暫停是,伺服器可以進一步降低自身的負載,因為這將降低使用者端重新連線的速度。
注意:沒有限制一個使用者端可以與單個遠端主機有的已建立的WebSocket連線數量。伺服器可以拒絕接受來自具有大量的現有連線的主機IP地址的連線或當遭受高負載時斷開佔用資源的連線。
3. _使用代理_:當有WebSocket協定連線主機/host/和埠/port/時,如果使用者端設定使用代理,那麼使用者端應該連線到代理並要求它開啟一個倒由/host/給定主機和/port/給定埠的TCP連線。
例子:例如,如果使用者端所有的資訊傳輸使用一個HTTP代理,那麼如果它檢視連線到伺服器example.com的埠80,它可能會傳送以下行到代理伺服器:
CONNECT example.com:80 HTTP/1.1
Host: example.com
如果還有密碼,連線可能看起來像:
CONNECT example.com:80 HTTP/1.1
Host: example.com
Proxy-authorization: Basic ZWRuYW1vZGU6bm9jYXBlcyE=
如果使用者端沒有設定使用一個代理,那麼應該開啟一個直接TCP連線到由/host/給定的主機和/port/給定的埠。
注意:不暴露明確的UI來為WebSocket連線選擇一個獨立於其他代理的代理實現,鼓勵使用SOCKS5 [RFC1928]代理用於WebSocket連線,如果有的話,或做不到這一點,選擇為HTTPS連線設定代理勝過為HTTP連線設定代理。
為了代理自動設定指令碼,傳給函數的URI必須從/host/, /port/,/resource name/和/secure/標記來構造,使用第三章給定的WebSocket URI定義。
注意:WebSocket協定可以在代理自動設定指令碼中從模式中識別(「WS」用於未加密的連線和「wss」用於加密的連線)。
4. 如果連線無法開啟,或者因為直接連線失敗或者因為任何使用的代理返回一個錯誤,那麼使用者端必須_失敗WebSocket連線_並終止連線嘗試。
5. 如果/secure/是true,使用者端必須在連線之上執行一個TLS握手在開啟連線之後和發生握手資料之前[RFC2818]。如果這個失敗了(例如,伺服器的證書不能被驗證),那麼使用者端必須_失敗WebSocket連線_並終止連線。否則,所有在該通道山的進一步通訊必須通過加密隧道[RFC5246]。
使用者端必須在TLS握手中使用伺服器命名指示拓展[RFC6066]。
一旦一個到伺服器的連線(包括通過代理或在TLS加密隧道之上的連線),使用者端必須傳送一個開啟階段握手到伺服器。該握手包括一個HTTP Upgrade請求,連同一個必需的和可選的頭欄位列表。該握手的要求如下所示:
1. 握手必須是像[RFC2616]指定的那樣的有效的HTTP請求。
2. 請求方法必須是GET、且HTTP版本必須是至少1.1。
例如,如果WebSocket URI是"ws://example.com/chat",傳送的第一行應該是"GET /chat HTTP/1.1"。
3. 請求的"Request-URI"部分必須匹配定義在第三章的(一個相對URI)/resource name/或是一個絕對的http/https URI,當解析時,有一個/resource name/, /host/,和/port/匹配相應的ws/wss URI。
4. 請求必須包含一個|Host|頭欄位,其值包含/host/加上可選的":"後跟/port/(當沒有預設埠時)。
5. 請求必須包含一個|Upgrade|頭欄位,其值必須包含"websocket"關鍵字。
6. 請求必須包含一個|Connection|頭欄位,其值必須包含"Upgrade"標記。
7. 請求必須包含一個名字為|Sec-WebSocket-Key|的頭欄位,這個頭欄位的值必須是臨時組成的一個隨機選擇的已經base64編碼的(參見[RFC4648]第4章)16位元的值。臨時必須是為每個連線隨機選擇的。
注意:作為一個例子,如果隨機選擇的值是位元組序列0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10,頭欄位的值必須是"AQIDBAUGBwgJCgsMDQ4PEC=="。
8. 如果請求來自一個瀏覽器使用者端,請求必須包含一個名字為|Origin| [RFC6454]的頭欄位。如果連線是來自非瀏覽器使用者端,如果該使用者端的語意匹配描述在這的用於瀏覽器的使用情況時,請求可以包含這個欄位。該頭欄位的值是建立正在執行的連執行緒式碼中的環境的origin的ASCII序列化。參考[RFC6454]獲取如果構造該頭欄位的值的詳細資訊。
作為一個例子,如果從www.example.com下載的程式碼試圖建立到ww2.example.com的連線,該頭欄位的值將是"http://www.example.com"。
9. 請求必須包含一個名字為|Sec-WebSocket-Version|的頭欄位。該頭欄位的值必須是13。
注意:儘管本檔案的草案版本(-09, -10, -11,和 -12)釋出了(它們多不是編輯上的修改和澄清而不是改變電報協定),值09, 10, 11,和 12不被用作有效的Sec-WebSocket-Version。這些值被保留在IANA註冊中心,但並將不會被使用。
10. 請求可以包含一個名字為|Sec-WebSocket-Protocol|的頭欄位。如果存在,該值表示一個或者多個逗號分割的使用者端要表達的子協定,按優先順序排列,包含該值的元素必須是非空字串,且字元在U+0021 到U+007E範圍內但不包含定義在[RFC2616]中的分割字元且必須所有是唯一的字串。用於該頭欄位值的ABNF是1#token,其構造和規則定義在[RFC2616]給出。
11. 請求可以包含一個名字為|Sec-WebSocket-Extensions|的頭欄位。如果存在,該值表示使用者端想要表達的協定級的擴充套件。該頭欄位的解釋和格式描述在第9.1節。
12. 請求可以包含任意其他頭欄位,例如,cookies [RFC6265]和/或驗證相關的頭欄位,例如|Authorization頭欄位 [RFC2616],其根據定義它們的檔案處理。
一旦使用者端的開啟階段握手已經傳送,使用者端在傳送任何進一步資料之前必須等待自伺服器的一個響應。使用者端必須驗證伺服器的響應,如下所示:
1. 如果收到的伺服器的狀態碼不是101,使用者端處理每個HTTP [RFC2616]程式的響應。尤其是,如果收到一個401狀態碼使用者端可能執行身份驗證;伺服器可能使用一個3XX狀態碼重定向使用者端(但使用者端不需要跟隨他們)等等。否則,按以下步驟處理。
2. 如果響應缺少一個|Upgrade|頭欄位或|Upgrade|頭欄位包含的值不是一個不區分大小寫的ASCII匹配值"websocket",使用者端必須_失敗WebSocket連線_。
3. 如果想要缺少一個|Connection|頭欄位或者|Connection|頭欄位不包含一個不區分大小寫的ASCII匹配值"Upgrade"符號,使用者端必須_失敗WebSocket連線_。
4. 如果想要缺少一個|Sec-WebSocket-Accept|頭欄位或 |Sec-WebSocket-Accept| 包含一個不是|Sec-WebSocket-Key|(一個字串,不是base64編碼的)與字串 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"但忽略任何前導和結尾空格相關聯的base64編碼的SHA-1值,使用者端必須_失敗WebSocket連線_。
5. 如果響應包含一個|Sec-WebSocket-Extensions|頭欄位且頭欄位表示使用一個擴充套件但沒有出現在使用者端握手中(伺服器表示的一個擴充套件,不是使用者端請求的),使用者端必須_失敗WebSocket連線_。解析該頭欄位以確定請求了哪些擴充套件在9.1節討論。
6. 如果響應包含一個|Sec-WebSocket-Protocol|頭欄位且該頭欄位表示使用一個字協定但沒出現在使用者端握手中(伺服器表示的一個子協定,不是使用者端請求的),使用者端必須_失敗WebSocket連線_。
如果伺服器響應不符合定義在本節和4.2.2節中的伺服器握手的要求,使用者端必須_失敗WebSocket連線_。
請注意,根據[RFC2616],所有命名在HTTP請求和HTTP響應中的頭欄位是不區分大小寫的。
如果伺服器響應驗證了以上提供的,這是說,_WebSocket連線建立了_且WebSocket連線處於OPEN狀態。_使用中的擴充套件_被定義為一個(可能為空)字串,其值等於伺服器握手中提供的|Sec-WebSocket-Extensions|頭欄位的值或如果在伺服器握手中沒有該頭欄位則為null值。_使用中的子協定_被定義為伺服器握手中的|Sec-WebSocket-Protocol|頭欄位的值或如果在伺服器握手中沒有該頭欄位則為null值。另外,如果在伺服器握手中表示cookie應該被設定(定義在[RFC6265])的任何頭欄位,這些cookie被稱為_在伺服器開啟階段握手期間的Cookie設定。
4.2 伺服器端要求
伺服器可以下移(offload)連線管理到網路上的其他代理,例如,負載均衡和反向代理。在這樣的情況下,用於本規範的目的的伺服器被認為是包括伺服器端基礎設施的所有部分,從開始的裝置到終止TCP連線,處理請求和傳送響應的伺服器的所有方式。
例如:一個資料中心可能有一個用適當的握手來響應WebSocket請求的伺服器,並按照傳遞連線到另一個伺服器來真正處理資料框。對於本規範的目的,「伺服器」是結合了兩種計算機。
4.2.1 讀取使用者端的開啟階段握手
當用戶端開始一個WebSocket連線,它傳送它的開啟階段握手部分。伺服器必須至少解析這個握手為了獲取必要的資訊來生成伺服器握手部分。
使用者端開啟階段握手包括以下部分。如果伺服器,當讀取握手時,發現使用者端沒有傳送一個匹配下面描述的握手(注意,按照[RFC2616],頭欄位順序是不重要的),包括但不限制任何違反ABNF語法指定的握手元件,伺服器必須停止處理使用者端握手並返回一個具有一個適當錯誤碼的(例如400錯誤的請求)HTTP響應。
1. 一個HTTP/1.1或更高版本的GET請求,包括一個"Request-URI" [RFC2616]應該被解釋為定義在第3幢的/resource name/(或一個包含/resource name/的絕對HTTP/HTTPS URI)。
2. 一個|Host|頭欄位包含伺服器的許可權。
3. 一個|Upgrade|頭欄位包含值"websocket",視為一個不區分大小寫的ASCII值。
4. 一個|Connection|頭欄位包含符號"Upgrade",視為一個不區分大小寫的ASCII值。
5. 一個|Sec-WebSocket-Key|頭欄位,帶有一個base64編碼的值(參見[RFC4648]第4章),當解碼是,長度是16位元組。
6. 一個|Sec-WebSocket-Version|頭欄位,帶有值13.
7. 可選的,一個|Origin|頭欄位。該頭欄位由所有瀏覽器使用者端傳送。一個試圖缺失此頭欄位的連線不應該被解釋為來自瀏覽器使用者端。
8. 可選的,一個|Sec-WebSocket-Protocol|頭欄位,帶有表示使用者端想要表達的協定的值列表,按優先順序排列。
9. 可選的,一個|Sec-WebSocket-Extensions|頭欄位,帶有表示使用者端想要表達的擴充套件的值列表。此頭欄位的解釋在9.1節討論。
10. 可選的,其他頭欄位,例如這些用於傳送cookie或請求伺服器身份驗證的。未知的頭欄位被忽略,按照[RFC2616]。
4.2.2 傳送伺服器的開啟階段握手
當用戶端建議一個到伺服器的WebSocket連線,伺服器必須完成以下步驟來接受該連線並行送伺服器的開啟階段握手。
1. 如果連線傳送在一個HTTPS (HTTP-over-TLS)埠上,在連線之上執行一個TLS握手。如果失敗了(例如,在擴充套件的使用者端hello "server_name"擴充套件中的使用者端指示的一個主機名,伺服器對主機不可用),則關閉該連線;否則,用於該連線的所有進一步的通訊必須貫穿加密的隧道[RFC5246]。
2. 伺服器可以執行額外的使用者端身份認證,例如,返回401狀態碼與描述在[RFC2616]中的相關|WWW-Authenticate|頭欄位。
3. 伺服器可以使用3xx狀態碼[RFC2616]重定向使用者端。注意,此步驟可能連同,之前,或之後的可選的上面描述的身份驗證步驟一起發生。
4. 建立如下資訊:
/origin/
使用者端握手中的|Origin|頭欄位表示建立連線的指令碼的來源。Origin是序列化為ASCII並轉換為小寫。伺服器可以使用這個資訊作為決定是否接受傳入連線的一部分。如果伺服器沒有驗證origin,它將接受來自任何地方的連線。如果伺服器不想接受這個連線,它必須返回一個適當的HTTP錯誤碼(例如,403 Forbidden)並中斷描述在本章中的WebSocket握手。更多的詳細資訊,請參閱第10章。
/key/
在使用者端握手中的|Sec-WebSocket-Key|頭欄位包括一個base64編碼值,如果解碼,長度是16位元組。這個(編碼的)值用在建立伺服器握手時來表示接受連線。伺服器沒必要使
/version/
使用者端手中的|Sec-WebSocket-Version|頭欄位包括使用者端試圖通訊的WebSocket協定的版本。如果該版本沒有匹配伺服器理解的一個版本,伺服器必須中斷描述在本節的WebSocket握手並替代返回一個適當的HTTP錯誤碼(例如,426 Upgrade Required)且一個|Sec-WebSocket-Version|頭欄位表示伺服器能理解的版本。
/resource name/
由伺服器提供的服務的識別符號。如果伺服器提供多個服務,那麼該值應該源自使用者端手中的GET方法的"Request-URI"中給定的資源名。如果請求的伺服器不可用,伺服器必須發生一個適當的HTTP錯誤碼(例如404 NotFound)並中斷WebSocket握手。
/subprotocol/
或者一個代表伺服器準備使用的子協定的單個值或者null。選擇的值必須源自使用者端握手,從|Sec-WebSocket-Protocol|欄位具體選擇一個值,伺服器將使用它用於這個連線(如果有)。如果使用者端握手不包含這樣一個頭欄位或者如果伺服器不同意任何使用者端請求的子協定,僅接受的值為null。這個欄位不存在等價於null值(意思是如果伺服器不想同意任何建議的子協定,它必須在它的響應中不傳送回一個|Sec-WebSocket-Protocol|頭欄位)。用於這些目的,空字串與null值是不一樣的,且它不是這個欄位合法的值。用於該頭欄位的值ABNF是(符號),構造定義和規則在[RFC2616]中給出。
/extensions/
表示伺服器準備使用的協定級別擴充套件的一個列表(可能為空)。如果伺服器支援多個擴充套件,那麼該值必須源自使用者端握手,通過從|Sec-WebSocket-Extensions|欄位具體地選擇一個或多個值。這個欄位不存在等價於null值。用於這些目的,空字串與null值是不一樣的。客戶未列出的擴充套件必須不被列出。那些值應該被選擇和解釋的方法在9.1節討論。
5. 如果伺服器選擇接受傳入的連線,它必須以一個有效的表示以下的HTTP響應應答。
5.1一個按照RFC 2616 [RFC2616]帶有101響應嗎的Status-Line。這樣的響應可能看起來像"HTTP/1.1 101 Switching Protocols"。
5.2 一個按照RFC 2616 [RFC2616]帶有"websocket"的 |Upgrade|頭欄位。
5.3 一個帶有"Upgrade"的|Connection|頭欄位。
5.4 一個|Sec-WebSocket-Accept|頭欄位,該頭欄位的值通過連線/key/構造,它定義在4.2.2節第4步,帶有字串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",採用SHA-1雜湊這個連線的值來獲取一個20位元組的值並base64編碼(參考[RFC4648]第4章)這個20位元組的雜湊。
該頭欄位的ABNF [RFC2616]定義如下:
Sec-WebSocket-Accept = base64-value-non-empty
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character "==") |
(3base64-character "=")
base64-character = ALPHA | DIGIT | "+" | "/"
注意:例如,如果使用者端握手中的|Sec-WebSocket-Key|頭欄位的值是"dGhlIHNhbXBsZSBub25jZQ==",伺服器將追加字串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"為字串"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-
C5AB0DC85B11"形式。伺服器將採取SHA-1雜湊這個字串,並給出值0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea。這個值接著base64編碼,給出值"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",這將在|Sec-WebSocket-Accept|頭欄位中被返回。
5.5 可選的,一個|Sec-WebSocket-Protocol|頭欄位,帶有一個定義在4.2.2節第4不的值/subprotocol/。
5.6可選的,一個|Sec-WebSocket-Extensions|頭欄位,帶有一個定義在4.2.2節第4步的值/extensions/。如果使用多個擴充套件,那麼可以把所有都列在一個|Sec-WebSocket-Extensions|頭欄位中或者分配到|Sec-WebSocket-Extensions|頭欄位的多個範例之間。
這就完成了伺服器握手。如果伺服器完成這些步驟且沒有中斷WebSocket握手,伺服器認為WebSocket連線已建立且WebSocket連線處於OPEN狀態。此時,伺服器可以開始傳送(和接收)資料了。
4.3 為握手中使用的新的頭欄位整理的ABNF
本節中使用的ABNF語法/規範來自[RFC2616]第2.1節,包括「隱式*LWS規則」。
注意:以下ABNF約定於本節中。一些規則的名相當於相應的頭欄位名字。這樣的規則表示相應的頭欄位值,例如Sec-WebSocket-Key ABNF規則描述了 |Sec-WebSocket-Key|頭欄位值的語法。在名字中帶有"-Client"字尾的ABNF規則僅用在由使用者端到伺服器端傳送請求的情況:在名字中帶有"-Server"字尾的ABNF規則僅用在由伺服器到使用者端發生響應的情況。例如,ABNF規則Sec-WebSocket-Protocol-Client描述了使用者端到伺服器傳送的|Sec-WebSocket-Protocol|頭欄位值的語法。
以下新的頭欄位可以在從使用者端到伺服器握手期間被傳送:
Sec-WebSocket-Key = base64-value-non-empty
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Protocol-Client = 1#token
Sec-WebSocket-Version-Client = version
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character "==") |
(3base64-character "=")
base64-character = ALPHA | DIGIT | "+" | "/"
extension-list = 1#extension
extension = extension-token *( ";" extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ "=" (token | quoted-string) ]
; When using the quoted-string syntax variant, the value
; after quoted-string unescaping MUST conform to the
; 'token' ABNF.
NZDIGIT = "1" | "2" | "3" | "4" | "5" | "6" |
"7" | "8" | "9"
version = DIGIT | (NZDIGIT DIGIT) |
("1" DIGIT DIGIT) | ("2" DIGIT DIGIT)
; Limited to 0-255 range, with no leading zeros
以下新的頭欄位可以在伺服器到使用者端握手期間被傳送:
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Accept = base64-value-non-empty
Sec-WebSocket-Protocol-Server = token
Sec-WebSocket-Version-Server = 1#version
4.4 支援多個版本的WebSocket協定
本節提供了在使用者端和伺服器中支援多個版本的WebSocket協定的一些指導。
使用WebSocket版本通知能力(|Sec-WebSocket-Version|頭欄位),使用者端可以初始請求它選擇的WebSocket協定的版本(這並不一定必須是使用者端支援的最新的)。如果伺服器支援請求的版本且握手訊息是本來有效的,伺服器將接受該版本。如果伺服器不支援請求的版本,它必須以一個包含所有它將使用的版本的|Sec-WebSocket-Version|頭欄位(或多個|Sec-WebSocket-Version|頭欄位)來響應。此時,如果使用者端支援一個通知的版本,它可以使用新的版本值重做WebSocket握手。
以下範例演示了上述的版本協商:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
...
Sec-WebSocket-Version: 25
伺服器的響應可能看起來像如下:
HTTP/1.1 400 Bad Request
...
Sec-WebSocket-Version: 13, 8, 7
注意伺服器最後的響應可能也看起來像:
HTTP/1.1 400 Bad Request
...
Sec-WebSocket-Version: 13
Sec-WebSocket-Version: 8, 7
使用者端現在可以重做符合版本13的握手:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
...
Sec-WebSocket-Version: 13
5 資料框
5.1 概述
在WebSocket協定中,資料使用幀序列來傳輸。為避免混淆網路中介軟體(例如攔截代理)和出於安全原因,第10.3節進一步討論,使用者端必須掩碼它傳送到伺服器的所有幀(更多詳細資訊請參見5.3節)。(注意不管WebSocket協定是否允許在TLS之上,掩碼都要做。)當收到一個沒有掩碼的幀時,伺服器必須關閉連線。在這種情況下,伺服器可能傳送一個定義在7.4.1節的狀態碼1002(協定錯誤)的Close幀。伺服器必須不掩碼傳送到使用者端的所有幀。如果使用者端檢測到掩碼的幀,它必須關閉連線。在這種情況下,它可能使用定義在7.4.1節的狀態碼1002(協定錯誤)。(這些規則可能在未來規範中放寬。)
基本幀協定定義了帶有操作碼的幀型別、負載長度、和用於「擴充套件資料」與「應用資料」以及它們一起定義的「負載資料」的指定位置。某些位元組和操作碼保留用於未來協定的擴充套件。
一個資料框可以被使用者端或者伺服器在開啟階段握手完成之後和端點傳送Close幀之前的任何時候傳輸(5.5.1節)。
5.2 基本幀協定
用於資料傳輸部分的報文格式是通過本節中詳細描述的ABNF來描述。(注意,不像本檔案的其他章節,本節中的ABNF是在位bit組上的操作。每一個位組的長度在註釋中指出。在編碼報文時,最重要的位是在ABNF的最左邊。)下圖給出了幀的高層次概述。在下圖和本節後邊指定的ABNF之間衝突的,這個圖表時權威的。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
FIN: 1 bit
指示這個是訊息的最後片段。第一個片段可能也是最後的片段。
RSV1, RSV2, RSV3: 1 bit each
必須是0,除非一個擴充套件協商為非零值定義含義。如果收到一個非零值且沒有協商的擴充套件定義這個非零值的含義,接收端點必須_失敗WebSocket連線_。
Opcode: 4 bits
定義了一個「負載資料」的解釋。如果收到一個未知的操作碼,接收端點必須_失敗WebSocket連線_。定義了以下值:
* %x0 代表一個繼續幀
* %x0 denotes a continuation frame
* %x1 代表一個文字幀
* %x1 denotes a text frame
* %x2 代表一個二進位制幀
* %x2 denotes a binary frame
* %x3-7 保留用於未來的非控制幀
* %x3-7 are reserved for further non-control frames
* %x8 代表連線關閉
* %x8 denotes a connection close
* %x9 代表ping
* %x9 denotes a ping
* %xA 代表pong
* %xA denotes a pong
* %xB-F 保留用於未來的控制幀
* %xB-F are reserved for further control frames
Mask: 1 bit
定義是否「負載資料」是掩碼的。如果設定為1,一個掩碼鍵出現在masking-key,且這個是用於根據5.3節解掩碼(unmask)「負載資料」。從使用者端傳送到伺服器的所有幀有這個位置為1。
Payload length: 7 bits, 7+16 bits, 或者 7+64 bits
「負載資料」的長度,以位元組為單位:如果0-125,這是負載長度。如果126,之後的兩位元組解釋為一個16位元的無符號整數是負載長度。如果127,之後的8位元組解釋為一個64位元的無符號整數(最高有效位必須是0)是負載長度。多位元組長度數量以網路位元組順序來表示。注意,在所有的情況下,最小數量的位元組必須用於編碼長度,例如,一個124位元組長的字串的長度不能被編碼為序列126,0,124.負載長度是「擴充套件資料」長度+「應用資料」長度。「擴充套件資料」長度可能是零,在這種情況下,負載長度是「應用資料」長度。
Masking-key: 0 or 4 bytes
使用者端傳送到伺服器的所有幀通過一個包含在幀中的32位元值來掩碼。如果mask位設定為1,則該欄位存在,如果mask位設定為0,則該欄位缺失。詳細資訊請參見5.3節 使用者端到伺服器掩碼。
Payload data: (x+y) bytes
「負載資料」定義為「擴充套件資料」連線「應用資料」。
Extension data: x bytes
「擴充套件資料」是0位元組除非已經協商了一個擴充套件。任何擴充套件必須指定「擴充套件資料」的長度,或長度是如何計算的,以及擴充套件如何使用必須在開啟階段握手期間協商。如果存在,「擴充套件資料」包含在總負載長度中。
Application data: y bytes
任意的「應用資料」,佔用「擴充套件資料」之後幀的剩餘部分。「應用資料」的長度等於負載長度減去「擴充套件資料」長度。
基本幀協定是由以下ABNF[RFC5234]正式定義的。重要的是要注意這個資料是二進位制表示的,而不是ASCII字元。因此,一個1位長度的欄位取值為%x0 / %x1是表示為單個為,其值為0或1,不是以ASCII編碼代表字元「0」或「1」的完整位元組(8位元位組)。4位元長度的欄位值介於%x0-F之間,是通過4位元表示的,不是通過ASCI字元或者這些值的完整位元組(8位元位組)。[RFC5234]沒有指定字元編碼:「規則解析為最終值的字串,有時候被稱為字元。在ABNF中,一個字元僅僅是一個非負整數。在某些上下文中,一個值到一個字元集的特定對映(編碼)將被指定。」在這裡,指定的編碼是二進位制編碼,每一個最終值是編碼到指定數量的位元中,每個欄位是不同的。
ws-frame = frame-fin ; 1 bit in length
frame-rsv1 ; 1 bit in length
frame-rsv2 ; 1 bit in length
frame-rsv3 ; 1 bit in length
frame-opcode ; 4 bits in length
frame-masked ; 1 bit in length
frame-payload-length ; either 7, 7+16, or 7+64 bits in length
[ frame-masking-key ] ; 32 bits in length
frame-payload-data ; n*8 bits in length, where n >= 0
frame-fin = %x0 ; more frames of this message follow
/ %x1 ; final frame of this message
; 1 bit in length
frame-rsv1 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv2 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv3 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-opcode = frame-opcode-non-control /
frame-opcode-control /
frame-opcode-cont
frame-opcode-cont = %x0 ; frame continuation
frame-opcode-non-control= %x1 ; text frame
/ %x2 ; binary frame
/ %x3-7
; 4 bits in length,
; reserved for further non-control frames
frame-opcode-control = %x8 ; connection close
/ %x9 ; ping
/ %xA ; pong
/ %xB-F ; reserved for further control
; frames
; 4 bits in length
frame-masked = %x0
; frame is not masked, no frame-masking-key
/ %x1
; frame is masked, frame-masking-key present
; 1 bit in length
frame-payload-length = ( %x00-7D )
/ ( %x7E frame-payload-length-16 )
/ ( %x7F frame-payload-length-63 )
; 7, 7+16, or 7+64 bits in length,
; respectively
frame-payload-length-16 = %x0000-FFFF ; 16 bits in length
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
; 64 bits in length
frame-masking-key = 4( %x00-FF )
; present only if frame-masked is 1
; 32 bits in length
frame-payload-data = (frame-masked-extension-data
frame-masked-application-data)
; when frame-masked is 1
/ (frame-unmasked-extension-data
frame-unmasked-application-data)
; when frame-masked is 0
frame-masked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-masked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
frame-unmasked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-unmasked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
5.3 使用者端到伺服器掩碼
一個掩碼的幀必須有5.2節定義的欄位frame-masked設定為1。
掩碼鍵完全包含在幀中,5.2節定義的frame-masking-key。它用於掩碼定義在相同章節的frame-payload-data中的「負載資料」,其包含「擴充套件資料」和「應用資料」。
掩碼鍵是由使用者端隨機選擇的32位元值。當準備一個掩碼的幀時,使用者端必須從允許的32位元值集合中選擇一個新的掩碼鍵。掩碼鍵需要是不可預測的;因此,掩碼鍵必須來自一個強大的熵源,且用於給定幀的掩碼鍵必須不容易被伺服器/代理預測用於後續幀的掩碼鍵。掩碼鍵的不可預測性對防止惡意應用的作者選擇出現在報文上的位元組是必要的。RFC 4086 [RFC4086]討論了什麼需要一個用於安全敏感應用的合適熵源。
掩碼不影響「負載資料」的長度。變換掩碼資料到解掩碼資料,或反之亦然,以下演演算法被應用。相同的演演算法應用,不管轉化的方向,例如,相同的步驟即應用到掩碼資料也應用到解掩碼資料。
變換資料的八位位組i是原始資料的八位位組互斥或i取模4位元置的掩碼鍵的八位位組:
j = I MOD 4
transformed-octet-I = original-octet-I XOR masking-key-octet-j
負載長度,在幀中以frame-payload-length表示,不包括掩碼鍵的長度。它是「負載資料」的長度,例如,跟在掩碼鍵後邊的位元組數。
5.4 分片
分片的主要目的是執行當訊息開始但不必緩衝該訊息時傳送一個未知大小的訊息。如果訊息不能被分片,那麼端點將不得不緩衝整個訊息以便在首位元組發生之前統計出它的長度。對於分片,伺服器或中介軟體可以選擇一個合適大小的緩衝,當緩衝滿時,寫一個片段到網路。
第二個分片的用例是用於多路複用,一個邏輯通道上的一個大訊息獨佔輸出通道是不可取的,因此多路複用需要可以分割訊息為更小的分段來更好的共用輸出通道。(注意,多路複用擴充套件在本檔案中沒有描述。)
除非另有擴充套件規定,幀沒有語意含義。一箇中介軟體可能合併並且/或分割幀,如果使用者端和伺服器沒有協商擴充套件;或如果已協商了一些擴充套件,但中介軟體理解所有協商的擴充套件且知道如何去合併且/或分割在這些擴充套件中存在的幀。這方面的一個含義是,在沒有擴充套件的情況下,傳送者和接收者必須不依賴於特定幀邊界的存在。
以下規則應用到分片:
1、一個沒有分片的訊息由單個帶有FIN位設定(5.2節)和一個非0操作碼的幀組成。
2、一個分片的訊息是單個帶有FIN為清零(5.2節)和一個非0操作碼的幀組成,跟隨零個或多個帶有FIN為清零和操作碼設定為0的幀,且終止於一個帶有FIN為設定且0操作碼的幀。一個分片的訊息概念上是等價於單個大的訊息。其複雜是等價於按順序串聯片段的負載;然而,在存在擴充套件的情況下,這個可能不適用擴充套件定義的「擴充套件資料」存在的解釋。例如:「擴充套件資料」可能僅在首個片段開始處存在且應用到隨後的片段,或「擴充套件資料」可以存在於僅用於到特定片段的每個片段。在沒有「擴充套件資料」的情況下,以下例子展示了分片如何工作。
例子:對於一個作為三個片段傳送的文字訊息,第一個片段將有一個0x1操作碼和一個FIN位清零,第二個片段將有一個0x0操作碼和一個FIN位清零,且第三個片段將有0x0操作碼和一個FIN位設定。
3、控制幀(參見5.5節)可能被注入到一個分片訊息的中間。控制幀本身必須不被分割。
4、訊息分片必須按傳送者傳送順序交付給收件人。
5、片段中的一個訊息必須不能與片段中的另一個訊息交替,除非已協商了一個能解釋交替的擴充套件。
6、一個端點必須能處理一個分片訊息找那個間的控制幀。
7、一個傳送者可以為非控制訊息建立任何大小的片段。
8、 使用者端和伺服器必須支援接收分片和非分片的訊息。
9、由於控制幀不能被分片,一箇中介軟體必須不嘗試改變控制幀的分片。
10、如果使用了任何保留的位置且這些值的意思對中介軟體是未知的,一箇中介軟體必須不改變一個訊息的分片。
11、在一個連線的上下文中,已經協商了擴充套件且中介軟體不知道協商的擴充套件的語意,一箇中介軟體必須不改變任何訊息的分片。同樣,沒有看見WebSocket握手(且沒有被通知有關它的內容)、導致一個WebSocket連線的一箇中介軟體,必須不改變這個連線的任何訊息的分片。
12、由於這些規則,一個訊息的所有分片是相同型別的,以第一個片段的操作碼設定。因為控制幀不能被分片,用於一個訊息中的所有分片的型別必須是文字、或者二進位制、或者一個保留的操作碼。
注意:如果控制幀不能被插入,一個ping延遲,例如,如果跟著一個大訊息將是非常長的。因此,要求在分片訊息的中介軟體處理控制幀。
實現注意:在沒有任何擴充套件時,一個接收者不必按順序緩衝整個幀來處理它,例如,如果使用了一個流式API,一個幀的一部分能被交付到應用。但是,請注意這個假設可能不適用所有未來的WebSocket擴充套件。
5.5 控制幀
控制幀由操作碼確定,其中操作碼最重要的位是1.當前定義的用於控制幀的操作碼包括0x8 (Close), 0x9 (Ping),和0xA (Pong)。操作碼0xB-0xF保留用於未來尚未定義的控制幀。
控制幀用於傳達有關WebSocket的狀態。控制幀可以插入到分片訊息的中間。
所有的控制幀必須有一個125位元組的負載長度或者更少,必須不被分段。
5.5.1 關閉
關閉幀包含0x8操作碼。
關閉幀可以包含內容體(幀的「應用資料」部分)指示一個關閉的原因,例如端點關閉了、端點收到的幀太大、或端點收到的幀不符合端點期望的格式。如果有內容體,內容體的頭兩個位元組必須是2位元組的無符號整數(按網路位元組順序)代表一個在7.4節的/code/值定義的狀態碼。跟蹤2位元組的整數,內容體可以包含UTF-8編碼的/reason/值,本規範沒有定義它的解釋。資料不必是人類可讀的但可能對偵錯或傳遞開啟連線的指令碼相關的資訊是有用的。由於資料不保證人類可讀,使用者端必須不把它顯示給最終的使用者。
使用者端傳送到伺服器的關閉幀必須根據5.3節被掩碼。
在應用傳送關閉幀之後,必須不傳送任何更多的資料框。
如果一個端點接收到一個關閉幀且先前沒有傳送一個關閉幀,端點必須在響應中傳送一個關閉幀。(當在響應中發生關閉幀時,端點通常回送它接收到的狀態碼)它應該根據實際情況儘快這樣做。端點可以延遲傳送關閉幀直到它當前訊息傳送了(例如,如果一個分片訊息的大多數已經傳送了,端點可以傳送剩餘的片段在傳送一個關閉幀之前)。但是,不保證一個已經傳送關閉幀的端點將繼續處理資料。
傳送並接收一個關閉訊息後,一個端點認為WebSocket連線關閉了且必須關閉底層的TCP連線。伺服器必須立即關閉底層TCP連線,使用者端應該等待伺服器關閉連線但可能在傳送和接收一個關閉訊息之後的任何時候關閉連線,例如,如果它沒有在一個合理的時間週期內接收到伺服器的TCP關閉。
如果使用者端和伺服器同時都傳送了一條關閉訊息,兩個端點都將傳送和接收一個關閉訊息且應該認為WebSocket連線關閉了並關閉底層TCP連線。
5.5.2 Ping
Ping幀包含0x9操作碼。
Ping幀可以包含「應用資料」。
當接收到一個ping幀時,一個端點必須在響應中傳送一個Pong幀,除非它早已接收到一個關閉幀。它應該儘可能快地以Pong幀響應。Pong幀在5.5.3節討論。
一個端點可以在連線建立之後並在連線關閉之前的任何時候傳送一個Ping幀。
注意:一個Ping可以充當一個keepalive,也可以作為驗證遠端端點仍可響應的手段。
5.5.3 Pong
Pong幀包含一個0xA操作碼。
5.5.2節詳細說明了應用Ping和Pong幀的要求。
一個Pong幀在響應中傳送到一個Ping幀必須有在將恢復的Ping幀的訊息內容體中發現相同的「應用資料」。
如果端點接收到一個Ping幀且尚未在響應找那個傳送Pong幀到之前的Ping幀,端點可以選擇僅為最近處理的Ping幀傳送一個Pong幀。
一個Pong幀可以未經允許請求的傳送。這個充當單向的心跳。到未經請求的Pong幀的一個響應式不期望的。
5.6 資料框
資料框(例如,非控制幀)由操作碼最高位是0的操作碼標識。當前為資料框定義的操作碼包括0x1 (文字), 0x2 (二進位制)。 操作碼 0x3-0x7保留用於未來尚未定義的非控制幀。
資料框攜帶應用層和/或擴充套件層資料。操作碼決定了資料的解釋:
Text
「負載資料」是編碼為UTF-8的文字資料。注意,一個特定的文字幀可能包括部分UTF-8序列;不管怎麼樣,整個訊息必須包含有效的UTF-8。重新組裝的訊息中無效的UTF-8的處理描述在8.1節。
Binary
「負載資料」是隨意的二進位制資料,其解釋僅僅是在應用層。
5.7 範例
1、未掩碼檔案訊息的單個幀
* 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello")
2、掩碼的文字訊息的單個幀
* 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
(contains "Hello")
3、一個分片的未掩碼的文字訊息
* 0x01 0x03 0x48 0x65 0x6c (contains "Hel")
* 0x80 0x02 0x6c 0x6f (contains "lo")
4、未掩碼的Ping請求和掩碼的Ping響應
* 0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (包含內容體「Hello」,但內容體的內容是隨意的)
* 0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58(包含內容體「Hello」,匹配ping的內容體)
5、單個未掩碼幀中的256位元組的二進位制訊息
* 0x82 0x7E 0x0100 [256位元組的二進位制資料]
6、單個未掩碼幀中的64KB的二進位制訊息
* 0x82 0x7F 0x0000000000010000 [65536位元組的二進位制資料]
5.8 可拓展性
協定被設計為允許擴充套件,這將增加功能到基礎協定。端點的一個連線必須在開啟階段握手期間協商使用的任何擴充套件。本規範提供了用於擴充套件的操作碼0x3 到 0x7 和0xB 到 0xF、「擴充套件資料」欄位、和幀-rsv1、幀-rsv2、和幀-rsv3幀頭位。9.1節進一步討論了擴充套件協商。以下是一些預期使用的擴充套件。這個列表是不完整的也是不規範的。
1、「擴充套件資料」可以放置在「負載資料」中的「應用資料」之前。
2、保留的位可以分配給需要的每一個幀。
3、保留的操作碼值能被定義。
4、如果需要更多的操作碼值,保留的位可以分配給操作碼欄位。
5、一個保留的位或一個「擴充套件」操作碼可以定義以從「負載資料」中分配額外的位來定義更大的操作碼或更多的每位幀位。
6 傳送和接收資料
6.1 傳送資料
為了_傳送一個WebSocket訊息_,其中包括WebSocket連線之上的/data/,端點必須執行以下步驟。
1. 端點必須確保WebSocket連線處於OPEN狀態(比較4.1節和4.2.2節)。如果在任何時候WebSocket連線的狀態改變了,端點必須終止以下步驟。
2. 端點必須封裝/data/到定義在5.2節的一個WebSocket幀。如果要傳送的資料太大或如果在端點想要開始發生資料時資料作為一個整體不可用,端點可以按照5.2節的定義交替地封裝資料到一系列的幀中。
3. 第一個包含資料的幀的操作碼(幀-opcode)必須按照5.2節的定義被設定為適當的值用於接收者解釋資料是文字還是二進位制資料。
4. 包含資料的最後幀的FIN位(幀-FIN)必須按照5.2節的定義設定為1。
5. 如果資料正由使用者端傳送,幀必須按照5.3節的定義被掩碼。
6. 如果任何擴充套件(第9章)已經協商用於WebSocket連線,額外的考慮可以按照這些擴充套件定義來應用。
7. 已成型的幀必須在底層網路連線之上傳輸。
6.2 接收資料
為了接收WebSocket資料,端點監聽底層網路連線。傳入資料必須按照5.2節的定義解析為WebSocket幀。如果接收到一個控制幀(5.5節),幀必須按照5.5節的定義來處理。當接收到一個資料框時(5.6節),端點必須注意5.2節由操作碼(幀-opcode)定義的資料的/type/。這個真的「應用資料」被定義為訊息的/data/。如果幀由一個未分片的訊息組成(5.4節),這是說_已經接收到一個WebSocket訊息_,其型別為/type/且資料為/data /。如果幀時一個分片訊息的一部分,隨後資料框的「應用資料」連線在一起形成/data /.當接收到由FIN位(幀-fin)指示的最後片段時,這是說_已經接收到一個WebSocket訊息_,其資料為/data/(由連續片段的「應用資料」組成)且其型別為/type/(分配訊息的第一個幀指出)。隨後的資料框必須被解釋為屬於一個新的WebSocket訊息。
擴充套件(第9章)可以改變資料如何讀的語意,尤其包括什麼組成一個訊息的邊界。擴充套件,除了在負載中的「應用資料」之前新增「擴充套件資料」外,也可以修改「應用資料」(例如壓縮它)。
伺服器必須按照5.3節的定義為從使用者端接收到的資料框移除掩碼。
7 關閉連線
7.1 定義
7.1.1 關閉WebSocket連線
為_關閉WebSocket_連線_,端點需關閉底層TCP連線。端點應該使用一個方法完全地關閉TCP連線,以及TLS對談,如果合適,丟棄任何可能已經接收的尾隨的位元組。當必要時端點可以通過任何可用的手段關閉連線,例如當受到攻擊時。
底層TCP連線,在大多數正常情況下,應該首先被伺服器關閉,所以它持有TIME_WAIT狀態而不是使用者端(因為這會防止它在2個報文最大生存時間(2MSL)內重新開啟連線,然而當一個新的帶有更高的seq number的SYN時沒有對應的伺服器影響TIME_WAIT連線被立即重新開啟)。在異常情況下(例如在一個合理的時間量後沒有接收到伺服器的TCP Close)使用者端可以發起TCP Close。因此,當伺服器被指示_關閉WebSocekt連線_,它應該立即發起一個TCP Close,且當用戶端被指示時也這麼做時,它應該等待伺服器的一個TCP Close。
例如一個如何使用Berkeley sockets在C中得到完全地關閉的例子,一端會在socket上以SHUT_WR呼叫shutdown(),呼叫recv()直到獲得一個指示那個節點也已經執行了一個有序關閉的0返回值,且最終在socket上呼叫close()方法。
7.1.2 啟動WebSocket關閉階段握手
為了_啟動WebSocket關閉階段握手_,其帶有一個狀態碼(7.4節)/code/和一個可選的關閉原因(7.1.6節)/reason/,一個端點必須按照5.5.1節的描述傳送一個Close控制幀,其狀態碼設定為/code/且其關閉原因設定為/reason/。一旦一個端點已經傳送並接收到一個Close控制幀,哪個端點應該按照7.1.1節的描述_關閉WebSocket連線_。
7.1.3 WebSocket關閉階段握手已啟動
一旦傳送或接收到一個Close控制幀,這就是說,_WebSocket關閉階段握手已啟動_,且WebSocket連線處於CLOSING狀態。
7.1.4 WebSocket已關閉
當底層TCP連線已關閉,這就是說_WebSocket連線已關閉_且WebSocket連線處於CLOSED狀態。如果TCP連線在WebSocket關閉階段我是已經完成後被關閉,WebSocket連線被說成已經_完全地_關閉了。
如果WebSocket連線不能被建立,這就是說,_ WebSocket 連線關閉了_,但不是_完全的_。
7.1.5 WebSocket連線關閉程式碼
按照5.5.1 和 7.4節的定義,一個Close控制幀可以包含一個表示關閉原因的狀態碼。一個正關閉的WebSocket連線可以同時由兩個端點初始化。_WebSocket連線Close Code_定義為包含在由實現該協定的應用接收到的第一個Close控制幀的狀態碼(7.4節)。如果這個Close控制幀不包含狀態碼,_WebSocket連線Close Code_被認為是1005。如果_WebSocket連線已關閉_且端點沒有接收到Close狀態碼(例如可能發生在底層傳輸連線丟失時),_WebSocket連線Close Code_被認為是1006。
注意:兩個端點可以有不一致的_WebSocket連線關閉程式碼_。例如,如果遠端端點傳送了一Close幀,但本地應用還沒有從它的socket接收緩衝區中讀到包含Close幀的資料,且本地應用獨立地決定關閉連線和傳送一個Close幀,兩個端點都將傳送和接收Close幀且將不傳送更多的Close幀。每一個端點將看見另一端傳送的以_WebSocket連線關閉程式碼_結束的狀態碼。例如,在兩個端點獨立且在大致相同的時間同時_開啟WebSocket關閉階段握手_的情況下,兩個端點可以有不一致的_WebSocket連線關閉程式碼_是可能的。
7.1.6 WebSocket連線關閉原因
按照5.5.1 和 7.4節的定義,一個Close控制幀可以包含一個指示關閉原因的狀態碼,接著是UTF-8編碼的資料,上述資料留給斷點解釋且本協定沒有定義。WebSocket連線的關閉可以被任何一個端點初始化,可能同時發生。_WebSocket連線關閉原因_由跟在包含在實現該協定的應用接收到的第一個Close控制幀狀態碼(7.4節)後邊的UTF-8編碼的資料定義。如果Close控制幀中沒有這樣的資料,_WebSocket連線關閉原因_是空字串。
注意:按照7.1.5節指出的相同的邏輯,兩個端點可以有不一致的_WebSocket連線關閉原因_。
7.1.7 失敗WebSocket連線
某些演演算法和規範要求端點_失敗WebSocket 連線_。要做到這一點,使用者端必須_關閉WebSocket 連線_,並可以以適當的方式把問題報告給使用者(這將對開發人員非常有用的)。同樣的,為了做到這一點,伺服器必須_關閉WebSocket 連線_,並應該記錄下問題。
如果_已建立的WebSocket連線_在端點需要_失敗WebSocket 連線_之前,端點應該在處理_關閉WebSocket 連線_之前傳送一個帶有適當狀態碼的Close幀(7.4節)。如果端點認為另一邊不太可能接收到並處理關閉幀可以省略傳送一個關閉幀,因為錯誤的性質,導致WebSocket連線失敗擺在首要位置。端點必須在被指示為_失敗WebSocket 端點_之後不繼續嘗試處理來自遠端端點的資料(包括響應關閉幀)。
除了上邊指出的或由應用層指定的(例如,使用WebSocket API的指令碼),使用者端應該關閉連線。
7.2 異常關閉
7.2.1 使用者端發起的關閉
某些演演算法,尤其在開啟階段握手期間,需要使用者端_失敗WebSocket 連線_。為了做到這一點,使用者端必須按照7.1.7節定義的那樣_失敗WebSocket 連線_。
如果在任何時候,底層的傳輸層連線意外丟失,使用者端必須_失敗WebSocket 連線_。
除了上邊指出的或由應用層指定的(例如,使用WebSocket API的指令碼),使用者端應該關閉連線。
7.2.2 伺服器端發起的關閉
某些演演算法需要或推薦伺服器端在開啟階段握手期間_中斷WebSocket 連線_。為了做到這一點,伺服器端必須簡單地_關閉WebSocket 連線_(7.1.1節)。
7.2.3 從異常關閉中恢復
異常關閉可能由任何原因引起。這樣的關閉可能是一個瞬時錯誤導致的,在這種情況下重新連線可能導致一個好的連線和一個重新開始的正常操作。這樣的關閉也可能是一個非瞬時問題導致的,在這種情況下如果每個部署的使用者端遇到異常關閉並立即且持續地嘗試重新連線,伺服器端可能會因為大量的使用者端嘗試重新連線遇到的拒絕服務攻擊。這種情況的最終結果可能是服務不能及時恢復或恢復是更加困難。
為了避免這個,當用戶端遇到本節描述的異常關閉之後嘗試重新連線時,應該使用某種形式的補償。
第一個重新連線嘗試應該延遲一個隨機的時間量。這種隨機延遲的引數的選擇留給使用者端決定;一個可隨機選擇的值在0到5秒是一個合理的初始延遲,不過使用者端可以選擇不同的間隔由於其選擇一個延遲長度基於實現經驗和特定的應用。
第一次重新連線嘗試失敗,隨後的重新連線嘗試應該延遲遞增的時間量,使用的方法如截斷二進位制指數退避演演算法。
7.3 正常連線關閉
伺服器端在需要是可能關閉WebSocket連線。使用者端不能隨意關閉WebSocket連線。在這兩種情況下,端點通過如下過程_開始WebSocket 關閉握手_初始化一個關閉(7.1.2節)。
7.4 狀態碼
當關閉一個已經建立的連線(例如,當在開啟階段握手已經完成後傳送一個關閉幀),端點可以表明關閉的原因。由端點解釋這個原因,並且端點應該給這個原因採取動作,本規範是沒有定義的。本規範定義了一組預定義的狀態碼,並指定哪些範圍可以被擴充套件、框架和最終應用使用。狀態碼和任何相關的文字訊息是關閉幀的可選的元件。
7.4.1 定義的狀態碼
當傳送關閉幀時端點可以使用如下預定義的狀態碼。
1000:1000表示正常關閉,意思是建議的連線已經完成了。
1001:1001 表示端點「離開」,例如伺服器關閉或瀏覽器導航到其他頁面。
1002:1002 表示端點因為協定錯誤而終止連線。
1003:1003 表示端點由於它收到了不能接收的資料型別(例如,端點僅理解文字資料,但接收到了二進位制訊息)而終止連線。
1004:保留。可能在將來定義某具體的含義。
1005:1005 是一個保留值,且不能由端點在關閉控制幀中設定此狀態碼。它被指定用在期待一個用於表示沒有狀態碼是實際存在的狀態碼的應用中。
1006:1006 是一個保留值,且不能由端點在關閉控制幀中設定此狀態碼。它被指定用在期待一個用於表示連線異常關閉的狀態碼的應用中。
1007:1007 表示端點因為訊息中接收到的資料是不符合訊息型別而終止連線(比如,文字訊息中存在非UTF-8 [RFC3629]資料)。
1008:1008 表示端點因為接收到的訊息違反其策略而終止連線。這是一個當沒有其它合適狀態碼(例如1003或1009)或如果需要隱藏策略的具體細節時能被返回的通用狀態碼。
1009:1009 表示端點因接收到的訊息對它的處理來說太大而終止連線。
1010:1010 表示端點(使用者端)因為它期望伺服器協商一個或多個擴充套件,但伺服器沒有在WebSocket握手響應訊息中返回它們而終止連線。所需要的擴充套件列表應該出現在關閉幀的/reason/部分。注意,這個狀態碼不能被伺服器端使用,因為它可以使WebSocket握手失敗。
1011:1011 表示伺服器端因為遇到了一個不期望的情況使它無法滿足請求而終止連線。
1015:1015是一個保留值,且不能由端點在關閉幀中被設定為狀態碼。它被指定用在期待一個用於表示連線由於執行TLS握手失敗而關閉的狀態碼的應用中(比如,伺服器證書不能驗證)。
7.4.2 保留的狀態碼範圍
0-999:0-999 範圍內的狀態碼不被使用。
1000-2999:1000-2999 範圍內的狀態碼保留給本協定、其未來的修訂和一個永久的和現成的公共規範中指定的擴充套件的定義。
3000-3999:3000-3999 範圍內的狀態碼保留給庫、框架和應用使用。這些狀態碼直接向IANA註冊。本規範未定義這些狀態碼的解釋。
4000-4999:4000-4999 範圍內的狀態碼保留用於私有使用且因此不能被註冊。這些狀態碼可以被在WebSocket應用之間的先前的協定使用。本規範未定義這些狀態碼的解釋。
8 錯誤處理
8.1 處理UTF-8編碼資料的錯誤
當一個端點解析位元組流為UTF-8資料,但發現位元組流實際上不是一個有效的UTF-8流,那麼端點必須_失敗WebSocket連線_。這條規則則應用在開啟握手期間和隨後的資料交換期間。
9 擴充套件
WebSocket使用者端可以請求本規範的擴充套件,且WebSocket伺服器可以接受一些或所有使用者端請求的擴充套件。伺服器不必響應不是使用者端請求的任何擴充套件。如果擴充套件引數包含在使用者端和伺服器之間的協商中,這些引數必須按照引數應用到的擴充套件規範來選擇。
WebSocket clients MAY request extensions to this specification, and
9.1 協商擴充套件
使用者端通過包含一個|Sec-WebSocket- Extensions|頭欄位請求擴充套件,其按照正常的HTTP頭欄位規則(參考[RFC2616],4.2節)並且頭欄位的值按照以下ABNF定義[RFC2616]。注意本章使用的ABNF語法/規則來源於[RFC2616]包括「隱式的*LWS規範」。如果使用者端或伺服器在協商階段接收到的值不符合下邊的ABNF,這種畸形資料的接收人必須立即_失敗WebSocket連線_。
Sec-WebSocket-Extensions = extension-list
extension-list = 1#extension
extension = extension-token *( ";" extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ "=" (token | quoted-string) ]
當使用參照字串的語法變種是,參照字串之後的值必須符合token' ABNF.
注意,像其他HTTP頭欄位,這個頭欄位可以跨多個行分割或組合,因此,以下是等價的:
Sec-WebSocket-Extensions: foo
Sec-WebSocket-Extensions: bar; baz=2
完全等價於
Sec-WebSocket-Extensions: foo, bar; baz=2
所有使用的extension-token必須是一個registered token(參考11.4節)。任何給定擴充套件提供的引數必須被擴充套件定義。注意,使用者端只需要提供使用任何公佈的擴充套件,除非伺服器表示它希望使用擴充套件,否則必須使用它們。
注意:擴充套件的的順序是重要的。在多個擴充套件間的相互作用可以定義在定義擴充套件的檔案中。在沒有這樣定義的情況下,解釋是它請求中的使用者端列出的頭欄位表示一個它希望使用的頭欄位的偏好,第一個列出的選項是最優選的。伺服器在響應中列出的擴充套件表示擴充套件是實際政治用於連線的擴充套件。擴充套件應該修改資料和/或組幀,資料的操作順序應該假定是與開啟階段握手期間伺服器響應中列出的擴充套件順序是一樣的。
例如,如果有兩個擴充套件"foo" 和 "bar" ,且如果伺服器傳送的頭欄位|Sec-WebSocket-Extensions|有值"foo, bar" ,那麼資料上的操作將變為 bar(foo(data)),是更改資料本身(如壓縮)或更改可能「堆疊」的組幀。
可接受的擴充套件頭欄位(注意:為了可讀性,將摺疊較長行)的非規範例子:
Sec-WebSocket-Extensions: deflate-stream
Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
deflate-stream
Sec-WebSocket-Extensions: private-extension
伺服器通過包含一個容納了一個或多個擴充套件的使用者端請求的|Sec-WebSocket-Extensions|頭欄位來接受一個或多個擴充套件。所有擴充套件引數的解釋,和什麼構成一個有效的
9.2 已知擴充套件
擴充套件提供了一種機制來實現選擇性加入的附加協定特性。本檔案沒有定義任何擴充套件。但實現可以使用單獨定義的擴充套件。
10 安全注意事項
本章描述了一些適用於WebSocket協定的安全注意事項。具體的安全注意事項在本章的子章節描述。
10.1 非瀏覽器使用者端
WebSocket協定防止惡意的JavaScript執行在一個受信任的應用內部,比如web瀏覽器,例如,通過檢查頭欄位|Origin|(見下文)。更多細節請參考1.6節。在一個更強大的客戶短的情況下,這樣的假設不成立。
雖然該協定的目的是被web頁面中的指令碼使用,它也可以被主機直接使用。這樣的主機按照它們自己的行為行事,因此可以傳送偽造的|Origin|頭欄位,騙過伺服器。因此伺服器應該小心,假設它們是直接與來自已知源的指令碼通訊,且必須考慮到它們可能以非預期方式存取。尤其,伺服器不應該相信任何輸入是有效的。
例如:如果伺服器使用輸入作為SQL查詢的一部分,所有輸入文字在傳輸到SQL伺服器之前應該被跳脫,以免伺服器收到SQl注入攻擊。
10.2 Origin注意事項
伺服器不打算處理來自任意web頁面的輸入,但僅限於網站應該驗證|Origin|頭是一個它們盼望的源。如果源指示時伺服器不可接受的,那麼它應該以一個包含HTTP 403 Forbidden的狀態碼的回覆響應WebSocket握手。
當不受信任方通常是一個執行在受信任的使用者端上下文中的JavaScript應用的作者時,|Origin|頭欄位可以保護攻擊的情況。使用者端本身可以與伺服器聯絡,並通過|Origin|頭欄位機制,決定是否提供JavaScript應用的這些通訊許可權。目的不是為了阻止非瀏覽器建立連線,而是確保受信的瀏覽器在潛在的惡意JavaScript控制下不能偽造WebSocket握手。
10.3 攻擊基礎設施(掩碼)
除了端點是WebSockets攻擊的目標之外,web基礎設施的其他部分,如代理,也可能是攻擊的物件。在本協定正在開發時,進行了一個實驗旨在演示一類代理上的攻擊,其導致部署在野的快取代理中毒。一般的攻擊形式是在「攻擊者」的控制下建立一個到伺服器的連線,執行類似於WebSockets協定建立連線的HTTP連線上的UPGRADE,且隨後在已經UPGRADE的連線上傳送資料,看起來像一個GET請求一個特定的已知資源(在一次攻擊中,很可能會像廣泛部署的用於跟蹤點選或一個廣告服務網路資源的指令碼)。遠端伺服器響應的東西看起來像是到偽造的GET請求的一個響應,且這個響應將被一個非零百分比的部署的中介軟體快取,因此使快取中了毒了。這種攻擊的靜效應將是如果說服使用者去存取攻擊者控制的網站,攻擊者可能使使用者和其他晚於相同快取的使用者的快取中毒且在其他源執行惡意指令碼,影響網路安全模型。
為避免這種部署的中介軟體上的攻擊,前置不相容HTTP的和幀一起的應用提供的資料是不夠的,因為無法詳盡地發現和測試每一個不符合中介軟體的不跳過這樣的非HTTP幀和錯誤地假裝幀負載。因此,採用的防禦是掩碼所有從使用者端到伺服器端傳送的資料,使遠端指令碼(攻擊者)無法控制資料如何在電線上傳送,從而無法構造一個可能被中介軟體誤解釋的作為一個HTTP請求的訊息。
使用者端必須為每一幀選擇一個新的掩碼金鑰,使用一個不能被提供資料的終端應用預測。例如,每次掩碼可以從一個強加密的亂數生成器獲取。如果使用相同的金鑰或存在一個可預測的模式用於選擇下一個金鑰,當掩碼後,攻擊者可以傳送一個訊息,可能作為一個HTTP請求出現(通過交換訊息,攻擊者希望觀察線上並用下一個將被使用的掩碼金鑰掩碼它,當用戶端應用它時掩碼金鑰將有效的解碼資料)。
一旦從使用者端傳輸一個幀已經開始,幀的負載(應用提供的資料)必須不能被應用修改也是必要的。
否則,攻擊者可能傳送一個已知初始資料(如都是0)的長幀,一收到資料的第一部分後就開始計算使用的掩碼金鑰,當掩碼後,接著修改尚未傳送的作為一個請求出現的幀中的資料(這本質上是和前面段落描述的使用一個已知的或可預測的掩碼金鑰是相同的問題)。
如果需要傳送額外的資料或要傳送的資料以某種方式修改了,新的修改了的資料必須在一個新的幀中傳送,那麼需要一個新的掩碼金鑰。總之,一旦開始傳輸一個幀,對於遠端指令碼(應用)來說,內容必須不能是可修改的。
威脅模型用來保護使用者端傳送的在一個請求出現的資料。因此,需要掩碼的通道是從使用者端到伺服器的資料。伺服器到使用者端的資料可以作出看起來像一個響應,但為了完成這個請求,使用者端也必須有能力去偽造一個請求。因此,沒必要在兩個方向上掩碼資料(從使用者端到伺服器的資料沒有掩碼)。
儘管掩碼提供了保護,對於使用者端和伺服器沒有掩碼的這種型別的中毒攻擊,非相容的HTTP代理將依然是脆弱的。
10.4 實現特定限制
實現已經實現-和/或特定平臺的有關幀大小或總訊息大小的限制,從多個幀重新組裝後,必須保證它們自己不超過這些限制。(例如,一個惡意終端無論是通過單一的大幀(例如,2**60大小)還是通過傳送一個長流的分片訊息的一部分的小幀,可以設法耗盡它的對等體端點(Peer,即要攻擊的那一方)的記憶體或安裝一個拒絕服務攻擊)。這樣的實現應該對幀大小和從多個幀重組後的總訊息大小加以限制。
10.5 WebSocket使用者端驗證
本規範沒有規定任何特定的方式在WebSocket握手期間伺服器可以驗證使用者端。WebSocket伺服器可以使用任何使用者端對普通HTTP伺服器可用的驗證機制,如Cookie,HTTP驗證,或者TLS驗證。
10.6 連線的保密性和完整性
連線的保密性和完整性是通過執行在TLS (wss URIs)上的WebSocket協定提供的。WebSocket實現必須支援TLS並應該在它們的對等端點通訊時使用它。
對於使用TLS的連線,TLS提供的受益量在很大程度上取決於在TLS握手期間協商的演演算法強度。例如,一些TLS加密機制不提供連線的保密性。為了實現合理級別的包含,使用者端應該僅適用強TLS演演算法。Web安全上下文:使用者介面指南[W3C.REC-wsc-ui-20100812]討論了什麼構成強TLS演演算法。[RFC5246]的附錄 A.5 和附錄D.3中提供了額外的指導。
10.7 處理無效資料
傳入的資料必須始終由使用者端和伺服器驗證。如果,在任何時候,一個端點不理解它的資料或違反了一些端點確定的安全輸入標準,或當端點看到一個開啟階段握手沒有符合它期望的值(例如,在使用者端請求中不正確的路徑或源),端點可以終止TCP連線。如果在WebSocket握手成功後接收到了無效資料,端點應該在進行_關閉WebSocket連線_之前傳送一個帶有適當狀態碼(7.4節)的關閉幀。使用一個帶有適當狀態碼的關閉幀能幫助診斷問題。如果在WebSocket握手期間傳送了無效的資料,伺服器應該返回一個適當的HTTP[RFC2616]狀態碼。使用錯誤的編碼傳送文字資料是通常出現的一類安全問題。本協定規定一個Text資料型別(而不是Binary或其他型別)的訊息包含UTF-8編碼的資料。雖然仍指定了長度,且實現本協定的應用應該使用長度來決定幀從哪真正的結束,但以一個不當的編碼傳送資料仍可能打破建立在本協定之上的應用的假設,導致從誤解釋資料到丟失資料或潛在的安全漏洞。
10.8 使用SHA-1的WebSocket握手
本檔案中描述的WebSocket 握手不依賴於任何SHA-1安全特性,例如碰撞性或抗第二前像攻擊(如同[RFC4270]中的描述)。
11 IANA考慮
11.1 註冊新的URI模式
11.1.1 註冊"ws"模式
一個|ws| URI標識一個WebSocket伺服器和資源名稱。
URI模式名稱:ws
狀態: 永久的
URI模式語法:使用ABNF [RFC5234]語法和URI規範[RFC3986]的ABNF終結符:
"ws:" "//" authority path-abempty [ "?" query ]
<path-abempty> 和 <query> [RFC3986]元件形成的資源名發生給伺服器來確定服務期望的型別。其他元件的含義描述在[RFC3986]。
URI模式語意:這個模式的作用僅是使用WebSocket協定開啟一個連線。
編碼考慮:上邊定義的語法不包括host元件中的字元,必須按照[RFC3987]從Unicode轉換為ASCII或其替換。為了模式標準化的目的,國際化域名(IDN)形式的host元件和它們轉換的域名程式碼被認為是等價的(參考[RFC3987]5.3.3節)。
上邊定義的語法不包括其他元件中的字元,必須按照定義在URI [RFC3986]和國際化資源識別符號(IRI) [RFC3986]規範從Unicode編碼轉換為ASCII,通過首先編碼字元為UTF-8,接著使用它們百分數編碼的形式替換相應的位元組。
應用/協定使用這個URI模式命名:WebSocket協定
互操作性考慮:使用WebSocket需要使用HTTP版本1.1或更高
安全考慮:參考「安全考慮」章節
聯絡方式: HYBI WG hybi@ietf.org
作者/變更管理員: IETF iesg@ietf.org
參考資源:RFC 6455
11.1.2 註冊"wss"模式
一個|wss| URI標識一個WebSocket伺服器和資源名稱,並標明在受TLS保護的連線之上的通訊(包括標準的TLS的好吃,比如資料保密性和完整性和端點認證)。
URI模式名稱:wss
狀態:永久的
URI模式語法:使用ABNF [RFC5234]語法和URI規範[RFC3986]的ABNF終結符:
"wss:" "//" authority path-abempty [ "?" query ]
<path-abempty> 和 <query>[RFC3986]元件形成的資源名發生給伺服器來確定服務期望的型別。其他元件的含義描述在[RFC3986]。
URI模式語意:這個模式的作用僅是使用WebSocket協定開啟一個使用TLS的連線。
編碼考慮:上邊定義的語法不包括host元件中的字元,必須按照[RFC3987]從Unicode轉換為ASCII或其替換。為了模式標準化的目的,國際化域名(IDN)形式的hsot元件和它們轉換的域名程式碼被認為是等價的(參考[RFC3987]5.3.3節)。
上邊定義的語法不包括其他元件中的字元,必須按照定義在URI [RFC3986]和國際化資源識別符號(IRI)[RFC3987]規範從Unicode編碼轉換為ASCII,通過首先編碼字元為UTF-8,接著使用它們百分數編碼的形式替換相應的位元組。
應用/協定使用這個URI模式命名:WebSocket協定
互操作性考慮:使用WebSocket需要使用HTTP版本1.1或更高。
安全考慮:參考「安全考慮」章節
聯絡方式: HYBI WG <hybi@ietf.org>
作者/變更管理員: IETF <iesg@ietf.org>
參考資源: RFC 6455
11.2 註冊「WebSocket」HTTPUpgrade關鍵字
本節按照RFC 2817 [RFC2817]定義了在HTTP Upgrade符號註冊中心註冊一個關鍵字。
符號名稱:WebSocket
作者/變更管理員:IETF <iesg@ietf.org>
聯絡方式:HYBI <hybi@ietf.org>
參考資源: RFC 6455
11.3 註冊新的HTTP頭欄位
11.3.1 Sec-WebSocket-Key
本節描述了在永久訊息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Key
使用協定:http
狀態:標準
作者/變更管理員:IETF
參考資源:RFC 6455
相關資訊:該頭欄位僅用於WebSocket開啟階段握手。
|Sec-WebSocket-Key|頭欄位用於WebSocket開啟階段握手。它從使用者端傳送到伺服器,提供部分資訊用於伺服器檢驗它收到了一個有效的WebSocket握手。這有助於確保伺服器不接收正被濫用來傳送資料給毫不知情的WebSocket伺服器的非WebSocket使用者端的連線(例如HTTP使用者端)。
|Sec-WebSocket-Key|頭欄位在一個HTTP請求中不能出現多於一個。
11.3.2 Sec-WebSocket-Extensions
本節描述了在永久訊息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Extensions
使用協定:http
狀態:標準
作者/變更管理員:IETF
參考資源:RFC 6455
相關資訊:該頭欄位僅用於WebSocket開啟階段握手。
|Sec-WebSocket-Extensions|頭欄位用於WebSocket開啟階段握手。它最初是從使用者端傳送到伺服器,隨後從伺服器端傳送到使用者端,用來達成在整個連線階段的一組協定級擴充套件。
|Sec-WebSocket-Extensions|頭欄位在HTTP請求中可以出現多次(邏輯上等價於單個|Sec-WebSocket-Extensions|頭欄位包含的所有值)。但是,|Sec-WebSocket-Extensions|頭欄位在一個HTTP響應中必須不出現多於一次。
11.3.3 Sec-WebSocket-Accept
本節描述了在永久訊息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Accept
使用協定:http
狀態:標準
作者/變更管理員:IETF
規範檔案: RFC 6455
相關資訊:該頭欄位僅用於WebSocket開啟階段握手。
|Sec-WebSocket-Accept|頭欄位用於WebSocket開啟階段握手。它從伺服器傳送到使用者端來確定伺服器願意啟動WebSocket連線。
|Sec-WebSocket-Accept|頭在一個HTTP響應中必須不出現多於一次。
11.3.4 Sec-WebSocket-Protocol
本節描述了在永久訊息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Protocol
使用協定:http
狀態:標準
作者/變更管理員:IETF
規範檔案:RFC 6455
相關資訊:該頭欄位僅用於WebSocket開啟階段握手。
|Sec-WebSocket-Protocol|頭欄位用於WebSocket開啟階段握手。它從使用者端傳送到伺服器端,並從伺服器端發回到使用者端來確定連線的子協定。這使指令碼可以選擇一個子協定和確定伺服器同一服務子協定。
|Sec-WebSocket-Protocol|頭欄位在一個HTTP請求中可以出現多次(邏輯上等價於|Sec-WebSocket-Protocol|頭欄位包含的所有值)。但是,|Sec-WebSocket-Protocol|頭欄位在一個HTTP響應必須不出現多於一次。
11.3.5. Sec-WebSocket-Version
本節描述了在永久訊息頭欄位命名註冊中心[RFC3864]中註冊一個頭欄位。
頭欄位名:Sec-WebSocket-Version
使用協定:http
狀態:標準
作者/變更管理員:IETF
規範檔案:RFC 6455
相關資訊:該頭欄位僅用於WebSocket開啟階段握手。
|Sec-WebSocket-Version|頭欄位用於WebSocket開啟階段握手。它從使用者端傳送到伺服器端來指定連線的協定版本。這能使伺服器正確解釋開啟階段握手和傳送資料的隨後資料。如果伺服器不能以安全的方式解釋資料則關閉連線。當從使用者端接收到不匹配伺服器端理解的版本是,WebSocket握手錯誤,|Sec-WebSocket-Version|頭欄位也從伺服器端傳送到使用者端。在這種情況下,頭欄位包括伺服器端支援的協定版本。
注意:如果沒有期望更高版本號,必然是向下相容低版本號。
|Sec-WebSocket-Version|頭欄位在一個HTTP響應中可以出現多次(邏輯行等價於單個|Sec-WebSocket-Version|透過自動包含的所有值)。但是,|Sec-WebSocket-Version|頭欄位在HTTP請求中必須不出現多於一次。
11.4 WebSocket擴充套件名註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,建立一個新的IANA註冊用於與WebSocket協定一起使用的WebSocket擴充套件名。
作為本註冊的一部分,IANA維護以下資訊:
擴充套件識別符號:擴充套件識別符號,將被用在註冊到本規範11.3.2節的|Sec-WebSocket-Extensions|頭欄位。其值必須符合定義在本規範9.1節的擴充套件-符號要求。
擴充套件通用名稱:拓展名稱,通常稱為擴充套件。
拓展定義:在擴充套件用於的WebSocket協定中定義了檔案參考。
已知的不相容擴充套件:與此擴充套件是不相容的一個擴充套件識別符號列表。
WebSocket擴充套件名受制於「先來先服務」的IANA註冊策略[RFC5226]。
在此註冊中心沒有初始值。
11.5 WebSocket子協定名註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,建立了一個新的IANA註冊用於與WebSocket協定一曲使用的WebSocket子協定名。
作為本註冊的一部分,IANA維護以下資訊:
子協定識別符號:子協定識別符號,將被用在註冊到本規範11.3.4節的 |Sec-WebSocket-Protocol|頭欄位。其值必須符合定義在本規範4.1節給出的第10條的符號要求--也就是,其值必須是RFC 2616 [RFC2616]定義的一個符號。
子協定通用名稱:子協定名稱,通常稱為子協定
子協定定義:在子協定用於定義了WebSocket協定的檔案參考
WebSocket子協定名受制於「先來先服務」的IANA註冊策略[RFC5226]。
11.6 WebSocket Version Number Registry WebSocket版本號註冊
本規範依據RFC 5226 [RFC5226]陳述的原則,建立了一個新的IANA註冊用於與WebSocket協定一起使用的WebSocket版本號。
作為註冊的一部分,IANA維護以下資訊:
版本號: 用於|Sec-WebSocket-Version|的版本號指定在本規範4.1節。其值必須是一個在0到255(包括)之間的非負整數。
參考:RFC請求一個新的版本號或帶版本號的草案名稱(見下文)。
狀態:「臨時的」或「標準的」。參考下面的說明。
一個版本號被臨時指定為「臨時的」或「標準的」。
「標準的」版本號是記錄在一個RFC中並用來識別一個主要的、穩定的WebSocket協定版本,例如本RFC定義的版本。「標準的」版本號受制於「IETF評審「IANA註冊策略。[RFC5226].
"Interim"的版本號記錄在一個Internet草案中用並用於幫助實現這識別和與部署的WebSocket版本互操作,例如在公佈這個RFC之前指定的版本。「臨時的」版本號受制於「專家評審」IANA註冊策略[RFC5226],HYBI工作組主席(或,如果工作組關閉了,IETF應用區域的區域董事)將是初始的指定專家。
IANA已經新增如下的初始值到註冊中心:
+--------+-----------------------------------------+----------+ |Version | Reference | Status | | Number | | | +--------+-----------------------------------------+----------+ | 0 + draft-ietf-hybi-thewebsocketprotocol-00 | Interim | +--------+-----------------------------------------+----------+ | 1 + draft-ietf-hybi-thewebsocketprotocol-01 | Interim | +--------+-----------------------------------------+----------+ | 2 + draft-ietf-hybi-thewebsocketprotocol-02 | Interim | +--------+-----------------------------------------+----------+ | 3 + draft-ietf-hybi-thewebsocketprotocol-03 | Interim | +--------+-----------------------------------------+----------+ | 4 + draft-ietf-hybi-thewebsocketprotocol-04 | Interim | +--------+-----------------------------------------+----------+ | 5 + draft-ietf-hybi-thewebsocketprotocol-05 | Interim | +--------+-----------------------------------------+----------+ | 6 + draft-ietf-hybi-thewebsocketprotocol-06 | Interim | +--------+-----------------------------------------+----------+ | 7 + draft-ietf-hybi-thewebsocketprotocol-07 | Interim | +--------+-----------------------------------------+----------+ | 8 + draft-ietf-hybi-thewebsocketprotocol-08 | Interim | +--------+-----------------------------------------+----------+ | 9 + Reserved | | +--------+-----------------------------------------+----------+ | 10 + Reserved | | +--------+-----------------------------------------+----------+ | 11 + Reserved | | +--------+-----------------------------------------+----------+ | 12 + Reserved | | +--------+-----------------------------------------+----------+ | 13 + RFC 6455 | Standard | +--------+-----------------------------------------+----------+
11.7. WebSocket關閉程式碼註冊
本規範依據RFC 5226 [RFC5226]陳述的原則,建立了一個新的IANA註冊用於WebSocket關閉程式碼。
作為本註冊的一部分,IANA維護以下資訊:
狀態碼:狀態碼錶示一個按照本檔案7.4節的WebSocket連線關閉的原因。狀態時一個在1000到4999(包括)之間的一個整數數位。
含義:狀態碼的含義。每一個狀態碼都必須有唯一的含義。
聯絡方式:保留狀態程式碼實體的聯絡方式。
參考:穩定的檔案要求狀態碼並定義它們的含義。在1000-2999範圍內的狀態碼是必須的且推薦的狀態碼在3000-3999範圍內。
WebSocket關閉程式碼根據它們的範圍受不同的註冊要求。本協定請求使用的狀態碼和其後續版本或拓展受制於「標準功能」、「規定要求」(這意味著「指定專家」)或「IESG審查」IANA註冊策略中的任何一個,且應該允許在1000-2999範圍內。庫、框架和應用請求使用的狀態碼受制於「先來先服務」IANA註冊策略且應該允許在3000-3999範圍內。4000-4999範圍的狀態碼被指定用於私有使用。請求應該指出他們要求的狀態碼是用於WebSocket協定(或未來版本的協定)、擴充套件,或庫/框架/應用。
IANA已經新增如下初始值到註冊中心:
|Status Code | Meaning | Contact | Reference | -+------------+-----------------+---------------+-----------| | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1001 | Going Away | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1002 | Protocol error | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 | | | payload data | | | -+------------+-----------------+---------------+-----------| | 1008 | Policy Violation| hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------| | 1011 | Internal Server | hybi@ietf.org | RFC 6455 | | | Error | | | -+------------+-----------------+---------------+-----------| | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 | -+------------+-----------------+---------------+-----------|
11.8 WebSocket操作碼註冊
本規範根據RFC 5226 [RFC5226]陳述的原則,建立一個新的IANA註冊用於WebSocket操作碼。
作為本註冊的一部分,IANA維護以下資訊:
操作碼:操作碼錶示WebSocket幀的幀型別,定義在5.2節。操作碼是一個0到15(包括)之間的數位。
含義:狀態碼值的含義。
參考:規範要求的操作碼。
WebSocket狀態碼受制於「標準功能」IANA註冊策略[RFC5226]。
IANA已經新增瞭如下的初始值到註冊中心:
|Opcode | Meaning | Reference | -+--------+-------------------------------------+-----------| | 0 | Continuation Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 1 | Text Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 2 | Binary Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 8 | Connection Close Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 9 | Ping Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 10 | Pong Frame | RFC 6455 | -+--------+-------------------------------------+-----------|
11.9 WebSocket幀頭位註冊
本規範依據RFC 5226 [RFC5226] 陳述的原則,建立了一個新的IANA註冊用於WebSocket 幀頭位。此註冊控制的位分別標記為5.2節的RSV1,RSV2和 RSV3。
這些位被保留用於未來版本或本規範的擴充套件。
WebSocket幀頭位分配受制於「標準功能」IANA註冊策略[RFC5226]。
12 其他規範使用WebSocket協定
WebSocket協定的目的是被另一個規範使用來提供一個通用機制來動態作者-定義內容,例如,在一個規範中定義一個指令碼API。
這樣的規範首先需要_建議一個WebSocket連線_,該演演算法是:
1、目的地,包含一個/host/和一個 /port/。
2、一個 /resource name/,執行在一個host和port標識多個服務。
3、一個 /secure/標記,如果連線時加密的則為true,否則為false。
4、一個源[RFC6454]的ASCII序列化,負責連線。
5、可選的,一個字串標識一個協定,層疊在WebSocket連線之上。
/host/、 /port/、 /resource name/和 /secure/標記通常從一個URI中使用該步驟解析一個WebSocket URI元件獲得。如果沒有指定一個WebSocket,則這些步驟失敗。
如果在任何時候連線將被關閉,那麼規範需要使用_關閉WebSocket 連線_演演算法(7.1.1節)。
7.1.4節定義了什麼時候_ WebSocket 連線關閉_。
當開啟一個連線,規範將需要處理什麼時候_已經接收了一個WebSocket 訊息_的情況(6.2節)。
要傳送一些資料/data/到一個開啟的連線,規範需要_傳送一個WebSocket 訊息_(6.1節)
13 致謝
略
14參考資料
略
The WebSocket Protocol [RFC6455],張開濤[譯],部分內容有修改。
相關文章