Backend interview: How would you use Expect: 100-continue for large uploads?
Prompt and scope
A client uploads a file of several hundred megabytes to object storage. The server can reject an invalid token, quota, or media type using only request headers. Design the HTTP/1.1 upload handshake and explain Expect: 100-continue, 417 fallback, proxy hops, timeouts, retries, and metrics.
This is a backend protocol and reliability question. The file size and statuses are assumptions for the exercise, not frequency claims.
What the interviewer is testing
- Whether you separate header-only admission from body processing.
- Whether you describe 100, the final response, and 417 precisely.
- Whether you handle ignored expectations, wait timeouts, and connection reuse.
- Whether retries, idempotency, checksums, and observability have clear owners.
Clarifying questions to ask
- Is the upload idempotent, and is there an upload session or idempotency key?
- Can the client rewind the body source and reopen the file?
- Which gateways and storage services preserve informational responses?
- Can a failed attempt be retried without Expect, and could it create a duplicate?
- Which rules are header-only checks, and which require reading the full body?
A 30-second answer
“The client sends headers with Expect: 100-continue. The server checks authentication, length, type, quota, and routing; it sends a final 4xx for an immediate rejection or 100 Continue when it is ready for the body. The client sends the body only after the interim response, with a bounded wait. A 417 response can trigger a no-Expect retry only when the body is rewindable and the operation is safe to replay. I keep the same idempotency key and measure proxy behavior, bytes saved, 417 rate, and aborted bodies.”
Step-by-step design
1. Send headers first
The request carries authentication, Content-Length, Content-Type, a digest, tenant information, an idempotency key, and Expect: 100-continue. Before reading the body, the server can check the token, route, quota, and static size limit. The specification requires a response after the decision; the client must not wait forever.
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. Handle admission and rejection
For admission, return 100 Continue, then receive the body. For invalid authentication, an excessive length, or insufficient quota, return a final status such as 401, 403, 413, or 415; the client should not send body bytes that it has not sent yet. Preflight covers only header-visible rules, so the final result still depends on body validation.
3. Use 417 narrowly
417 Expectation Failed means the server or intermediary cannot meet the expectation. If the operation allows it, the client may remove Expect and resend, but it must be able to rewind the body, preserve the idempotency key, and ensure the old connection has no unread body. Do not treat every 4xx as 417 or replay irreversible non-idempotent work.
4. Account for waits, proxies, and connections
The client gives the wait for 100 a finite deadline and records the timeout path. HTTP/1.0 intermediaries may ignore Expect, and a proxy may generate 100 itself. Test every hop for header forwarding, informational response handling, and whether a final rejection closes or drains the connection; otherwise leftover bytes can corrupt connection reuse.
5. Make retries idempotent
Retry only when the body source is rewindable and the business operation permits repetition. Object creation or quota deduction should use a stable idempotency key so attempts map to one result. After a disconnect, the outcome can be unknown; query the upload session or object state before creating another object.
6. Validate in both phases
Preflight checks cover authentication, tenant, length, type, and quota. After receiving the body, validate actual byte count, digest, malicious content, and storage policy. Do not trust Content-Length or a client-declared MIME type alone. Log request ID, admission result, and received bytes, never tokens or file content.
Model high-quality answer
“I split the upload into a header decision and body ingestion. The client sends authentication, length, type, digest, an idempotency key, and Expect: 100-continue. The server rejects cheap, deterministic failures before the body; after admission it sends 100 and the client uploads. A 417 causes a no-Expect retry only when replay is safe, with the same idempotency key. Wait timeouts, disconnects, and proxy behavior recover through status queries. Body size, digest, and content safety are still validated. I test 100 latency, 417, early 4xx, connection reuse, and bytes saved across real proxies.”
Common mistakes
- Treating 100 as success → it only permits the body → wait for the final 2xx or failure.
- Retrying without Expect after any 4xx → side effects can duplicate → fallback only for 417 and safe replay.
- Waiting forever for 100 → requests hang → bound and observe the wait.
- Checking only headers → corrupt or malicious bodies enter storage → validate after ingestion too.
- Ignoring proxy differences → early bodies or leftover bytes break reuse → test each hop and control connection reuse.
Follow-up questions and responses
What if the client sent body bytes before receiving a 401?
The server follows its connection policy to close or continue reading and discard the body. The client stops sending and marks the attempt failed. Reuse requires proving that protocol state is aligned again.
Why not always send the body immediately?
Small requests may not justify the handshake. Large requests benefit when authentication, length, or quota failures can be detected early. Roll out by request size, proxy compatibility, and implementation cost.
Does the idea still matter for HTTP/2 or HTTP/3?
Do not assume identical byte-level behavior. Verify how the chosen client, gateway, and server handle informational responses, flow control, and stream cancellation. The durable goals remain early rejection, recoverable upload state, and idempotency.