#2722·uvicorn

Bug report candidate: Uvicorn drops body with Upgrade: h2c + chunked"

Author: KludexCreated Oct 9, 2025Updated Sep 16, 2026

Discussed in https://github.com/Kludex/uvicorn/discussions/2637

Originally posted by jinho7 May 15, 2025

Summary

Hi maintainers! I'm reporting a low-level parsing issue that likely affects Uvicorn users under certain HTTP/1.1 upgrade edge cases.

In short: when a client (e.g., Java RestClient) sends an HTTP/1.1 request with:

Upgrade: h2c

Transfer-Encoding: chunked

...Uvicorn (via httptools_impl) ignores the upgrade (correctly), but the request body is never parsed or passed to the app.

Observed Behavior

Example request:

POST / HTTP/1.1
Host: localhost
Upgrade: h2c
Connection: Upgrade, HTTP2-Settings
Transfer-Encoding: chunked
Content-Type: application/json

3\r\nabc\r\n0\r\n\r\n

Log output:

WARNING: Unsupported upgrade request.
INFO: No request body
WARNING: Invalid HTTP request received.

But the request is valid and should fallback to HTTP/1.1 parsing per RFC 7230 Section 6.7.

If I route the request through ngrok or remove Upgrade: h2c, everything works fine. Body is delivered to the ASGI app.

Root Cause

I've traced the issue and confirmed that:

httptools.HttpRequestParser sets upgrade = True

Uvicorn does not upgrade (e.g., to WebSocket)

But because the upgrade wasn't accepted, on_body() never fires

The result is: the parser never emits the body events.

Upstream discussion and patch suggestion (at httptools): https://github.com/MagicStack/httptools/issues/124

Proposal

Once httptools exposes a method like resume_after_upgrade(), I suggest Uvicorn update:

python
def on_headers_complete(self):
    if self.upgrade and self.upgrade.lower() != b"websocket":
        self.parser.resume_after_upgrade()  # <-- resume HTTP/1.1 body parsing

This would allow Uvicorn to properly recover and parse chunked request bodies.

Why this matters

Java's HttpClient sends Upgrade: h2c by default

Spring Boot RestClient uses chunked transfer by default for object bodies

Uvicorn fails silently unless proxy (e.g., nginx, ngrok) strips headers

This causes confusing bugs in real-world scenarios, despite all parties behaving correctly by spec.

If the maintainers think this is worth fixing, I’m happy to open a proper issue or submit a PR once httptools is ready.

Thanks again for your amazing work!