9. 接続管理 (Connection Management)
HTTP メッセージングは、基礎となるトランスポート層またはセッション層の接続プロトコルから独立しています。HTTP は、リクエストの順序配信とそれに対応するレスポンスの順序配信を伴う信頼性のあるトランスポートのみを前提としています。HTTP リクエストとレスポンス構造の基礎となるトランスポートプロトコルのデータユニットへのマッピングは、本仕様書の範囲外です。
[HTTP] の第 7.3 節で説明されているように、HTTP インタラクションに使用される特定の接続プロトコルは、クライアント構成とターゲット URI によって決定されます。例えば、"http" URI スキーム ([HTTP] の第 4.2.1 節) は、IP 上の TCP のデフォルト接続を示し、デフォルトの TCP ポートは 80 ですが、クライアントは他の接続、ポート、またはプロトコルを介してプロキシを使用するように構成される場合があります。
HTTP 実装は、接続管理に従事することが期待されます。これには、現在の接続の状態の維持、新しい接続の確立または既存の接続の再利用、接続で受信したメッセージの処理、接続障害の検出、および各接続の終了が含まれます。ほとんどのクライアントは、サーバーエンドポイントごとに複数の接続を含む、複数の接続を並行して維持します。ほとんどのサーバーは、公平な使用を可能にし、サービス拒否攻撃を検出するためにリクエストキューを制御しながら、数千の同時接続を維持するように設計されています。
9.1. 確立 (Establishment)
さまざまなトランスポート層またはセッション層プロトコルを介して接続を確立する方法を説明することは、本仕様書の範囲を超えています。各 HTTP 接続は、1 つの基礎となるトランスポート接続にマップされます。
9.2. レスポンスとリクエストの関連付け (Associating a Response to a Request)
HTTP/1.1 には、特定のリクエストメッセージとそれに対応する 1 つ以上のレスポンスメッセージを関連付けるためのリクエスト識別子が含まれていません。したがって、レスポンス到着の順序が、同じ接続で行われたリクエストの順序と正確に対応することに依存しています。1 つのリクエストに対する複数のレスポンスメッセージは、1 つ以上の情報レスポンス (1xx; [HTTP] の第 15.2 節参照) が同じリクエストに対する最終レスポンスの前にある場合にのみ発生します。
接続上に複数の未処理のリクエストを持つクライアントは、送信された順序で未処理のリクエストのリストを維持しなければならず (MUST)、その接続で受信した各レスポンスメッセージを、まだ最終 (非 1xx) レスポンスを受信していない最初の未処理のリクエストに関連付けなければなりません (MUST)。
クライアントが、未処理のリクエストがない接続でデータを受信した場合、クライアントはそのデータを有効なレスポンスと見なしてはなりません (MUST NOT)。クライアントは接続を閉じるべきです (SHOULD)。メッセージの区切りが曖昧になるためです。ただし、データが 1 つ以上の CRLF のみで構成されている場合を除きます (これは第 2.2 節に従って破棄できます)。
9.3. 持続性 (Persistence)
HTTP/1.1 は、"持続的接続" の使用をデフォルトとし、複数のリクエストとレスポンスを単一の接続で運ぶことを可能にします。HTTP 実装は持続的接続をサポートすべきです (SHOULD)。
受信者は、最近受信したメッセージ (存在する場合) のプロトコルバージョンと Connection ヘッダーフィールド ([HTTP] の第 7.6.1 節) に基づいて、接続が持続的であるかどうかを判断します:
- "close" 接続オプションが存在する場合 (第 9.6 節)、接続は現在のレスポンスの後に持続しません。そうでない場合、
- 受信したプロトコルが HTTP/1.1 (またはそれ以降) の場合、接続は現在のレスポンスの後に持続します。そうでない場合、
- 受信したプロトコルが HTTP/1.0 の場合、"keep-alive" 接続オプションが存在し、受信者がプロキシでないかメッセージがレスポンスであり、受信者が HTTP/1.0 "keep-alive" メカニズムを尊重したい場合、接続は現在のレスポンスの後に持続します。そうでない場合、
- 接続は現在のレスポンスの後に閉じます。
持続的接続をサポートしないクライアントは、すべてのリクエストメッセージで "close" 接続オプションを送信しなければなりません (MUST)。
持続的接続をサポートしないサーバーは、1xx (Informational) ステータスコードを持たないすべてのレスポンスメッセージで "close" 接続オプションを送信しなければなりません (MUST)。
クライアントは、"close" 接続オプションを送信または受信するか、"keep-alive" 接続オプションのない HTTP/1.0 レスポンスを受信するまで、持続的接続で追加のリクエストを送信してもかまいません (MAY)。
持続的であり続けるために、接続上のすべてのメッセージは、第 6 節で説明されているように、自己定義されたメッセージ長を持つ必要があります (つまり、接続の終了によって定義されないもの)。サーバーは、レスポンスを送信した後、リクエストメッセージ本文全体を読み取るか接続を閉じなければなりません (MUST)。そうでない場合、持続的接続上の残りのデータは次のリクエストとして誤解されます。同様に、クライアントは、同じ接続を後続のリクエストに再利用する場合、レスポンスメッセージ本文全体を読み取らなければなりません (MUST)。
プロキシサーバーは、HTTP/1.0 クライアントとの持続的接続を維持してはなりません (MUST NOT) (多くの HTTP/1.0 クライアントによって実装された Keep-Alive ヘッダーフィールドの問題に関する情報と議論については、付録 C.2.2 を参照)。
HTTP/1.0 クライアントとの下位互換性の詳細については、付録 C.2.2 を参照してください。
9.3.1. リクエストの再試行 (Retrying Requests)
接続は、意図的かどうかに関わらず、いつでも閉じることができます。実装は、非同期クローズイベントから回復する必要性を予測すべきです。クライアントが未処理のリクエストのシーケンスを自動的に再試行できる条件は、[HTTP] の第 9.2.2 節で定義されています。
9.3.2. パイプライン化 (Pipelining)
持続的接続をサポートするクライアントは、リクエストを "パイプライン化" してもかまいません (MAY) (つまり、各レスポンスを待たずに複数のリクエストを送信します)。サーバーは、すべてが安全なメソッド ([HTTP] の第 9.2.1 節) を持つ場合、パイプライン化されたリクエストのシーケンスを並行して処理してもかまいません (MAY) が、受信した順序と同じ順序で対応するレスポンスを送信しなければなりません (MUST)。
リクエストをパイプライン化するクライアントは、対応するレスポンスをすべて受信する前に接続が閉じた場合、未回答のリクエストを再試行すべきです (SHOULD)。失敗した接続の後にパイプライン化されたリクエストを再試行する場合 (サーバーが最後の完全なレスポンスで明示的に閉じていない接続)、クライアントは接続確立の直後にパイプライン化してはなりません (MUST NOT)。以前のパイプラインの最初の残りのリクエストがエラーレスポンスを引き起こした可能性があり、複数のリクエストが途中で閉じた接続で送信された場合、そのエラーレスポンスが再び失われる可能性があるためです (第 9.6 節で説明されている TCP リセット問題を参照)。
冪等メソッド ([HTTP] の第 9.2.2 節) は、接続障害の後に自動的に再試行できるため、パイプライン化にとって重要です。ユーザーエージェントは、ユーザーエージェントがパイプライン化されたシーケンスに関連する部分的な障害条件を検出および回復する手段を持っていない限り、非冪等メソッドの後、そのメソッドの最終レスポンスステータスコードを受信するまで、リクエストをパイプライン化すべきではありません (SHOULD NOT)。
パイプライン化されたリクエストを受信する中継装置は、それらを転送する際にパイプライン化してもかまいません (MAY)。アウトバウンドユーザーエージェントに依存して、どのリクエストを安全にパイプライン化できるかを判断できるためです。インバウンド接続がレスポンスを受信する前に失敗した場合、パイプライン化中継装置は、リクエストがすべて冪等メソッドを持つ場合、まだレスポンスを受信していないリクエストのシーケンスを再試行しようとしてもかまいません (MAY)。そうでない場合、パイプライン化中継装置は、受信したレスポンスを転送し、対応するアウトバウンド接続を閉じて、アウトバウンドユーザーエージェントが適切に回復できるようにすべきです (SHOULD)。
9.4. 並行性 (Concurrency)
クライアントは、特定のサーバーに対して維持する同時オープン接続の数を制限すべきです。
HTTP の以前のリビジョンでは、接続数に特定の上限を与えていましたが、これは多くのアプリケーションにとって実用的でないことがわかりました。その結果、本仕様書は特定の最大接続数を義務付けていませんが、代わりにクライアントが複数の接続を開く際に保守的であることを奨励しています。
複数の接続は通常、"ヘッドオブラインブロッキング" 問題を回避するために使用されます。この問題では、重要なサーバー側処理を必要とするリクエストまたは非常に大きなコンテンツを転送するリクエストが、同じ接続上の後続のリクエストをブロックします。ただし、各接続はサーバーリソースを消費します。
さらに、複数の接続を使用すると、輻輳したネットワークで望ましくない副作用を引き起こす可能性があります。より多くの接続を使用すると、そうでなければ輻輳していないネットワークでも副作用を引き起こす可能性があります。集約された初期同期送信動作が、より少ない並列接続が使用された場合に存在しなかった輻輳を引き起こす可能性があるためです。
サーバーは、単一のクライアントからの過度な数のオープン接続など、悪意があると見なすトラフィックまたはサービス拒否攻撃の特性を持つトラフィックを拒否する場合があることに注意してください。
9.5. 障害とタイムアウト (Failures and Timeouts)
サーバーは通常、非アクティブな接続を維持しなくなるタイムアウト値を持ちます。プロキシサーバーは、クライアントが同じプロキシサーバーを介してより多くの接続を行う可能性が高いため、これをより高い値にする場合があります。持続的接続の使用は、クライアントまたはサーバーのいずれに対しても、このタイムアウトの長さ (または存在) に関する要件を課しません。
クライアントまたはサーバーがタイムアウトしたい場合は、接続で正常なクローズを発行すべきです (SHOULD)。実装は、受信したクローズシグナルのためにオープン接続を常に監視し、適切に応答すべきです (SHOULD)。接続の両側の迅速なクローズにより、割り当てられたシステムリソースを再利用できるようになります。
クライアント、サーバー、またはプロキシは、いつでもトランスポート接続を閉じてもかまいません (MAY)。例えば、クライアントがサーバーが "アイドル" 接続を閉じることを決定したのと同時に新しいリクエストの送信を開始している場合があります。サーバーの視点からは、接続はアイドル状態で閉じられていますが、クライアントの視点からは、リクエストが進行中です。
サーバーは、可能な限り持続的接続を維持し、クライアントが再試行することを期待して接続を終了するのではなく、基礎となるトランスポートのフロー制御メカニズムに一時的な過負荷を解決させるべきです (SHOULD)。後者の手法は、ネットワークの輻輳またはサーバーの負荷を悪化させる可能性があります。
メッセージ本文を送信するクライアントは、リクエストの送信中にエラーレスポンスのためにネットワーク接続を監視すべきです (SHOULD)。クライアントがサーバーがメッセージ本文を受信したくなく接続を閉じていることを示すレスポンスを見た場合、クライアントは直ちに本文の送信を停止し、接続の自分側を閉じるべきです (SHOULD)。
9.6. 切断 (Tear-down)
"close" 接続オプションは、送信者がレスポンスの完了後にこの接続を閉じるというシグナルとして定義されています。送信者は、接続を閉じる意図がある場合、"close" 接続オプションを含む Connection ヘッダーフィールド ([HTTP] の第 7.6.1 節) を送信すべきです (SHOULD)。例えば、
Connection: close
リクエストヘッダーフィールドとして、これはクライアントがこの接続で送信する最後のリクエストであることを示します。一方、レスポンスでは、同じフィールドはサーバーがレスポンスメッセージの完了後にこの接続を閉じることを示します。
"close" 接続オプションを送信するクライアントは、その接続で ("close" を含むものの後に) さらなるリクエストを送信してはならず (MUST NOT)、このリクエストに対応する最終レスポンスを読み取った後に接続を閉じなければなりません (MUST)。
"close" 接続オプションを受信するサーバーは、"close" 接続オプションを含むリクエストに対する最終レスポンスを送信した後、接続の終了を開始しなければなりません (MUST) (以下を参照)。サーバーは、その接続の最終レスポンスで "close" 接続オプションを送信すべきです (SHOULD)。サーバーは、その接続で受信したさらなるリクエストを処理してはなりません (MUST NOT)。
"close" 接続オプションを送信するサーバーは、"close" 接続オプションを含むレスポンスを送信した後、接続の終了を開始しなければなりません (MUST) (以下を参照)。サーバーは、その接続で受信したさらなるリクエストを処理してはなりません (MUST NOT)。
"close" 接続オプションを受信するクライアントは、その接続でのリクエストの送信を停止し、"close" 接続オプションを含むレスポンスメッセージを読み取った後に接続を閉じなければなりません (MUST)。追加のパイプライン化されたリクエストが接続で送信されていた場合、クライアントはそれらがサーバーによって処理されると仮定すべきではありません (SHOULD NOT)。
サーバーが TCP 接続の即座のクローズを実行する場合、クライアントが最後の HTTP レスポンスを読み取れない重大なリスクがあります。サーバーが完全に閉じた接続でクライアントから追加データを受信した場合、例えばサーバーのレスポンスを受信する前にクライアントが送信した別のリクエストなど、サーバーの TCP スタックはクライアントにリセットパケットを送信します。残念ながら、リセットパケットは、クライアントの HTTP パーサーによって読み取りおよび解釈される前に、クライアントの未確認入力バッファーを消去する可能性があります。
TCP リセット問題を回避するために、サーバーは通常、段階的に接続を閉じます。まず、サーバーは、読み取り/書き込み接続の書き込み側のみを閉じることでハーフクローズを実行します。次に、サーバーは、クライアントによる対応するクローズを受信するまで、または独自の TCP スタックがサーバーの最後のレスポンスを含むパケットのクライアントの確認応答を受信したことを合理的に確信するまで、接続から読み取り続けます。最後に、サーバーは接続を完全に閉じます。
リセット問題が TCP に固有であるか、他のトランスポート接続プロトコルでも見られる可能性があるかは不明です。
クライアントによってハーフクローズされた TCP 接続は、リクエストメッセージを区切らず、クライアントがレスポンスに興味がなくなったことを意味するものでもありません。一般に、HTTP/1.1 はトランスポートから独立しているため、トランスポートシグナルはエッジケースを通知するために依存することはできません。
9.7. TLS 接続の開始 (TLS Connection Initiation)
概念的には、HTTP/TLS は、TLS [TLS13] を介してセキュアな接続上で HTTP メッセージを送信するだけです。
HTTP クライアントは TLS クライアントとしても機能します。適切なポートでサーバーへの接続を開始し、TLS ハンドシェイクを開始するために TLS ClientHello を送信します。TLS ハンドシェイクが終了すると、クライアントは最初の HTTP リクエストを開始できます。すべての HTTP データは TLS "application data" として送信されなければなりませんが (MUST)、それ以外は HTTP の通常の接続として扱われます (持続的接続としての潜在的な再利用を含む)。
9.8. TLS 接続の終了 (TLS Connection Closure)
TLS は、(非エラー) 接続終了の前にクローズアラートの交換を使用して、セキュアな接続終了を提供します。[TLS13] の第 6.1 節を参照してください。有効なクローズアラートを受信すると、実装はその接続でこれ以上データが受信されないことを保証できます。
実装が、通常は HTTP メッセージ境界を検出することによって、関心のあるすべてのメッセージデータを送信または受信したことを知っている場合、クローズアラートを送信し、ピアから対応するクローズアラートを受信するのを待たずに接続を閉じることで、"incomplete close" を生成する場合があります。
incomplete close は、既に受信したデータのセキュリティに疑問を投げかけませんが、後続のデータが切り捨てられた可能性があることを示す可能性があります。TLS は HTTP メッセージフレーミングを直接認識していないため、HTTP データ自体を調べてメッセージが完全かどうかを判断する必要があります。不完全なメッセージの処理は、第 8 節で定義されています。
incomplete close に遭遇した場合、クライアントは、次のいずれかを受信したすべてのリクエストを完了したものとして扱うべきです (SHOULD):
- Content-Length ヘッダーフィールドで指定された量のデータ、または
- 終端のサイズ 0 のチャンク (チャンクの Transfer-Encoding が使用される場合)。
チャンク転送コーディングも Content-Length も持たないレスポンスは、有効なクローズアラートを受信した場合にのみ完了します。不完全なメッセージを完全として扱うと、実装が攻撃にさらされる可能性があります。
incomplete close を検出するクライアントは、正常に回復すべきです (SHOULD)。
クライアントは、接続を閉じる前にクローズアラートを送信しなければなりません (MUST)。これ以上データを受信することを期待しないクライアントは、サーバーのクローズアラートを待たずに接続を単に閉じることを選択してもかまいません (MAY)。これにより、サーバー側で incomplete close が生成されます。
サーバーは、クライアントから incomplete close を受信する準備をすべきです (SHOULD)。クライアントはサーバーデータの終わりを見つけることができる場合が多いためです。
サーバーは、接続を閉じる前にクライアントとクローズアラートの交換を開始しようと試みなければなりません (MUST)。サーバーは、クローズアラートを送信した後に接続を閉じてもかまいません (MAY)。これにより、クライアント側で incomplete close が生成されます。