WebSocket protocol整理

WebSocket作為在HTTP下一個很重要的雙向通信延伸的protocol,HTTP 1.0/1.1的設計是half-duplex,也就是同一個時間只會有單向傳輸(request->response),雖然HTTP 1.1有支援http pipelining,但本質上他還是單向傳輸的的架構(第二個request不用等到第一個response收到就可以發出,但是response order還是根據request),並且這項功能在大部分的client/server實作不完整

WebSocket出現於2009年左右(並於Chrome 4開始實作支援),在此之前要在Web上透過HTTP protocol進行雙向通信,需要用一些技巧模擬出來

因為HTTP 通信是half-duplex,從web browser client的角度,browser可以知道什麼時候要send資料,隨時可以由browser發起http request,而recv則是由server傳來的資料,但是在http request-response的架構下,因為不知道server什麼時候有資料,最簡單的做法就是要定期去polling,這個在早期的聊天室系統都是這樣處理,每隔幾秒鐘就發一次http request更新最新的內容,但是這樣做有個缺點 – 不即時

即時性的問題在後來發展出hidden iframe以及long polling兩種做法,以兩種做法解決問題主要是因為browser的限制(browser只支援單純的http request-response),雖然在HTTP協定中其實有定義可以做雙向通信的,像是HTTP/1.1 CONNECT(主要用在tunnel proxy情境),或是HTTP/1.1 Upgrade header,但是tunnel或是upgrade完的通信方式要由app決定。

hidden iframe的做法算是巧妙地利用browser load javascript的行為,他透過inline iframe建立起一個隱藏的iframe,在iframe裡面load一個特別的網頁,那個網頁會一直傳<script>,將要server通知的內容即時透過<script>結合javascript,在裡面嵌code和data,因為browser收到script會立即執行,並且按順序執行,透過巧妙地安排script內容,將訊息傳出來給parent page,因為網頁還沒load完,連線就會一直持續

long polling則是透過http XMLHttpRequest方式,連上server後,在沒有新的event data情況下,server就掛起連線,等有資料再response,當client收完資料後連線結束,再馬上發起新的long polling request,這個好處是可以即時的收到event data,缺點是XMLHttpRequest browser有一些限制(例如cross origin等問題),以及每次HTTP request的header overhead。

以上兩種方式都是因為既有的HTTP限制下產生的做法,HTTP的連線甚至在browser還有其他的限制,像是同一個時間對同一個site最多有幾條連線(concurrent connections)等,如果掛太多long polling連線,會占住正常的使用

WebSocket的出現讓Web環境下雙向通信變得容易,也不再需要用特別的方式處理了,整體來說,WebSocket的設計是基於既有HTTP的架構下,並且考慮到一些既有HTTP component的相容性(如proxy)來設計,並且在某種程度上考量到舊有component的相容性(如proxy),以及加上framing的機制,使得可以在TCP streaming的傳輸上以不限制大小的message方式來進行,另外也有限度的考慮了一些安全性(特別是針對XMLHttpRequest)

WebSocket的規格歷經多代演進,最後在RFC 6455標準化

https://en.wikipedia.org/wiki/WebSocket

在使用library時,我們可以看到所support的版本

例如: https://github.com/zaphoyd/websocketpp

  • Full support for RFC6455
  • Partial support for Hixie 76 / Hybi 00, 07-17 draft specs (server only)

在server的實作常會見到同時支援hixie, hybi第幾版之類(hixie這邊的命名是與原始起草規範Ian Hickson有關,hybi的bi應是指bidirection。另可參考: https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 )的,主要的原因就是瀏覽器作為client連線時,因為browser的版本不同,支援的websocket版本也不同,所以如果考量到不確定target使用者瀏覽器版本時,常需要對相容性做處理,相對來說,如果websocket僅是作為app之間的通信或是client版本可控時,server只要實作RFC就足夠了。

以下整理一下RFC 6455中提到的資訊

protocol分成兩部分

  • handshake (open handshake、close handshake)
  • data transfer

Open Handshake (主要整理Section 1.3)

open handshake基本上就是透過http request – response的方式在header內傳遞相關資訊,以下從RFC節錄

The handshake from the client looks as follows:

        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

The handshake from the server looks as follows:

        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat

實測的範例,client side使用nodejs ws,server side使用websocketpp

GET / HTTP/1.1
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: vDJK2hgGUHeVDSXRDYeAEw==
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Host: 127.0.0.1:80

HTTP/1.1 101 Switching Protocols
Connection: upgrade
Sec-WebSocket-Accept: tjloVFK8w9BH5RxPicVb03W3Ams=
Server: WebSocket++/0.7.0
Upgrade: websocket

在request、response header中沒有Content-Length的欄位,在RFC 7230 3.3.2有說明

A server MUST NOT send a Content-Length header field in any response with a status code of 1xx (Informational) or 204 (No Content).

對於client則是因為GET本身不帶body,也是method不預期有body,所以不需要帶Content-Length欄位

A user agent SHOULD NOT send a Content-Length header field when the request message does not contain a payload body and the method semantics do not anticipate such a body.

附帶一提的是原來HTTP常見的應用如Cookie、Authorization等欄位也可使用,使得既有HTTP框架下的驗證機制可以reuse, 除了一般HTTP request既有的header外,WebSocket 另外定義了Sec-WebSocket-Protocol、Sec-WebSocket-Extensions作為protocol的擴展

Sec-WebSocket-Protocol是subprotocol selector,client如果提供支援的subprotocol(可能多筆),而server就必須回應一個支援的subprotol,類似的設計在SSL handshake也可以看到,ClientHello會送出多組支援的cipher suite,server只會回應一組選擇的cipher suite

subprotocol指的是application-level protocols layered over the WebSocket Protocol,也就是在WebSocket protocol架構下的application protocol,可以想成WebSocket提供了基礎的通信收發,而應用端具體地定義message的內容與回應方式,相關資訊可以參考IANA的列表

https://www.iana.org/assignments/websocket/websocket.xhtml#subprotocol-name

這邊可以看到message broker常見的protocol如amqp、mqtt都有各自定義websocket subprotocol

Sec-WebSocket-Extensions 則是定義在protocol level的extension,如如permessage-deflate可參考 https://tools.ietf.org/html/rfc7692 Compression Extensions for WebSocket

上面handshake中有兩個欄位: Sec-WebSocket-Key 和Sec-WebSocket-Accept是一串亂碼,這是base64 encoding,內容並不重要, 這邊的 Sec-WebSocket-Key 跟security或是key沒什麼關係(Sec-名稱的原因後面詳述), 他主要的目的是讓client確定server是一個WebSocket server,計算方式如下

Sec-WebSocket-Accept = base64(sha1( Sec-WebSocket-Key + “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”))

以上面的例子就是 把 dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11 這串字做sha1然後將hex octect做base64

從上面可以看到這邊的計算完全是公開的,單純只是讓client確認server看得懂 Sec-WebSocket-Key 並且回應正確的 Sec-WebSocket-Accept,是一個WebSocket server,事實上這步應該是多餘的,但如果從防止XMLHttpRequest request模擬WebSocket client連接WebSocket Server的角度來看,又有其用處。因為browser會阻擋 javascript設置Sec-WebSocket-Key, Sec-開頭的是forbidden name之一, 這也是為什麼欄位名稱是Sec-開頭,在瀏覽器中不可被programmatically修改設置,可參考 https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name

另外client side random Sec-WebSocket-Key 也可以避開老舊的proxy cache機制

Sec-WebSocket-Version在RFC版本中 = 13

在handshake中,成功的handshake,server一定是回傳status code 101(switch protocol),如果是其他的status code就按照原有的意義去解讀,譬如說404 not found, 403 forbidden, 302 redirect等,如果不是101,client不應進行後續的websocket傳輸

server要回傳的欄位還包括

  • Upgrade: websocket
  • Connection: Upgrade
  • Sec-WebSocket-Accept:

而其他如Set-Cookie,在RFC中也允許server handshake response回傳。

Closing Handshake(主要整理Section 1.4)

當要連線結束時,一方先送出close control frame,另一方收到close要求時,也送出close frame,在RFC中同時也關注simultaneous close的處理,也就是雙方都同時送出close,基本的原則就是

  • 送出close之後不再送任何data,並且等待收到close,再斷線
  • 收到close之後不再收任何data,並且如果收到close時還沒回應close,要送出close,再斷線

事實上要斷線應該可以靠TCP的close handshake(4-way handshaking)就好,在RFC提到需要在WebSocket層加上close的handshake主要是因為可能在整體架構上有proxy或是其他intermediaries,沒有完整處理好TCP斷線,透過在application layer做end-to-end的close handshake可以避開這個問題(當然這個前提是TCP連線正常時),或是說可以很明確的讓雙方知道意圖,主動將連線中斷送出close之後不再送任何data,並且等待收到close,再斷線

Data transfer 主要整理 Section 5 與 Section 6

WebSocket介面是常以message為單位,而protocol傳輸是以frame為單位,這裡的frame跟網路transport如tcp或是更底層的ethernet無關,純粹是application層的架構上的定義,並且有些通用的概念也不同,雖然這邊說以frame為單位,但是他不像switch一樣,把ethernet frame完整的接收才store and forward,在RFC 6455 page 35有提到,可以在frame還沒接收完時,邊收邊處理。

RFC 6455總共定義了6種frame type(在Section 5: binary、text、continuation、close、ping、pong),而message由frame組成,在application 層只需要關注 兩種message type: text(UTF8)、binary。

當handshake完成後,就進入data transfer的狀態,這時候雙方可以各自傳送資料, frame在WebSocket算是滿重要的部分,他可以將大的message切割成很多小的chunk傳輸,使得在protocol設計上可以透過multiplex機制來支援同時傳輸兩個大的message成為可能,並且在protocol設計可以做更彈性的控制,例如在frame加入checksum,當發生錯誤時,可以做recover或要求重傳或斷線,如果checksum是做在message level,會導致要等到整個message傳完後,才知道是否有錯。上面提的是framing的重要性,在WebSocket中他的frame沒有檢查正確性的機制,依賴的是底層TCP的checksum或是在AP層設計的檢查機制。在RFC 6455也沒有定義multiplex的機制,而是保留在extension去擴展。 (可參考 Section 5.4)

另外需要注意的是,在RFC並沒有定義frame的大小或是切割message的規則,這部分是由傳輸的各方自行處理,像是chromium是以131KB來切割(參考: https://bugs.chromium.org/p/chromium/issues/detail?id=517090)

FIN: 代表此frame是message的最後一筆frame (frame-fin)

RSV1 RSV2 RSV3是保留flag

opcode 是 frame type,共4個bit 可代表16種不同的frame type,RFC定義了6種,分別是

  • continuation frame(0x0)
  • text frame(0x1)
  • binary frame(0x2)
  • connection close(0x8) – control frame
  • ping(0x9) – control frame
  • pong(0xA) – control frame

MASK 是傳輸的payload內容需不需要做mask,在RFC中,client->server要mask,server->client不用mask,mask是透過Masking key進行xor運算

Payload length: Extension data + Application data的長度。編碼採用為可變長度的encoding、network byte order,7 bits, 7+16 bits, or 7+64 bits。如果payload length是0-125就只用7bit,如果payload length 是126 – 65535就將第一個7bits指定為126,接下來的16bits指定為對應的值,如果payload length是65536 – (2^64 – 1),就將第一個7bits指定為127,接下來的64bits指定為對應的值。另外規範要求值的表示法唯一,這邊可以看到frame定義的payload length可以大到2^64-1(16EB-1),所以理論上任何大小的message都可以塞在一個data frame內,只是缺點就如前面所提的:

  • protocol設計上會少了很多彈性
  • 在實作上,在有限的buffer size,過大的message無法一次放進buffer中
  • payload length需要是一個已知的payload大小

Masking-key (0 or 4 bytes),client到server的傳輸才會帶masking key

從上面可以看到,data frame並沒有指定原始message的大小,在RFC規範中,甚至可以在一開始message不知道大小的情況進行傳輸,只要fragment payload length知道即可,在fragmented messages裡面描述的data frame行為是

  • 第一個data frame會有op code (例如 text /binary) + FIN=0
  • 後續的data frame會是 op code = 0(continuation) + FIN=0
  • 最後一個data frame是 op code = 0 + FIN =1

這種設計需要資料流是連續且按照順序的(沒有serial number),並且很明顯是無法支援multiplex的,除非另外紀錄message sequence number之類的資訊。當然,額外的資訊可透過extension來定義,extension提供了方式可以解讀payload中 extension data的意義並做對應的處理,5.4也有明確提到:

The fragments of one message MUST NOT be interleaved between the fragments of another message unless an extension has been negotiated that can interpret the interleaving.

雖然前面提到fragmented message的data frame是連續的,但在RFC有定義例外,就是control frame可以插隊在fragmented message中間,當然control frame本身不能再切割,這邊主要的設計應該是為了讓像close handshake可以立即被處理,或是keep alive的機制可以立即被回應,所以在RFC中也作了對應的要求

An endpoint MUST be capable of handling control frames in the middle of a fragmented message.

control frame共有 close、ping、pong,並且規範中要求 control frame的payload length <= 125

close frame: 可以帶payload body [2 byte status code – uint16 + [reason – utf8]],這邊的payload也要符合mask rule,如果沒有帶status code,則對於API層來說,close status code = 1005,如果沒有帶reason,則對於API層來說 ,reason為empty string

規範也提到close frame response的原則: 收到close frame時,如果先前還沒有送出close frame,才要送出close frame,並且建議回覆相同的close code

When sending a Close frame in response, the endpoint typically echos the status code it received.

當送出close並且收到close後(或是收到close回覆close後),在WebSocket protocol層就算close了,但是底層的TCP連線,在規範中也定義的建議作法

  • server立即斷線(此時在TCP層會送出 FIN給client)
  • client等到收到斷線(recv() = 0, FIN packet),或是自訂一個timeout值後斷線

ping frame、pong frame: 作為keep alive的機制,或是確認對方是否responsive,可帶application data,ping、pong的application data要相同(echo ping),pong可以只選擇最近一筆的ping回應

data frame 包括 text frame、binary frame

text frame: payload UTF8 text,在8.1. Handling Errors in UTF-8-Encoded Data提到,如果收到的text不是utf8要做Fail the WebSocket Connection (fail the websocket connection要做的事可參考7.1.7)

binary frame: 內容是application自行解釋

接收端收到data frame,將payload的application data連接組成為一個message,直到frame-fin,frame-fin後面的data frame就是新的message

Extension 主要整理5.8

在frame的欄位中,op code = 0x3-0x7, 0xB-0xF保留未用,以及RSV1, RSV2, RSV3的flag,payload的Extension data區域都提供了protocol擴展的可能性,在RFC中,上述的保留op code、flags、或是以payload的內容都可拿來進行擴展,

目前有廣泛使用的extension只有 permessage-deflate ,相關資訊也可看IANA的registry https://www.iana.org/assignments/websocket/websocket.xhtml#extension-name

在nodejs ws 套件中的 permessage-deflate extension,server side預設是關起來的 (https://github.com/websockets/ws 參考WebSocket compression一節)

上面的紅框處是從chrome截下來的,extension list是以 逗號 , 分隔,分號; 後面是extension的參數,例如 client_max_window_bits是parameter,可參考 rfc7692 7.1.2.2 (https://tools.ietf.org/html/rfc7692#section-7.1.2.2)

另外是當指定多個extension時處理的方式,在RFC 6455 9.1 p.49也有說明,有先後的處理順序

if there are two extensions “foo” and “bar” and if the header field |Sec-WebSocket-Extensions| sent by the server has the value “foo, bar“, then operations on the data will be made as bar(foo(data)), be those changes to the data itself (such as compression) or changes to the framing that may “stack”.

Closing The Connection 主要整理Section 7

在RFC裡,明確的描述一些操作的細節,特別是closing the connection這一個Section,因為如handshake或是傳輸中有一些不預期的內容時,必須作錯誤處理,最常見的方式就是關閉連線,在RFC中,client主動關閉連線只有兩種情形會發生,1.錯誤處理 2. application api主動呼叫close (參考p44 7.2.1)

錯誤處理的關閉連線又有分直接斷線或是經過Closing Handshake的方式,Section 7定義了一些操作或狀態,以下做一些整理

Start the WebSocket Closing Handshake

送出close control frame,當送出close frame也收到close frame,就進行Close the WebSocket Connection

The WebSocket Closing Handshake is Started

當收到或是送出close frame,就進入The WebSocket Closing Handshake is Started的狀態,此時WebSocket connection是CLOSING狀態

The WebSocket Connection is Closed

當TCP連線關閉時,就是 The WebSocket Connection is Closed的狀態,WebSocket connection是CLOSED狀態,如果TCP連線關閉時,已經完成closing handshake,則是close cleanly,如果TCP連線關閉時沒有收到close frame,則傳給API層的close status code = 1006

Close the WebSocket Connection

當提到 Close the WebSocket Connection 是指close TCP connection,在TCP連線以建立下,對於server side是指 立即關掉,對於client side是指等待server close在關閉

Fail the WebSocket Connection

Fail the WebSocket Connection 大部分定義在open handshake操作,並且是client的行為(因為server如果再open handshake發生問題,可以透過http status code回應),而當text不是UTF8時,也會進行此操作,如果WebSocket connection還沒有established,就進行Close the WebSocket Connection,如果已經established,就送出close frame + Close the WebSocket Connection(不處理後續的frame,包括接收close frame)。這裡RFC描述得比較簡單,整理一下RFC內所有提到 Fail the WebSocket Connection 的case

  1. p15. Open handshake時,client發現URI invalid (before WebSocket connection established)
  2. p17. client建立TCP連線失敗或是proxy return error (before WebSocket connection established)
  3. p19. Open handshake,client接收的handshake response Upgrade != websocket (before WebSocket connection established)
  4. p19. Open handshake,client接收的handshake response Connection != Upgrade (before WebSocket connection established)
  5. p19. Open handshake,client接收的handshake response Sec-WebSocket-Extensions不預期 (before WebSocket connection established)
  6. p20. Open handshake,client接收的handshake response 內容不符4.2.2的描述 (before WebSocket connection established)
  7. p44. client發生TCP連線中斷underlying transport layer connection is unexpectedly lost, the client MUST Fail the WebSocket Connection.
  8. p.48. client/server 預期收到UTF8但不是UTF8的encoding sequence時

不過RFC對於操作並沒有完全的描述清楚,例如7.2.2 提到的Abort the WebSocket Connection並沒有在該文件內出現,另外是還有一些case沒有明確定義是否歸類在fail the websocket connection, 例如 5.1 p27

server MUST NOT mask any frames that it sends to the client. A client MUST close a connection if it detects a masked frame. In this case, it MAY use the status code 1002 (protocol error) as defined in Section 7.4.1.

這邊只提到說要送close code 1002,但沒有提到要使用fail connection還是start close handshake。當然如果像收到不預期(mask錯誤)應該歸類在 Fail the WebSocket Connection

Predefined close status code 整理7.4.1,這邊只列出nodejs ws 有在使用的status code或是常見的code

  • 1000 normal closure
  • 1002 protocol error
  • 1005 close frame without status code (for API)
  • 1006 reserved value. It is designated for use in applications expecting a status code to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame. 這個在nodejs ws是WebSocket物件的預設值 (for API)
  • 1007 invalid UTF-8 sequence
  • 1009 frame payload size limit exceeds,在nodejs ws是2^53-1,跟Javascript integer MAX_SAFE_INTERGER有關

在nodejs ws的status code validation可以清楚的看到control frame可以帶的status code,像是1005、1006不應出現在control frame中,它是直接由程式內部判斷狀態產生的

exports.isValidStatusCode = (code) => {
  return (
    (code >= 1000 &&
      code <= 1014 &&
      code !== 1004 &&
      code !== 1005 &&
      code !== 1006) ||
    (code >= 3000 && code <= 4999)
  );
};

Security Considerations 主要整理Section 10

在RFC提到檢查Origin的機制,這段是做在server site,對於client side,原本有一些如cross origin 的阻擋機制,很可惜他主要是針對XMLHttpRequest,沒有規範WebSocket(參考https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)。所以要防止惡意的網頁存取必須要在server side加上Origin機制的檢查,需要注意的是cross origin的機制是保護browser的使用者,當他瀏覽到惡意的網頁,惡意的網頁可能可以建立起WebSocket連線到指定的site取得相關的資料,正常來說,browser可以將這類的連線阻斷,不過在CORS並沒有限制WebSocket,所以相關的限制必須做在server side(透過檢查Origin,這個header在網頁中是無法被修改的)。

其他security機制的幾個重點:

masking: masking的用途主要是防止proxy cache poisoning,在連線過程中可能會經過多個proxy,而有機會中間的proxy實作有缺陷,導致惡意的client-server安排可以將它的cache代換其他內容,透過在payload裡面送出如HTTP GET的request,安排好的server或被控制的server在WebSocket返回HTTP GET response,雖然理論上WebSocket還有frame的機制,payload前面有包含一些frame的header bytes,資料流的pattern不應該會被判斷成HTTP,但是可能有些實作是直接掃連線內容作切割(應該跟HTTP/1.1的Keep-alive connection機制有關),而沒有檢查所有完整資料內容的正確性 (也可參考 https://security.stackexchange.com/questions/36930/how-does-websocket-frame-masking-protect-against-cache-poisoning 的回答),所以透過client-server的mask,可以讓client的request內容不被有問題的proxy所看懂。至於server到client就不一定需要mask了,因為proxy更新cache主要是根據http request做對應,如果看不懂request,拿到response也就無從更新了

但是client -> server payload加上mask還不夠,RFC中還提到要求mask key的選取要足夠亂數,雖然在browser下透過javascript是拿不到mask key的,但這個主要是防止惡意的使用者猜到key(譬如說總是用同樣的key),直接在控制的client頁面建立WebSocket連線送出binary message,而message的內容mask完剛好是一個HTTP GET request,這樣就有可能導致proxy cache poisoning。

當然,這邊所有的防護都是針對,被控制的server + 網頁的使用者來思考的,被控制的server: 代表可能可以在上面修改WebSocket server的回傳,並且讓使用者load到惡意的網頁程式,使得使用者瀏覽網頁時,惡意的javascript程式執行,透過連到被控制的server,在WebSocket的傳輸內容動手腳,進而有機會汙染proxy cache(需要那台proxy實作有問題 + cache miss or expired)。

如果是非網頁的application,那以上的限制更是無用了。事實上,以上的機制對於惡意網頁程式,也只能防止一部分的攻擊,因為如果server被控制了,mask key也就可以即時的從WebSocket server取得再傳給client端,接下來惡意的程式一樣可以產生想要的data pattern。不過至少mask key的機制透過簡易的設計,大幅減少意外汙染proxy cache的可能性。

其他補充:

4.1 提到header沒有 Sec-WebSocket-Protocol 代表沒有 subprotocol時,在一些程式的API是透過指定subprotocol: null來描述

The Subprotocol In Use is defined to be the value of the |Sec-WebSocket-Protocol| header field in the server’s handshake or the null value if that header field was not present in the server’s handshake.

4.2.2對於subprotocol server如果無法支援client的要求,就是只支援null,也就是回應不帶 Sec-WebSocket-Protocol header field

另外4.2.1提到一些server端對於open handshake request的要求

  • GET verb
  • header field Host
  • header field Upgrade = websocket (case-insensitive value)
  • header field Connection = Upgrade (case-insensitive value)
  • header field Sec-WebSocket-Key
  • header field Sec-WebSocket-Version = 13
  • header field Origin (optional)
  • header field Sec-WebSocket-Protocol (optional)
  • header field Sec-WebSocket-Extensions (optional)
  • other optional header fields

4.2.2 (5) 描述server端對於open handshake response(accepted)要求的header欄位

  • status line 101
  • header field Upgrade = websocket
  • header field Connection = Upgrade
  • header field Sec-WebSocket-Accept
  • header field Sec-WebSocket-Protocol (optional, only one is selected)
  • header field Sec-WebSocket-Extensions (optional, can have multiple value or split in to multiple header field instances)

open handshake階段,在server side,送出handshake response後,此連線狀態為OPEN,server可以開始send/recv data,對於client side,收到server response並驗證成功後,連線狀態也是OPEN,至於client side要驗證的server response有哪些呢?在4.1的後半段有描述 The client MUST validate the server’s response as follows:

  • status code = 101
  • check header field Upgrade = websocket (value case-insensitive)
  • check header field Connection = Upgrade (value case-insensitive)
  • check header field Sec-WebSocket-Accept value
  • check header field (if exists) Sec-WebSocket-Extensions value is in the requested handshake
  • check header field (if exists) Sec-WebSocket-Protocol value is in the requested handshake

4.3 4.4 Sec-WebSocket-Version-Server = 1#version 在ABNF描述中,server response可返回多筆version number主要是用在當client handshake request要求的version server不支援時,server可返回所支援的version list

client handshake request example from RFC

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade

Sec-WebSocket-Version: 25

server response example from RFC

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

5.4 有關 fragmentation,裡面多次提到intermediaries,他可以根據需要重組重切message,但是對於看不懂的extension或是不確定該條WebSocket連線所使用的handshake,就不能隨便更動fragmentation

參考:

This entry was posted in Network. Bookmark the permalink.

1 Response to WebSocket protocol整理

  1. Pingback: WebSocket node.js ws source 整理 – 1 | C++ Essay

Leave a Reply