httpx: Graceful handling of 204 responses that incorrectly a include non-zero Content-Length.

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

HTTPX Session encounters parsing error (RemoteProtocolError: malformed data) when an endpoint returns content when status code is 204 (HTTP No Content). Other Python http clients handle this scenario… I’m not sure if httpx should actually handle this case, since this is a weird case, but maybe it’s best to handle it?

To reproduce

setup FastAPI server with this code:

from typing import Optional

from fastapi import FastAPI
from starlette.status import HTTP_204_NO_CONTENT
app = FastAPI()


@app.get("/", status_code=HTTP_204_NO_CONTENT)
def read_root():
    print("d")

Run it using uvicorn. In another terminal run the following snippet:

import httpx
client = httpx.Client()
client.get('http://localhost:8000/")
# The second one will get an error
client.get('http://localhost:8000/")

Expected behavior

No errors?

Actual behavior

malformed data

Debugging material

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in get(self, url, params, headers, cookies, auth, allow_redirects, timeout)
    905         **Parameters**: See `httpx.request`.
    906         """
--> 907         return self.request(
    908             "GET",
    909             url,

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in request(self, method, url, content, data, files, json, params, headers, cookies, auth, allow_redirects, timeout)
    731             cookies=cookies,
    732         )
--> 733         return self.send(
    734             request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
    735         )

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in send(self, request, stream, auth, allow_redirects, timeout)
    765         auth = self._build_request_auth(request, auth)
    766 
--> 767         response = self._send_handling_auth(
    768             request,
    769             auth=auth,

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in _send_handling_auth(self, request, auth, timeout, allow_redirects, history)
    803 
    804         while True:
--> 805             response = self._send_handling_redirects(
    806                 request,
    807                 timeout=timeout,

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in _send_handling_redirects(self, request, timeout, allow_redirects, history)
    835                 )
    836 
--> 837             response = self._send_single_request(request, timeout)
    838             response.history = list(history)
    839 

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_client.py in _send_single_request(self, request, timeout)
    859 
    860         with map_exceptions(HTTPCORE_EXC_MAP, request=request):
--> 861             (status_code, headers, stream, ext) = transport.request(
    862                 request.method.encode(),
    863                 request.url.raw,

~/.pyenv/versions/3.8.2/lib/python3.8/contextlib.py in __exit__(self, type, value, traceback)
    129                 value = type()
    130             try:
--> 131                 self.gen.throw(type, value, traceback)
    132             except StopIteration as exc:
    133                 # Suppress StopIteration *unless* it's the same exception that

~/.pyenv/versions/3.8.2/lib/python3.8/site-packages/httpx/_exceptions.py in map_exceptions(mapping, **kwargs)
    341 
    342         message = str(exc)
--> 343         raise mapped_exc(message, **kwargs) from exc  # type: ignore
    344 
    345 

RemoteProtocolError: malformed data

Environment

  • OS: macOS
  • Python version: 3.8.2
  • HTTPX version: 0.16.1
  • Async environment: Both (asyncio, blocking)
  • HTTP proxy: no
  • Custom certificates: no

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 16 (9 by maintainers)

Most upvoted comments

Yep, FastAPI was at fault. This was solved in https://github.com/tiangolo/fastapi/pull/5145, available since FastAPI 0.79.0. 🎉