/** * Copyright (c) 2026 AzerothJS. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * The lazy Request the Node adapter hands to the kernel. * * Constructing a real (undici) Request per request is the single largest fixed cost on the * hot path: URL parsing, Headers normalization, internal-state setup - none of which a * typical handler ever observes. This class is a Request-shaped view over the raw Node * request that computes each member ON FIRST ACCESS: * * - `method`/`url` are string work only; * - `headers` builds the real Headers once, when someone actually reads a header; * - `signal` allocates its AbortController and attaches its listener only for handlers * that use cancellation - which also fixes a real leak: on http1 an unconditional * per-request `socket.once('close')` accumulates listeners for the lifetime of a * keep-alive socket (MaxListenersExceededWarning under load), so that listener is * removed when the request completes. On h2 there is nothing to detach: the listener * lives on the per-stream request and dies with it; * - `body` wraps the incoming stream on demand; * - everything exotic (clone, formData, blob, cache, integrity, ...) delegates to a real * Request materialized at that moment - full spec behavior, paid only when used. * * `instanceof Request` holds (the prototype chain ends in Request.prototype), and every * member undici's brand checks would reject is overridden here, so the shim never falls * through to internals it does not have. The kernel itself stays 100% web-standard: it * types against Request and never knows the difference - which is the whole point. */ import type { IncomingMessage } from 'node:http'; import type { Http2ServerRequest } from 'node:http2'; import { releaseSignal, type FastCapabilities } from './body.ts'; /** The structural surface shared by http1's IncomingMessage and http2's compat request. */ export type AnyIncoming = (IncomingMessage | Http2ServerRequest) & { headers: Record; }; /** * Which forwarded headers to believe when building the request URL. OFF by default - * `X-Forwarded-*` is client-controlled, so believing it without a proxy in front lets any * caller forge its scheme and host (the same trust boundary clientIp draws for addresses). * * There is no hop count here, unlike `clientIp`: proto and host are REPLACED by each proxy * that writes them, not appended per hop, so a chain of any depth leaves one entry and the * value read is the last one written (see `replacedForwardedValue`). */ export interface ForwardedTrust { /** Believe `X-Forwarded-Proto` for the URL scheme (only `http`/`https` values count). */ proto?: boolean; /** Believe `X-Forwarded-Host` for the URL authority (validated as host[:port]). */ host?: boolean; } /** * Builds the adapter Request for one incoming Node message. The factory (not the class) is * the module's export: its explicit return type is what declaration emit needs under * isolatedDeclarations (computed symbol-keyed class methods cannot be emitted), and the * class stays module-internal - its whole public shape IS `Request & FastCapabilities`. */ export declare function createAdapterRequest(incoming: AnyIncoming, scheme: 'http' | 'https', trust?: ForwardedTrust): Request & FastCapabilities & { [releaseSignal](): void; };