Skip to content

perf: skip request-body compression for already-compressed content types - #1053

Merged
vdusek merged 7 commits into
v3from
perf/skip-compression-compressed-types
Sep 10, 2026
Merged

vdusek merged 7 commits into
v3from
perf/skip-compression-compressed-types

Conversation

@vdusek

@vdusek vdusek commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

What

maybeCompressRequest() now skips compression when the request Content-Type is a media type that already carries its own compression: image/*, audio/*, video/*, archives, ZIP-based office formats and packages, and web fonts. Raw formats sitting under those prefixes (image/bmp, audio/wav) still compress, as do +json / +xml structured suffixes such as image/svg+xml. application/octet-stream is deliberately off the list: it is the catch-all for unknown binary and setRecord()'s fallback when no contentType is passed.

Compressing an incompressible body burns CPU and holds a second copy of it in memory for nothing. Brotli on a 50 MiB PNG, which is what setRecord() did before this change, costs 0.44 s of CPU and 111 MiB of resident memory, and the output comes out 120 bytes larger than the input.

Header lookups also go through a case-insensitive getHeader() now. Axios stores a request header under whatever casing the caller used, so bracket access on content-encoding missed a caller writing the conventional Content-Encoding: gzip: the body got compressed a second time and br replaced the caller's header over a brotli-wrapped gzip stream. The same bug in serializeRequest() dropped function-valued Actor input fields on actor.call() when Content-Type: application/json was oddly cased.

Issue

Closes #1033.

Ports apify/apify-client-python#987, with one deliberate difference: the Python client strips a caller-supplied Content-Encoding when it skips compression, while this client reads it as "the body is already encoded and the header describes it" and keeps both, so that escape hatch survives.

✍️ Drafted by Claude Code

@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Sep 9, 2026
@vdusek vdusek self-assigned this Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ There are broken links in the documentation.

See more at https://github.com/apify/apify-client-js/actions/runs/34461187304#summary-102819158595

The one conflict was the `contentType` docstring in `setRecord`: v3 reworded the
`application/octet-stream` bullet while this branch appended a note about skipped
compression. Both are kept.

v3 also widened the `setRecord` value type to accept binary data, so the two new
compression tests drop their `as any` casts.
Axios stores a header under whatever casing the caller used, and bracket access is
case-sensitive, so `Content-Encoding: gzip` missed the guard that skips compression
for an already-encoded body. The body was then compressed a second time, and axios
collapsed the two header keys last-write-wins, leaving `br` on a brotli-wrapped
gzip stream.

The same lookup in `serializeRequest` dropped function-valued Actor input fields
when the content type carried an unconventional casing.
…ompressed-types

# Conflicts:
#	test/http_client.test.ts
@vdusek
vdusek requested a review from Pijukatel September 10, 2026 09:42
@vdusek
vdusek marked this pull request as ready for review September 10, 2026 09:42
@vdusek
vdusek requested review from janbuchar and removed request for Pijukatel September 10, 2026 09:42
@vdusek
vdusek merged commit ca769cc into v3 Sep 10, 2026
8 checks passed
@vdusek
vdusek deleted the perf/skip-compression-compressed-types branch September 10, 2026 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants