/
314.txt
604 lines (512 loc) · 37.2 KB
/
314.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
[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]]。
[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)[
[FIGCAPTION[
[[要求]]
]FIGCAPTION]
: [F[URL][要求URL]] : [VAR[URL]]
: [F[クライアント][要求のクライアント]] : [VAR[クライアント]]
: [F[skip-service-worker flag]] : [[真]]
: [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]] を [[RFC 4648]] [[Base64]] 符号化したもの
:[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]]、
[[クッキー]]、[[Referrer Policy]]、 [[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:]]]] は、[[Referrer Policy]] の適用対象となります。
[58] [[要求ヘッダー]]には [CODE(HTTP)@en[[[User-Agent:]]]] [SRC[[[Chrome]], [[Firefox]]]] や
[CODE(HTTP)@en[[[Referer:]]]] や [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ブラウザー]]に対する[[認証]]のような仕組みとして用いることはできません。
[28] 状態が [CODE(HTTP)[[[OPEN]]]] となると、データを送受信できます [SRC[>>1]]。
[7] この手順の途中で停止する時は、[[WebSocket接続を閉じる]]ものと思われます。
[55] 受信したデータが理解できない、想定外であるなどの理由があれば、
いつでも[[TCP接続]]を閉じることができます [SRC[>>53]]。
不正なデータを受信した時は、適切な [[HTTP応答]]を送信する[['''べきです''']] [SRC[>>53]]。
[48] この手順を完走したら、[[WebSocket接続確立]]となるものと思われます。
;; [112] [[TLS]] の場合については [[HTTPS]] も参照。
-*-*-
[29] [[要求本体]]が含まれる場合の[[サーバー]]の処理について明文規定はありません。
正常な[[クライアント]]の [[WebSocket接続の確立]]では、[[要求本体]]が含まれることはありません。
従って、[[要求本体]]が[[空]]でないなら、[[サーバー]]は [CODE[400]]
[[応答]]などを返して拒絶して構わないと思われます。
[[要求ヘッダー]]より後のデータが [[WebSocket]] データと考えている[[サーバー]]は、
[[要求本体]]が含まれているとおかしくなってしまいます。
* [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>