Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ const handleResponseError = (e: unknown, outgoing: ServerResponse | Http2ServerR
}
}

const flushHeaders = (outgoing: ServerResponse | Http2ServerResponse) => {
// If outgoing is ServerResponse (HTTP/1.1), it requires this to flush headers.
// However, Http2ServerResponse is sent without this.
if ('flushHeaders' in outgoing && outgoing.writable) {
outgoing.flushHeaders()
}
}

const responseViaCache = async (
res: Response,
outgoing: ServerResponse | Http2ServerResponse
Expand All @@ -69,6 +77,7 @@ const responseViaCache = async (
} else if (body instanceof Blob) {
outgoing.end(new Uint8Array(await body.arrayBuffer()))
} else {
flushHeaders(outgoing)
return writeFromReadableStream(body, outgoing)?.catch(
(e) => handleResponseError(e, outgoing) as undefined
)
Expand Down Expand Up @@ -129,6 +138,7 @@ const responseViaResponseObject = async (
!regContentType.test(contentType as string)
) {
outgoing.writeHead(res.status, resHeaderRecord)
flushHeaders(outgoing)

await writeFromReadableStream(res.body, outgoing)
} else {
Expand Down
13 changes: 12 additions & 1 deletion test/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,14 @@ describe('various response body types', () => {
})
return response
})
let resolveReadableStreamPromise: () => void
const readableStreamPromise = new Promise<void>((resolve) => {
resolveReadableStreamPromise = resolve
})
app.get('/readable-stream', () => {
const stream = new ReadableStream({
async start(controller) {
await readableStreamPromise
controller.enqueue('Hello!')
controller.enqueue(' Node!')
controller.close()
Expand Down Expand Up @@ -187,15 +192,21 @@ describe('various response body types', () => {

it('Should return 200 response - GET /readable-stream', async () => {
const expectedChunks = ['Hello!', ' Node!']
const res = await request(server)
const resPromise = request(server)
.get('/readable-stream')
.parse((res, fn) => {
// response header should be sent before sending data.
expect(res.headers['transfer-encoding']).toBe('chunked')
resolveReadableStreamPromise()

res.on('data', (chunk) => {
const str = chunk.toString()
expect(str).toBe(expectedChunks.shift())
})
res.on('end', () => fn(null, ''))
})
await new Promise((resolve) => setTimeout(resolve, 100))
const res = await resPromise
expect(res.status).toBe(200)
expect(res.headers['content-type']).toMatch('text/plain; charset=UTF-8')
expect(res.headers['content-length']).toBeUndefined()
Expand Down