* 仕様書

[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]] [[受信バッファー]]からデータを随時読み込まなければ[MUST[なりません]]。
そうしなければ、 [CODE[WINDOW_UPDATE]] など重要な[[フレーム]]を読んで処理することができず、
[[デッドロック]]に陥る虞があります。 [SRC[>>1]]

;; [90] [[ストリーム]]の多重化が存在しない [[HTTP/1]] では、
[[受信者]]の用意が整っていなければ、[[プラットフォーム]]の [[TCP]]
からの受信データの読み出しを行わないことにより、[[プラットフォーム]]側で 
(受信バッファーのデータ長を使った) [[TCP]] の[[フロー制御]]を利用することができました。
ところが [[HTTP/2]] ではある[[ストリーム]]をそれ以上読む必要が無いとしても、
その後に他の[[ストリーム]]のデータやデータ以外の[[フレーム]]が続いているかもしれませんから、
常に[[プラットフォーム]]から受信データを受け取り続ける必要が生じています。

** 送信者の処理

[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]]。

;; [84] [[サーバー]]から [[Chrome]] や [[Firefox]] にまとめて大量の [CODE[[[DATA]]]]
を送信しても [CODE[[[FLOW_CONTROL_ERROR]]]] にはならないようです。
[TIME[2015-10-10T08:48:40.100Z]]

[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] あるいは[[ストリームフロー制御窓]]は初期値からの差分として保持しておくのも良いかもしれません。

;; [87] [[接続フロー制御窓]]は、設定の値に影響されません。

[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]]。

** 処理

[85] [[ストリーム]]の状態遷移の項も参照。

[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]]。

[86] [[Chrome]] も [[Firefox]] も、こうした場合に (少なくても) [[接続エラー]]にはしていないようです。[TIME[2015-10-12T03:14:07.800Z]]

* 誤り符号 [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]]

[82] [[Chrome]] は一定量の ([[窓]]をかなり使うくらいの) [CODE[[[DATA]]]]
を受信するごとに、その[[ストリーム]]についての [CODE[[[WINDOW_UPDATE]]]] で
3153920 = 2[SUP[21]] + 2[SUP[20]] + 2[SUP[13]] を送るようです。
[TIME[2015-10-10T08:43:33.00Z]]

[83] [[Chrome]] はそれに加えて (より少ない頻度で) [[接続]]についての [CODE[[[WINDOW_UPDATE]]]]
で 7872512 = 2[SUP[22]] + 2[SUP[21]] + 2[SUP[20]] + 2[SUP[19]] + 2[SUP[13]]
を送るようです。
[TIME[2015-10-10T08:44:36.900Z]]

[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]]

[81] [[Firefox]] は一定量の ([[窓]]をかなり使うくらいの) [CODE[[[DATA]]]] を受信するごとに、
その[[ストリーム]]についての [CODE[[[WINDOW_UPDATE]]]] と、
[[接続]]についての [CODE[[[WINDOW_UPDATE]]]] でそれぞれ
4194304 = 2[SUP[22]] ずつ増やすようです。 [TIME[2015-10-10T08:37:41.00Z]]

[88] [CITE[#959 (Permit post before acking settings) – nginx]]
( ([TIME[2016-10-17 20:47:49 +09:00]]))
<https://trac.nginx.org/nginx/ticket/959>

[89] [CITE[ios - NSURLErrorDomain Code=-1004 for few seconds after app start up - Stack Overflow]]
( ([TIME[2016-10-17 20:47:58 +09:00]]))
<http://stackoverflow.com/questions/36907767/nsurlerrordomain-code-1004-for-few-seconds-after-app-start-up>