mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
The [flakiness](https://app.datadoghq.com/ci/test-runs?query=test_level%3Atest%20env%3Aci%20%40git.repository.id%3Agithub.com%2Fvercel%2Fnext.js%20%40test.service%3Anextjs%20%40test.status%3Afail%20%40test.suite%3A%22streaming%20responses%20cancel%20inner%20stream%20after%20disconnect%22&agg_m=count&agg_m_source=base&agg_t=count¤tTab=overview&eventStack=&fromUser=false&index=citest&start=1720903715972&end=1728679715972&paused=false) of the test is underreported because due to masked errors, the test sometimes yields false-positive results. Due to a slight increase in compilation times for route handlers in #70897, the test started to fail consistently in that PR. This PR fixes the flakiness by handling the case where the request might already be aborted before the response was sent. This leads to the stream not being consumed, and subsequently its `finished` promise is never resolved, which finally leads to test timeouts.
58 lines
1.3 KiB
TypeScript
58 lines
1.3 KiB
TypeScript
import { IncomingMessage, ServerResponse } from 'http'
|
|
import { pipeline } from 'stream'
|
|
import { Readable } from '../../readable'
|
|
|
|
export const config = {
|
|
runtime: 'nodejs',
|
|
}
|
|
|
|
let readable: ReturnType<typeof Readable> | undefined
|
|
|
|
export default async function handler(
|
|
req: IncomingMessage,
|
|
res: ServerResponse
|
|
): Promise<void> {
|
|
const url = new URL(req.url!, 'http://localhost/')
|
|
|
|
if (url.searchParams.has('compile')) {
|
|
// The request just wants to trigger compilation.
|
|
res.statusCode = 204
|
|
res.end()
|
|
return
|
|
}
|
|
|
|
// Pages API requests have already consumed the body.
|
|
// This is so we don't confuse the request close with the connection close.
|
|
|
|
const write = url.searchParams.get('write')
|
|
|
|
if (write) {
|
|
const r = (readable = Readable(+write))
|
|
res.on('close', () => {
|
|
r.abort()
|
|
})
|
|
return new Promise((resolve) => {
|
|
pipeline(r.stream, res, () => {
|
|
resolve()
|
|
res.end()
|
|
})
|
|
})
|
|
}
|
|
|
|
// The 2nd request should render the stats. We don't use a query param
|
|
// because edge rendering will create a different bundle for that.
|
|
const old = readable
|
|
|
|
if (!old) {
|
|
res.statusCode = 500
|
|
res.end(
|
|
'The streamable from the prime request is unexpectedly not available'
|
|
)
|
|
return
|
|
}
|
|
|
|
readable = undefined
|
|
const i = await old.finished
|
|
res.end(`${i}`)
|
|
}
|