Repository navigation
Request builds incorrectly if data values are passed as bytes #3153
Replies: 1 comment
|
For the OAuth values in your example, normalize the base64/ASCII bytes to strings before giving them to data=. HTTPX 0.28.1 still stringifies bytes in that mapping rather than treating them as already-decoded field values: from urllib.parse import parse_qs
import httpx
def oauth_request(data):
normalized = {
key: value.decode("ascii") if isinstance(value, bytes) else value
for key, value in data.items()
}
request = httpx.Request("POST", "https://example.org", data=normalized)
decoded = parse_qs(request.content.decode("ascii"), keep_blank_values=True)
return request, decoded
request, fields = oauth_request({"oauth_signature": b"abc+/=", "oauth_body_hash": b"hash"})
assert fields["oauth_signature"] == ["abc+/="]
assert fields["oauth_body_hash"] == ["hash"]There are two separate issues in the original assertions: the decoded form value is a str while the input is bytes, and splitting on '&'/'=' does not perform percent/plus decoding and can mishandle '=' inside a value. parse_qs handles form decoding and repeated keys. Strict ASCII decoding is appropriate for these ASCII OAuth outputs; do not silently UTF-8-decode arbitrary binary form data. For genuinely binary values, choose the endpoint's documented representation, such as multipart files or explicit base64. The HTTP request body is serialized before HTTP/1.1 versus HTTP/2 transport, so switching protocol does not change this conversion. Checked the original bytes-repr behavior and the example above. Form data. Validation: HTTPX 0.28.1, Python 3.11.9. |
Uh oh!
There was an error while loading. Please reload this page.
OS platform: Windows
Python version: 3.12.0
Installed dependencies and versions (python -m pip freeze):
Conda environment: conda_env.log
httpx - 0.27.0
Code snippet
Error traceback
This fails because the oauth_signature and oauth_body_hash have the
b''byte object markers included when the body of the url is encodedreq.content = b"oauth_body_hash=b%27myoauthbodyhash%27&oauth_signature=b%27oauthsignature%27"req.content = b"oauth_body_hash=myoauthbodyhash&oauth_signature=myoauthsignature"Does the issue exist on HTTP/1.1, or HTTP/2, or both? HTTP/1.1, unsure about /2
Does the issue exist with Client, AsyncClient, or both? Both
When using AsyncClient does the issue exist when using asyncio or trio, or both? Both
This is loosely related to #3115
This will apply to any parameter in
data, I came across it because the oauth lib I'm using returns the signature and body hash as bytes. If it's best practice that data should be passed as str not bytes then I'm happy to have this closed, but the RequestData typing is currentlyMapping[str, Any], so that might be updated to exclude bytes.The issue arises because
Requestcalls_content.encode_urlencoded_datawhich uses_utils.primitive_value_to_strto stringify the values indata._utils.primitive_value_to_strtakes typePrimitiveData = Optional[Union[str, int, float, bool]], so isn't expecting bytes. Aside from decoding any bytes values indatabefore they're passed tobuild_requestthe fix that I've used is to add anelifto_utils.primitive_value_to_strto return the utf-8 decoded string iftype == bytes. The typing for_utils.primitive_value_to_strwould need to change though.All reactions