Debug an HTTP Client That Hangs After a 307 Redirect With a Streamed Request Body
Company: Stripe
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Technical Screen
You are given a copy of Requests, the open-source Python HTTP client library, into which a defect has been introduced, along with its test suite. A user reports the following:
- They upload a payload by passing a file-like object, such as an `io.BytesIO` buffer, as the body of a POST request.
- The server answers with `307 Temporary Redirect` and a `Location` header that points at another path.
- The client follows the redirect, and the call then hangs until it times out. No response is returned.
A minimal reproduction looks like this (the URL and payload are illustrative):
```python
import io
import requests
payload = io.BytesIO(b'{"amount": 100}')
resp = requests.post("http://localhost:8000/upload", data=payload, timeout=10)
# /upload answers 307 with Location: /v2/upload; this call raises a read timeout
```
Find the root cause, fix it in the library, and add tests that prove the fix.
```hint Follow the body object
Trace the one body object through the first send and the redirected send. What state is it in after the first send, and what do the redirected request's headers still promise the server?
```
```hint A hang, not an error
Once the redirected request's headers are on the wire, work out who is waiting for whom.
```
### Constraints and Clarifications
- Redirects are followed, which is the library's default for POST.
- Treat this as the only defect, but rerun the full test suite after your fix.
### Clarifying Questions
- Does the problem reproduce when the body is a `bytes` object instead of a file-like object?
- Which redirect status codes must repeat the same method and body, and which switch to GET and drop the body?
- If the caller passes a buffer that has already been partly read, should the redirected request resend from where the body started or from the beginning of the buffer?
- What should happen when the body cannot be rewound at all, such as a generator or a pipe: raise an error, or return the 307 response to the caller?
- May the fix read a file-like body fully into memory, or must large uploads keep streaming?
### What a Strong Answer Covers
- A minimal reproduction against a local server, with evidence of what the redirected request actually sends
- The exact mechanism: the state of the stream, the length the headers announce, and why neither side reports an error
- Why only redirects that keep the method and body are affected
- A fix that handles partly read and unrewindable bodies, compared with at least one alternative
- Regression tests that run with short timeouts and cover the variants, not just the reported case
### Follow-up Questions
- The `Location` header points at a different host. Which headers should change, and should the body still be sent?
- The upload is a multi-gigabyte file. How would you support this redirect without buffering it, and when would you rather not follow the redirect at all?
- How would you make calls that run into this kind of problem fail fast in production instead of waiting out a long timeout?
- How would the outcome change if the body were sent with chunked transfer encoding instead of a `Content-Length` header?
Overview: A debugging exercise on the Python HTTP library Requests: a POST whose body is a file-like stream hangs until it times out after the server answers 307 Temporary Redirect. It tests redirect semantics, Content-Length framing, how a stream behaves when a request is resent, and regression testing of the fix.
Read the full Stripe Software Engineer interview experience this question came from