-
Notifications
You must be signed in to change notification settings - Fork 4
/
352.txt
321 lines (235 loc) · 18.8 KB
/
352.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
* 仕様書
[REFS[
- [62] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-2.2>
- [63] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-4.1>
- [64] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-5.1>
- [1] '''[CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-5.2>'''
- [18] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-6.5.2>
- [70] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-6.8>
- [23] '''[CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-6.9>'''
- [56] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-7>
- [59] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-10.5>
- [60] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-10.8>
]REFS]
* フロー制御
[17] [[HTTP/2接続]]では複数の[[ストリーム]]を含めることができ、
複数のデータを[[多重化]]して[[並行]]に送受信することができます。
しかし[[受信者]]の[[メモリー]]などの[[資源]]は有限ですから、
[[HTTP]] や[[アプリケーション]]の処理能力を超えない配慮が必要となります。
[[フロー制御]]は、[[資源]]の制約がある[[エンドポイント]]を保護するためのものです [SRC[>>1]]。
[EG[
[2] [[接続]]内で過剰に[[ストリーム]]を使って[[多重化]]すると、
他の[[ストリーム]]の処理に干渉して処理しきれなくなるかもしれません [SRC[>>1]]。
]EG]
[EG[
[12] [[プロキシ]]は多くの[[接続]]で[[メモリー]]を共有する必要がありますし、
[[上流]]の[[接続]]が遅く[[下流]]の[[接続]]が速かったりすることもあります。 [SRC[>>1]]
]EG]
** 要件
[5] [[HTTP/2]] の[[フロー制御]]は、具体的なアルゴリズムは規定せず送受信者と[[フレーム]]が満たすべき要件のみを規定しています。
プロトコルを変更せずに適切な[[フロー制御]]アルゴリズムを実装できます [SRC[>>1]]。
[3] [[フロー制御]]は、[[接続]]全体に関するものと、その中の個別の[[ストリーム]]に関するもので
2段階で行われます [SRC[>>1, >>23]]。
;; [13] [[受信者]]がある[[ストリーム]]の処理はできないものの他の[[ストリーム]]は処理したい、
という場合にも対応できます [SRC[>>1]]。
[6] [[フロー制御]]は特定の[[接続]]について制御するもので、
単一の[[ホップ]] ([[エンドポイント]]間) に適用されます [SRC[>>1, >>23]]。
[[利用者エージェント]]から[[起源サーバー]]までの全体に適用されるものではありません。
[[中間器]]は、 [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を[[転送]]しません [SRC[>>23]]。
(もちろん、間接的に影響を及ぼすことはあります [SRC[>>23]]。)
[4] [[フロー制御]]には、 [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を使います。
[[受信者]]は、[[ストリーム]]について、および[[接続]]全体について、
受信できる[[バイト]]数を[[広告]]します。これは [RUBYB[credit に基づく方式]@en[credit-based scheme]]です。
[SRC[>>1]]
[11] しかし [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]をいつ送信するか、
どのように値を決めるか、[[送信者]]が送信するかどう判断するかを [[HTTP/2]]
仕様としては規定していません。実装は適切な[[アルゴリズム]]を実装することができます。
[SRC[>>1]]
[8] 新しい[[ストリーム]]および[[接続]]全体の[[フロー制御窓]]の初期値は、
65535 [[バイト]]です。 [SRC[>>1]]
[7] [[フロー制御]]は[[受信者]]によって制御されます。
[[受信者]]は[[ストリーム]]について、また[[接続]]全体について、
任意の[[窓サイズ]]を設定することができます。
[[送信者]]は、[[受信者]]による[[フロー制御]]上の制限に従わなければ[['''なりません''']]。
[SRC[>>1]]
[10] [[フロー制御]]は、無効にはできません。 [SRC[>>1]]
[14] [[フロー制御]]が必要ない[[受信者]]は、データを受信する度に最大の
2[SUP[31]]-1 の[[フロー制御窓]]を[[広告]]することにより、
実質的に[[フロー制御]]を無効化できます。 [SRC[>>1]]
[9] [[フロー制御]]が適用されるかどうかは、[[フレーム型]]に依存します。
[RUBYB[[[フロー制御]]対象の[[フレーム]]]@en[flow-controlled frame]]は、 [CODE[[[DATA]]]] [[フレーム]]のみです。 [SRC[>>1, >>23]]
従って重要な制御フレームが[[フロー制御]]によりブロックされることはありません [SRC[>>1]]。
[16] [[帯域遅延積]]がわからない状態で[[フロー制御]]を行うと、
[[ネットワーク資源]]を十分使い切れないかもしれません。しかし[[帯域遅延積]]がわかっていたとしても、
[[フロー制御]]は難しいです。[[受信者]]は、 [[TCP]] [[受信バッファー]]からデータを随時読み込まなけれ[['''ばなりません''']]。
そうしなければ、 [CODE[[[WINDOW_UPDATE]]]] など重要な[[フレーム]]を読んで処理することができず、
[[デッドロック]]に陥る虞があります。 [SRC[>>1]]
** 送信者の処理
[15] [[送信者]]は、常に[[受信者]]が[[広告]]した[[フロー制御窓]]に従う必要があります。
[SRC[>>1]]
[[送信者]]は、[[受信者]]が[[広告]]した[[接続フロー制御窓]]と[[ストリームフロー制御窓]]のいずれかを超える長さの[[フロー制御]]対象の[[フレーム]]を送信しては[['''なりません''']] [SRC[>>23]]。
[[ストリームフロー制御窓]]の値が[[負]]の時も、送信しては[['''なりません''']] [SRC[>>23]]。
[40] [[送信者]]は、 [CODE[[[END_STREAM]]]] フラグが設定され長さが 0 の[[フレーム]]なら、
[[フロー制御窓]]に空きがなくても送信して構いません [SRC[>>23]]。
[41] [[送信者]]は、[[フロー制御]]対象の[[フレーム]]を送信したら、
[[接続フロー制御窓]]と[[ストリームフロー制御窓]]から送信した[[フレーム]]の
[[payload]] の長さ分を減らします [SRC[>>23]]。
** 受信者の処理
[25] [[フロー制御]]の適用対象でない[[フレーム型]]の[[フレーム]]は、
[[フロー制御]]の状態に関わらず受け入れて処理しなければ[['''なりません''']] [SRC[>>23]]。
[26] [[フロー制御]]により[[フレーム]]を受け入れられないのに[[フレーム]]を受信したら、
[[ストリームエラー]]または[[接続エラー]] [CODE[[[FLOW_CONTROL_ERROR]]]]
として構いません [SRC[>>23]]。
[36] [[フロー制御]]対象の[[フレーム]]を受信したら、
これを[[接続エラー]]として扱う場合を除き、
([[エラー]]である場合も含めて)
その[[接続フロー制御窓]]への貢献を勘案しなければ[['''なりません''']] [SRC[>>23]]。
[69] [[closed]] 状態の[[ストリーム]]で [CODE(HTTP)@en[[[DATA]]]]
[[フレーム]]を受信した場合でも、[[接続フロー制御窓]]には数えます [SRC[>>64]]。
;; [[ストリーム]]の状態遷移の項を参照。
[71] [CODE(HTTP)@en[[[GOAWAY]]]] [[フレーム]]の送信後で無視する[[ストリーム]]の
[CODE(HTTP)@en[[[DATA]]]] [[フレーム]]を受信した場合、
[[接続フロー制御窓]]には数えなければ[['''なりません''']] [SRC[>>70]]。
[42] [[フロー制御]]対象の[[フレーム]]の[[受信者]]は、データを消費して[[フロー制御窓]]の領域が解放される度に、
[CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を送信します。[[ストリームフロー制御窓]]と[[接続フロー制御窓]]について別々に送信します。
[SRC[>>23]]
* フロー制御窓
[38] [[送信者]]は、[DFN[[RUBY[[[窓]]][ウィンドウ]@en[window]]]]
([DFN[[RUBYB[フロー制御窓]@en[flow-control window]]]]) を持ちます。
[[フロー制御窓]]には、接続全体の[DFN[[RUBYB[接続フロー制御窓]@en[connection flow-control window]]]]と、
ストリーム毎の[DFN[[RUBYB[ストリームフロー制御窓]@en[stream flow-control window]]]]があります。
[[フロー制御窓]]は、単純な[[整数]]値で、[[送信者]]が転送できるデータの[[バイト]]数です。
これは[[受信者]]のバッファリング能力を表しています。 [SRC[>>23]]
[39] [[フロー制御]]の計算では、[[フレーム]]の[[ヘッダー]]は数えません [SRC[>>23]]。
[[payload]] の長さのみが対象となります。[[詰め]]は対象になります。
[49] [[フロー制御窓]]のサイズの初期値は、 65535 [[バイト]]です [SRC[>>23]]。
[50] [[接続]]の確立時に、[[接続フロー制御窓]]は初期値に設定されます [SRC[>>23]]。
;; [51] [CODE[[[SETTINGS]]]] [[フレーム]]による設定の変更の影響は受けません。
[52] [[ストリーム]]の生成時に、[[ストリームフロー制御窓]]は初期値に設定されます [SRC[>>23]]。
[46] [CODE[[[SETTINGS]]]] [[フレーム]]の
[CODE[[[SETTINGS_INITIAL_WINDOW_SIZE]]]] [[設定]]で、
初期値が変更されます [SRC[>>23]]。
[47] 初期値の設定が変更されると、既存の[[ストリーム]]の[[ストリームフロー制御窓]]もその差分に応じて調整しなければ[['''なりません''']] [SRC[>>23]]。
その結果値が[[負]]になることもあります [SRC[>>23]]。
;; [48] あるいは[[ストリームフロー制御窓]]は初期値からの差分として保持しておくのも良いかもしれません。
[54] [[フロー制御]]対象[[フレーム]]の[[受信者]]が現在より小さな[[フロー制御窓]]を使いたい時は、
[CODE[[[SETTINGS]]]] [[フレーム]]を送信できます。しかし、
その処理前に既にデータが送られている場合がありますから、
[[受信者]]は新しい[[フロー制御窓]]サイズより大きなデータを受信する準備をしなければ[['''なりません''']]。
[[受信者]]は [CODE[[[SETTINGS]]]] [[フレーム]]を削減した後も、
[[フロー制御]]の限界を超えた[[ストリーム]]の処理を継続して構いません。
その場合[[フロー制御窓]]に割り当てた領域を直ちに開放することはできません。
[CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を送らない限り[[送信者]]が送信を再開しないため、
処理が止まってしまう可能性もあります。[[誤り符号]] [CODE[[[FLOW_CONTROL_ERROR]]]]
の [CODE[[[RST_STREAM]]]] [[フレーム]]を送信することにしても構いません。 [SRC[>>23]]
;; [55] [[ストリームエラー]]とはされていませんが、違いがあるのかは不明です。
[61] [[フロー制御窓]]の管理方法は、[[fingerprinting vector]] です [SRC[>>60]]。
* [CODE[WINDOW_UPDATE]] フレーム
** 意味
[24] [CODE[[[WINDOW_UPDATE]]]] [[フレーム]] ([[フレーム型]] [CODE[[[0x8]]]])
は、[[フロー制御]]の実装に使います [SRC[>>23]]。
** 構文
[32] [[ストリーム識別子]]は、特定の[[ストリーム識別子]]を指定することもできますし、
[CODE[[[0x0]]]] により[[接続]]全体を表すこともできます [SRC[>>23]]。
[30] [[フラグ]]はありません [SRC[>>23]]。 0 でなければ[['''なりません''']] [SRC[>>63]]。
[[受信者]]は無視しなければ[['''なりません''']] [SRC[>>63]]。
[FIG(packet)[
:width:8
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
]FIG]
[27] [[payload]] は次の欄で構成されます。
[FIG(list members)[
:[28] [[R]]:
予約の1ビットのフラグです [SRC[>>23]]。
:[29] [DFN[[RUBYB[[[窓サイズ増分]]]@en[Window Size Increment]]]]:
[[符号無し]]31ビット[[整数]] ([[ネットワークバイト順]] [SRC[>>62]]) の欄で、
既存の[[フロー制御窓]]に加えて[[送信者]]が転送できる[[バイト]]数を表します [SRC[>>23]]。
[[合法]]な範囲は、 1 から 2[SUP[31]]-1 です [SRC[>>23]]。
]FIG]
[FIG(packet)[
:width:32
= 1 R
= 31 窓サイズ増分
]FIG]
** 文脈
[34] [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]は、 [CODE[[[END_STREAM]]]]
[[フラグ]]の設定された[[フレーム]]を送信した[[エンドポイント]]が送信できます。
(つまり [[half-closed (remote)]] や [[closed]] の[[ストリーム]]で受信する可能性があります。)
[SRC[>>23]]
[66] [[reserved (remote)]]、[[open]]、[[half-closed (local)]] で送信できます。
;; [67] [[half-closed (local)]] の時相手はまだデータを送信できる状態ですから、
必要に応じて [CODE(HTTP)@en[[[WINDOW_UPDATE]]]] [[フレーム]]は送らなければなりません
[SRC[>>64]]。
** 処理
[35] [[受信者]]は、 [[half-closed (remote)]] や [[closed]] で
[CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を受信しても、
エラーとしては[['''なりません''']] [SRC[>>23]]。
;; [68] しかし [[closed]] に遷移して十分な時間が経つと、エラーとなるかもしれません [SRC[>>64]]。
[65] [[half-closed (local)]] でも受信するかもしれません [SRC[>>64]]。
[33] [[受信者]]は、[[フロー制御窓]]の増分が 0 なら、
[[ストリームエラー]] ([[接続]]全体に対するものなら[[接続エラー]])
[CODE[[[PROTOCOL_ERROR]]]] としなければ[['''なりません''']] [SRC[>>23]]。
;; [78] [[ストリーム]]について、[[Firefox]] は仕様通り[[ストリームエラー]]
[CODE[[[PROTOCOL_ERROR]]]] とします。
[[Chrome]] は[[ストリームエラー]] [CODE[[[FLOW_CONTROL_ERROR]]]] にします。
[TIME[2015-10-03T12:59:17.300Z]]
;; [79] [[接続]]について、[[Firefox]] も [[Chrome]] も仕様通り[[接続エラー]]
[CODE[[[PROTOCOL_ERROR]]]] にします。 [TIME[2015-10-03T13:01:14.300Z]]
[37] [[payload]] の長さが 4 以外なら、[[接続エラー]] [CODE[[[FRAME_SIZE_ERROR]]]]
としなければ[['''なりません''']] [SRC[>>23]]。
[44] [[フロー制御]]対象の[[フレーム]]の[[送信者]]は、
[[フロー制御窓]]が 2[SUP[31]]-1 を超えることを認めては[['''なりません''']]。
そのように求める [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を受信したら、
[[接続]]または[[ストリーム]]を閉じなければ[['''なりません''']]。
[CODE[[[FLOW_CONTROL_ERROR]]]] [[誤り符号]]を使って
[CODE[[[GOAWAY]]]] [[フレーム]]か [CODE[[[RST_STREAM]]]] [[フレーム]]を送信します。
[SRC[>>23]]
;; [45] なぜか[[接続エラー]]や[[ストリームエラー]]とはされていませんが、
違いがあるのかは不明です。
;; [80] [[Chrome]] は接続について上限を超えるとき、
[[接続エラー]] [CODE[[[PROTOCOL_ERROR]]]] を送信します。
ストリームについては[[ストリームエラー]] [CODE[[[FLOW_CONTROL_ERROR]]]]
を送信します。[[Firefox]] はどちらも仕様通りです。 [TIME[2015-10-03T13:23:54.500Z]]
[43] [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]を受信したら、
[[フロー制御窓]]を指定に従い更新します [SRC[>>23]]。
[58] [CODE[[[WINDOW_UPDATE]]]] [[フレーム]]は、無駄な処理をさせるために濫用できます。
[[エンドポイント]]は利用状況を監視して制限する[['''べきです''']]。
[[接続エラー]] [CODE[[[ENHANCE_YOUR_CALM]]]] としても構いません。 [SRC[>>59]]
* 設定
[19] [[設定]] [DFN[[CODE[[[SETTINGS_INITIAL_WINDOW_SIZE]]]]]] ([CODE[[[0x4]]]]) は、
[[送信者]]の[[ストリーム]]レベルの[[フロー制御]]の[RUBYB[[[初期窓サイズ]]]@en[initial window size]]を[[バイト]]単位で表します [SRC[>>18]]。
;; [21] [[設定]]は、すべての[[ストリーム]]で共通です。
[20] 初期値は、 2[SUP[16]]-1 です [SRC[>>18]]。
[22] 最大[[フロー制御窓]]サイズである 2[SUP[31]]-1 より大きな値は、
[[接続エラー]] [DFN[[CODE[[[FLOW_CONTROL_ERROR]]]]]] としなければ[['''なりません''']]
[SRC[>>18]]。
[53] いずれかの[[フロー制御窓]]の値が最大サイズを超えるように
[CODE[[[SETTINGS_INITIAL_WINDOW_SIZE]]]] を変更しようとした場合は、
[[接続エラー]] [CODE[[[FLOW_CONTROL_ERROR]]]] としなければ[['''なりません''']] [SRC[>>23]]。
* 誤り符号 [CODE[FLOW_CONTROL_ERROR]]
[57] [[誤り符号]] [DFN[[CODE[[[FLOW_CONTROL_ERROR]]]]]] ([CODE[[[0x3]]]])
は、 [[peer]] が[[フロー制御プロトコル]]に違反していることを示します [SRC[>>56]]。
* 実装
[75] 具体的な動作はプラットフォームやメモリー利用状況などで異なるかもしれません。
[72] [[Chrome]] は[[接続序文]]の [CODE(HTTP)[[[SETTINGS]]]] で、
[CODE(HTTP)@en[[[SETTINGS_INITIAL_WINDOW_SIZE]]]] を 10485760 = 2[SUP[23]] + 2[SUP[21]]
に設定します。 [TIME[2015-10-03T12:26:55.000Z]]
[73] [[Chrome]] は[[接続序文]]直後に
[CODE[[[WINDOW_UPDATE]]]] で値 10420225 = 2[SUP[23]] + 2[SUP[21]] - (2[SUP[16]] - 1) を送信するようです。
[TIME[2015-10-03T12:29:07.600Z]]
[74] [[Firefox]] は[[接続序文]]の [CODE[[[SETTINGS]]]] で、
[CODE[[[SETTINGS_INITIAL_WINDOW_SIZE]]]] を 131072 = 2[SUP[16]] × 2
に設定します。 [TIME[2015-10-03T12:42:46.300Z]]
[76] [[Firefox]] は[[接続序文]]直後に [CODE[[[WINDOW_UPDATE]]]]
で値 268369921 = 2[SUP[28]] - (2[SUP[16]] - 1) を送信するようです。
[TIME[2015-10-03T12:40:55.200Z]]
[77] [[Firefox]] は [CODE[[[HEADERS]]]] の直後に、その[[ストリーム]]について
[CODE[[[WINDOW_UPDATE]]]] で値 268304384 = 2[SUP[28]] - 2[SUP[16]] × 2
を送信するようです。 [TIME[2015-10-03T12:49:02.300Z]]