httpx reads the boundary back from that header when it encodes files=, so the header and the body agree. But a boundary must not occur in the content it delimits (RFC 2046 §5.1.1), and a fixed three-character one can. An uploaded file that contains the bytes \r\n--+++ is split at that point: the server receives a truncated file, and the rest of it as a malformed extra part.
This used to work for endpoints with a single multipart/form-data body. Up to 0.28.x their _get_kwargs set no Content-Type, and httpx generated a random boundary for each request. #1005 fixed the endpoints with several body types, which sent no boundary at all (#947). As a side effect, the single-body endpoints changed from a random boundary to the fixed one.
The file arrives as b'first line', and the rest of it becomes a second part. A server sees the same: Rack 3.2.6, which Rails uses, parses this request into file = "first line" plus a stray parameter named text/plain.
Desktop (please complete the following information):
Two ways to keep #1005's fix without the fixed boundary:
A random boundary for each request, in the template. For example, boundary = secrets.token_hex(16) in _get_kwargs, used in the header. httpx would then encode the body with it, as it does with +++ today. httpx's own default is 16 random bytes in hex.
No Content-Type for an endpoint whose only body is multipart/form-data, as before 0.29.0, so that httpx sets the header with its own random boundary. Only the endpoints with several body types would need the explicit header from (1).
In practice a collision needs those exact seven bytes in the uploaded content, so it is rare with binary files. But it silently truncates the file, and it reaches the server as a successful upload.
Activity
fix: Let httpx2 choose the multipart/form-data boundary (openapi-gene…
Release 0.30.0 (openapi-generators#1510)
chore(quarantine): allow openapi-python-client 0.30.0 for openapi-gen…
Merge pull request #5 from eye2gene/allow/openapi-python-client-0.30.…