Reading the full response body in middleware
Understanding ASGI's streaming send model.
responses stream as multiple body messages and headers go first, so you cannot add a header after seeing the body; you must buffer all chunks, compute the hash, set the header, then resend.
WHAT THIS TESTS Whether you understand the ASGI response protocol, specifically that headers are sent before the body streams, and can implement buffering correctly while acknowledging the costs.
A GOOD ANSWER COVERS In ASGI an application sends a response as a sequence of events: first an http.response.start message carrying status code and headers, then one or more http.response.body messages each with a chunk and a more_body flag. The protocol streams, so by the time you would know the full body, the start message with its headers has typically already been emitted. That ordering is why you cannot simply read the body and then add a body-derived header in a plain pass-through middleware. The solution is to wrap the downstream send callable. Your wrapper holds back the http.response.start message instead of forwarding it, accumulates each body chunk into a buffer until more_body is false, then computes the content hash over the complete buffer, mutates the saved start message's headers to add X-Content-Hash, and finally sends the modified start followed by the buffered body (or the original chunks). Pure ASGI middleware gives the most control; with Starlette you can subclass BaseHTTPMiddleware, though it has its own streaming caveats.
COMMON WRONG ANSWERS Assuming the entire body is available at the moment headers are produced. Trying to set a header after the start message has already been sent. Buffering arbitrarily large or streaming responses, defeating streaming and risking memory blowups. Ignoring that this breaks true streaming responses.
LIKELY FOLLOW-UPS What are the trade-offs versus computing the hash in the endpoint instead? How does this interact with StreamingResponse? What is the memory and latency impact?
ONE CONCRETE EXAMPLE The wrapper does: if message['type'] == 'http.response.start': save it; elif type is body: buffer chunk, and when more_body is false, hash the buffer, append the header to the saved start, send start, then send the body. The trade-off is real: you must hold the whole response in memory and you delay first-byte until the body is complete, eliminating streaming and adding latency for large payloads, so it suits small responses only.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.