Nice writeup! I have one small question please.
When gunicorn is processing the request, it honors the Content-Length: 4 and so it sees GET /flag HTTP/1.1 as a 2nd request.
However, when it is done processing that, it seems gunicorn will then read the 0 as the next thing for it to study.
I'm curious why it doesn't then treat that 0 as an invalid request? (or maybe it does and it doesn't matter???)