Skip to content

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

@Kludex

Description

@Kludex

Discussed in #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):
MagicStack/httptools#124

Proposal

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

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions