[61] [[WebSocket]] では、 [[HTTP]] の [CODE(HTTP)@en[[[Upgrade:]]]] [[ヘッダー]]と
[CODE(HTTP)[[[101]]]] [[応答]]を使って[[接続]]の[[確立]] ([[ハンドシェイク]])
を行います。

[62] [CODE(DOMi)@en[[[WebSocket]]]] [[コンストラクター]]は、
新たに [[WebSocket接続]]を確立するものです。

* 仕様書

[REFS[
- [15] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-2.1>
- [1] '''[CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-4>'''
- [47] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-7.1.4>
- [49] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-9>
- [50] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-10.2>
- [51] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-10.5>
- [53] [CITE@en[RFC 6455 - The WebSocket Protocol]] ([TIME[2015-03-11 20:42:50 +09:00]] 版) <http://tools.ietf.org/html/rfc6455#section-10.7>
- [56] [CITE[RFC Errata Report]] ([TIME[2015-05-16 22:26:12 +09:00]] 版) <http://www.rfc-editor.org/errata_search.php?rfc=6455>
-- [113] [CITE[RFC Errata Report]] ([TIME[2015-06-30 11:25:51 +09:00]] 版) <http://www.rfc-editor.org/errata_search.php?rfc=6455&eid=4398>
- [130] [CITE@en-US[Fetch Standard]] ([TIME[2016-03-14 19:33:09 +09:00]] 版) <https://fetch.spec.whatwg.org/#websocket-protocol>
- [60] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-05-06 10:42:35 +09:00]] 版) <https://html.spec.whatwg.org/#dom-websocket>
- [69] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-05-06 10:42:35 +09:00]] 版) <https://html.spec.whatwg.org/#dom-websocket-url>
- [77] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-05-06 10:42:35 +09:00]] 版) <https://html.spec.whatwg.org/#feedback-from-the-protocol>
]REFS]

* クライアントの動作

** コンストラクター

[84] [CODE(DOMi)@en[WebSocket]] を[[コンストラクター]]として呼び出すと、
[[WebSocket]] [[サーバー]]に接続することができます。

[125] 第1引数には、 [[WebSocket]] [[サーバー]]の[[エンドポイント]]の [[URL]]
を指定します。これは [[URL scheme]] が [CODE(URI)@en[wss:]] または [CODE(URI)@en[ws:]]
のものです。第1引数は必須です。

[126] 第2引数には[[部分プロトコル]]の名前を指定できます。
[[部分プロトコル]]の指定が必要ない場合は、省略できます。
複数ある場合は名前の[[配列]]として指定できます。

[127] [[コンストラクター]]は [CODE(DOMi)@en[WebSocket]] [[オブジェクト]]を返します。
以後この[[オブジェクト]]を通じて送受信したり、エラーを受け取ったり、
接続を閉じたりできます。

[63] [CODE(DOMi)@en[[[WebSocket]]]] [[コンストラクター]]は、
次のように動作しなければ[['''なりません''']]。
[FIG(steps)[
= [71] [VAR[URL]] を、必須の第1[[引数]]を [CODE(IDL)@en[USVString]] として解釈した結果に設定します。
[SRC[>>60]]
= [72] [VAR[プロトコル群]]を、次の値に設定します。 [SRC[>>60]]
[FIG(switch)[
: 第2[[引数]]が省略された場合 :
空の[[リスト]]。
: 第2[[引数]]が [CODE(IDL)@en[sequence]] の場合 :
第2[[引数]]を [CODE(IDL)@en[DOMString]] の[[リスト]]として解釈した結果。
: それ以外の場合 :
第2[[引数]]を [CODE(IDL)@en[DOMString]] として解釈した結果のみを含む[[リスト]]。
]FIG]
= [75] [VAR[URL記録]]を、[VAR[URL]] に[[URL構文解析器]]を適用した結果に設定します。 [SRC[>>60]]
= [76] 次のいずれかの場合:
[FIG(list)[
- [172] [VAR[URL記録]]が失敗
- [171] [VAR[URL記録]]の[F[scheme][URL scheme]]が
[CODE(URI)@en[ws][ws:]] でも [CODE(URI)@en[wss][wss:]] でもない
- [173] [VAR[URL記録]]の[F[素片]]が [[null]] でない
- [174] [VAR[プロトコル群]]に重複がある
- [175] [VAR[プロトコル群]]に [CODE(HTTP)@en[Sec-WebSocket-Protocol:]] 
の値の制約を満たさないものがある
]FIG]
== [170] [CODE(DOMe)@en[SyntaxError]] [[例外]]を[[投げ]]、ここで停止します。 [SRC[>>60]]
= [73] [VAR[ws]] を、新しい [CODE(DOMi)@en[WebSocket]] [[オブジェクト]]に設定します。
[FIG(list members)[
[FIGCAPTION[
[CODE(DOMi)@en[[[WebSocket]]]] ([[WebSocket接続]])
]FIGCAPTION]
:接続の状態:[DFN[[CODE[[[CONNECTING]]]]]] [SRC[>>1]]。
:[CODE(DOMa)@en[[[readyState]]]]:[DFN[[CODE[[[CONNECTING]]]]]] ([CODE(HTTP)[[[0]]]]) [SRC[>>60]]。
: [F[URL][url (WebSocket)]] : [VAR[URL記録]] [SRC[>>60]]。
:[CODE(DOMa)@en[[[extensions]]]]:[[空文字列]] [SRC[>>60]]。
:[CODE(DOMa)@en[[[protocol]]]]:[[空文字列]] [SRC[>>60]]。
:[CODE(DOMa)@en[[[binaryType]]]]:[CODE(DOM)@en[[[blob]]]] [SRC[>>60]]。
]FIG]
= [78] [[文脈オブジェクト]]の[F[関連設定群オブジェクト]]の[F[大域オブジェクト]]の
[F[[CODE(DOMi)@en[WebSocket]]群]]に、
[[弱い参照]]で [VAR[ws]] を追加します。
[WEAK[([[unloading document cleanup steps]] で参照されます。)]]
= [80] [VAR[設定群オブジェクト]]を、[[入口設定群オブジェクト]]に設定します。
= [82] [VAR[ws]] を返します。 [SRC[>>60]]
]FIG]

[83] 更に、[[並列に]]、次のようにしなければ[MUST[なりません]]。 [SRC[>>60]]
[FIG(steps)[
= [85] [[WebSocket接続の確立]]を行います。
[FIG(list members)[
: [VAR[URL]] : [VAR[URL記録]]
: [VAR[プロトコル群]] : [VAR[プロトコル群]]
: [VAR[クライアント]] : [VAR[設定群オブジェクト]]
]FIG]
]FIG]

[81] [[Webブラウザー]]以外の [[WebSocket]] [[クライアント]]は必ずしもこの通りに処理できないかもしれませんが
(例えば[[設定群オブジェクト]]に相当するものが存在しないことも多いでしょう。)、
有用であるためには [[Webブラウザー]]と同様の形で動作することが期待されます。

** 接続の作成

[137] [DFN[[RUBYB[WebSocket接続を得る]@en[obtain a WebSocket connection]]]]とは、
[[URL]] [VAR[URL]] について次のようにすることをいいます [SRC[>>130]]。
[FIG(steps)[
= [138] [VAR[ホスト]]を、[VAR[URL]] の[F[ホスト][URLのホスト]]に設定します [SRC[>>130]]。
= [139] [VAR[ポート]]を、[VAR[URL]] の[F[ポート][URLのポート]]に設定します [SRC[>>130]]。
= [140] [VAR[保安]]を、[VAR[URL]] の[F[scheme][URL scheme]]が
[CODE(URI)@en[http][http:]] なら[[偽]]、それ以外なら[[真]]に設定します [SRC[>>130]]。
= [132] [[他の接続を待つ]]とします。
= [141] [[プロキシ]]を使うかどうかを決定します。
[VAR[ホスト]]、[VAR[ポート]]、[[資源名]]、[VAR[保安]]を使います。 [SRC[>>1]]
= [142] [[プロキシ]]を使う場合は、
== [143] 当該[VAR[ホスト]]、[VAR[ポート]]への [[TCP接続]]を開くよう[[プロキシ]]に接続して要求する[SHOULD[べき]]です [SRC[>>1]]。
= [144] そうでない場合は、
== [145] 当該[VAR[ホスト]]、[VAR[ポート]]への [[TCP接続]]を開く[SHOULD[べきです]] [SRC[>>1]]。
= [146] [[TCP接続]]を開くのに失敗したり、[[プロキシ]]がエラーを返したりした時は、
== [147] [[WebSocket接続失敗]]を実行し [SRC[>>1]]、[[失敗]]を返して停止します [SRC[>>130]]。
= [148] [VAR[保安]]フラグが設定されていれば、
== [149] [[RFC 2818]] [[HTTPS]] の方法で [[TLS handshake]] を行います。[[SNI]] を使います。
[SRC[>>1]]
== [150] 失敗したら、
=== [151] [[WebSocket接続失敗]]を実行し [SRC[>>1]]、[[失敗]]を返して停止します [SRC[>>130]]。
]FIG]

;; [152] [[RFC]] は[VAR[資源名]] ([[URL]] の[F[path]]と[F[query]])
を[[プロキシ]]の選択に使うと規定していますが、[[Fetch]] 
はなぜか[VAR[資源名]]を定義していません。


[6] [[プロキシ自動設定スクリプト]]に渡す [[URL]] は、
[[ホスト]]、[[ポート]]、[[資源名]]、[[「保安」]]フラグ ([CODE(URI)@en[[[ws:]]]] or [CODE(URI)@en[[[wss:]]]])
から構築します [SRC[>>1]]。

[5] [[WebSocket]] 用の[[プロキシ]]の設定を設けていない場合は、
[[SOCKS5]]、[[HTTPS]] 用、[[HTTP]] 用の優先順で[[プロキシ]]を選択することが[RUBYB[推奨]@en[encourage]]されています [SRC[>>1]]。

;; [52] [[PAC]] も参照。

[9] [[TLS]] を使う場合 [[SNI]] が必須とされていますが、[[ホスト]]が [[IPアドレス]]で指定された時どうしなければならないのかは不明です。

;; [111] [[TLS]] の場合については [[HTTPS]] も参照。

@@ [[TLSクライアント認証]]

[HISTORY[
[153] [[WebSocket接続を得る]]手続きは、 [[RFC]] では 
[[WebSocket接続の確立]]の一部として規定されていましたが、
[[Fetch Standard]] によって独立した手順とされました。
]HISTORY]

[128] この接続を得る処理は、 [[HTTP-network fetch]] から呼び出されます。
通常の[[HTTP接続を得る]]処理と似た処理ではありますが、
通常の[[HTTP接続]]のように[[要求]]と[[応答]]のやり取りの後に他の[[要求]]に再利用できない点が大きく異なっています。

** 接続の確立

[2] [DFN[[RUBYB[WebSocket接続の確立]@en[establish a WebSocket connection]]]]は、
[VAR[URL]]、[VAR[プロトコル群]]、[VAR[クライアント]]について、
次のようにすることを言います [SRC[>>130]]。

[FIG(steps)[
= [154] [VAR[要求URL]]を、[VAR[URL]] の複製に設定します。
= [155] [VAR[URL]] の[F[scheme][URL scheme]]を、
[CODE(URI)@en[ws][ws:]] なら [CODE(URI)@en[http][http:]] に、
それ以外なら [CODE(URI)@en[https][https:]] に変更します。
= [156] [VAR[要求]]を、新しい[[要求]]に設定します。
[FIG(list members)[ [186] [[要求]]

: [F[URL][要求URL]] : [VAR[URL]]
: [F[クライアント][要求のクライアント]] : [VAR[クライアント]]
: [F[サービスワーカー群モード]] : [CODE[none][サービスワーカー群モード]]
: [F[参照子]] : [CODE[no-referrer]]
: [F[同期フラグ]] : [[真]]
: [F[モード][要求のモード]] : [CODE[websocket]]
: [F[credentialsモード]] : [CODE[include]]
: [F[キャッシュモード]] : [CODE[no-store]]
: [F[リダイレクトモード]] : [CODE[error]]
: [F[ヘッダーリスト]] :
[FIG(list members)[

:[CODE(HTTP)@en[[[Upgrade]]]]:[CODE(HTTP)@en[[[websocket]]]]
:[CODE(HTTP)@en[[[Connection]]]]:[CODE(HTTP)@en[[[Upgrade]]]]
:[CODE(HTTP)@en[[[Sec-WebSocket-Key]]]]:[[接続]]毎に[[無作為]]に選択した
16バイトの [[nonce]] を、
[[forgiving-base64 encode]] し、
[[同型符号化]]したもの
:[CODE(HTTP)@en[[[Sec-WebSocket-Version]]]]:[CODE(HTTP)[[[13]]]]
:[CODE(HTTP)@en[[[Sec-WebSocket-Protocol]]]]:
([VAR[プロトコル群]]が空でない場合のみ)
[VAR[プロトコル群]]内の各値を[[結合][リスト (HTTP)]]したもの
:[CODE(HTTP)@en[[[Sec-WebSocket-Extensions]]]]:
[CODE[permessage-deflate]] [[拡張][WebSocket拡張]]の値
(例えば [CODE[permessage-deflate; client_max_window_bits]])

]FIG]
]FIG]
= [157] [VAR[要求]]を [[fetch]] します。
続きの処理は >>159 とします。
]FIG]

[134] [[HSTS]]、[[Upgrade Insecure Requests]]、[[CSP]]、[[Mixed Content]]、
[[クッキー]]、[[port blocking]] など [[fetch]] に関わる諸機能は、
[[WebSocket]] 接続にも適用されます。これらは [[fetch]] アルゴリズム内部で処理されます。

[158] [[fetch]] 内で [CODE(HTTP)@en[Host:]] や [CODE(HTTP)@en[User-Agent:]]
や [CODE(HTTP)@en[Origin:]] などの[[ヘッダー]]が追加されます。

[109] [CODE(HTTP)@en[[[Referer:]]]] は使用されず、[[参照元ポリシー]]も適用されません。

[58] [[要求ヘッダー]]には [CODE(HTTP)@en[[[User-Agent:]]]] [SRC[[[Chrome]], [[Firefox]]]] や
[CODE(HTTP)@en[[[DNT:]]]] [SRC[[[Chrome]], [[Firefox]]]] や
[CODE(HTTP)@en[[[Accept-Encoding:]]]] [SRC[[[Chrome]], [[Firefox]]]] や
[CODE(HTTP)@en[[[Accept:]]]] [SRC[[[Firefox]]]] も含まれるかもしれません。

[117] [[Chrome]] は仕様通り [CODE(HTTP)@en[[[Connection:]] [[Upgrade]]]]
ですが、 [[Firefox]] は [CODE(HTTP)@en[[[Connection:]] [[keep-alive]], [[Upgrade]]]]
です。 [TIME[2015-08-08T13:34:03.400Z]]

[12] [[Web互換性]]のため、
[CODE(HTTP)@en[[[Accept-Language:]]]] [[ヘッダー]]を含めるべきです [SRC[>>114]]。

;; [110] [[ヘッダー]]によっては、またどの[[ヘッダー]]を送信するかは、
[[fingerprinting vector]] です。

[HISTORY[
[184] この[DFN[[RUBYB[WebSocket接続の確立]@en[Establish a WebSocket Connection]]]] [SRC[>>1]]
の処理はかつては [[RFC]] で規定されていましたが、
2016年3月の [[Fetch Standard]] と [[HTML Standard]] の大規模リファクタリングによってほとんどが
[[Fetch Standard]] に移動しました。

[64] 入力 [[URL]] の処理について、
[[HTML Standard]] は [[URL Standard]] に基づき[[正準化]]した値を使っており、
[[RFC]] は [[RFC 3986]] [[URI]] を参照して規定しているため、
理論上は両者の差異のために予測できない挙動となる可能性がありました。
実際はすべて当初より [[URL Standard]] に近い動作で統一されているものと思われますが、
リファクタリングにより [[URL Standard]] ベースの処理になり、理論上も問題は解消しました。

[10] [[RFC]] は、 [[Webブラウザー]]は[[起源]]を指定しなければ[MUST[ならず]]、
それ以外は指定しても[MAY[良い]] [SRC[>>1]] と規定していました。
これは [CODE(HTTP)@en[Origin:]] [[ヘッダー]]が指定されるかどうかに影響します。
本来 [[WebSocket]] は [[Webブラウザー]]向けプロトコルなのですから、
[[Webブラウザー]]以外の[[クライアント]]もできるだけ 
[[Webブラウザー]]に似せた動作にするのが適当そうです。 (が 
[[Webブラウザー]]以外の[[クライアント]]は [[Web]] 
の[[起源]]ベースのセキュリティーモデルには従っていないかもしれませんから、
適当な値を決めるのが難しいこともあるかもしれません。)

[8] [DFN[[RUBYB[適切なクッキーを送るヘッダー群]@en[headers to send appropriate cookies]]]]は、
送信するべき [CODE(HTTP)@en[[[Cookie:]]]] [[ヘッダー]]を指定するものです。
かつての [[HTML Standard]] が参照していますが、 [[RFC 6455]] にはありません。
([[WHATWG]] 時代には存在していたものが、 [[IETF]] で削除されたようです。 [[IETF]]
ではよくあることです。)

[11] [[RFC]] は [[HTTP/1.1]] [[以上]]を使って[[要求]]を送信することを求めていましたが、
現在の [[Fetch Standard]] はなぜかこれを ([[HTTP]] についても [[WebSocket]]
についても) 明言していません。

[14] [[要求対象]]は、直接接続なら[[資源名]]のみ、 [[プロキシ]]接続なら
[[HTTP]] [[URL]] と思われますが、明記されていません。どちらでも良いということでしょうか
(それは一般的な慣習とは異なります)。また [CODE(URI)@en[[[ws:]]]]/[CODE(URI)@en[[[wss:]]]]
がそれぞれ [CODE(URI)@en[[[http:]]]]/[CODE(URI)@en[[[https:]]]] に変換されるものと思われますが、
明記されていません。

]HISTORY]

[159] [[fetch]] により[[応答]][VAR[応答]]が利用可能となったら、次のようにします
[SRC[>>130]]。
[FIG(steps)[
= [160] [VAR[応答]]が[[ネットワークエラー]]か、
[VAR[応答]]の[F[状態][状態符号]]が [CODE[101]] ''以外''なら、
== [161] [[WebSocket接続失敗]]を実行し、停止します。
= [164] [VAR[応答]]が次のいずれかの条件を満たす場合:
[FIG(list)[
- [31] [CODE(HTTP)@en[[[Upgrade:]]]] [[ヘッダー]]が無いか、
[CODE(HTTP)@en[[[websocket]]]] ([[ASCII大文字・小文字不区別]]) が含まれていない場合 [SRC[>>1]]
- [32] [CODE(HTTP)@en[[[Connection:]]]] [[ヘッダー]]が無いか、
[CODE(HTTP)@en[[[Upgrade]]]] ([[ASCII大文字・小文字不区別]]) が含まれていない場合 [SRC[>>1]]
- [33] [CODE(HTTP)@en[[[Sec-WebSocket-Accept:]]]] [[ヘッダー]]が無いか、
値が[[要求]]の [CODE(HTTP)@en[[[Sec-WebSocket-Key]]]] の値に
[DFN[[CODE[[[258EAFA5-E914-47DA-95CA-C5AB0DC85B11]]]]]]
を連結した値の [[SHA-1]] を [[Base64]] 符号化したものでない場合
(前後に[[空白]]があっても構いません。) [SRC[>>1]]
- [34] [CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]] 
[[ヘッダー]]が構文的に正しくない場合や、[[要求]]で指定しなかった拡張が指定されている場合
[SRC[>>1]]
- [35] [CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] 
[[ヘッダー]]が構文的に正しくない場合や、
[[要求]]で指定しなかった[[部分プロトコル]]が指定されている場合 [SRC[>>1]]
- [162] [VAR[プロトコル群]]が空でない場合で、
[VAR[応答]]の[F[ヘッダーリスト]]の [CODE(HTTP)@en[Sec-WebSocket-Protocol]]
[[ヘッダー]]を[[ヘッダー値の構文解析]]した結果が
[[null]]、[[失敗]]、[[空バイト列]]のいずれかの場合
]FIG]
== [163] [[WebSocket接続失敗]]を実行し、停止します。
= [177] [[WebSocket接続]]の状態を、 [CODE[[[OPEN]]]] に設定します。 [SRC[RFC]]
= [178] [DFN[[RUBYB[利用中拡張群]@en[Extensions In Use]]]]を、[[応答]]の
[CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]] [[ヘッダー]]の値
(なければ [[null]]) に設定します。 [SRC[RFC]]
= [179] [DFN[[RUBYB[利用中部分プロトコル]@en[Subprotocol In Use]]]]を、[[応答]]の
[CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] [[ヘッダー]]の値
(なければ [[null]]) に設定します。 [SRC[RFC]]
= [181] [DFN[[RUBYB[WebSocket接続確立]@en[The WebSocket Connection is Established]]]]とします。
[SRC[RFC]]
= [86] [[タスク]]を追加します [SRC[>>77]]。
[FIG(list members)[
[FIGCAPTION[
[[タスク]]
]FIGCAPTION]
:[[タスク源]]:[[WebSocketタスク源]]
:処理:
[FIG(steps)[
= [183] [CODE(DOMa)@en[[[readyState]]]] を、 [DFN[[CODE(DOM)@en[[[OPEN]]]]]] ([CODE(DOM)[[[1]]]])
に設定します。
= [[利用中拡張群]]が [[null]] でなければ、
== [CODE(DOMa)@en[[[extensions]]]] を、[[利用中拡張群]]に設定します。
= [[利用中プロトコル]]が [[null]] でなければ、
== [CODE(DOMa)@en[[[protocol]]]] を、[[利用中プロトコル]]に設定します。
= [182] [[単純イベントを発火]]します。
[FIG(list members)[
[FIGCAPTION[
[[単純イベント]]
]FIGCAPTION]
:[[イベント型]]:[CODE(DOMe)@en[[[open]]]]
:[[イベント対象]]:[CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]
]FIG]
]FIG]
]FIG]
= [89] 以後受信データは[[WebSocketメッセージ受信]]の処理を行います。 [SRC[RFC]]
]FIG]

[118] [[応答]]の[[プロトコルの版]]の検査は求められていないようです。

;; [119] [[Firefox]] も [[Chrome]] も、 [[HTTP/1.0]] でも [[HTTP/1.1]]
でもかわらず続行します。 [TIME[2015-08-11T12:54:08.600Z]]

[65] [[RFC]] では、[CODE(HTTP)[[[101]]]] 以外の[[状態符号]]の[[応答]]を受信した時、
[[HTTP]] に従い処理することになっていました。例えば [CODE(HTTP)[[[401]]]]
なら[[HTTP認証]]の処理を行えますし、 [CODE(HTTP)[[[3xx]]]] なら
[[HTTPリダイレクト]]しても良いことになっています。 [SRC[>>1]]
しかし、 [[HTML Standard]] は [[WebSocket接続失敗]]として処理しなければ[['''ならない''']]と規定しています [SRC[>>60]]。
[[HTML Standard]] を引き継いだ [[Fetch Standard]] も、
[CODE(HTTP)[101]] 以外をエラーとして処理することを求めています [SRC[>>130]]。

[66] これは、[[サーバー]]が[[リダイレクト]]することで、[[スクリプト]]が意図しない相手と接続してしまうため危険であるのが理由 [SRC[>>60]]  とされています。
理論上は [CODE(DOMi)@en[[[WebSocket]]]] 以外の [[WebSocketクライアント]]はこの規定に従う義務はありませんが、
敢えて危険をおかして [[Webブラウザー]]と異なる動作を採る必要性も無さそうです。

;; [67] [[HTTP認証]]はそのような危険性は無さそうですが...

[115] [[Chrome]] は、 [CODE(HTTP)[[[401]]]] が返されたら、適切な [[credentials]]
を持っていなければエラーとして扱います。 [[Firefox]] は、通常の
[[HTTP認証]]の[[モーダルダイアログ]]を表示します。
[[Firefox]] は[[HTTP接続]]を再利用可能なら、再利用して再試行するようです。 [TIME[2015-08-04T13:33:15.200Z]]

@@ 407

[122] [[Firefox]] も [[Chrome]] も、 [CODE(HTTP)[[[1xx]]]] ([CODE(HTTP)[[[101]]]] 以外)
をエラーとして扱います。 [TIME[2015-08-17T08:38:07.400Z]]

[124] [[Chrome]] は[[ヘッダー]]を読み終わったところでエラーなら切断するようですが、
[[Firefox]] はそうでもないようで、しばらく (ずっとではない) 切断されません。 [TIME[2015-08-23T05:48:04.100Z]]

[68] [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]は、
内部状態として[DFN[[RUBYB[クライアント指定プロトコル群]@en[client-specified protocols]]]]を持ちます。

[54] 受信したデータが理解できない、想定外であるなどの理由があれば、
いつでも[[TCP接続]]を閉じることができます [SRC[>>53]]。

[116] [[Chrome]] は [CODE(HTTP)@en[[[Content-Length:]]]] [[ヘッダー]]について、
通常の[[HTTP応答]]同様の検査を行うようです。 [TIME[2015-08-04T13:46:51.00Z]]

[120] [[応答]]の [CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] の値の検査を
[[Chrome]] は行いますが、 [[Firefox]] は行わないようです。
なお [[Chrome]] は ([[RFC]] と異なり) 値を[[リスト]] ([CODE[#]])
として構文解析するようです。 [TIME[2015-08-15T14:15:24.600Z]]

[121] [[応答]]の [CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]] の値が[[空文字列]]の時、
[[Firefox]] はエラーにせず、 [[Chrome]] はエラーにします。値が [CODE[,]]
だけの時 (空のリストだが空文字列でない時)、 [[Firefox]] はエラーにし、
[[Chrome]] はエラーにしません。 [[RFC]] に従うならどちらもエラーになるべきです。 [TIME[2015-08-15T14:25:48.600Z]]

[123] [[Chrome]] は接続成功時と [CODE(HTTP)[[[401]]]] 時に [CODE(HTTP)@en[[[Set-Cookie:]]]]
を処理するようです。 [[Firefox]] は接続が成功か否かや[[状態符号]]に関わらず、
[CODE(HTTP)@en[[[Set-Cookie:]]]] を処理するようです。 [TIME[2015-08-17T08:46:08.900Z]]

[90] [[WebSocket]] の規定に従わない[[サーバー]]は、[[クライアント]]からの要求を待たずに[[応答]]
(や[[応答]]になっていないデータ) を送信するかもしれません。[[クライアント]]はそれに特別な対処を行う必要はありませんが、 >>54 を根拠に切断しても良いのかもしれません。

[HISTORY[
[180] かつて [[RFC]] は[[クッキー]]の処理のため[[応答]]の処理で
[DFN[[[Cookies Set During the Server's Opening Handshake]]]]
という語を使い、これを [[HTML Standard]] が利用する形を採っていました。
これは [[Fetch Standard]] との統合により死文化しました。
]HISTORY]

* サーバーの動作

[30] [[サーバー]]は、 [[TCP]] ないし [[TLS]] over [[TCP]] で[[ホスト]]と[[ポート]]の組を
[[listen]] していることが期待されています。

[16] [[サーバー]]は、[[クライアント]]との[[TCP接続]]が確立されたら、
次のようにしなければ[['''なりません''']]。

[FIG(steps)[
= [19] [[TLS]] を使う場合、[[TLS handshake]] を行います [SRC[>>1]]。
[[TLSクライアント認証]]を使っても構いません [SRC[>>51]]。
失敗なら、[[接続]]を閉じ、停止します [SRC[>>1]]。
= [18] [[要求]]を受信したら、
その一部または全部を構文解析して、必要な情報を取得します。
次のような問題があれば:
[FIG(list)[
- [36] [[プロトコルの版]]が [[HTTP/1.1]] [[以上]]でない場合
- [37] [[要求メソッド]]が [CODE(HTTP)@en[[[GET]]]] でない場合
- [38] [[要求対象]]が[[資源名]]か、[[資源名]]を含む [CODE(URI)@en[[[http:]]]]/[CODE(URI)@en[[[https:]]]] の[[絶対URL]]でない場合
(構文的に正しくない場合を含む。)
- [39] [CODE(HTTP)@en[[[Host:]]]] が無いか、構文的に正しくないか、
[[サーバー]]の [[authority]] でない場合
- [40] [CODE(HTTP)@en[[[Upgrade:]]]] が無いか、 [CODE(HTTP)@en[[[websocket]]]]
([[ASCII大文字・小文字不区別]]) を含まない場合
- [41] [CODE(HTTP)@en[[[Connection:]]]] が無いか、 [CODE(HTTP)@en[[[Upgrade]]]]
([[ASCII大文字・小文字不区別]]) を含まない場合
- [42] [CODE(HTTP)@en[[[Sec-WebSocket-Key:]]]] が無いか、
16バイトの値を [[Base64]] 符号化したもので無い場合
- [43] [CODE(HTTP)@en[[[Sec-WebSocket-Version:]]]] が無いか、
[CODE(HTTP)[[[13]]]] で無い場合
- [44] [CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] や
[CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]]
がある場合で、構文的に正しくない場合
- [45] その他[[クッキー]]や[[HTTP認証]]などの既知の[[ヘッダー]]で構文などが正しくないものがある場合
]FIG]
== [98] エラーを表す [CODE(HTTP)[[[400]]]] [[応答]]など適切な[[HTTP応答]]を返します。 [SRC[>>1]]
==- [22] [CODE(HTTP)@en[[[Sec-WebSocket-Version:]]]] [[ヘッダー]]の値が
[N[13]] でなければ、
[CODE(HTTP)[[[426]]]] [[応答]]など適切な[[応答]]を返します。
[CODE(HTTP)@en[[[Sec-WebSocket-Version:]]]] に対応する版番号を指定します。 [SRC[>>1]]
== [99] [[Abort the WebSocket Connection]] します。
== [103] 停止します。
= [20] 必要に応じて、
== [100] [CODE(HTTP)@en[[[WWW-Authenticate:]]]] [[ヘッダー]]を検査して
[CODE(HTTP)@en[[[401]]]] [[応答]]を返したり、
[[クッキー認証]]など適切な[[認証]]を行い [SRC[>>51]] 適切なエラーの[[応答]]を返したり、
[CODE(HTTP)[[[3xx]]]] [[応答]]で[[HTTPリダイレクト]]したりします。 [SRC[>>1]]
== [102] [[Abort the WebSocket Connection]] します。
== [104] 停止します。
= [21] [CODE(HTTP)[[[Origin:]]]] [[ヘッダー]]を検査して不適切なら、
== [95] [CODE(HTTP)[[[403]]]] [[応答]]など適切な[[応答]]を返します [SRC[>>1, >>50]]。
== [96] [[Abort the WebSocket Connection]] します。
== [105] 停止します [SRC[>>1]]。
= [23] [[要求対象]]から得た[[資源名]]で示されるものが利用できないなら、
== [93] [CODE(HTTP)[[[404]]]] [[応答]]など適切な[[応答]]を返します。[SRC[>>1]]
== [94] [[Abort the WebSocket Connection]] します。
== [106] 停止します [SRC[>>1]]。
= [24] [CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] [[ヘッダー]]の値から得られた[[部分プロトコル]]群から、
[[サーバー]]が承認するもののみのリストを得ます。該当するものがなければ、 [[null]] とします。 [SRC[>>1]]
= [25] [CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]] [[ヘッダー]]の値から得られたプロトコル拡張群から、
[[サーバー]]が承認するもののみのリストを得ます。該当するものがなければ、 [[null]] とします。 [SRC[>>1]]
= [26] [[HTTP応答]]を送ります。 [SRC[>>1]]
[FIG(list members)[
[FIGCAPTION[
[[HTTP応答]]
]FIGCAPTION]
:[[状態符号]]:[CODE(HTTP)[[[101]]]]。
:[CODE(HTTP)@en[[[Upgrade]]]]:[CODE(HTTP)@en[[[websocket]]]]。
:[CODE(HTTP)@en[[[Connection]]]]:[CODE(HTTP)@en[[[Upgrade]]]]。
:[CODE(HTTP)@en[[[Sec-WebSocket-Accept]]]]:
[[要求]]の [CODE(HTTP)@en[[[Sec-WebSocket-Key:]]]] の値に
[CODE(HTTP)@en[[[258EAFA5-E914-47DA-95CA-C5AB0DC85B11]]]] を連結し、
[[SHA-1]] を計算し、[[RFC 4648]] [[Base64]] 符号化したもの。
:[CODE(HTTP)@en[[[Sec-WebSocket-Protocol]]]]:>>24 で得られた[[部分プロトコル]]の[[リスト (HTTP)]] ([CODE(HTTP)[#]]) ([[null]] でない場合のみ)。
:[CODE(HTTP)@en[[[Sec-WebSocket-Extensions]]]]:>>25 で得られたプロトコル拡張 ([[null]] でない場合のみ)。
[[引数]]は、[[拡張]]の規定に従い適切に決定します [SRC[>>49]]。
(複数の本[[ヘッダー]]があっても構いません。)
]FIG]
= [27] [[WebSocket接続]]の状態を [CODE(HTTP)[[[OPEN]]]] とします。 [SRC[>>1]]
= [88] 以後受信データは[[WebSocketメッセージ受信]]の処理を行います。
]FIG]

;; [97] サーバーのエラー処理について [[RFC]] は明確に記述していませんが、
他の部分の記述から、 [[Abort the WebSocket Connection]] を呼び出すことが想定されているようです。

;; [107] [CODE(HTTP)@en[[[Host:]]]] と[[要求対象]]の衝突時の処理を [[RFC]]
は明記していませんが、後の [[RFC 723x]] による [[HTTP]] における処理
([[要求対象]]参照。) に従うべきと思われます。

[17] [CODE(HTTP)@en[[[Origin:]]]] [[ヘッダー]]が無ければ、
[[Webブラウザー]]からの[[要求]]と解釈する[['''べきではありません''']] [SRC[>>1]]。

[59] なお [[Webブラウザー]]以外の[[クライアント]]は、好きな [CODE(HTTP)@en[[[Origin:]]]]
を送ることができます。[[クライアント]]が [[Webブラウザー]]であることを判定する方法はありません
([[Webブラウザー]]でないことは >>17 の通り判定できますが)。 
[CODE(HTTP)@en[[[Origin:]]]] は [[Webブラウザー]]上で適切なアクセス元からの接続かどうかを判断するために使うことができますが、
非 [[Webブラウザー]]に対する[[認証]]のような仕組みとして用いることはできません。

[87] [[サーバー]]が返す[[応答]]に他の[[HTTPヘッダー]]を指定できるかどうか、
[[RFC]] [SRC[>>1]] は明記していません。 [[HTTPサーバー]]としては [CODE[Server:]]
や [CODE[Date:]] のような必須の[[ヘッダー]]が他にありますし、
受信する[[クライアント]]の側では [CODE[Set-Cookie:]] や [[HSTS]] や [[UIR]]
などの一般的な[[HTTPヘッダー]]の処理を行いますから、
現実問題としてこれらを使うことは可能です。しかし [CODE[Upgrade:]] や
[CODE[Content-Language:]] や [CODE[Refresh:]] のようにここでの意味が明確でないものは、
使うべきではないでしょう。

[7] この手順の途中で停止する時は、[[WebSocket接続を閉じる]]ものと思われます。

[55] 受信したデータが理解できない、想定外であるなどの理由があれば、
いつでも[[TCP接続]]を閉じることができます [SRC[>>53]]。
不正なデータを受信した時は、適切な [[HTTP応答]]を送信する[['''べきです''']] [SRC[>>53]]。

[28] 状態が [CODE(HTTP)[[[OPEN]]]] となると、データを送受信できます [SRC[>>1]]。

[48] この手順を完走したら、[[WebSocket接続確立]]となるものと思われます。

;; [112] [[TLS]] の場合については [[HTTPS]] も参照。

-*-*-

[29] [[要求本体]]が含まれる場合の[[サーバー]]の処理について明文規定はありません。
正常な[[クライアント]]の [[WebSocket接続の確立]]では、[[要求本体]]が含まれることはありません。
従って、[[要求本体]]が[[空]]でないなら、[[サーバー]]は [CODE[400]]
[[応答]]などを返して拒絶して構わないと思われます。
[[要求ヘッダー]]より後のデータが [[WebSocket]] データと考えている[[サーバー]]は、
[[要求本体]]が含まれているとおかしくなってしまいます。

-*-*-

[189] 実際には、
[[WebSocketハンドシェイク]]に失敗した時や、
[CODE[Sec-WebSocket-Protocol]] がサーバーの予期せぬ値 (または予期せぬ未指定)
だったとき、
[[応答]]を返すかわりにすぐに[[接続]]を閉じたり、
[CODE[RST]] を送ったりするサーバーもあるようです。
[TIME[2018-03-22T14:01:35.000Z]]

* [CODE(DOMi)@en[WebSocket]] インターフェイス [CODE(DOMa)@en[url]] 属性

[70] [CODE(DOMi)@en[WebSocket]] [[インターフェイス]]の
[DFN[[CODE(DOMa)@en[url][url (WebSocket)]]]] [[IDL属性]]は、
[[文脈オブジェクト]]の [F[[RUBYB[URL]@en[url]]][url (WebSocket)]] 
に[[URLの直列化]]を適用した結果を返さなければ[MUST[なりません]] [SRC[>>69]]。
この値は常に [CODE(URI)@en[[[ws:]]]] または [CODE(URI)@en[[[wss:]]]]
の[[絶対URL]]です。

[176] [CODE(DOMi)@en[WebSocket]] [[オブジェクト]]は、
[DFN[[F[[RUBYB[URL]@en[url]]][url (WebSocket)]]]] を持ちます [SRC[>>53]]。
これは [CODE(DOMi)@en[WebSocket]] [[コンストラクター]]により設定され、
以後変化しません。接続先として指定された [[URL]] を表します。

[74] この[[IDL属性]]の[[データ型]]は、 [CODE(IDL)@en[USVString]] です [SRC[>>69]]。

* 歴史

** 誕生

@@

** Fetch 統合

[131] [[CSP]]、[[Mixed Content]]、[[Upgrade Insecure Requests]] は[[猿パッチ]]を定義し、
後にこれを [[RFC]] や [[HTML Standard]] と統合しようとしましたが、
[[IETF]] は仕様の改訂を行おうとしませんでした ([[WebSocket]] の歴史の項を参照)。

[133] 2016年3月、 [[RFC]] と [[HTML Standard]] にあった接続の確立に関わる部分がこれら[[猿パッチ]]を解消する形で
[[Fetch Standard]] の [[fetch]] アルゴリズムと統合される形で再定義され、
一連の処理の流れが明確化されました。 [[クッキー]]や [[HSTS]]
や [[Referrer Policy]] や [[Service Workers]]
など [[fetch]] に関わる諸仕様との関係も明確になりました。

[REFS[
- [108] [CITE@en[Mixed Content]] ([TIME[2015-05-17 18:30:14 +09:00]] 版) <https://w3c.github.io/webappsec/specs/mixedcontent/#websockets-integration>
- [46] [CITE@en[Upgrade Insecure Requests]] ([TIME[2015-10-07 03:24:10 +09:00]] 版) <https://w3c.github.io/webappsec-upgrade-insecure-requests/#websockets-integration>
- [165] [CITE@en[Content Security Policy]] ([TIME[2015-05-17 18:30:14 +09:00]] 版) <https://w3c.github.io/webappsec/specs/content-security-policy/#directive-connect-src>
- [449] [CITE@en[664284 – Add HSTS support for websockets]]
( ([TIME[2014-12-11 11:50:38 +09:00]] 版))
<https://bugzilla.mozilla.org/show_bug.cgi?id=664284>
- [450] [CITE@en[''''''[''''''chrome'''''']'''''' Revision 82069]]
( ([TIME[2014-12-11 11:51:03 +09:00]] 版))
<http://src.chromium.org/viewvc/chrome?revision=82069&view=revision>
- [451] [CITE@en[Bug 27554 – After the WebSocket object is returned we should probably integrate with HSTS. For Fetch that happen ''''''[''''''...'''''']'''''']]
( ([TIME[2014-12-11 11:51:13 +09:00]] 版))
<https://www.w3.org/Bugs/Public/show_bug.cgi?id=27554>
- [114] [CITE[Issue 174956 - chromium - Websocket request does not send Accept-Language header - An open-source project to help move the web forward. - Google Project Hosting]]
([TIME[2015-04-24 20:08:54 +09:00]] 版)
<https://code.google.com/p/chromium/issues/detail?id=174956>
- [129] [CITE@en[Apply HSTS to WebSocket · whatwg/html@8b46c20]]
([TIME[2015-09-19 13:23:28 +09:00]] 版)
<https://github.com/whatwg/html/commit/8b46c205cc2b54bb4d57bd7ad12baf9492e40edd>
- [135] [CITE@en[Define the WebSocket client handshake in terms of Fetch · whatwg/fetch@ce16adc]] ([TIME[2016-03-17 00:52:25 +09:00]] 版) <https://github.com/whatwg/fetch/commit/ce16adc5c13b56b0f8a6487c71fb030a9bccafce>
- [136] [CITE@en[Fix #238: excellent WebSocket nits · whatwg/fetch@a8392d0]] ([TIME[2016-03-17 00:52:42 +09:00]] 版) <https://github.com/whatwg/fetch/commit/a8392d0933ea7d771ed86acd4734e735eb81e8ac>
- [166] [CITE@en[Fix #240: one more WebSocket nit · whatwg/fetch@f3e1da8]] ([TIME[2016-03-17 00:53:35 +09:00]] 版) <https://github.com/whatwg/fetch/commit/f3e1da80e141cf2c92ba921ed07873ea726cf9a9>
- [167] [CITE@en[Define the WebSocket subprotocol check as part of response validation · whatwg/fetch@08f8560]] ([TIME[2016-03-17 00:53:53 +09:00]] 版) <https://github.com/whatwg/fetch/commit/08f8560f6cecaedc1c3372f9fff1ee39c3bfc86f>
- [168] [CITE@en[Define permessage-deflate extension and cache-mode for WebSocket · whatwg/fetch@267a8ef]] ([TIME[2016-03-17 00:54:41 +09:00]] 版) <https://github.com/whatwg/fetch/commit/267a8effeacfe5d641010d1d4f4609f9602cdab6>
]REFS]

[169] [CITE@en[WebSocket: leave port blocking to the network layer (Fetch) · whatwg/html@17336ad]]
([TIME[2016-03-23 21:18:12 +09:00]] 版)
<https://github.com/whatwg/html/commit/17336ad69be4744dfc17194f2ee51bd730ca4d93>

[79] [CITE@en[Update WebSocket to use Fetch's WebSocket alterations · whatwg/html@3dadbca]]
([TIME[2016-03-28 22:55:32 +09:00]] 版)
<https://github.com/whatwg/html/commit/3dadbcad063a10b586ef52dd4b427aa339048ee7>

[185] [CITE@en[Dealing with websockets #61]]
( ([[deian]]著, [TIME[2016-05-17 16:28:06 +09:00]]))
<https://github.com/w3c/webappsec-cowl/commit/05757139e1a8a4e842d7d43d2acc91d1cf62cce9>

[FIG(quote)[
[FIGCAPTION[
[3] [CITE@en[RFC 7977 - The WebSocket Protocol as a Transport for the Message Session Relay Protocol (MSRP)]]
([TIME[2016-10-04 23:50:28 +09:00]])
<https://tools.ietf.org/html/rfc7977#section-7>
]FIGCAPTION]

> 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.
> 

]FIG]


[4] [CITE@en[Properly set the Origin header for WebSocket requests]]
([[nox]]著, [TIME[2017-03-27 20:35:48 +09:00]])
<https://github.com/whatwg/fetch/commit/406c5a60595c63d323693050b45d40823933e185>

[57] [CITE@en[426736 - WebSocket connections not using configured system HTTPS Proxy in MacOS - chromium - Monorail]]
([TIME[2017-08-11 16:57:51 +09:00]])
<https://bugs.chromium.org/p/chromium/issues/detail?id=426736>

[91] [CITE@en[WebSocket connections don't appear to send referrers · Issue #630 · whatwg/fetch]]
([TIME[2017-11-16 18:26:47 +09:00]])
<https://github.com/whatwg/fetch/issues/630>

[92] [CITE@en[WebSocket handshake has no referrer]]
([[annevk]]著, [TIME[2017-11-18 00:07:59 +09:00]])
<https://github.com/whatwg/fetch/commit/60db35ed0d18c6636f002da91593bfa5ee58494e>

[187] [CITE@en[WebSocket handshake has no referrer by annevk · Pull Request #632 · whatwg/fetch]]
([TIME[2017-11-18 14:44:21 +09:00]])
<https://github.com/whatwg/fetch/pull/632>

[188] [CITE@en[WebSocket connections don't appear to send referrers · Issue #630 · whatwg/fetch]]
([TIME[2017-11-18 14:45:00 +09:00]])
<https://github.com/whatwg/fetch/issues/630>

[190] [CITE@en[Stop saying HTTP authentication over WebSocket is disallowed]]
([[ricea]]著, [TIME[2018-06-16 23:40:25 +09:00]])
<https://github.com/whatwg/fetch/commit/8b070f14a1fb2d1f8f4d07e1902a40a14f77b060>

[191] [CITE@en[WebSocket: "HTTP authentication will not function" is not correct · Issue #565 · whatwg/fetch]]
([TIME[2018-06-17 12:34:57 +09:00]])
<https://github.com/whatwg/fetch/issues/565>

[192] [CITE@en[Stop saying WebSocket auth is disallowed by ricea · Pull Request #761 · whatwg/fetch]]
([TIME[2018-06-17 12:35:31 +09:00]])
<https://github.com/whatwg/fetch/pull/761>

[193] [CITE@en[Use forgiving-base64 encode on WebSocket's keyValue]]
([[Garee]]著, [TIME[2018-12-13 18:05:56 +09:00]])
<https://github.com/whatwg/fetch/commit/e26376910a3f1b230177104759de8fcf33a02f2e>

[194] [CITE@en[Use forgiving-base64 encode for WebSocket · Issue #712 · whatwg/fetch]]
([TIME[2019-06-01 11:16:56 +09:00]])
<https://github.com/whatwg/fetch/issues/712>

[195] [CITE@en[Change encoding of WebSocket keyValue to forgiving-base64-encoding by Garee · Pull Request #844 · whatwg/fetch]]
([TIME[2019-06-01 11:17:12 +09:00]])
<https://github.com/whatwg/fetch/pull/844>