Skip to content

Commit c8e103b

Browse files
committed
fix: ensure background work is finished when response has 3xx or 5xx status code
1 parent 62ed00c commit c8e103b

File tree

1 file changed

+18
-21
lines changed

1 file changed

+18
-21
lines changed

src/run/handlers/server.ts

+18-21
Original file line numberDiff line numberDiff line change
@@ -113,34 +113,31 @@ export default async (request: Request) => {
113113
setVaryHeaders(response.headers, request, nextConfig)
114114
setCacheStatusHeader(response.headers)
115115

116-
// Temporary workaround for an issue where sending a response with an empty
117-
// body causes an unhandled error. This doesn't catch everything, but redirects are the
118-
// most common case of sending empty bodies. We can't check it directly because these are streams.
119-
// The side effect is that responses which do contain data will not be streamed to the client,
120-
// but that's fine for redirects.
121-
// TODO: Remove once a fix has been rolled out.
122-
if ((response.status > 300 && response.status < 400) || response.status >= 500) {
123-
const body = await response.text()
124-
return new Response(body || null, response)
116+
async function waitForBackgroundWork() {
117+
// it's important to keep the stream open until the next handler has finished
118+
await nextHandlerPromise
119+
120+
// Next.js relies on `close` event emitted by response to trigger running callback variant of `next/after`
121+
// however @fastly/http-compute-js never actually emits that event - so we have to emit it ourselves,
122+
// otherwise Next would never run the callback variant of `next/after`
123+
res.emit('close')
124+
125+
// We have to keep response stream open until tracked background promises that are don't use `context.waitUntil`
126+
// are resolved. If `context.waitUntil` is available, `requestContext.backgroundWorkPromise` will be empty
127+
// resolved promised and so awaiting it is no-op
128+
await requestContext.backgroundWorkPromise
125129
}
126130

127131
const keepOpenUntilNextFullyRendered = new TransformStream({
128132
async flush() {
129-
// it's important to keep the stream open until the next handler has finished
130-
await nextHandlerPromise
131-
132-
// Next.js relies on `close` event emitted by response to trigger running callback variant of `next/after`
133-
// however @fastly/http-compute-js never actually emits that event - so we have to emit it ourselves,
134-
// otherwise Next would never run the callback variant of `next/after`
135-
res.emit('close')
136-
137-
// We have to keep response stream open until tracked background promises that are don't use `context.waitUntil`
138-
// are resolved. If `context.waitUntil` is available, `requestContext.backgroundWorkPromise` will be empty
139-
// resolved promised and so awaiting it is no-op
140-
await requestContext.backgroundWorkPromise
133+
await waitForBackgroundWork()
141134
},
142135
})
143136

137+
if (!response.body) {
138+
await waitForBackgroundWork()
139+
}
140+
144141
return new Response(response.body?.pipeThrough(keepOpenUntilNextFullyRendered), response)
145142
})
146143
}

0 commit comments

Comments
 (0)