プロンプトと適用範囲
クライアントが数百メガバイトのファイルをオブジェクトストレージにアップロードします。サーバーはリクエストヘッダーのみを使用して、無効なトークン、クォータ超過、または不正なメディアタイプを拒否できます。HTTP/1.1 のアップロードハンドシェイクを設計し、Expect: 100-continue、417 フォールバック、プロキシホップ、タイムアウト、リトライ、メトリクスについて説明してください。
これはバックエンドのプロトコルと信頼性に関する質問です。ファイルサイズやステータスは本演習のための前提条件であり、発生頻度の主張ではありません。
面接官がテストしているポイント
- ヘッダーのみによる受付判定とボディ処理を分離できているか。
- 100、最終レスポンス、417 を正確に説明できているか。
- 無視された expectation、待機タイムアウト、コネクションの再利用を適切に処理できているか。
- リトライ、冪等性、チェックサム、可観測性の担当区分が明確になっているか。
質問すべき確認事項
- アップロードは冪等であり、アップロードセッションまたは冪等性キー(Idempotency-Key)は存在しますか?
- クライアントはボディのソースを巻き戻し(rewind)、ファイルを再度開くことができますか?
- どのゲートウェイおよびストレージサービスが情報レスポンス(1xx)を保持しますか?
- 失敗した試行を Expect なしでリトライできますか?また、それによって重複が発生する可能性はありますか?
- ヘッダーのみでチェックできるルールと、ボディ全体を読み取る必要があるルールはどれですか?
30秒での回答
「クライアントは Expect: 100-continue を含むヘッダーを送信します。サーバーは認証、サイズ、タイプ、クォータ、ルーティングを検証し、即座に拒否する場合は最終的な 4xx を送信し、ボディを受け入れる準備ができている場合は 100 Continue を返します。クライアントは、時間制限を設けた待機の後、暫定レスポンスを受け取って初めてボディを送信します。417 レスポンスを受け取った場合、ボディが巻き戻し可能で操作の再実行が安全であるときに限り、Expect なしでリトライできます。同一の冪等性キーを維持し、プロキシの挙動、削減されたバイト数、417 の発生率、中断されたボディ数を計測します。」
ステップごとの設計
1. 最初にヘッダーを送信する
リクエストには、認証情報、Content-Length、Content-Type、ダイジェスト、テナント情報、冪等性キー、および Expect: 100-continue が含まれます。ボディを読み取る前に、サーバーはトークン、ルート、クォータ、静的なサイズ制限を検証できます。仕様上、判定後にレスポンスを返すことが求められており、クライアントは無期限に待機してはなりません。
PUT /objects/o-123 HTTP/1.1
Host: upload.example
Authorization: Bearer ...
Content-Length: 524288000
Content-Type: application/octet-stream
Idempotency-Key: up-7f2
Expect: 100-continue2. 受付と拒否の処理
受け入れる場合は 100 Continue を返し、その後にボディを受信します。認証が無効な場合、サイズが超過している場合、またはクォータが不足している場合は、401、403、413、415 などの最終ステータスを返します。クライアントはまだ送信していないボディバイトを送信すべきではありません。プリフライトはヘッダーで確認可能なルールのみをカバーするため、最終的な結果は依然としてボディの検証に依存します。
3. 417 を限定的に使用する
417 Expectation Failed は、サーバーまたは中継機器が要求(expectation)を満たせないことを意味します。操作で許可されている場合、クライアントは Expect を削除して再送信できますが、ボディを巻き戻し可能であり、冪等性キーを維持し、古いコネクションに未読のボディが残っていないことを確認できなければなりません。すべての 4xx を 417 として扱ったり、取り消し不可能な非冪等の処理を再実行したりしないでください。
4. 待機、プロキシ、コネクションへの配慮
クライアントは 100 の待機に対して有限のタイムリミットを設定し、タイムアウトの経路を記録します。HTTP/1.0 の中継機器は Expect を無視することがあり、プロキシが自身で 100 を生成する場合もあります。ヘッダーの転送、情報レスポンスの処理、および最終的な拒否時にコネクションが切断されるかドレイン(読み捨て)されるかをホップごとにテストしてください。そうしないと、残余バイトによってコネクションの再利用が破損する可能性があります。
5. リトライの冪等性を確保する
ボディのソースが巻き戻し可能であり、ビジネスロジック上再試行が許可されている場合にのみリトライします。オブジェクトの作成やクォータの消費では安定した冪等性キーを使用し、試行が単一の結果に対応するようにします。切断後は結果が不明な場合があるため、別のオブジェクトを作成する前にアップロードセッションやオブジェクトの状態を問い合わせます。
6. 両方のフェーズで検証する
プリフライトチェックでは、認証、テナント、サイズ、タイプ、クォータを検証します。ボディ受信後は、実際のバイト数、ダイジェスト、悪意のあるコンテンツ、ストレージポリシーを検証します。Content-Length やクライアントが宣言した MIME タイプのみを信用してはなりません。リクエスト ID、受付結果、受信バイト数をログに記録し、トークンやファイルコンテンツ自体は絶対に記録しないでください。
質の高い模範解答
「アップロードをヘッダーによる判定とボディの取り込みに分割します。クライアントは認証、サイズ、タイプ、ダイジェスト、冪等性キー、および Expect: 100-continue を送信します。サーバーはボディを受信する前に、低コストで確定的なエラーを拒否します。受付後は 100 を送信し、クライアントがアップロードを行います。417 の場合は、同一の冪等性キーを用い、再実行が安全な場合にのみ Expect なしのリトライを実行します。待機タイムアウト、切断、プロキシの挙動は、ステータス問い合わせを通じて回復します。ボディサイズ、ダイジェスト、コンテンツの安全性は引き続き検証します。本番相当のプロキシ全体で、100 のレイテンシ、417、早期 4xx、コネクション再利用、削減されたバイト数をテストします。」
よくある間違い
- 100 を成功と見なす → これはボディの送信を許可するだけです → 最終的な 2xx または失敗を待つ必要があります。
- 任意の 4xx の後に Expect なしでリトライする → 副作用が重複する可能性があります → 417 かつ安全な再実行の場合のみフォールバックします。
- 100 を無期限に待機する → リクエストがハングします → 待機時間を制限し、観測可能にします。
- ヘッダーのみを検証する → 破損したファイルや悪意のあるボディがストレージに入り込みます → 取り込み後にも検証します。
- プロキシの差異を無視する → 早期のボディ送信や残余バイトによって再利用が壊れます → 各ホップをテストし、コネクションの再利用を制御します。
フォローアップの質問と回答
クライアントが 401 を受信する前にボディのバイトを送信してしまった場合はどうなりますか?
サーバーは自身のコネクションポリシーに従い、切断するか、読み取りを継続してボディを破棄します。クライアントは送信を停止し、その試行を失敗として記録します。再利用を行うには、プロトコル状態が再び整合していることを証明する必要があります。
なぜ常にボディを即座に送信しないのですか?
小さなリクエストではハンドシェイクを行う価値がない場合があります。大容量リクエストでは、認証、サイズ、クォータの失敗を早期に検出できるメリットがあります。リクエストサイズ、プロキシの互換性、実装コストを考慮して導入します。
この考え方は HTTP/2 や HTTP/3 でも重要ですか?
バイトレベルで全く同一の挙動になると想定してはなりません。採用するクライアント、ゲートウェイ、サーバーが情報レスポンス、フロー制御、ストリームのキャンセルをどのように処理するかを確認してください。早期拒否、回復可能なアップロード状態、冪等性という恒久的な目標は変わりません。