[55] [[HTTP/2]] [[ストリーム]]は、[DFN[[RUBYB[[[優先度]]]@en[priority]]]]を持ちます。
複数の[[メッセージ]]の送受信は、[[優先度]]を参考に順序が決定されます。

* 仕様書

[REFS[
- [50] [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>
- [48] [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-3.2>
- [51] [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>
- [52] [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>
- [2] [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.3>
- [66] [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.2>
- [34] [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.3>
- [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>
- [46] [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>
- [76] [CITE@en[Fetch Standard]] ([TIME[2017-06-15 14:00:01 +09:00]]) <https://fetch.spec.whatwg.org/#concept-request-priority>
]REFS]

* 意味

[5] [[エンドポイント]]は、各[[ストリーム]]について、 
[[peer]] が当該[[ストリーム]]に[[資源]]を割り当てる際に (他の[[ストリーム]]に対して)
どの程度優先するべきかを指定できます。 [SRC[>>2]]

[6] [[エンドポイント]]は、 (送信能力が限られる時に) 送信する[[ストリーム]]を選ぶに当たり、
[[優先度]]を使うことができます。 [SRC[>>2]]

[57] [[優先度]]の指定は、 [[peer]] に対する[[ヒント]]に過ぎません。
その値によって他の[[ストリーム]]に対する転送処理の順序が保証されるわけではありません [SRC[>>2]]。
具体的な順序の決定方法も規定されていません。

[47] なお[[優先度]]の使い方は、[[fingerprinting vector]] です [SRC[>>46]]。

[8] [[優先度]]は、[[依存性木]]により決まります。

* 依存性

[12] ある[[ストリーム]]から他の[[ストリーム]]への[DFN[[RUBYB[依存性]@en[dependency]]]]を指定することができます。
[[依存性]]の[[関係]]において、
依存している方の[[ストリーム]]を[DFN[[RUBYB[[[依存ストリーム]]]@en[dependent stream]]]]、
依存されている方の[[ストリーム]]を[DFN[[RUBYB[[[親ストリーム]]]@en[parent stream]]]]といいます
[SRC[>>12]]。

[56] [[ストリーム]]の[[親ストリーム]]は、1つです。
[[ストリーム]]の[[依存ストリーム]]は、0個[[以上]]の任意の個数存在できます。
従って[[依存性]]の[[関係]]は、[[ストリーム]]を[[節点]]とする[[木]]になります。
これを[DFN[[RUBYB[[[依存性木]]]@en[dependency tree]]]]といいます。

[15] 同じ[[親ストリーム]]を持つ[[依存ストリーム]]は、
互いに順序を持ちません。 [SRC[>>2]]

[20] [[依存ストリーム]]には、 1 [[以上]] 256 [[以下]]の[[整数]]の[DFN[[RUBYB[重み]@en[weight]]]]が割り当てられます [SRC[>>2]]。 

;; [60] [[重み]]は、[[依存性木]]における[[枝]]の[[重み]]と考えることができます。

[61] なお、[[節点]]としては通常の[[ストリーム]]の他に、[[ストリーム識別子]]
[CODE[[[0x0]]]] の (ダミーの) [[ストリーム]]を使います [SRC[>>2]]。

;; [62] 他の方法で[[依存性]]が設定されなかった[[ストリーム]]は、この特別な[[ストリーム]]の[[依存ストリーム]]となります。
従って[[依存性木]]は、[[接続]]中のすべての[[ストリーム]]を含む唯一の[[木]]になります。

* 依存性の指定

[10] [CODE(HTTP)@en[[[HEADERS]]]] [[フレーム]]や [CODE(HTTP)@en[[[PRIORITY]]]]
[[フレーム]]を使って、[[依存ストリーム]]と[[重み]]を明示的に指定できます。
[FIG(list members)[
[FIGCAPTION[
[[依存性]]
]FIGCAPTION]
:[[親ストリーム]]:[[ストリーム識別子]]欄に示された[[ストリーム]]
:[[依存ストリーム]]:[[payload]] の[[ストリーム依存性]]欄に示された[[ストリーム]]
:[[重み]]:[[payload]] の[[重み]]欄の値
]FIG]

[3] その場合の変更操作は、次のようにします。
[FIG(steps)[
= [18] 新[[依存ストリーム]]と新[[親ストリーム]]が同じなら、
[[ストリームエラー]] [CODE[[[PROTOCOL_ERROR]]]] としなければ[['''なりません''']]
[SRC[>>2]]。 停止します。
= [19] 新[[依存ストリーム]]が新[[親ストリーム]]の[[依存性木]]上の[[祖先]]なら、
== [9] 新[[依存ストリーム]]の各[[依存ストリーム]]について、
=== [13] [[親ストリーム]]を、新[[依存ストリーム]]の現在の[[親ストリーム]]に設定します。
[[重み]]は変更しません。 [SRC[>>2]]
= [14] [RUBYB[排他的]@en[exclusive]]フラグが設定されていれば、
== [63] 新[[親ストリーム]]の各[[依存ストリーム]]について、それが新[[依存ストリーム]]でなければ、
=== [16] [[親ストリーム]]を、新[[依存ストリーム]]に設定します [SRC[>>2]]。
= [4] [[依存ストリーム]]の[[親ストリーム]]を、新[[親ストリーム]]に設定します。
新しい[[重み]]を設定します。
]FIG]

;; [7] 変更操作は、新[[依存ストリーム]]の[[依存ストリーム]]には影響しません。
従って新[[依存ストリーム]]と共に[[木]]上の位置が移動することになります [SRC[>>2]]。

;; [72] この操作を[[フレーム]]処理のどの段階で行うべきなのかは定かではありません。
[[ヘッダー]]のエラー検査や[[ストリーム]]の状態遷移の処理の後が妥当でしょうか。

[70] [CODE(HTTP)@en[[[HEADERS]]]] [[フレーム]]の
[DFN[[CODE[[[PRIORITY]]]]]] ([CODE[[[0x20]]]] = 第5ビット) フラグは、
設定されていれば、[[排他的]] ([[E]]) フラグ、[[ストリーム依存性]]欄、[[重み]]欄が存在することを表します。 [SRC[>>66]]
設定されていなければこれらの欄は存在しません。

[67] [CODE(HTTP)@en[[[HEADERS]]]] [[フレーム]]や
[CODE(HTTP)@en[[[PRIORITY]]]] [[フレーム]]の
[DFN[[[E]]]] ([DFN[[RUBYB[[[排他的]]]@en[Exclusive]]]]) フラグは、
[[ストリーム依存性]]が[[排他的]]かどうかを表す1ビットのフラグです [SRC[>>66, >>34]]。

[68] [CODE(HTTP)@en[[[HEADERS]]]] [[フレーム]]や
[CODE(HTTP)@en[[[PRIORITY]]]] [[フレーム]]の[DFN[[RUBYB[ストリーム依存性]@en[Stream Dependency]]]]欄は、
[[ストリーム]]が依存する[[ストリーム]]の[[ストリーム識別子]]を表す31ビットの欄
([[ネットワークバイト順]] [SRC[>>50]]) です。 [SRC[>>66, >>34]]

[71] 存在しない[[ストリーム]]が指定された時、受信者がどうするべきなのかは定かではありません。
[[idle]] 状態も含め、適切な[[ストリーム]]を表していれば、それについて依存性を設定するべきでしょう。
[[peer]] が開始するべき[[ストリーム]]が指定された時どうするべきなのかは不明です。
[[Chrome]] も [[Firefox]] も、そのような指定を受信してもエラーとはしません。
[TIME[2015-10-03T13:48:01.00Z]]

;; [[クライアント]]が優先度の指定を受信してもエラーにならなければ挙動が変化しないようなので、
どう扱っているのか観測できません。

[69] [CODE(HTTP)@en[[[HEADERS]]]] [[フレーム]]や
[CODE(HTTP)@en[[[PRIORITY]]]] [[フレーム]]の[DFN[[RUBYB[[[重み]]]@en[Weight]]]]欄は、
[[ストリーム]]の[[優先度]]の[[重み]]を表す[[符号無し]]8ビット[[整数]]です。
重みは 1 [[以上]] 256 [[以下]]で、ここで指定するのはそれより 1 小さい値です。
[SRC[>>66, >>34]]

[33] [CODE(HTTP)@en[[[PUSH_PROMISE]]]] [[フレーム]]によって予約された[[ストリーム]]は、
元の[[ストリーム]]に依存します [SRC[>>2]]。
[FIG(list members)[
[FIGCAPTION[
[[依存性]]
]FIGCAPTION]
:[[親ストリーム]]:[[ストリーム識別子]]欄に示された[[ストリーム]]
:[[依存ストリーム]]:[[payload]] の[[約束ストリーム識別子]]欄に示された[[ストリーム]]
:[[重み]]:既定値 (16) [SRC[>>2]]
]FIG]

[11] 他の[[ストリーム]]への[[依存性]]が指定されていなければ、
[[ストリーム]] [CODE[[[0x0]]]] に依存することとします [SRC[>>2]]。
[FIG(list members)[
[FIGCAPTION[
[[依存性]]
]FIGCAPTION]
:[[親ストリーム]]:[CODE[[[0x0]]]]
:[[依存ストリーム]]:[[ストリーム]]
:[[重み]]:既定値 (16) [SRC[>>2]]
]FIG]

;; [49] [CODE(HTTP)@en[[[Upgrade: h2c]]]] により作成される[[ストリーム識別子]]
[CODE[[[1]]]] の[[ストリーム]]も、既定値となります [SRC[>>48]]。

* ストリームの削除

[64] [[エンドポイント]]は、[[ストリーム]]の情報を[[依存性木]]から削除することも認められています。

[22] その場合、削除[[ストリーム]]の[[依存ストリーム]]は、
削除[[ストリーム]]の[[親ストリーム]]の[[依存ストリーム]]となります。
[[依存性]]の[[重み]]は、削除される[[ストリーム]]の[[重み]]を各[[依存ストリーム]]の[[重み]]で分配した値とします。 [SRC[>>2]]

;; [23] これによって[[優先度]]の情報はいくらか失われることになります。
例えばある[[ストリーム]]の[[依存ストリーム]]間で[[資源]]を共有していた状態でそのいずれかの[[依存ストリーム]]が閉じられると、
開いた[[資源]]はその[[親ストリーム]]の他の[[依存ストリーム]]に割り当てられることになります。
しかし[[親ストリーム]]が変わっていると違った形で再分配されることになります。 [SRC[>>2]]

[24] [[依存性]]で指定された[[ストリーム]]に[[優先度]]が関連付けられていない場合は、
[[既定優先度]] ([[重み]] 16) を割り当てます [SRC[>>2]]。

;; [25] これは意図とは異なるかもしれませんから、好ましくない[[優先度]]付けとなるかもしれません [SRC[>>2]]。

[26] これらの問題を防ぐため、閉じた[[ストリーム]]の[[優先度]]の状態を一定期間保持し続ける[['''べきです''']]。
[SRC[>>2]]

[27] [[idle]] や [[closed]] の状態の[[ストリーム]]にも[[優先度]]を割り当てたり他の[[ストリーム]]の[[親ストリーム]]としたりできますから、
これを[[依存性]]の[[木]]の中で[[ストリーム]]をまとめる[[節点]]を作り、
[[優先度]]を柔軟に表現するために使うことができます。 [SRC[>>2, >>34]]

[28] こうした [CODE[[[SETTINGS_MAX_CONCURRENT_STREAMS]]]] 制限に含まれない[[ストリーム]]の[[優先度]]情報を多数保持するのは大変かもしれませんから、
[[優先度]]を保持する量は制限しても構いません。保持する量は[[負荷]]に依存するかもしれません。 [SRC[>>2]]

;; [29] 極端な場合には、活性の[[ストリーム]]や予約されている[[ストリーム]]の[[優先度]]も捨てることもできます。 [SRC[>>2]]

[30] しかし制限する場合、最低でも [CODE[[[SETTINGS_MAX_CONCURRENT_STREAMS]]]]
分の[[ストリーム]]の状態は保持する[['''べきです''']] [SRC[>>2]]。

[31] 活性の[[ストリーム]]の状態は、保持するよう試みる[['''べきです''']] [SRC[>>2]]。

;; [65] [[closed]] 状態以外の[[ストリーム]]で (他の送受信操作は通常通り行うのに)
[[優先度]]の情報は削除せざるを得ない状況が存在するのかは謎です。
SHOULD ではなくて MUST で良い気がしますが......

[32] 閉じた[[ストリーム]]の[[優先度]]を保持している場合は、
[CODE[[[PRIORITY]]]] [[フレーム]]を受信したら、
[[依存ストリーム]]の[[優先度]]を変更する[['''べきです''']] [SRC[>>2]]。

* 資源の割り当て

[17] [[依存性]]木における[[依存ストリーム]]には、
その[[親ストリーム]]や[[祖先]]のすべてが閉じられるか、
それ以上の進捗ができなくなったかの場合に限り、
[[資源]]を割り当てる[['''べきです''']] [SRC[>>2]]。

[21] 同じ[[親ストリーム]]を持つ[[ストリーム]]には、
[[重み]]の比率によって[[資源]]を割り当てる[['''べきです''']] [SRC[>>2]]。

[74] [[依存性木]]は[[サーバー]]の処理順序の指定として用いられることが想定されているようです。
プロトコル上は[[サーバー]]も優先度を指定できますが、[[クライアント]]がそれをどう利用するか
(しないか) は明らかではありません。

* [CODE(HTTP)@en[PRIORITY]] フレーム

** 意味

[35] [CODE[[[PRIORITY]]]] [[フレーム]] ([[フレーム型]] [CODE[[[0x2]]]])
は、[[ストリーム]]の[[優先度]]について[[送信者]]が[[ヒント]]を示すものです。 [SRC[>>34]]

** 構文

[42] 常に[[ストリーム識別子]]を指定しなければなりません [SRC[>>34]]。
[CODE[[[0x0]]]] を指定することはできません。

[41] [[フラグ]]はありません [SRC[>>34]]。0 でなければ[['''なりません''']] [SRC[>>51]]。
[[受信者]]は無視しなければ[['''なりません''']] [SRC[>>51]]。

[FIG(packet)[
:width:8

= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
]FIG]

[37] [[payload]] は次の欄で構成されます。
[FIG(list members)[
:[38] [[E]]:
[[ストリーム依存性]]が[[排他的]]かどうかを示す1ビットの[[フラグ]]です
[SRC[>>34]]。
:[39] [RUBYB[ストリーム依存性]@en[stream dependency]]:
この[[ストリーム]]が依存する[[ストリーム]]を表す31ビット[[ストリーム識別子]]
([[ネットワークバイト順]] [SRC[>>50]]) です [SRC[>>34]]。
:[40] [RUBYB[重み]@en[weight]]:
[[ストリーム]]の[[優先度]]の[[重み]]を表す[[符号無し]]8ビット[[整数]]です。
1 [[以上]] 256 [[以下]]の値から1引いたものです。 [SRC[>>34]]
]FIG]

[FIG(packet)[
:width:32

= 1 E
= 31 ストリーム依存性
= 8 重み
]FIG]

** 文脈

[36] [[idle]] や [[closed]] を含め、任意の状態の[[ストリーム]]で送信できます [SRC[>>34, >>52]]。

;; [44] ただし [[header block]] 送信の途中には送信できません [SRC[>>34]]。

** 処理

[43] [[受信者]]は、[[ストリーム識別子]]が [CODE[[[0x0]]]] なら、
[[接続エラー]] [CODE[[[PROTOCOL_ERROR]]]] としなければ[['''なりません''']] [SRC[>>34]]。

[45] [[受信者]]は、[[payload]] の長さが 5 以外なら、
[[ストリームエラー]] [CODE[[[FRAME_SIZE_ERROR]]]] としなければ[['''なりません''']]
[SRC[>>34]]。

[73] [[ストリーム]]の状態遷移の項も参照。

[75] [[Chrome]] も [[Firefox]] も、 [CODE[[[HEADERS]]]] または [CODE[[[PUSH_PROMISE]]]]
より先に [CODE[[[PRIORITY]]]] を受信すると ([[RFC]] 的には [[open]] 状態で受け入れられるはずですが)
[[接続エラー]] [CODE[[[PROTOCOL_ERROR]]]] とします。
[TIME[2015-10-10T09:20:00.300Z]]

[53] [[half-closed (local)]] でも受信するかもしれません [SRC[>>52]]。
[[依存ストリーム]]の[[優先度]]の設定に使われます [SRC[>>52]]。

[54] [[closed]] でも受信するかもしれません。処理する[['''べきです''']]。
しかし[[依存性木]]から削除した後なら、無視できます。 [SRC[>>52]]

[58] [CODE[[[PRIORITY]]]] [[フレーム]]は、無駄な処理をさせるために濫用できます。
[[エンドポイント]]は利用状況を監視して制限する[['''べきです''']]。
[[接続エラー]] [CODE[[[ENHANCE_YOUR_CALM]]]] としても構いません。 [SRC[>>59]]

* 歴史

[REFS[
- [1] [CITE@en[Say something rather vague about priority to at least acknowledge it … · whatwg/fetch@600ccb7]]
([TIME[2015-05-11 11:23:34 +09:00]] 版)
<https://github.com/whatwg/fetch/commit/600ccb7e4279a2c795cbba1c93edc53f8a14db03>
]REFS]

[77] [CITE@en[Fold request type into destination]]
([[annevk]]著, [TIME[2017-08-28 18:15:21 +09:00]])
<https://github.com/whatwg/fetch/commit/d7052e2b6d24d04caa2cea8ef664923ecdb1e35c>

[FIG(quote)[
[FIGCAPTION[
[78] [CITE@en[APNs Provider API]]
([TIME[2017-09-27 02:19:03 +09:00]])
<https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/APNsProviderAPI.html>
]FIGCAPTION]

> APNsはHTTP/2 PRIORITYフレームを無視するので、ストリーム上でこれを送信することはできません。
> 

]FIG]


[79] [CITE@en[Copy priority for service worker passthrough requests]]
([[jakearchibald]]著, [TIME[2018-08-16 15:00:58 +09:00]])
<https://github.com/whatwg/fetch/commit/b281c58a6dde6c36fd18d62d2b05e3899cd6d595>

[80] [CITE@en[Copy priority so service worker pass-through requests don't lose it by jakearchibald · Pull Request #785 · whatwg/fetch]]
([TIME[2018-08-22 19:03:26 +09:00]])
<https://github.com/whatwg/fetch/pull/785>