Summary
content, data, json and files are silently ignored for every method other than POST/PUT/PATCH. client.delete(url, json={...}) sends an empty body with no error, no warning, and no Content-Type header.
Cause
src/lib.rs:468 (and src/lib.rs:641 in _stream):
let is_post_put_patch = matches!(method, Method::POST | Method::PUT | Method::PATCH);
// ...
if is_post_put_patch {
// content / data / json / files applied only here
}
Reproduction
Against a local server echoing the request line, Content-* headers and body:
httpr DELETE json={"id":1} -> DELETE /x [] body=b'' ❌
httpr DELETE content=b"..." -> DELETE /x [] body=b'' ❌
httpr OPTIONS json={"id":1} -> OPTIONS /x [] body=b'' ❌
httpr POST json={"id":1} -> POST /x [content-type: application/json, content-length: 8] body=b'{"id":1}' (control, OK)
httpx DELETE json={"id":1} -> DELETE /x [Content-Length: 8, Content-Type: application/json] body=b'{"id":1}'
httpx DELETE content=b"..." -> DELETE /x [Content-Length: 9] body=b'raw-bytes'
httpx OPTIONS json={"id":1} -> OPTIONS /x [Content-Length: 8, Content-Type: application/json] body=b'{"id":1}'
Impact
RFC 9110 permits a body on any method, and real APIs rely on it — Elasticsearch's DELETE _search / _delete_by_query, and many REST APIs that take a filter document on DELETE. Because the body is dropped silently, a DELETE meant to remove a filtered subset can reach the server as an unfiltered request.
Expected
Bodies are sent for any method the caller supplies one for, as in httpx and requests.
Proposed fix
Remove the is_post_put_patch guard in both request() and _stream() and apply the body unconditionally. (If you'd rather stay conservative, raising on GET/HEAD with a body would at least be loud — but matching httpx by just sending it is the drop-in-compatible choice.)
Suggested tests
DELETE with json= sends the JSON body and Content-Type: application/json.
DELETE with content= sends the raw bytes.
OPTIONS with json= sends the body.
Size
~30 minutes including tests.
Summary
content,data,jsonandfilesare silently ignored for every method other than POST/PUT/PATCH.client.delete(url, json={...})sends an empty body with no error, no warning, and noContent-Typeheader.Cause
src/lib.rs:468(andsrc/lib.rs:641in_stream):Reproduction
Against a local server echoing the request line,
Content-*headers and body:Impact
RFC 9110 permits a body on any method, and real APIs rely on it — Elasticsearch's
DELETE _search/_delete_by_query, and many REST APIs that take a filter document on DELETE. Because the body is dropped silently, a DELETE meant to remove a filtered subset can reach the server as an unfiltered request.Expected
Bodies are sent for any method the caller supplies one for, as in
httpxandrequests.Proposed fix
Remove the
is_post_put_patchguard in bothrequest()and_stream()and apply the body unconditionally. (If you'd rather stay conservative, raising on GET/HEAD with a body would at least be loud — but matching httpx by just sending it is the drop-in-compatible choice.)Suggested tests
DELETEwithjson=sends the JSON body andContent-Type: application/json.DELETEwithcontent=sends the raw bytes.OPTIONSwithjson=sends the body.Size
~30 minutes including tests.