Skip to content

Commit 754c360

Browse files
Decode compressed JS HTTP/2 responses
1 parent df40769 commit 754c360

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

packages/js-sdk/src/envd/http2.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class NodeHttp2Fetch {
183183
return
184184
}
185185

186-
const responseBody = new ReadableStream<Uint8Array>({
186+
let responseBody = new ReadableStream<Uint8Array>({
187187
start(controller) {
188188
bodyController = controller
189189
stream.on('data', (chunk: Buffer) => {
@@ -208,6 +208,7 @@ class NodeHttp2Fetch {
208208
stream.close(cancelCode)
209209
},
210210
})
211+
responseBody = decodeResponseBody(responseBody, responseHeaders)
211212

212213
resolve(
213214
new Response(responseBody, { status, headers: responseHeaders })
@@ -271,6 +272,21 @@ class NodeHttp2Fetch {
271272
}
272273
}
273274

275+
function decodeResponseBody(
276+
body: ReadableStream<Uint8Array>,
277+
headers: Headers
278+
) {
279+
const encoding = headers.get('content-encoding')?.toLowerCase()
280+
if (encoding !== 'gzip' && encoding !== 'deflate') {
281+
return body
282+
}
283+
284+
headers.delete('content-encoding')
285+
headers.delete('content-length')
286+
287+
return body.pipeThrough(new DecompressionStream(encoding))
288+
}
289+
274290
function isRedirect(status: number) {
275291
return (
276292
status === 301 ||

packages/js-sdk/tests/envd/http2.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import http2 from 'node:http2'
22
import { AddressInfo } from 'node:net'
3+
import { gzipSync } from 'node:zlib'
34
import { afterEach, beforeEach, expect, test, vi } from 'vitest'
45

56
import { createEnvdFetchForRuntime } from '../../src/envd/http2'
@@ -141,6 +142,18 @@ test('body cancel is safe', async () => {
141142
await expect(res.body!.cancel()).resolves.toBeUndefined()
142143
})
143144

145+
test('decodes gzip responses', async () => {
146+
const origin = await startServer((stream) => {
147+
stream.respond({ ':status': 200, 'content-encoding': 'gzip' })
148+
stream.end(gzipSync('compressed'))
149+
})
150+
151+
const res = await createEnvdFetchForRuntime('node')(`${origin}/gzip`)
152+
153+
expect(res.headers.has('content-encoding')).toBe(false)
154+
await expect(res.text()).resolves.toBe('compressed')
155+
})
156+
144157
test('pauses streaming response bodies when the reader is behind', async () => {
145158
const calls = { pause: 0, resume: 0 }
146159
const connect = http2.connect

0 commit comments

Comments
 (0)