「送る前に聞け」の作法 —— HTTP/1.1の `100 Continue` が秘める現場の知恵
ネットワークエンジニアとして現場に立っていると、プロトコルの仕様書(RFC)を「ただのルールブック」として読むか、「通信の最適化を勝ち取る武器」として読むかで、トラブルシューティングの質が劇的に変わることに気づきます。
今回は、HTTP/1.1の隠れた名脇役である `100 Continue` について深掘りしましょう。APIを設計する際、あるいは巨大なファイルをアップロードするアプリケーションを運用する際、この仕組みを知っているかどうかで、帯域の浪費を防げるかどうかが決まります。
—
1. なぜ「100 Continue」が必要なのか?
HTTP通信において、クライアントが数メガバイト、あるいはギガバイト単位のリクエストボディを送信しようとした時、もしそのリクエストが「認証エラー」や「バリデーションエラー」で弾かれるものだとしたらどうでしょう?
サーバーにデータが到達した瞬間に `401 Unauthorized` や `400 Bad Request` を返されたら、その通信時間はすべて無駄になります。「本番のデータを送る前に、サーバーが受け入れ可能かを確認する」。これが `100 Continue` の存在意義です。
通信のシーケンス
1. クライアント: `Expect: 100-continue` を含むヘッダーのみを先行して送信。
2. サーバー: リクエストヘッダーを確認し、問題なければ `100 Continue` ステータスを返す。
3. クライアント: サーバーの応答を確認し、ここで初めてリクエストボディを送信。
この「握手(ハンドシェイク)」によって、サーバーが拒絶するリクエストに対して、無駄なボディ送信をスキップできるのです。
—
2. 実装の現場:コードで挙動を制御する
多くのクライアントライブラリは、この挙動を自動的に、あるいは明示的に設定できるようになっています。
curl でのデバッグ
まずは挙動を可視化しましょう。`-v` オプションをつけて、プロトコルのやり取りを覗きます。
Expectヘッダーを明示的に付与してリクエストを送る
curl -v -H “Expect: 100-continue” -T large_file.zip http://api.example.com/upload
これを行うと、`HTTP/1.1 100 Continue` が返ってきた瞬間に通信が一旦停止し、その後にデータが転送される様子が観察できるはずです。
Python (requests) の場合
requestsライブラリはデフォルトで適切な挙動をしますが、明示的に制御するなら以下のようなアプローチをとります。
import requests
url = “http://api.example.com/upload”
ヘッダーにExpectを追加
headers = {‘Expect’: ‘100-continue’}
ボディをストリームとして送ることで、メモリ効率を最大化
with open(‘large_file.zip’, ‘rb’) as f:
response = requests.post(url, data=f, headers=headers)
レスポンスを確認
print(f”ステータスコード: {response.status_code}”)
—
3. インフラ運用における落とし穴
「すべてのリクエストに `100 Continue` をつければ万全だ」と考えるのは早計です。ここからは、現場で僕が何度もハマった「落とし穴」を共有します。
① サーバー側のタイムアウト
サーバーが `100 Continue` を返す前に、クライアントが待ちきれずにボディを送り始めてしまう(あるいはその逆)というケースがあります。特にロードバランサー(ALBやNginx)を通す場合、設定次第でこの挙動が干渉することがあります。
Nginxでの調整例:
サーバー側が100 Continueをどう扱うか設定
巨大なボディを送る際のクライアント待ち時間を調整する場合
proxy_http_version 1.1;
proxy_set_header Expect “100-continue”;
② 互換性の問題
古いプロキシサーバーや、特定のセキュリティアプライアンスは `Expect` ヘッダーを解釈できず、リクエストそのものをドロップしたり、予期せぬ `417 Expectation Failed` を返したりすることがあります。モダンな環境であれば問題ありませんが、レガシーな中継装置が経路にある場合は注意が必要です。
—
4. エンジニアへのアドバイス:使いどころを見極めよ
`100 Continue` は、「リクエストが失敗する確率が高い」、あるいは 「リクエストボディが非常に大きい」 という場合にこそ真価を発揮します。
- 推奨: 大容量ファイルアップロード、複雑なJSONバリデーションを伴うPOSTリクエスト。
- 不要: 1KB以下の小さなGETリクエストや、瞬時に終わるAPIコール。これらに付けると、かえって「1往復分(RTT)」の遅延を増やしてしまうことになります。
ネットワークの最適化とは、常にトレードオフとの戦いです。「今の通信は、1往復の遅延を許してでも、無駄な転送を削る価値があるか?」——この問いを常に持ち続けること。それこそが、シニアエンジニアとして一歩先へ進むための思考法だと僕は信じています。
皆さんの設計するAPIが、無駄なパケットを吐き出さず、スマートに通信できることを願っています。何かトラブルがあれば、まずは `tcpdump` で `Expect` ヘッダーの行方から追ってみてください。答えは必ずそこにあります。
コメント