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.
53 lines
1.3 KiB
TypeScript
53 lines
1.3 KiB
TypeScript
import { NextRequest } from 'next/server'
|
|
import { Streamable } from './streamable'
|
|
|
|
export const config = {
|
|
matcher: '/middleware',
|
|
}
|
|
|
|
let streamable: ReturnType<typeof Streamable> | undefined
|
|
|
|
export default async function handler(req: NextRequest): Promise<Response> {
|
|
if (req.nextUrl.searchParams.has('compile')) {
|
|
// The request just wants to trigger compilation.
|
|
return new Response(null, { status: 204 })
|
|
}
|
|
|
|
// Consume the entire request body.
|
|
// This is so we don't confuse the request close with the connection close.
|
|
await req.text()
|
|
|
|
const write = req.nextUrl.searchParams.get('write')
|
|
|
|
if (write) {
|
|
const s = (streamable = Streamable(+write))
|
|
|
|
// The request was aborted before the response was returned.
|
|
if (req.signal.aborted) {
|
|
s.abort()
|
|
return new Response(null, { status: 204 })
|
|
}
|
|
|
|
req.signal.onabort = () => {
|
|
s.abort()
|
|
}
|
|
|
|
return new Response(s.stream)
|
|
}
|
|
|
|
// 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 = streamable
|
|
|
|
if (!old) {
|
|
return new Response(
|
|
'The streamable from the prime request is unexpectedly not available',
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
|
|
streamable = undefined
|
|
const i = await old.finished
|
|
return new Response(`${i}`)
|
|
}
|