From 05904c390a423e64233929a3937d34a4deda3b88 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 23 Jun 2023 16:20:03 -0400 Subject: [PATCH 1/4] Ensure API/Route handler abort signal aborts on client disconnect --- packages/next/src/server/next-server.ts | 7 +++++++ packages/next/src/server/web/adapter.ts | 1 + .../web/spec-extension/adapters/next-request.ts | 13 +++++++++++++ .../next/src/server/web/spec-extension/request.ts | 1 + packages/next/src/server/web/types.ts | 1 + 5 files changed, 23 insertions(+) diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 27a5f3cb0bc7e..1a199746616c7 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -110,6 +110,7 @@ import { filterReqHeaders } from './lib/server-ipc/utils' import { createRequestResponseMocks } from './lib/mock-request' import chalk from 'next/dist/compiled/chalk' import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers' +import { signalFromNodeRequest } from './web/spec-extension/adapters/next-request' export * from './base-server' @@ -2351,6 +2352,9 @@ export default class NextNodeServer extends BaseServer { url: url, page: page, body: getRequestMeta(params.request, '__NEXT_CLONABLE_BODY'), + signal: signalFromNodeRequest( + (params.request as NodeNextRequest).originalRequest + ), }, useCache: true, onWarning: params.onWarning, @@ -2860,6 +2864,9 @@ export default class NextNodeServer extends BaseServer { ...(params.params && { params: params.params }), }, body: getRequestMeta(params.req, '__NEXT_CLONABLE_BODY'), + signal: signalFromNodeRequest( + (params.req as NodeNextRequest).originalRequest + ), }, useCache: true, onWarning: params.onWarning, diff --git a/packages/next/src/server/web/adapter.ts b/packages/next/src/server/web/adapter.ts index 60c2abfcd5e52..5a8c953ad71a3 100644 --- a/packages/next/src/server/web/adapter.ts +++ b/packages/next/src/server/web/adapter.ts @@ -132,6 +132,7 @@ export async function adapter( ip: params.request.ip, method: params.request.method, nextConfig: params.request.nextConfig, + signal: params.request.signal, }, }) diff --git a/packages/next/src/server/web/spec-extension/adapters/next-request.ts b/packages/next/src/server/web/spec-extension/adapters/next-request.ts index 2248629df1e20..f19221432fb37 100644 --- a/packages/next/src/server/web/spec-extension/adapters/next-request.ts +++ b/packages/next/src/server/web/spec-extension/adapters/next-request.ts @@ -1,6 +1,7 @@ import type { BaseNextRequest } from '../../../base-http' import type { NodeNextRequest } from '../../../base-http/node' import type { WebNextRequest } from '../../../base-http/web' +import type { IncomingMessage } from 'node:http' import { getRequestMeta } from '../../../request-meta' import { fromNodeOutgoingHttpHeaders } from '../../utils' @@ -46,6 +47,7 @@ export class NextRequestAdapter { headers: fromNodeOutgoingHttpHeaders(request.headers), // @ts-expect-error - see https://github.com/whatwg/fetch/pull/1457 duplex: 'half', + signal: signalFromNodeRequest(request.originalRequest), // geo // ip // nextConfig @@ -65,9 +67,20 @@ export class NextRequestAdapter { headers: fromNodeOutgoingHttpHeaders(request.headers), // @ts-expect-error - see https://github.com/whatwg/fetch/pull/1457 duplex: 'half', + signal: request.request.signal, // geo // ip // nextConfig }) } } + +export function signalFromNodeRequest(request: IncomingMessage) { + const { errored } = request + if (errored) return AbortSignal.abort(errored) + const controller = new AbortController() + request.on('error', (e) => { + controller.abort(e) + }) + return controller.signal +} diff --git a/packages/next/src/server/web/spec-extension/request.ts b/packages/next/src/server/web/spec-extension/request.ts index 84cdba908e0c5..ae6f447d42929 100644 --- a/packages/next/src/server/web/spec-extension/request.ts +++ b/packages/next/src/server/web/spec-extension/request.ts @@ -111,4 +111,5 @@ export interface RequestInit extends globalThis.RequestInit { i18n?: I18NConfig | null trailingSlash?: boolean } + signal: AbortSignal } diff --git a/packages/next/src/server/web/types.ts b/packages/next/src/server/web/types.ts index 6d4cb7f6f89a3..4ca61bb6c644c 100644 --- a/packages/next/src/server/web/types.ts +++ b/packages/next/src/server/web/types.ts @@ -27,6 +27,7 @@ export interface RequestData { } url: string body?: ReadableStream + signal: AbortSignal } export type NodejsRequestData = Omit & { From 7bb3b8220545dd2ce2bc6b4390f992cc30c1afed Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 26 Jun 2023 23:06:29 -0400 Subject: [PATCH 2/4] Fix types by requiring arg --- packages/next/src/server/web/spec-extension/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/server/web/spec-extension/request.ts b/packages/next/src/server/web/spec-extension/request.ts index ae6f447d42929..45f0e4e0e2944 100644 --- a/packages/next/src/server/web/spec-extension/request.ts +++ b/packages/next/src/server/web/spec-extension/request.ts @@ -16,7 +16,7 @@ export class NextRequest extends Request { nextUrl: NextURL } - constructor(input: URL | RequestInfo, init: RequestInit = {}) { + constructor(input: URL | RequestInfo, init: RequestInit) { const url = typeof input !== 'string' && 'url' in input ? input.url : String(input) validateURL(url) From 36d0b583f04ca551890c8efae20b5f2b97d0e8ca Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 26 Jun 2023 23:17:23 -0400 Subject: [PATCH 3/4] Fix prettier --- .../spec-extension/adapters/next-request.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/next/src/server/web/spec-extension/adapters/next-request.ts b/packages/next/src/server/web/spec-extension/adapters/next-request.ts index f19221432fb37..3efe37370b341 100644 --- a/packages/next/src/server/web/spec-extension/adapters/next-request.ts +++ b/packages/next/src/server/web/spec-extension/adapters/next-request.ts @@ -7,6 +7,16 @@ import { getRequestMeta } from '../../../request-meta' import { fromNodeOutgoingHttpHeaders } from '../../utils' import { NextRequest } from '../request' +export function signalFromNodeRequest(request: IncomingMessage) { + const { errored } = request + if (errored) return AbortSignal.abort(errored) + const controller = new AbortController() + request.on('error', (e) => { + controller.abort(e) + }) + return controller.signal +} + export class NextRequestAdapter { public static fromBaseNextRequest(request: BaseNextRequest): NextRequest { // TODO: look at refining this check @@ -74,13 +84,3 @@ export class NextRequestAdapter { }) } } - -export function signalFromNodeRequest(request: IncomingMessage) { - const { errored } = request - if (errored) return AbortSignal.abort(errored) - const controller = new AbortController() - request.on('error', (e) => { - controller.abort(e) - }) - return controller.signal -} From abbbc42023919a0d456bc6fe236a862249b9bd26 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 26 Jun 2023 23:35:39 -0400 Subject: [PATCH 4/4] Allow missing AbortSignal --- packages/next/src/server/web/spec-extension/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/server/web/spec-extension/request.ts b/packages/next/src/server/web/spec-extension/request.ts index 45f0e4e0e2944..de893ce522d75 100644 --- a/packages/next/src/server/web/spec-extension/request.ts +++ b/packages/next/src/server/web/spec-extension/request.ts @@ -111,5 +111,5 @@ export interface RequestInit extends globalThis.RequestInit { i18n?: I18NConfig | null trailingSlash?: boolean } - signal: AbortSignal + signal?: AbortSignal }