HTTPの100 Continue、大きな本文を送る前の返事は何を保証するか

薄紫の門の手前に緑の円盤と巻かれた桃色の帯があり、奥には空の青い器が離れて置かれている。

大きなファイルを送った後に「認証が必要です」と返されると、送信に使った時間と通信量が無駄になってしまいます。宛先や認証情報だけで分かる問題なら、本文を送り始める前に知らせてもらいたいところです。HTTPのExpect: 100-continueは、そのやり取りに使う仕組みです。

ただし、100 Continueが返ったからといって、ファイルの受付や保存が成功したわけではありません。「本文を送る段階へ進むための返事」と「要求を処理した結果」を分けて読むことが、この仕組みを使う出発点になります。

簡単にまとめると?

  • Expect: 100-continueは、本文を送る前にヘッダーを見た段階の返事を求めます。認証などの問題を先に返せれば、大きな本文の不要な転送を避けられます。
  • 100は暫定応答です。本文を送った後にエラーになる場合もあり、処理成功や保存完了を保証しません。
  • クライアントは100を受け取る前に本文を送ることもできます。待ち時間の設定、中継サーバー、最終応答まで確認して利用します。

先にヘッダーを送り、本文の前で判断する

HTTPの要求は、何をしたいかを表すメソッド、対象のURI、認証や本文の種類・長さなどを伝えるヘッダー、そして送信する本文から成ります。たとえば動画をPUTで送るとき、動画の中身を読む前でも、認証情報がない、対象でそのメソッドを使えない、といった判断ができる場合があります。

クライアントが本文を送る前の返事を待つなら、要求ヘッダーへExpect: 100-continueを付けます。RFC 9110で定義される期待値は、パラメーターを伴わない100-continueです。本文のない要求には、この期待値を付けてはいけません。[1、10.1.1節]

説明用に、あるアップロード先へ1 GiBのデータを送る場面を考えます。最初にメソッド・宛先・認証情報・Content-Lengthなどを送ります。サーバーがそこで401 Unauthorizedを返し、クライアントがまだ本文を送り出していなければ、1 GiBの本文転送を避けられます。これは条件を置いた通信順序の例であり、実際の回線で節約できる量や時間を測定した結果ではありません。

反対に、ヘッダーだけでは最終結果を決められず、本文を受け取る必要があるなら、サーバーは100 Continueを返して送信を促します。HTTP/1.1以降で、この期待値と本文が続くことを示すヘッダー一式を受けたオリジンサーバーは、ヘッダーから判断できる最終応答か、100を直ちに返す必要があります。本文が届くまで待ってから100を返す手順にはしません。双方が相手の送信を待つ状態を避けるためです。[1]

100の後に、エラーが返ることもある

100は、同じ要求の途中に置かれる暫定応答です。たとえば100を受け取って本文を送り、その中身を検査した結果、内容がサービスの条件に合わず4xxの最終応答が返る流れもあります。ヘッダーまでを見た段階と、本文まで処理した段階では、判断に使える情報が異なります。

100を送ったサーバーは、本文を受信して処理した後、接続が途中で閉じられた場合を除き、最終ステータスを送らなければなりません。100を受信した時点で、アプリの表示を「アップロード完了」に変えると、この最終結果を取り落とします。[1、10.1.1節・15.2.1節]

本文前に401、100の後に本文と最終4xx、100を待たずに本文送信、という三つの通信順序。100と最終結果は別段階。
HTTP/1.1を想定した説明用の通信順序。実測や処理時間の比較ではなく、本文を送る時点と応答の意味を分けている。

202 Acceptedとの違いもここにあります。202は、処理がまだ完了していなくても、受け付けた要求に対する最終応答です。100はその要求の途中の応答であり、その後の最終ステータスを待ちます。「まだ作業が終わっていない」という見た目が似ていても、HTTPの応答列の中での役割は異なります。[1、15.3.3節]

100を待たずに、本文が出ていく場合

Expectを付けることと、必ず100が来るまで本文を止めることも同じではありません。RFC 9110は、クライアントに特定の長さだけ待つことを要求していません。応答をまだ受信していなくても、本文の送信へ進むことを認めています。HTTP/1.0の中継先では100が通らないため、無期限に待つことも勧めていません。[1、10.1.1節]

したがって、通信記録にExpectがあるだけでは、「本文を一切送らずにエラーを受け取れた」とは言えません。クライアントが一定時間待って本文を送り始め、その後に最終エラーが届く場合もあります。どのくらい待つか、待機を設定できるかは、使うクライアントの仕様と設定を確認する必要があります。

サーバー側も、本文の一部または全部をすでに受信していれば、100を省略できます。100が記録されていない通信を見つけても、それだけで不正な実装と決めず、本文がいつ送られたか、どの最終応答が返ったかを合わせて調べます。

その100を、誰が返したのか

要求がプロキシなどの中継サーバーを通ると、クライアントの相手と、実際に要求を処理するオリジンサーバーが分かれます。RFC 9110では、中継先がHTTP/1.0だけを扱うと判断したプロキシが、クライアントへ100を返して本文送信を促すことを認めています。[1]

このため、100を見たことを「オリジンが認証と入力検査をすべて終えた証拠」として扱えません。プロキシが本文の転送を始めるために返した応答かもしれず、いずれの場合も本文処理後の最終結果は別です。調査では、クライアントから見た応答列と、中継先での受信・処理結果を分けると、どの段階で判断が変わったのかを追いやすくなります。

導入時に比較するのは、待機と不要転送の釣り合い

この仕組みが役立つのは、大きな本文を送り、しかも本文を読む前に拒否を判断できる可能性がある場面です。一方、待機を入れることで、すぐ本文を送り出す場合とは開始のタイミングが変わります。小さな要求も含めて一律に速くなると考えず、本文の大きさ、早期エラーの発生、往復の時間、待機設定を対象環境で比較します。

実装の確認では、まず「ヘッダー段階の401で本文を送らずに済むか」、次に「100の後の最終エラーを正しく扱うか」、さらに「100が返らないときに待ち続けないか」を分けて試します。今回の三つの通信順序は、その確認項目を整理するモデルであり、特定製品の互換性や高速化を実証するものではありません。

417 Expectation Failedを受けた場合、RFCは100-continueを外してその要求を繰り返すことを勧めています。ただし、この応答を実際に受けた場合と、通信が途切れて結果が分からない場合は別です。Expectを使えば要求の二重実行が防げるわけではなく、結果不明の更新要求を扱うには、サービス側の再送・重複検出の契約も必要です。[1、10.1.1節・9.2.2節]

利用側の確認は、本文を送る合図、本文の送信、最終応答の三段階に分けます。100が担う範囲をそこまでに留めることで、通信量を減らす工夫と、処理が成功したかを確かめる手順を混同せずに組み立てられます。

出典

  1. RFC 9110: HTTP Semantics(2022年6月、STD 97)、10.1.1節Expect、15.2.1節100 Continue、15.3.3節202 Accepted、9.2.2節Idempotent Methods。