httpx version: 0.13.1
Python version: 3.7.3
OS: Windows 7
I am not sure how to classify this issue: it could be a bug, since it is an incompatible with requests, on the other hand, it could be feature request, if you consider it is not that important. So, I decided to go with question. :D
Was replacing requests with httpx in my small script and received an error from the server httpx._exceptions.HTTPError: 411 Client Error: Length Required for url: ... while doing a POST request without a body (same behavior is applicable for PUT and PATCH requests too).
Steps to reproduce:
import requests
import httpx
for client in (requests, httpx):
for method in ("get", "head", "delete", "post", "put", "patch"):
r = client.request(method, f"https://httpbin.org/headers")
print(
f"[{client.__name__}] method={method.upper()} "
f'Content-Length={r.request.headers.get("Content-Length")}'
)
requests adds Content-Length header in every possible http method, except GET and HEAD.
httpx does not add Content-Length header at all, unless requests has a body.
Content-Length to all methods, but since it is not needed in GET and HEAD methods, it was reverted.I assume, it should be handled during the Request building.
httpx decides whether to add Content-Length depending on the steam type, which is handled in _content_streams.py#L314.
To be honest, I am not sure what is the proper way to deal with this. :-(
My first idea was to use method inside the stream:
class ByteStream(ContentStream):
def __init__(self, body: typing.Union[str, bytes], method: str) -> None:
self.body = body.encode("utf-8") if isinstance(body, str) else body
self.method = method
def get_headers(self) -> typing.Dict[str, str]:
if self.method in ("GET", "HEAD"):
return {}
content_length = str(len(self.body)) if self.body else "0"
return {"Content-Length": content_length}
But this approach is not great, since it requires to pass method from Request to encode and then to ByteStream inside it. Also, it is not clear what method should I use when building stream in read methods.
Please, let me know what you think. Thanks!
I believe this is a bug as we're not following the RFC:
A user agent SHOULD send a Content-Length in a request message when no Transfer-Encoding is sent and the request method defines a meaning for an enclosed payload body. For example, a Content-Length header field is normally sent in a POST request even when the value is 0 (indicating an empty payload body).
Thanks!
As you mentioned I think it's best if content streams stay agnostic of the HTTP semantics here.
The .get_headers() method is called in Request.prepare(), so other options I can see as far as implementation goes…
.get_headers(method=...); this would fix the "pass method down the content stream chain" item but streams would then have to be aware of HTTP semantics, not great.Content-Length in all cases, but ignore that header in Request.prepare() if method does not accept a body.I’d suggest auto including it in Request.prepare, if the method requires one, and none is currently present.
^Sounds good to me too — let the content stream not return a Content-Length if there's no body, and add a default Content-Length: 0 in the request if it's required by the method.
We should cover POST, PUT, and PATCH here.
3.3.2 - "A user agent SHOULD send a Content-Length in a request message when no Transfer-Encoding is sent and the request method defines a meaning for an enclosed payload body"
And
4.3.5 - "A payload within a DELETE request message has no defined semantics;"
Most helpful comment
I believe this is a bug as we're not following the RFC: