巨大なリクエストを送り出すその前に——「Expect: 100-continue」という名の慎重なハンドシェイク
ネットワークの現場で「なぜか特定のAPIだけアップロードが遅い」「認証エラーなのに巨大なファイルを送りつけて帯域を無駄にしている」といった事象に遭遇したことはないだろうか。
HTTP/1.1で策定された `Expect: 100-continue` は、一言で言えば「大きな荷物を運び出す前に、相手が受け取れる状態かを確認する」ための紳士的なプロトコルだ。これを正しく理解し実装することは、単なる仕様の把握を超え、堅牢なAPI設計と帯域効率化への第一歩となる。今日は、この隠れた名脇役の挙動を解剖していこう。
—
1. なぜ「100-continue」が必要なのか
Web開発の現場では、数MBから時にはGB単位のファイルをPOSTする機会がある。もし、サーバー側で「認証トークンが期限切れ」だったり、「リクエストボディのサイズ制限に抵触」していたりした場合、どうなるだろうか。
標準的なHTTP通信では、クライアントはリクエストヘッダーとボディをセットで送信する。しかし、サーバーが後からエラーを返したときには、既に巨大なデータが回線を駆け抜けた後だ。これでは帯域の無駄遣いであり、低速回線やモバイル環境では致命的なUXの低下を招く。
ここで登場するのが `Expect: 100-continue` だ。
クライアントはまず「このヘッダーで送っていい?」とサーバーに問いかけ、サーバーが「OK、続きを送れ(100 Continue)」と返してから、満を持してボディを送信する。この「二段階認証」のようなプロセスが、無駄なデータ転送を防ぐ鍵となる。
—
2. 通信のシーケンス:パケットの往復を可視化する
このハンドシェイクの裏側では、以下のパケットフローが繰り広げられている。
1. Client: `POST /upload HTTP/1.1` + `Expect: 100-continue` ヘッダーのみを送信。
2. Server: 内容を確認し、受け入れ可能なら `HTTP/1.1 100 Continue` を返信。
3. Client: それを受け取ってから、本番の `Request Body` を流し込む。
4. Server: 処理完了後、最終的なレスポンス(`200 OK` など)を返す。
もしサーバーがこの仕様を知らない、あるいは拒否する場合は `417 Expectation Failed` が返されるため、クライアント側はその時点で送信を中止できる。
—
3. 実践:コードで挙動を制御する
実際にこの挙動を検証・実装するためのTipsを紹介しよう。
curl での検証
CLIでデバッグする際、`curl` はデフォルトでこの挙動を考慮している。意図的に試すには以下のように記述する。
-v で詳細なハンドシェイクの様子を表示
Expect: 100-continue を明示的に付与してリクエスト
curl -v -X POST http://api.example.com/upload \
-H “Expect: 100-continue” \
-T large_file.bin
Python (requests) でのハンドシェイク
`requests` ライブラリは、デフォルトでは `Expect: 100-continue` を送らないことが多い。明示的に制御したい場合は、ヘッダーに含める必要がある。
import requests
url = “http://api.example.com/upload”
headers = {
“Expect”: “100-continue”
}
大きなデータを開いて送信
with open(“data.bin”, “rb”) as f:
response = requests.post(url, data=f, headers=headers)
レスポンスコードを確認
print(f”Status Code: {response.status_code}”)
—
4. 運用上の注意点:タイムアウトの罠
シニアエンジニアとして、最後に「現場の落とし穴」を伝えておきたい。
このハンドシェイクには「クライアント待機問題」がある。サーバーが `100 Continue` を返すのに時間がかかっている場合、クライアントはいつまで待つべきか? RFC 2616(および現在のRFC 9112)では、クライアントは永久に待つべきではないとされている。
実務上、多くのクライアントライブラリは、約1秒から3秒程度のタイムアウトを設けている。
- サーバーサイドの負荷: もしバックエンドの認証ロジックが重く、`100 Continue` を返す前に処理が詰まると、クライアントはタイムアウトしてリクエストを打ち切る。
- プロキシ/ロードバランサー: NginxやAWS ALBを挟む場合、これらの設定で `Expect` ヘッダーの処理が意図せず阻害されることがある。Nginxなら `proxy_set_header Expect $http_expect;` を適切に設定しないと、バックエンドまでリクエストが正しく透過されないケースがある。
Nginx設定のチェックポイント
location /upload {
# クライアントからのExpectヘッダーをバックエンドに渡す設定
proxy_set_header Expect $http_expect;
# タイムアウト調整
proxy_read_timeout 60s;
}
—
まとめ:ネットワークの「礼儀」を理解する
`Expect: 100-continue` は、HTTPという巨大なプロトコルのなかで、クライアントとサーバーが「合意」を形成するための、極めて知的な仕組みだ。
大規模なデータを扱うAPIを設計する際は、単に「送る」ことだけでなく、「いつ送る許可を出すか」までを考慮に入れてほしい。それが、無用なサーバー負荷を減らし、安定した通信を実現するプロのエンジニアの流儀である。
今日からあなたのアプリケーションが、ネットワークに対して少しだけ「礼儀正しく」振る舞えるようになることを願っている。もしトラブルが起きたら、まずはパケットキャプチャで `100 Continue` が正しく返ってきているか確認することから始めよう。現場からは以上だ。
コメント