Skip to content

0.12

Choose a tag to compare

@shpaker shpaker released this 20 Aug 06:37
· 33 commits to main since this release

Six new packages join the supported list, and the release also carries a security-relevant rendering fix — if you render incoming requests server-side, upgrading is worth doing now.

New adapters

Runtime dependencies stay at zero: every adapter registers only when its library is importable.

Library Type Call Notes
niquests PreparedRequest to_curl HTTP/2 and HTTP/3 live on the transport, so no --http2
urllib.request (stdlib) Request to_curl an absent method is inferred the way urllib sends it
aiohttp client-side ClientRequest to_curl_async reachable in client middlewares (aiohttp 3.12+); the body is read without consuming the payload via Payload.as_bytes() (3.12.1+)
django HttpRequest to_curl the framework buffers the body, so the sync call is enough — async views included
flask / werkzeug werkzeug.wrappers.Request to_curl covers Flask through its Werkzeug base
tornado httpclient.HTTPRequest, httputil.HTTPServerRequest to_curl client and server

A body or form value starting with @ or < could read a local file

curl reads a leading @ in a -d value — and @ or < in a -F value — as the name of a local file to send the contents of. A request whose body or form field started with one was rendered so that the command, pasted out of a log, read that file and sent it to the url the request was rendered for. Reachable from the wire through the server-side adapters, where the value is the caller's to choose. Such values now go through --data-raw / --form-string, which take them literally; a file part keeps -F 'field=@file', where the @ is the intended meaning.

Multipart rendering rewritten

Parts are split on the boundary from Content-Type instead of matched by regex: field values with newlines are no longer truncated, single-character field names are no longer dropped, values that do not decode as UTF-8 render as ANSI-C literals instead of raising, parts come out in the order the body carries them, and a NUL byte in a field value is rejected like one in a body.

Cookies

  • A Cookie header held in a case-sensitive container was silently dropped — tornado's client request keeps whatever plain dict it was handed, and the value reached neither -b nor -H. The extraction now scans the lowercased header names.
  • An empty Cookie header no longer renders as -H 'cookie: '; django's RequestFactory puts one on every request it builds, which is how it surfaced.

Full details in CHANGELOG.md.