Exercise 2: Reproducing the Real Double-Read Hang — Possible Solution ==================================================================== THE HANDLER ------------------------------ def peek_then_read_app(environ, start_response): content_length = int(environ.get('CONTENT_LENGTH') or 0) peeked = environ['wsgi.input'].read(content_length) # real, bounded read request = Request(environ) after = request.body # asks the SAME stream for more bytes body = f"peeked={peeked!r} after={after!r}".encode('utf-8') start_response('200 OK', [('Content-type', 'text/plain'), ('Content-Length', str(len(body)))]) return [body] RUNNING IT AGAINST A REAL LIVE SERVER ------------------------------ server = make_server('127.0.0.1', 8813, peek_then_read_app) # ... served via serve_forever() on a background thread ... req = urllib.request.Request( 'http://127.0.0.1:8813/x', method='POST', data=b'name=Ada', ) resp = urllib.request.urlopen(req, timeout=3) VERIFIED, REAL RESULT ------------------------------ ERROR after 3.01s -> TimeoutError('timed out') The request never completes. Forcing a short 3-second client timeout (rather than waiting indefinitely, as a first naive attempt with an unbounded read() did for over two real minutes before being killed) confirms the hang quickly and safely rather than blocking the test run itself. WHY THIS HAPPENS, AND WHY BytesIO WOULDN'T HAVE SHOWN IT ------------------------------ The environ['wsgi.input'] object here is backed by a real, live TCP socket, not a bounded in-memory buffer. The first read(content_length) call succeeds instantly because the client already sent exactly that many bytes. The SECOND read - even though it's identically bounded, asking for the same content_length - has nothing left to read: the client has finished sending and is now waiting for a response on a persistent connection. A live socket has no logical "end of the HTTP body" signal of its own; it can only keep waiting for more bytes that are never coming, so the read blocks forever. A BytesIO-backed fake environ, by contrast, is a real bounded buffer with a genuine end. Reading past its end returns b'' immediately, every time, with no possible way to block - so a unit test built entirely on fake environs cannot reproduce this bug at all, no matter how thoroughly it's written. The bug only exists on a real, live socket-backed request, which is exactly why this exercise required testing against an actual running wsgiref server rather than a hand-built environ dict. WHY THIS WORKS AS AN ANSWER ------------------------------ It reproduces the real hang directly rather than describing it in the abstract, uses a bounded (not unbounded) second read specifically to show the danger isn't just about omitting a size argument, and explains the root cause - a live socket's lack of a logical body-end signal - in a way that also explains why the identical BytesIO test would pass cleanly and hide the bug.