Skip to content

fix(compress): skip compression for 206 and Content-Range responses#5023

Open
naveentehrpariya wants to merge 1 commit into
honojs:mainfrom
naveentehrpariya:fix-compress-range-responses
Open

fix(compress): skip compression for 206 and Content-Range responses#5023
naveentehrpariya wants to merge 1 commit into
honojs:mainfrom
naveentehrpariya:fix-compress-range-responses

Conversation

@naveentehrpariya

Copy link
Copy Markdown

Addresses item 2 of #5010.

compress() skips on Content-Encoding / Transfer-Encoding / HEAD / threshold / type / no-transform, but not on partial/range responses. A 206 Partial Content (or any response carrying Content-Range) gets gzipped while its Content-Range header still describes the uncompressed byte range — so a range client reassembling the stream gets corrupted data. nginx and apache skip compression on range responses for exactly this reason.

Reproduction (before this PR)

import { Hono } from 'hono'
import { compress } from 'hono/compress'

const app = new Hono()
app.use(compress())
app.get('/range', (c) => {
  c.header('Content-Type', 'text/plain')
  c.header('Content-Range', 'bytes 1000-2999/5000')
  c.header('Content-Length', '2000')
  return c.body('x'.repeat(2000), 206)
})

const res = await app.request('/range', { headers: { 'Accept-Encoding': 'gzip' } })
res.headers.get('Content-Encoding') // 'gzip'  ❌ — body gzipped, but Content-Range still says bytes 1000-2999

Fix

Add two guards to the existing skip condition:

ctx.res.status === 206 ||             // partial content
ctx.res.headers.has('Content-Range')  // byte-range response

Content-Range is checked in addition to the 206 status because it can also appear on a 200 response, and either way the header would no longer match the compressed bytes.

Tests

Added a Range Responses suite asserting a 206 response and a Content-Range response are both passed through uncompressed with their Content-Range / Content-Length intact. Both fail without this change and pass with it; the full compress suite (39 tests) passes, tsc --noEmit and eslint are clean.

The author should do the following, if applicable

  • Add tests
  • Run tests
  • bun run format:fix && bun run lint:fix to format the code
  • Add TSDoc/JSDoc to document the code (inline comments on the new guards)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant