/
314.txt
617 lines (539 loc) · 38.5 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
605
606
607
608
609
610
611
612
613
614
615
616
617
[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]
* クライアントの動作
** コンストラクター
[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] 新しい [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] [VAR[[[secure]]]] が[[偽]]の場合、
== [125] [[入口設定群オブジェクト]]の[[起源]]が [[URL scheme]] を持ち、それが[[保安プロトコル]]である場合、
[CODE(DOMe)@en[[[SecurityError]]]] [[例外]]を投げ、停止します。 [SRC[>>60]]
== [126] [VAR[[[ホスト]]]]が[[ドメイン]]であり、
[[既知HSTSホストドメイン名一致]] ([CODE(HTTP)@en[[[includeSubDomains]]]]
[[指令]]ありのものと[[超ドメイン一致]]するか、有無に関わらず[[合同一致]]) するなら、 [VAR[>>60]]
=== [127] [VAR[[[secure]]]] を[[真]]に設定します。
=== [128] [VAR[[[ポート]]]]が [CODE[[[80]]]] なら、[CODE[[[443]]]] に設定します。
= [80] 第2引数から得られた[[配列]]の要素のいずれかが[[部分プロトコル]]名として構文的に正しくなければ、
[CODE(DOMe)@en[[[SyntaxError]]]] [[例外]]を投げ、停止します。 [SRC[>>60]]
= [81] [VAR[[[起源]]]]を、[[入口設定群オブジェクト]]の[[起源]]の[[ASCII直列化]]したものに設定します。 [SRC[>>60]]
= [87] [[文書環境]]の場合、[CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]を、本[[コンストラクター]]が属する
[CODE(DOMi)@en[[[Window]]]] [[オブジェクト]]の [CODE(DOMi)@en[[[WebSocket]]]]
のリストに[[弱い参照]]として追加します。 ([[unloading document cleanup steps]] で参照されます。)
= [82] [CODE(DOMi)@en[[[WebSocket]]]] オブジェクトを返します。 [SRC[>>60]]
]FIG]
[83] 更に、[[並列に]]、次のようにしなければ[MUST[なりません]]。 [SRC[>>60]]
[FIG(steps)[
= [84] [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]の[[クライアント指定プロトコル群]]を、
第2引数から得られた[[配列]]に設定します。
= [85] [[WebSocket接続の確立]]を行います。
[FIG(list members)[
:[[ホスト]]:[VAR[[[ホスト]]]]
:[[ポート]]:[VAR[[[ポート]]]]
:[[資源名]]:[VAR[[[資源名]]]]
:[[secure]]:[VAR[[[secure]]]]
:[[部分プロトコル]]群:[[クライアント指定プロトコル群]]
:[[拡張]]:空のリスト
:[[適切なクッキーを送るヘッダー群]]:第1引数で指定された [[URL]]
について[[利用者]]の[[クッキーストア]]から計算した [[cookie-string]] が値である
[CODE(HTTP)@en[[[Cookie:]]]] [[ヘッダー]] ([[「非HTTP」API]]では''無い'')
]FIG]
]FIG]
** 接続の選択
[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] 他の接続を待ちます (>>3)。
= [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[資源名]]を定義していません。
[3] 他の接続を待つ場合は、次のようにしなければ[['''なりません''']] [SRC[>>1]]。
[FIG(steps)[
= [[ホスト]]から、 [[IPアドレス]]を得ます ([[名前解決]])。
= [[ホスト]]から[[IPアドレス]]を得られない場合 (例えば[[名前解決]]を[[プロキシ]]に委ねている場合)、
== [VAR[host]] を、[[ホスト]]に設定します。
== [VAR[n]] を、十分小さな値とする[['''べきです''']]。例えば開いている[[タブ]]数を考慮して決めます。
= それ以外の場合、
== [VAR[host]] を、[[IPアドレス]]に設定します。
== [VAR[n]] を、1 に設定します。
= 同じ [VAR[host]] と[[ポート]]への状態が [CODE[[[CONNECTING]]]]
である[[WebSocket接続]]があるか調べます。
= その数が [VAR[n]] [[未満]]となるまで、待ちます。
]FIG]
;; [4] この制限は、[[著者]]が多数の [[WebSocket接続]]を開いて [[DoS攻撃]]することを難しくするためのものです
[SRC[>>1]]。
;; [57] [[ホスト]]のみで制限する実装と[[ホスト]]と[[ポート]]の組で制限する実装があります
[SRC[>>56]]。
;; [29] これをうまく使うとある[[ホスト]]とある[[ホスト]]が同じ [[IPアドレス]]かどうかをある程度の精度で推測できるかもしれませんが、
それによって何か問題になることはあまり無さそうです。
[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]
** 接続の確立
[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:]]
などの[[ヘッダー]]が追加されます。
;; [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]]
;; [110] [[ヘッダー]]によっては、またどの[[ヘッダー]]を送信するかは、
[[fingerprinting vector]] です。
@@
[[クライアント]]は、[DFN[[RUBYB[WebSocket接続の確立]@en[Establish a WebSocket Connection]]]]で、
次のようにしなければ[['''なりません''']] [SRC[>>1]]。
@@
[8] この手順は、入力として次のものを受け取ります。
[FIG(list members)[
:[[ホスト]]:接続先の[[ホスト名]]です。必須です。
:[[ポート]]:接続先の[[ポート番号]]です。必須です。
:[[資源名]]:接続先の[[資源]]を表します。必須です。
:[[secure]]:[[TLS]] を使うか否かを表します。
:[[起源]]:
[10] [[Webブラウザー]]は、[[起源]]を指定しなければ[['''なりません''']]。
これは接続を開いた元を表すものです。
それ以外の[[クライアント]]は、[[起源]]を指定しても構いません。 [SRC[>>1]]
:[[部分プロトコル]]群:
[11] [[クライアント]]が通信したい[[部分プロトコル]]を優先順に指定するものです。
各[[部分プロトコル]]名は、[[字句]]です。
同じ[[部分プロトコル]]を複数指定しては[['''なりません''']]。 [SRC[>>1]]
:拡張:
[12] [[クライアント]]が利用したい[[WebSocket拡張]]を指定するものです [SRC[>>1]]。
:[DFN[[RUBYB[適切なクッキーを送るヘッダー群]@en[headers to send appropriate cookies]]]]:
送信するべき [CODE(HTTP)@en[[[Cookie:]]]] [[ヘッダー]]を指定するものです。
[[HTML Standard]] が参照していますが、 [[RFC 6455]] にはありません。
([[WHATWG]] 時代には存在していたものが、 [[IETF]] で削除されたようです。 [[IETF]]
ではよくあることです。)
]FIG]
@@
[FIG(steps)[
= 入力として与えられた[[ホスト]]、[[ポート]]、[[資源名]]、[[secure]]
フラグが [CODE(URI)@en[[[ws:]]]]/[CODE(URI)@en[[[wss:]]]] [[URL]]
の構成要素として正しいかどうかを検査します。
正しくなければ[[WebSocket接続失敗]]を実行し、停止します。
=
@@
= [[RFC 2616]] [[HTTP要求]]を送信します。 (なお、バッファリングなどで遅延しても構いません [SRC[>>15]]。)
[FIG(list members)[
[FIGCAPTION[
[[HTTP要求]]
]FIGCAPTION]
:[[要求メソッド]]:[CODE(HTTP)@en[[[GET]]]]
:[[要求対象]]:[[資源名]]、または[[ホスト]]、[[ポート]]、[[資源名]]を使った [CODE(URI)@en[[[http:]]]]/[CODE(URI)@en[[[https:]]]] [[URL]] のいずれか
:[[プロトコルの版]]:[[HTTP/1.1]] [[以上]]
:[CODE(HTTP)@en[[[Host]]]]:[[ホスト]]と、[[既定のポート番号]]で無い場合、
[CODE(HTTP)[[[:]]]] と[[ポート]]
:[CODE(HTTP)@en[[[Origin]]]]:([[起源]]の指定がある場合のみ) [[起源]]の[[ASCII直列化]]
:その他:[[HTTP認証]]その他の[[HTTPヘッダー]]を含めて構いません。
[CODE(HTTP)@en[[[Accept-Language:]]]] [[ヘッダー]]を含めるべきです [SRC[>>114]]。
[[適切なクッキーを送るヘッダー群]]を (あれば) 含めなければなりません [SRC[仕様無し]]。
]FIG]
= [[応答]]を待ちます。
]FIG]
[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接続失敗]]を実行し、停止します。
@@
= [[WebSocket接続]]の状態を、 [CODE[[[OPEN]]]] に設定します。
= [DFN[[RUBYB[利用中拡張群]@en[Extensions In Use]]]]を、[[応答]]の
[CODE(HTTP)@en[[[Sec-WebSocket-Extensions:]]]] [[ヘッダー]]の値
(なければ [[null]]) に設定します。
= [DFN[[RUBYB[利用中部分プロトコル]@en[Subprotocol In Use]]]]を、[[応答]]の
[CODE(HTTP)@en[[[Sec-WebSocket-Protocol:]]]] [[ヘッダー]]の値
(なければ [[null]]) に設定します。
= [[応答]]の [CODE(HTTP)@en[[[Set-Cookie:]]]] [[ヘッダー]]で[[クッキー]]の設定が指示されていれば、
これを[DFN[[[Cookies Set During the Server's Opening Handshake]]]]とします。
= [DFN[[RUBYB[WebSocket接続確立]@en[The WebSocket Connection is Established]]]]とします。
= [86] [[[CODE(DOMi)@en[WebSocket]]コンストラクター]]から本処理が始まった場合、
[[タスク]]を追加します [SRC[>>77]]。
[FIG(list members)[
[FIGCAPTION[
[[タスク]]
]FIGCAPTION]
:[[タスク源]]:[[WebSocketタスク源]]
:処理:
[FIG(steps)[
= [[クライアント指定プロトコル群]]が空のリストではなく、
[[利用中部分プロトコル]]が [[null]] なら、
== [[WebSocket接続失敗]]を実行します。
== [CODE(DOMa)@en[[[readyState]]]] を、 [CODE(DOM)@en[[[CLOSING]]]] ([CODE(DOM)[[[2]]]])
に設定します。
== 停止します。
= [CODE(DOMa)@en[[[readyState]]]] を、 [DFN[[CODE(DOM)@en[[[OPEN]]]]]] ([CODE(DOM)[[[1]]]])
に設定します。
= [[利用中拡張群]]が [[null]] でなければ、
== [CODE(DOMa)@en[[[extensions]]]] を、[[利用中拡張群]]に設定します。
= [[利用中プロトコル]]が [[null]] でなければ、
== [CODE(DOMa)@en[[[protocol]]]] を、[[利用中プロトコル]]に設定します。
= [[Cookies Set During the Server's Opening Handshake]] があれば、
[CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]の [CODE(DOMa)@en[[[url]]]] について、
これを [[set-cookie-string]] として受信したものとして処理します。
= [[単純イベントを発火]]します。
[FIG(list members)[
[FIGCAPTION[
[[単純イベント]]
]FIGCAPTION]
:[[イベント型]]:[CODE(DOMe)@en[[[open]]]]
:[[イベント対象]]:[CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]
]FIG]
]FIG]
]FIG]
= [89] 以後受信データは[[WebSocketメッセージ受信]]の処理を行います。
]FIG]
@@
[64] 入力が [CODE(URI)@en[[[ws:]]]]/[CODE(URI)@en[[[wss:]]]] として正しくなければ失敗となります。
[[HTML Standard]] は [[URL Standard]] に基づき[[正準化]]した値を使っており、
[[RFC]] は [[RFC 3986]] [[URI]] を参照して規定しているため、両者には違いが生じる可能性があります。
[[正準化]]が失敗しなかったということは、[[ホスト]]や[[ポート]]は [[RFC 3986]]
[[URI]] としても適切な値になっているはずです。[[資源名]]は、 [[RFC 3986]]
では認められない壊れた[[パーセント符号化]]もどきが含まれる可能性が残ります。
実装がこれをどう扱うのかは不明です。
@@
[14] [[要求対象]]は、直接接続なら[[資源名]]のみ、 [[プロキシ]]接続なら
[[HTTP]] [[URL]] と思われますが、明記されていません。どちらでも良いということでしょうか
(それは一般的な慣習とは異なります)。また [CODE(URI)@en[[[ws:]]]]/[CODE(URI)@en[[[wss:]]]]
がそれぞれ [CODE(URI)@en[[[http:]]]]/[CODE(URI)@en[[[https:]]]] に変換されるものと思われますが、
明記されていません。
[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 を根拠に切断しても良いのかもしれません。
* サーバーの動作
[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]]
== [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]]。
= [22] [CODE(HTTP)@en[[[Sec-WebSocket-Version:]]]] [[ヘッダー]]の値が
[CODE[[[13]]]] でなければ、
== [91] [CODE(HTTP)[[[426]]]] [[応答]]など適切な[[応答]]を返します。
[CODE(HTTP)@en[[[Sec-WebSocket-Version:]]]] に対応する版番号を指定します。 [SRC[>>1]]
== [92] [[Abort the WebSocket Connection]] します。
== [52] 停止します [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]] も参照。
* [CODE(DOMi)@en[WebSocket]] インターフェイス [CODE(DOMa)@en[url]] 属性
[70] [CODE(DOMi)@en[[[WebSocket]]]] [[インターフェイス]]の
[DFN[[CODE(DOMa)@en[[[url]]]]]] [[IDL属性]]は、
[[構築]]時に設定された値を返さなければ[['''なりません''']] [SRC[>>69]]。
この値は常に [CODE(URI)@en[[[ws:]]]] または [CODE(URI)@en[[[wss:]]]]
の[[絶対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>