WebSocket接続の確立

接続の確立 (WebSocket)

[61] WebSocket では、 HTTPUpgrade: ヘッダー101 応答を使って接続確立 (ハンドシェイク) を行います。

[62] WebSocket コンストラクターは、 新たに WebSocket接続を確立するものです。

仕様書

クライアントの動作

コンストラクター

[84] WebSocketコンストラクターとして呼び出すと、 WebSocket サーバーに接続することができます。

[125] 第1引数には、 WebSocket サーバーエンドポイントURL を指定します。これは URL schemewss: または ws: のものです。第1引数は必須です。

[126] 第2引数には部分プロトコルの名前を指定できます。 部分プロトコルの指定が必要ない場合は、省略できます。 複数ある場合は名前の配列として指定できます。

[127] コンストラクターWebSocket オブジェクトを返します。 以後このオブジェクトを通じて送受信したり、エラーを受け取ったり、 接続を閉じたりできます。

[63] WebSocket コンストラクターは、 次のように動作しなければなりません

  1. [71] URL を、必須の第1引数USVString として解釈した結果に設定します。 >>60
  2. [72] プロトコル群を、次の値に設定します。 >>60
    第2引数が省略された場合
    空のリスト
    第2引数sequence の場合
    第2引数DOMStringリストとして解釈した結果。
    それ以外の場合
    第2引数DOMString として解釈した結果のみを含むリスト
  3. [75] URL記録を、URLURL構文解析器を適用した結果に設定します。 >>60
  4. [76] 次のいずれかの場合:
    1. [170] SyntaxError 例外投げ、ここで停止します。 >>60
  5. [73] ws を、新しい WebSocket オブジェクトに設定します。
    WebSocket (WebSocket接続)
    接続の状態
    CONNECTING >>1
    readyState
    CONNECTING (0) >>60
    URL
    URL記録 >>60
    extensions
    空文字列 >>60
    protocol
    空文字列 >>60
    binaryType
    blob >>60
  6. [78] 文脈オブジェクト関連設定群オブジェクト大域オブジェクトWebSocketに、 弱い参照ws を追加します。 (unloading document cleanup steps で参照されます。)
  7. [80] 設定群オブジェクトを、入口設定群オブジェクトに設定します。
  8. [82] ws を返します。 >>60

[83] 更に、並列に、次のようにしなければなりません>>60

  1. [85] WebSocket接続の確立を行います。
    URL
    URL記録
    プロトコル群
    プロトコル群
    クライアント
    設定群オブジェクト

[81] Webブラウザー以外の WebSocket クライアントは必ずしもこの通りに処理できないかもしれませんが (例えば設定群オブジェクトに相当するものが存在しないことも多いでしょう。)、 有用であるためには Webブラウザーと同様の形で動作することが期待されます。

接続の作成

[137] WebSocket接続を得る (obtain a WebSocket connection) とは、 URL URL について次のようにすることをいいます >>130

  1. [138] ホストを、URLホストに設定します >>130
  2. [139] ポートを、URLポートに設定します >>130
  3. [140] 保安を、URLschemehttp なら、それ以外ならに設定します >>130
  4. [132] 他の接続を待つとします。
  5. [141] プロキシを使うかどうかを決定します。 ホストポート資源名保安を使います。 >>1
  6. [142] プロキシを使う場合は、
    1. [143] 当該ホストポートへの TCP接続を開くようプロキシに接続して要求するべきです >>1
  7. [144] そうでない場合は、
    1. [145] 当該ホストポートへの TCP接続を開くべきです >>1
  8. [146] TCP接続を開くのに失敗したり、プロキシがエラーを返したりした時は、
    1. [147] WebSocket接続失敗を実行し >>1失敗を返して停止します >>130
  9. [148] 保安フラグが設定されていれば、
    1. [149] RFC 2818 HTTPS の方法で TLS handshake を行います。SNI を使います。 >>1
    2. [150] 失敗したら、
      1. [151] WebSocket接続失敗を実行し >>1失敗を返して停止します >>130

[152] RFC資源名 (URLpathquery) をプロキシの選択に使うと規定していますが、Fetch はなぜか資源名を定義していません。

[6] プロキシ自動設定スクリプトに渡す URL は、 ホストポート資源名「保安」フラグ (ws: or wss:) から構築します >>1

[5] WebSocket 用のプロキシの設定を設けていない場合は、 SOCKS5HTTPS 用、HTTP 用の優先順でプロキシを選択することが推奨 (encourage) されています >>1

[52] PAC も参照。

[9] TLS を使う場合 SNI が必須とされていますが、ホストIPアドレスで指定された時どうしなければならないのかは不明です。

[111] TLS の場合については HTTPS も参照。

[153] WebSocket接続を得る手続きは、 RFC では WebSocket接続の確立の一部として規定されていましたが、 Fetch Standard によって独立した手順とされました。

[128] この接続を得る処理は、 HTTP-network fetch から呼び出されます。 通常のHTTP接続を得る処理と似た処理ではありますが、 通常のHTTP接続のように要求応答のやり取りの後に他の要求に再利用できない点が大きく異なっています。

接続の確立

[2] WebSocket接続の確立 (establish a WebSocket connection) は、 URLプロトコル群クライアントについて、 次のようにすることを言います >>130

  1. [154] 要求URLを、URL の複製に設定します。
  2. [155] URLschemeを、 ws なら http に、 それ以外なら https に変更します。
  3. [156] 要求を、新しい要求に設定します。
    [186] 要求
    URL
    URL
    クライアント
    クライアント
    サービスワーカー群モード
    none
    参照子
    no-referrer
    同期フラグ
    モード
    websocket
    credentialsモード
    include
    キャッシュモード
    no-store
    リダイレクトモード
    error
    ヘッダーリスト
    Upgrade
    websocket
    Connection
    Upgrade
    Sec-WebSocket-Key
    接続毎に無作為に選択した 16バイトの nonceRFC 4648 Base64 符号化したもの
    Sec-WebSocket-Version
    13
    Sec-WebSocket-Protocol
    (プロトコル群が空でない場合のみ) プロトコル群内の各値を結合したもの
    Sec-WebSocket-Extensions
    permessage-deflate 拡張の値 (例えば permessage-deflate; client_max_window_bits)
  4. [157] 要求fetch します。 続きの処理は >>159 とします。

[134] HSTSUpgrade Insecure RequestsCSPMixed Contentクッキーport blocking など fetch に関わる諸機能は、 WebSocket 接続にも適用されます。これらは fetch アルゴリズム内部で処理されます。

[158] fetch 内で Host:User-Agent:Origin: などのヘッダーが追加されます。

[109] Referer: は使用されず、参照元ポリシーも適用されません。

[58] 要求ヘッダーには User-Agent: Chrome, FirefoxDNT: Chrome, FirefoxAccept-Encoding: Chrome, FirefoxAccept: Firefox も含まれるかもしれません。

[117] Chrome は仕様通り Connection: Upgrade ですが、 FirefoxConnection: keep-alive, Upgrade です。

[12] Web互換性のため、 Accept-Language: ヘッダーを含めるべきです >>114

[110] ヘッダーによっては、またどのヘッダーを送信するかは、 fingerprinting vector です。

[184] このWebSocket接続の確立 (Establish a WebSocket Connection) >>1 の処理はかつては RFC で規定されていましたが、 2016年3月の Fetch StandardHTML Standard の大規模リファクタリングによってほとんどが Fetch Standard に移動しました。

[64] 入力 URL の処理について、 HTML StandardURL Standard に基づき正準化した値を使っており、 RFCRFC 3986 URI を参照して規定しているため、 理論上は両者の差異のために予測できない挙動となる可能性がありました。 実際はすべて当初より URL Standard に近い動作で統一されているものと思われますが、 リファクタリングにより URL Standard ベースの処理になり、理論上も問題は解消しました。

[10] RFC は、 Webブラウザー起源を指定しなければならず、 それ以外は指定しても良い >>1 と規定していました。 これは Origin: ヘッダーが指定されるかどうかに影響します。 本来 WebSocketWebブラウザー向けプロトコルなのですから、 Webブラウザー以外のクライアントもできるだけ Webブラウザーに似せた動作にするのが適当そうです。 (が Webブラウザー以外のクライアントWeb起源ベースのセキュリティーモデルには従っていないかもしれませんから、 適当な値を決めるのが難しいこともあるかもしれません。)

[8] 適切なクッキーを送るヘッダー群 (headers to send appropriate cookies) は、 送信するべき Cookie: ヘッダーを指定するものです。 かつての HTML Standard が参照していますが、 RFC 6455 にはありません。 (WHATWG 時代には存在していたものが、 IETF で削除されたようです。 IETF ではよくあることです。)

[11] RFCHTTP/1.1 以上を使って要求を送信することを求めていましたが、 現在の Fetch Standard はなぜかこれを (HTTP についても WebSocket についても) 明言していません。

[14] 要求対象は、直接接続なら資源名のみ、 プロキシ接続なら HTTP URL と思われますが、明記されていません。どちらでも良いということでしょうか (それは一般的な慣習とは異なります)。また ws:/wss: がそれぞれ http:/https: に変換されるものと思われますが、 明記されていません。

[159] fetch により応答応答が利用可能となったら、次のようにします >>130

  1. [160] 応答ネットワークエラーか、 応答状態101 以外なら、
    1. [161] WebSocket接続失敗を実行し、停止します。
  2. [164] 応答が次のいずれかの条件を満たす場合:
    1. [163] WebSocket接続失敗を実行し、停止します。
  3. [177] WebSocket接続の状態を、 OPEN に設定します。 RFC
  4. [178] 利用中拡張群 (Extensions In Use) を、応答Sec-WebSocket-Extensions: ヘッダーの値 (なければ null) に設定します。 RFC
  5. [179] 利用中部分プロトコル (Subprotocol In Use) を、応答Sec-WebSocket-Protocol: ヘッダーの値 (なければ null) に設定します。 RFC
  6. [181] WebSocket接続確立 (The WebSocket Connection is Established) とします。 RFC
  7. [86] タスクを追加します >>77
    タスク
    タスク源
    WebSocketタスク源
    処理
    1. [183] readyState を、 OPEN (1) に設定します。
    2. 利用中拡張群null でなければ、
      1. extensions を、利用中拡張群に設定します。
    3. 利用中プロトコルnull でなければ、
      1. protocol を、利用中プロトコルに設定します。
    4. [182] 単純イベントを発火します。
      単純イベント
      イベント型
      open
      イベント対象
      WebSocket オブジェクト
  8. [89] 以後受信データはWebSocketメッセージ受信の処理を行います。 RFC

[118] 応答プロトコルの版の検査は求められていないようです。

[119] FirefoxChrome も、 HTTP/1.0 でも HTTP/1.1 でもかわらず続行します。

[65] RFC では、101 以外の状態符号応答を受信した時、 HTTP に従い処理することになっていました。例えば 401 ならHTTP認証の処理を行えますし、 3xx なら HTTPリダイレクトしても良いことになっています。 >>1 しかし、 HTML StandardWebSocket接続失敗として処理しなければならないと規定しています >>60HTML Standard を引き継いだ Fetch Standard も、 101 以外をエラーとして処理することを求めています >>130

[66] これは、サーバーリダイレクトすることで、スクリプトが意図しない相手と接続してしまうため危険であるのが理由 >>60 とされています。 理論上は WebSocket 以外の WebSocketクライアントはこの規定に従う義務はありませんが、 敢えて危険をおかして Webブラウザーと異なる動作を採る必要性も無さそうです。

[67] HTTP認証はそのような危険性は無さそうですが...

[115] Chrome は、 401 が返されたら、適切な credentials を持っていなければエラーとして扱います。 Firefox は、通常の HTTP認証モーダルダイアログを表示します。 FirefoxHTTP接続を再利用可能なら、再利用して再試行するようです。

407

[122] FirefoxChrome も、 1xx (101 以外) をエラーとして扱います。

[124] Chromeヘッダーを読み終わったところでエラーなら切断するようですが、 Firefox はそうでもないようで、しばらく (ずっとではない) 切断されません。

[68] WebSocket オブジェクトは、 内部状態としてクライアント指定プロトコル群 (client-specified protocols) を持ちます。

[54] 受信したデータが理解できない、想定外であるなどの理由があれば、 いつでもTCP接続を閉じることができます >>53

[116] ChromeContent-Length: ヘッダーについて、 通常のHTTP応答同様の検査を行うようです。

[120] 応答Sec-WebSocket-Protocol: の値の検査を Chrome は行いますが、 Firefox は行わないようです。 なお Chrome は (RFC と異なり) 値をリスト (#) として構文解析するようです。

[121] 応答Sec-WebSocket-Extensions: の値が空文字列の時、 Firefox はエラーにせず、 Chrome はエラーにします。値が , だけの時 (空のリストだが空文字列でない時)、 Firefox はエラーにし、 Chrome はエラーにしません。 RFC に従うならどちらもエラーになるべきです。

[123] Chrome は接続成功時と 401 時に Set-Cookie: を処理するようです。 Firefox は接続が成功か否かや状態符号に関わらず、 Set-Cookie: を処理するようです。

[90] WebSocket の規定に従わないサーバーは、クライアントからの要求を待たずに応答 (や応答になっていないデータ) を送信するかもしれません。クライアントはそれに特別な対処を行う必要はありませんが、 >>54 を根拠に切断しても良いのかもしれません。

[180] かつて RFCクッキーの処理のため応答の処理で Cookies Set During the Server's Opening Handshake という語を使い、これを HTML Standard が利用する形を採っていました。 これは Fetch Standard との統合により死文化しました。

サーバーの動作

[30] サーバーは、 TCP ないし TLS over TCPホストポートの組を listen していることが期待されています。

[16] サーバーは、クライアントとのTCP接続が確立されたら、 次のようにしなければなりません

  1. [19] TLS を使う場合、TLS handshake を行います >>1TLSクライアント認証を使っても構いません >>51。 失敗なら、接続を閉じ、停止します >>1
  2. [18] 要求を受信したら、 その一部または全部を構文解析して、必要な情報を取得します。 次のような問題があれば:
    1. [98] エラーを表す 400 応答など適切なHTTP応答を返します。 >>1
    2. [99] Abort the WebSocket Connection します。
    3. [103] 停止します。
  3. [20] 必要に応じて、
    1. [100] WWW-Authenticate: ヘッダーを検査して 401 応答を返したり、 クッキー認証など適切な認証を行い >>51 適切なエラーの応答を返したり、 3xx 応答HTTPリダイレクトしたりします。 >>1
    2. [102] Abort the WebSocket Connection します。
    3. [104] 停止します。
  4. [21] Origin: ヘッダーを検査して不適切なら、
    1. [95] 403 応答など適切な応答を返します >>1, >>50
    2. [96] Abort the WebSocket Connection します。
    3. [105] 停止します >>1
  5. [23] 要求対象から得た資源名で示されるものが利用できないなら、
    1. [93] 404 応答など適切な応答を返します。>>1
    2. [94] Abort the WebSocket Connection します。
    3. [106] 停止します >>1
  6. [24] Sec-WebSocket-Protocol: ヘッダーの値から得られた部分プロトコル群から、 サーバーが承認するもののみのリストを得ます。該当するものがなければ、 null とします。 >>1
  7. [25] Sec-WebSocket-Extensions: ヘッダーの値から得られたプロトコル拡張群から、 サーバーが承認するもののみのリストを得ます。該当するものがなければ、 null とします。 >>1
  8. [26] HTTP応答を送ります。 >>1
    HTTP応答
    状態符号
    101
    Upgrade
    websocket
    Connection
    Upgrade
    Sec-WebSocket-Accept
    要求Sec-WebSocket-Key: の値に 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 を連結し、 SHA-1 を計算し、RFC 4648 Base64 符号化したもの。
    Sec-WebSocket-Protocol
    >>24 で得られた部分プロトコルリスト (HTTP) (#) (null でない場合のみ)。
    Sec-WebSocket-Extensions
    >>25 で得られたプロトコル拡張 (null でない場合のみ)。 引数は、拡張の規定に従い適切に決定します >>49。 (複数の本ヘッダーがあっても構いません。)
  9. [27] WebSocket接続の状態を OPEN とします。 >>1
  10. [88] 以後受信データはWebSocketメッセージ受信の処理を行います。
[97] サーバーのエラー処理について RFC は明確に記述していませんが、 他の部分の記述から、 Abort the WebSocket Connection を呼び出すことが想定されているようです。
[107] Host:要求対象の衝突時の処理を RFC は明記していませんが、後の RFC 723x による HTTP における処理 (要求対象参照。) に従うべきと思われます。

[17] Origin: ヘッダーが無ければ、 Webブラウザーからの要求と解釈するべきではありません >>1

[59] なお Webブラウザー以外のクライアントは、好きな Origin: を送ることができます。クライアントWebブラウザーであることを判定する方法はありません (Webブラウザーでないことは >>17 の通り判定できますが)。 Origin:Webブラウザー上で適切なアクセス元からの接続かどうかを判断するために使うことができますが、 非 Webブラウザーに対する認証のような仕組みとして用いることはできません。

[87] サーバーが返す応答に他のHTTPヘッダーを指定できるかどうか、 RFC >>1 は明記していません。 HTTPサーバーとしては Server:Date: のような必須のヘッダーが他にありますし、 受信するクライアントの側では Set-Cookie:HSTSUIR などの一般的なHTTPヘッダーの処理を行いますから、 現実問題としてこれらを使うことは可能です。しかし Upgrade:Content-Language:Refresh: のようにここでの意味が明確でないものは、 使うべきではないでしょう。

[7] この手順の途中で停止する時は、WebSocket接続を閉じるものと思われます。

[55] 受信したデータが理解できない、想定外であるなどの理由があれば、 いつでもTCP接続を閉じることができます >>53。 不正なデータを受信した時は、適切な HTTP応答を送信するべきです >>53

[28] 状態が OPEN となると、データを送受信できます >>1

[48] この手順を完走したら、WebSocket接続確立となるものと思われます。

[112] TLS の場合については HTTPS も参照。

[29] 要求本体が含まれる場合のサーバーの処理について明文規定はありません。 正常なクライアントWebSocket接続の確立では、要求本体が含まれることはありません。 従って、要求本体でないなら、サーバー400 応答などを返して拒絶して構わないと思われます。 要求ヘッダーより後のデータが WebSocket データと考えているサーバーは、 要求本体が含まれているとおかしくなってしまいます。


[189] 実際には、 WebSocketハンドシェイクに失敗した時や、 Sec-WebSocket-Protocol がサーバーの予期せぬ値 (または予期せぬ未指定) だったとき、 応答を返すかわりにすぐに接続を閉じたり、 RST を送ったりするサーバーもあるようです。

WebSocket インターフェイス url 属性

[70] WebSocket インターフェイスurl IDL属性は、 文脈オブジェクトURL (url) URLの直列化を適用した結果を返さなければなりません >>69。 この値は常に ws: または wss:絶対URLです。

[176] WebSocket オブジェクトは、 URL (url) を持ちます >>53。 これは WebSocket コンストラクターにより設定され、 以後変化しません。接続先として指定された URL を表します。

[74] このIDL属性データ型は、 USVString です >>69

歴史

誕生

Fetch 統合

[131] CSPMixed ContentUpgrade Insecure Requests猿パッチを定義し、 後にこれを RFCHTML Standard と統合しようとしましたが、 IETF は仕様の改訂を行おうとしませんでした (WebSocket の歴史の項を参照)。

[133] 2016年3月、 RFCHTML Standard にあった接続の確立に関わる部分がこれら猿パッチを解消する形で Fetch Standardfetch アルゴリズムと統合される形で再定義され、 一連の処理の流れが明確化されました。 クッキーHSTSReferrer PolicyService Workers など fetch に関わる諸仕様との関係も明確になりました。

[169] WebSocket: leave port blocking to the network layer (Fetch) · whatwg/html@17336ad ( 版) <https://github.com/whatwg/html/commit/17336ad69be4744dfc17194f2ee51bd730ca4d93>

[79] Update WebSocket to use Fetch's WebSocket alterations · whatwg/html@3dadbca ( 版) <https://github.com/whatwg/html/commit/3dadbcad063a10b586ef52dd4b427aa339048ee7>

[185] Dealing with websockets #61 ( (deian著, )) <https://github.com/w3c/webappsec-cowl/commit/05757139e1a8a4e842d7d43d2acc91d1cf62cce9>

[3] RFC 7977 - The WebSocket Protocol as a Transport for the Message Session Relay Protocol (MSRP) () <https://tools.ietf.org/html/rfc7977#section-7>

If the HTTP GET request contains an Origin header, the MSRP WebSocket

Server SHOULD indicate Cross-Origin Resource Sharing [CORS] by adding

an Access-Control-Allow-Origin header to the 101 response.

[4] Properly set the Origin header for WebSocket requests (nox著, ) <https://github.com/whatwg/fetch/commit/406c5a60595c63d323693050b45d40823933e185>

[57] 426736 - WebSocket connections not using configured system HTTPS Proxy in MacOS - chromium - Monorail () <https://bugs.chromium.org/p/chromium/issues/detail?id=426736>

[91] WebSocket connections don't appear to send referrers · Issue #630 · whatwg/fetch () <https://github.com/whatwg/fetch/issues/630>

[92] WebSocket handshake has no referrer (annevk著, ) <https://github.com/whatwg/fetch/commit/60db35ed0d18c6636f002da91593bfa5ee58494e>

[187] WebSocket handshake has no referrer by annevk · Pull Request #632 · whatwg/fetch () <https://github.com/whatwg/fetch/pull/632>

[188] WebSocket connections don't appear to send referrers · Issue #630 · whatwg/fetch () <https://github.com/whatwg/fetch/issues/630>

[190] Stop saying HTTP authentication over WebSocket is disallowed (ricea著, ) <https://github.com/whatwg/fetch/commit/8b070f14a1fb2d1f8f4d07e1902a40a14f77b060>

[191] WebSocket: "HTTP authentication will not function" is not correct · Issue #565 · whatwg/fetch () <https://github.com/whatwg/fetch/issues/565>

[192] Stop saying WebSocket auth is disallowed by ricea · Pull Request #761 · whatwg/fetch () <https://github.com/whatwg/fetch/pull/761>