Files
Hendrik LiebauandGitHub 84ee9e6916 Unflake "streaming responses cancel inner stream after disconnect" test (#71163)
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&currentTab=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.
2024-10-12 01:59:17 +02:00

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}`)
}