[45] [DFN[[CODE(HTTP)@en[[[GOAWAY]]]]]] [[フレーム]]は、
[[HTTP接続]]を閉じるものです。

* 仕様書

[REFS[
- [38] [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>
- [39] [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>
- [41] [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.2>
- [42] [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>
- [44] [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.4.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-6.8>'''
- [30] [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>
- [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-8.1.4>
- [36] [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-9.1>
]REFS]

* 意味

[2] [CODE[[[GOAWAY]]]] [[フレーム]] ([[フレーム型]] [CODE[[[0x7]]]])
は、[[接続]]の[RUBYB[終了]@en[shutdown]]をはじめたり、
[RUBYB[誤り条件]@en[error condition]]を通知したりするものです [SRC[>>1]]。

[3] [CODE[[[GOAWAY]]]] を使って、[[エンドポイント]]は既存の[[ストリーム]]の処理は続けつつ、
新しい[[ストリーム]]の受付は中止し、[RUBYB[華麗]@en[graceful]]に停止することができます [SRC[>>1]]。

;; [4] [[サーバー]]のメンテナンスなど管理操作に有用です [SRC[>>1]]。

* 構文

[13] [[ストリーム識別子]]は [CODE[[[0x0]]]] でなければなりません。
[CODE[[[GOAWAY]]]] [[フレーム]]は[[接続]]全体に適用されます [SRC[>>1]]。

[12] [[フラグ]]はありません [SRC[>>1]]。 0 でなければ[['''なりません''']] [SRC[>>39]]。
[[受信者]]は無視しなければ[['''なりません''']] [SRC[>>39]]。

[FIG(packet)[
:width:8

= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
= 1 0
]FIG]

[26] [[payload]] は次の欄で構成されます。
[FIG(list members)[
:[27] [[R]]:
1ビットのフラグとして予約されています。
:[15] [RUBYB[[[最終ストリーム識別子]]]@en[Last-Stream-ID]]:
[[符号無し]]31ビット[[整数]] ([[ネットワークバイト順]] [SRC[>>78]]) の[[ストリーム識別子]]です。
[[送信者]]が何らかの動作を行った、または今後なお行うかもしれない最大の番号の[[ストリーム識別子]]を指定します。
[CODE[[[0]]]] であれば、どの[[ストリーム]]も処理していないことを表します。
ここで「処理」とは、何らかのデータを何らかの上位層ソフトウェアに渡して、そこで何らかの動作が行われたかもしれないことを表します。 [SRC[>>1]]
:[23] [RUBYB[[[誤り符号]]]@en[Error Code]]:
32ビット[[誤り符号]] ([[ネットワークバイト順]] [SRC[>>38]])
で、[[接続]]を閉じる理由を含むものです [SRC[>>1]]。
:[24] [DFN[[RUBYB[[[追加デバッグデータ]]]@en[Additional Debug Data]]]]:
診断目的のみで意味を持たない不透明データです。 [[payload]] の末尾にあっても構いません。
[[セキュリティー]]上や[[プライバシー]]上繊細なデータを含むことがありますから、
記録その他の目的で永続的に保存する時は、認められないアクセスを防ぐ安全策を講じなければ[['''なりません''']]。 [SRC[>>1]]
]FIG]

[35] 既に[[アプリケーション層]]にデータがわたっていれば、その[[ストリーム]]よりも大きな[[ストリーム識別子]]を指定しなければ[['''なりません''']] [SRC[>>34]]。

[5] [CODE[[[GOAWAY]]]] [[フレーム]]の送信と [[peer]] の新しい[[ストリーム]]の開始には[[競合条件]]がありますから、
[CODE[[[GOAWAY]]]] [[フレーム]]には [[peer]] が開始した (上で[[送信者]]が処理した、またはしたかもしれない)
最後 (最大) の[[ストリーム]]の[[ストリーム識別子]]を指定します。
[[送信者]]は、[CODE[[[GOAWAY]]]] [[フレーム]]の送信以後、
その[[ストリーム識別子]]よりも大きな[[ストリーム識別子]]の[[フレーム]]を無視します。 [SRC[>>1]]

;; [46] [[ストリーム]]の状態が [[open]] 以降なら、処理したとみなしてもいいのかもしれません。
より厳密に判定するなら、 [[open]] 以降でも実際に[[アプリケーション層]]に引き渡す前なら未処理と考えてもよいかもしれません。

[22] ただし[[送信者]]は、[[接続]]の状態を変える[[フレーム]]を完全に無視することはできません。
例えば [CODE[[[HEADERS]]]]、[CODE[[[PUSH_PROMISE]]]]、[CODE[[[CONTINUATION]]]]
は、最低でも[[ヘッダー圧縮]]のための状態の管理のための処理は行わなければ[['''なりません''']]。
[CODE[[[DATA]]]] [[フレーム]]は、[[接続フロー制御窓]]の加算は行わなければ[['''なりません''']]。
[SRC[>>1]]

[25] [[送信者]]は[[追加デバッグデータ]]がどう扱われるとも保証されませんから、
開発者モードのような特別な設定がある場合を除き、
本欄を使うべきではないでしょう。 [[fingerprinting vector]] ともなります。

[FIG(packet)[
:width:32

= 1 R
= 31 最終ストリーム識別子
= 32 誤り符号
= 32... 追加デバッグデータ
]FIG]

* 文脈

[9] [[エンドポイント]]は、[[接続]]を閉じる前に [CODE[[[GOAWAY]]]]
[[フレーム]]を送信する[['''べきです''']] [SRC[>>1, >>36]]。

;; [37] 自身が送信した[[フレーム]]が処理されたのか、それとも未処理なのか判定できるようにするためです。

;; [10] おかしな動作の [[peer]] に対しては、 [CODE[[[GOAWAY]]]]
[[フレーム]]を送らずに[[接続]]を閉じても構いません [SRC[>>1]]。

[47] [[Chrome]] は正常終了時には [CODE[[[GOAWAY]]]] を送信せずに接続を閉じます
(ソースコードには、[[無線]]が寝ている場合に起こさないためとの注記があります)。
[[Firefox]] は[[誤り符号]] [CODE[[[NO_ERROR]]]] の [CODE[[[GOAWAY]]]]
を送信し、直ちに接続を閉じます。 [TIME[2015-10-12T03:48:59.300Z]]

[11] [CODE[[[GOAWAY]]]] [[フレーム]]を既に受信している場合も、
当該[[接続]]をそれ以上使わなくなった時点で、
[[接続]]を閉じるより前に [CODE[[[GOAWAY]]]] [[フレーム]]を送信する[['''べきです''']]。 [SRC[>>1]]

[18] [[エンドポイント]]は、状況が変わったら、新たに [CODE[[[GOAWAY]]]]
[[フレーム]]を送信して構いません [SRC[>>1]]。
ただし最終ストリーム識別子がより大きな値になっては[['''なりません''']] [SRC[>>1]]。

[EG[
[19] 例えば [CODE[[[NO_ERROR]]]] で送信した後、接続を直ちに閉じなければならない状況が発生したら、
改めて新しい[[誤り符号]]で送信できます [SRC[>>1]]。
]EG]

[20] 華麗に終了したい[[サーバー]]は、まず最終ストリーム識別子が 2[SUP[31]]-1で
[CODE[[[NO_ERROR]]]] の [CODE[[[GOAWAY]]]] [[フレーム]]を送信する[['''べきです''']]。
進行中の[[ストリーム]]作成のための時間 (最低でも1[[RTT]]) が経過したら、
改めて新しい最終ストリーム識別子による [CODE[[[GOAWAY]]]]
[[フレーム]]を送信することができます。 [SRC[>>1]]

;; [21] [[プロキシ]]など、処理されなかった[[要求]]を再試行できない[[クライアント]]が存在するための配慮です [SRC[>>1]]。

[52] [[Firefox]] は正常終了時の [CODE[[[GOAWAY]]]] で[[最終ストリーム識別子]]を常に
0 とします。 [TIME[2015-10-12T06:35:17.00Z]]

[32] [[誤り符号]] [DFN[[CODE[[[NO_ERROR]]]]]] ([CODE[[[0x0]]]]) は、
[[誤り]]ではないことを表します。例えば、 [CODE[[[GOAWAY]]]]
では[[接続]]の華麗な終了を表します。 [SRC[>>30]]

;; [33] [CODE[[[GOAWAY]]]] 以外で使えるのかどうかは不明です。

[29] [[接続エラー]]では [CODE(HTTP)@en[[[GOAWAY]]]] [[フレーム]]を送信する[['''べきです''']]
[SRC[>>44]]。
その後[[TCP接続]]を閉じなければ[['''なりません''']] [SRC[>>44]]。

;; [[HTTP接続]]、[[接続エラー]]も参照。

[28] [[フロー制御]]のエラーで送信されることがあります。

[43] [[サーバー]]は、[[ストリーム識別子]]が枯渇したら、
[CODE(HTTP)@en[[[GOAWAY]]]] [[フレーム]]を送信して[[クライアント]]に新しい[[接続]]を開かせることができます [SRC[>>42]]。

[51] [[Chrome]] も [[Firefox]] も、受信した [CODE[[[GOAWAY]]]] [[フレーム]]に関する[[接続エラー]]でも
[CODE[[[GOAWAY]]]] [[フレーム]]を送信します。 [TIME[2015-10-12T05:15:02.900Z]]

* 処理

[14] [[受信者]]は、[[ストリーム識別子]]が [CODE[[[0x0]]]] 以外なら、
[[接続エラー]] [CODE[[[PROTOCOL_ERROR]]]] としなければ[['''なりません''']] [SRC[>>1]]。

[40] [[受信者]]は、[[payload]] が短すぎるか、[[設定]] [CODE[[[SETTINGS_MAX_FRAME_SIZE]]]]
より長いなら、[[接続エラー]] [CODE[[[FRAME_SIZE_ERROR]]]] としなければ[['''なりません''']]
[SRC[>>41]]。

[8] [CODE[[[GOAWAY]]]] [[フレーム]]の[[受信者]]は、
示された[[ストリーム識別子]]より大きな[[ストリーム識別子]]の[[ストリーム]]は最初から作られなかったものとして扱うことができます。 [SRC[>>1]]

[6] [CODE[[[GOAWAY]]]] [[フレーム]]の[[受信者]]は、新しい[[ストリーム]]を開いては[['''なりません''']]。 [SRC[>>1]]

;; [7] 新しい[[接続]]を確立して新しい[[ストリーム]]を開始しても構いません [SRC[>>1]]。

[17] [[接続]]が閉じられるより前に完全に閉じられていない、
最大ストリーム識別子[[以下]]の[[ストリーム識別子]]の[[ストリーム]]では、
[[冪等]]な動作を除き、何らの動作も再試行できません。
最大ストリーム識別子より大きな[[ストリーム識別子]]の[[ストリーム]]では、
新しい[[接続]]で安全に再試行できます。 [SRC[>>1]]

;; [16] [CODE[[[GOAWAY]]]] [[フレーム]]無しで閉じられた[[接続]]では、
実質的な最終ストリーム識別子は最大の[[ストリーム識別子]]とします。 [SRC[>>1]]

[48] [[Chrome]] も [[Firefox]] も、 [CODE[[[GOAWAY]]]] を受信しても、
自身にとって特にエラーでなければ、エラー無しと扱うようです。
(接続を閉じる場合、受信した[[誤り符号]]に関わらず、 [[Firefox]] は [CODE[[[NO_ERROR]]]] の [CODE[[[GOAWAY]]]] を送信します。)
[TIME[2015-10-12T03:50:28.800Z]]

[49] [[Chrome]] も [[Firefox]] も、
[CODE[[[NO_ERROR]]]] なら、
[[最終ストリーム識別子]]より小さな[[ストリーム識別子]]の[[ストリーム]]は、
引き続き処理を続行します。 (接続を閉じませんし、
[CODE[[[GOAWAY]]]] の次に更に[[フレーム]]があれば通常通り処理します。)
大きな[[ストリーム識別子]]の[[ストリーム]]は、
[[ネットワークエラー]]とします。
その後 [[closed]] でない[[ストリーム]]がなくなった時点で、
([[Firefox]] は [CODE[[[NO_ERROR]]]] の [CODE[[[GOAWAY]]]] を送信して)
接続を閉じます。
[TIME[2015-10-12T03:51:38.200Z]]

;; [50] [[最終ストリーム識別子]]より大きな[[ストリーム]]も、自動再試行はしないようです。
[TIME[2015-10-12T03:53:01.700Z]]

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

> APNsは、確立済みのHTTP/2接続を停止しようとする場合、GOAWAYフレームを送信します。GOAWAYフレームのペイロードにはJSONデータがあり、そのreasonキーに、停止の理由を表す値が入っています。

]FIG]
