From 77980d25a365861296fb84230ab0816c8d332a53 Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 19 Aug 2026 12:01:56 +0000 Subject: [PATCH 1/4] perf(server): create request URLs lazily Avoid parsing common request targets until consumers access the URL while preserving enumerable context and WHATWG fallback semantics. Skip monitor context cloning when no monitors match. Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-8c38-777d-8af1-1f42f02050a9 Co-authored-by: Upd4ting --- src/server.ts | 124 +++++++++++---- src/test/request-context.test.ts | 253 +++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+), 27 deletions(-) create mode 100644 src/test/request-context.test.ts diff --git a/src/server.ts b/src/server.ts index 3ae70ea..dc626ae 100644 --- a/src/server.ts +++ b/src/server.ts @@ -126,6 +126,95 @@ function hasParameter(parameters: Record, name: string) { return Object.getOwnPropertyDescriptor(parameters, name) !== undefined; } +type RequestProtocol = "http" | "https" | "ws" | "wss"; + +const commonPathname = /^\/[A-Za-z0-9/_-]*$/; +const commonHost = /^[A-Za-z0-9.-]+(?::[0-9]+)?$/; +const safePathname = /^\/[A-Za-z0-9\-._~!$&'()*+,;=:@/%]*$/; +const dotPathSegment = /(?:^|\/)(?:(?:\.|%2e){1,2})(?:\/|$)/i; +const requestHost = Symbol(); +const requestTarget = Symbol(); +const requestUrl = Symbol(); + +interface LazyRequestContext extends RequestContext { + [requestHost]: string; + [requestTarget]: string; + [requestUrl]?: URL; +} + +function createRequestUrlDescriptor( + protocol: RequestProtocol, +): PropertyDescriptor { + return { + configurable: true, + enumerable: true, + get(this: LazyRequestContext) { + this[requestUrl] ??= new URL( + this[requestTarget], + `${protocol}://${this[requestHost]}`, + ); + return this[requestUrl]; + }, + set(this: LazyRequestContext, url: URL) { + this[requestUrl] = url; + }, + }; +} + +const requestUrlDescriptors: Record = { + http: createRequestUrlDescriptor("http"), + https: createRequestUrlDescriptor("https"), + ws: createRequestUrlDescriptor("ws"), + wss: createRequestUrlDescriptor("wss"), +}; + +function isCommonHost(host: string): boolean { + if (!commonHost.test(host)) { + return false; + } + const portDelimiter = host.lastIndexOf(":"); + return portDelimiter < 0 || Number(host.slice(portDelimiter + 1)) <= 65_535; +} + +function createRequestContext( + req: IncomingMessage, + res: ServerResponse, + protocol: RequestProtocol, +): RequestContext { + const context = { + rawRequest: req, + rawResponse: res, + [requestHost]: req.headers.host || "localhost", + [requestTarget]: req.url || "", + routeParameters: {}, + response: new HTTPResult(404, "Not Found"), + } as unknown as LazyRequestContext; + Object.defineProperty(context, "url", requestUrlDescriptors[protocol]); + if (!isCommonHost(context[requestHost])) { + void context.url; + } + return context; +} + +function getPathname(requestContext: RequestContext): string { + const requestTarget = requestContext.rawRequest.url; + if (!requestTarget || requestTarget.startsWith("//")) { + return requestContext.url.pathname; + } + + const delimiterIndex = requestTarget.search(/[?#]/); + const pathname = requestTarget.slice( + 0, + delimiterIndex < 0 ? undefined : delimiterIndex, + ); + if (commonPathname.test(pathname)) { + return pathname; + } + return safePathname.test(pathname) && !dotPathSegment.test(pathname) + ? pathname + : requestContext.url.pathname; +} + function findHandlers( path: string[], depth: number, @@ -828,25 +917,16 @@ function processRequest( res: ServerResponse, protocol: "http" | "https", ): Awaitable { - const url = new URL( - req.url || "", - `${protocol}://${req.headers.host || "localhost"}`, - ); - const requestContext: RequestContext = { - rawRequest: req, - rawResponse: res, - url, - routeParameters: {}, - response: new HTTPResult(404, "Not Found"), - }; - const path = url.pathname.split("/").filter((part) => part); + const requestContext = createRequestContext(req, res, protocol); + const pathname = getPathname(requestContext); + const path = pathname.split("/").filter(Boolean); const method = req.method?.toLowerCase() || "get"; try { const execution = executeRequest( method, path, - url.pathname, + pathname, requestContext, ); const then = getThen(execution); @@ -887,19 +967,9 @@ export async function upgradeListener( protocol: "ws" | "wss", ) { const res = new ServerResponse(req); - const url = new URL( - req.url || "", - `${protocol}://${req.headers.host || "localhost"}`, - ); - const requestContext: RequestContext = { - rawRequest: req, - rawResponse: res, - url, - routeParameters: {}, - response: new HTTPResult(404, "Not Found"), - }; - - const path = url.pathname.split("/").filter((part) => part); + const requestContext = createRequestContext(req, res, protocol); + const pathname = getPathname(requestContext); + const path = pathname.split("/").filter(Boolean); const method = req.method?.toLowerCase() || "get"; let requestError: unknown; let hasUpgradedConnection = false; @@ -912,7 +982,7 @@ export async function upgradeListener( path, roots.websocket, false, - url.pathname, + pathname, ); if (!handler || Array.isArray(handler)) { mustSendResponse = true; diff --git a/src/test/request-context.test.ts b/src/test/request-context.test.ts new file mode 100644 index 0000000..3af9abf --- /dev/null +++ b/src/test/request-context.test.ts @@ -0,0 +1,253 @@ +import assert from "node:assert"; +import { createServer, type Server } from "node:http"; +import type { AddressInfo } from "node:net"; +import { connect } from "node:net"; +import WebSocket from "ws"; +import { + registerHandler, + requestListener, + unregisterHandler, + upgradeListener, +} from "../server"; + +const handlerIds: string[] = []; +let server: Server; +let port: number; + +function register( + id: string, + mode: "handler" | "monitor" | "websocket", + location: string, + handler: Parameters[4], +): void { + handlerIds.push(id); + registerHandler(id, mode, "get", location, handler); +} + +function sendRawRequest(target: string, host?: string): Promise { + return new Promise((resolve, reject) => { + const socket = connect(port, "127.0.0.1"); + let response = ""; + socket.setEncoding("utf8"); + socket.on("connect", () => { + const hostHeader = host === undefined ? "" : `Host: ${host}\r\n`; + socket.end(`GET ${target} HTTP/1.0\r\n${hostHeader}\r\n`); + }); + socket.on("data", (chunk) => { + response += chunk; + }); + socket.on("end", () => resolve(response.split("\r\n\r\n")[1] ?? "")); + socket.on("error", reject); + }); +} + +describe("Request context URL", () => { + before(async () => { + server = createServer((req, res) => requestListener(req, res, "http")); + server.on("upgrade", (req, socket, head) => + upgradeListener(req, socket, head, "ws"), + ); + await new Promise((resolve) => + server.listen(0, "127.0.0.1", resolve), + ); + port = (server.address() as AddressInfo).port; + }); + + after(async () => { + handlerIds.forEach(unregisterHandler); + await new Promise((resolve) => server.close(() => resolve())); + }); + + it("keeps url enumerable without reading it", async () => { + register( + "lazy-url-descriptor", + "handler", + "/lazy/descriptor", + (context) => { + const descriptor = Object.getOwnPropertyDescriptor(context, "url"); + return { + enumerable: descriptor?.enumerable, + hasGetter: typeof descriptor?.get === "function", + keys: Object.keys(context), + }; + }, + ); + + const body = await sendRawRequest("/lazy/descriptor", "example.test"); + const result = JSON.parse(body); + assert.equal(result.enumerable, true); + assert.equal(result.hasGetter, true); + assert.ok(result.keys.includes("url")); + }); + + it("preserves encoded paths, query decoding, host and protocol", async () => { + register( + "lazy-url-encoded", + "handler", + "/lazy/encoded/:value", + (context) => ({ + host: context.url.host, + protocol: context.url.protocol, + query: context.url.searchParams.getAll("x"), + pathname: context.url.pathname, + value: context.routeParameters.value, + }), + ); + + const body = await sendRawRequest( + "/lazy/encoded/caf%C3%A9?x=a%2Bb&x=%2F", + "example.test:8080", + ); + assert.deepEqual(JSON.parse(body), { + host: "example.test:8080", + protocol: "http:", + query: ["a+b", "/"], + pathname: "/lazy/encoded/caf%C3%A9", + value: "caf%C3%A9", + }); + }); + + it("uses WHATWG normalization for dot segments", async () => { + register( + "lazy-url-dot", + "handler", + "/lazy/dot", + (context) => context.url.pathname, + ); + + const body = await sendRawRequest("/lazy/skipped/../dot", "example.test"); + assert.equal(body, "/lazy/dot"); + }); + + it("supports absolute request targets", async () => { + register( + "lazy-url-absolute", + "handler", + "/lazy/absolute", + (context) => context.url.href, + ); + + const body = await sendRawRequest( + "http://absolute.example/lazy/absolute?q=one%20two", + "ignored.example", + ); + assert.equal(body, "http://absolute.example/lazy/absolute?q=one%20two"); + }); + + it("supports protocol-relative request targets", async () => { + register( + "lazy-url-protocol-relative", + "handler", + "/lazy/protocol-relative", + (context) => context.url.origin, + ); + + const body = await sendRawRequest( + "//relative.example/lazy/protocol-relative", + "ignored.example", + ); + assert.equal(body, "http://relative.example"); + }); + + it("falls back to localhost when Host is absent", async () => { + register( + "lazy-url-hostless", + "handler", + "/lazy/hostless", + (context) => context.url.origin, + ); + + const body = await sendRawRequest("/lazy/hostless"); + assert.equal(body, "http://localhost"); + }); + + it("keeps url assignable", async () => { + register( + "lazy-url-assignable", + "handler", + "/lazy/assignable", + (context) => { + context.url = new URL("https://replacement.example/assigned"); + return context.url.href; + }, + ); + + const body = await sendRawRequest("/lazy/assignable", "original.example"); + assert.equal(body, "https://replacement.example/assigned"); + }); + + it("snapshots the request target and host before lazy access", async () => { + register("lazy-url-snapshot", "handler", "/lazy/snapshot", (context) => { + context.rawRequest.url = "/mutated"; + context.rawRequest.headers.host = "mutated.example"; + return context.url.href; + }); + + const body = await sendRawRequest( + "/lazy/snapshot?original=true", + "original.example", + ); + assert.equal(body, "http://original.example/lazy/snapshot?original=true"); + }); + + it("preserves encoded URL semantics for WebSocket upgrades", async () => { + register( + "lazy-url-websocket", + "websocket", + "/lazy/socket/:value", + (context) => { + const connection = context.connection as WebSocket; + connection.send( + JSON.stringify({ + pathname: context.url.pathname, + protocol: context.url.protocol, + value: context.routeParameters.value, + }), + ); + }, + ); + + const result = await new Promise((resolve, reject) => { + const socket = new WebSocket( + `ws://127.0.0.1:${port}/lazy/socket/caf%C3%A9?value=a%2Fb`, + ); + socket.once("message", (message) => { + resolve(message.toString()); + socket.close(); + }); + socket.once("error", reject); + }); + assert.deepEqual(JSON.parse(result), { + pathname: "/lazy/socket/caf%C3%A9", + protocol: "ws:", + value: "caf%C3%A9", + }); + }); + + it("preserves url through the monitor context spread", async () => { + let monitorResult: Record | undefined; + register( + "lazy-url-monitor-handler", + "handler", + "/lazy/monitor", + () => "ok", + ); + register("lazy-url-monitor", "monitor", "/lazy/monitor", (context) => { + const descriptor = Object.getOwnPropertyDescriptor(context, "url"); + monitorResult = { + enumerable: descriptor?.enumerable, + isDataProperty: "value" in (descriptor ?? {}), + isUrl: context.url instanceof URL, + pathname: context.url.pathname, + }; + }); + + await sendRawRequest("/lazy/monitor?observed=true", "example.test"); + assert.deepEqual(monitorResult, { + enumerable: true, + isDataProperty: true, + isUrl: true, + pathname: "/lazy/monitor", + }); + }); +}); From 693b35e0b20930647e5553975819ebdfbb373347 Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 19 Aug 2026 12:25:49 +0000 Subject: [PATCH 2/4] fix(server): validate numeric hosts before routing Preserve eager rejection for numeric Host values that the WHATWG parser rejects while retaining the common valid-host path. Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-8c38-777d-8af1-1f42f02050a9 Co-authored-by: Upd4ting --- src/server.ts | 14 +++++++++++++- src/test/request-context.test.ts | 25 ++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/server.ts b/src/server.ts index dc626ae..0b726f2 100644 --- a/src/server.ts +++ b/src/server.ts @@ -130,6 +130,8 @@ type RequestProtocol = "http" | "https" | "ws" | "wss"; const commonPathname = /^\/[A-Za-z0-9/_-]*$/; const commonHost = /^[A-Za-z0-9.-]+(?::[0-9]+)?$/; +const numericHost = /^[0-9.]+$/; +const commonIpv4 = /^(?:0|[1-9][0-9]{0,2})(?:\.(?:0|[1-9][0-9]{0,2})){3}$/; const safePathname = /^\/[A-Za-z0-9\-._~!$&'()*+,;=:@/%]*$/; const dotPathSegment = /(?:^|\/)(?:(?:\.|%2e){1,2})(?:\/|$)/i; const requestHost = Symbol(); @@ -173,7 +175,17 @@ function isCommonHost(host: string): boolean { return false; } const portDelimiter = host.lastIndexOf(":"); - return portDelimiter < 0 || Number(host.slice(portDelimiter + 1)) <= 65_535; + if (portDelimiter >= 0 && Number(host.slice(portDelimiter + 1)) > 65_535) { + return false; + } + const hostname = host.slice(0, portDelimiter < 0 ? undefined : portDelimiter); + if (!numericHost.test(hostname)) { + return true; + } + return ( + commonIpv4.test(hostname) && + hostname.split(".").every((part) => Number(part) <= 255) + ); } function createRequestContext( diff --git a/src/test/request-context.test.ts b/src/test/request-context.test.ts index 3af9abf..80d203d 100644 --- a/src/test/request-context.test.ts +++ b/src/test/request-context.test.ts @@ -1,5 +1,10 @@ import assert from "node:assert"; -import { createServer, type Server } from "node:http"; +import { + createServer, + type IncomingMessage, + type Server, + type ServerResponse, +} from "node:http"; import type { AddressInfo } from "node:net"; import { connect } from "node:net"; import WebSocket from "ws"; @@ -161,6 +166,24 @@ describe("Request context URL", () => { assert.equal(body, "http://localhost"); }); + it("rejects invalid numeric hosts before routing", async () => { + let handlerWasCalled = false; + register("lazy-url-invalid-host", "handler", "/lazy/invalid-host", () => { + handlerWasCalled = true; + }); + const request = { + headers: { host: "999.999.999.999" }, + method: "GET", + url: "/lazy/invalid-host", + } as IncomingMessage; + + await assert.rejects( + requestListener(request, {} as ServerResponse, "http"), + TypeError, + ); + assert.equal(handlerWasCalled, false); + }); + it("keeps url assignable", async () => { register( "lazy-url-assignable", From 46c1304547d793c8a0d4dca41da3408f59b18c5c Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 19 Aug 2026 12:27:10 +0000 Subject: [PATCH 3/4] perf(server): avoid host validation allocations Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-8c38-777d-8af1-1f42f02050a9 Co-authored-by: Upd4ting --- src/server.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/server.ts b/src/server.ts index 0b726f2..c45fe21 100644 --- a/src/server.ts +++ b/src/server.ts @@ -130,7 +130,7 @@ type RequestProtocol = "http" | "https" | "ws" | "wss"; const commonPathname = /^\/[A-Za-z0-9/_-]*$/; const commonHost = /^[A-Za-z0-9.-]+(?::[0-9]+)?$/; -const numericHost = /^[0-9.]+$/; +const commonDomainHost = /^[A-Za-z]/; const commonIpv4 = /^(?:0|[1-9][0-9]{0,2})(?:\.(?:0|[1-9][0-9]{0,2})){3}$/; const safePathname = /^\/[A-Za-z0-9\-._~!$&'()*+,;=:@/%]*$/; const dotPathSegment = /(?:^|\/)(?:(?:\.|%2e){1,2})(?:\/|$)/i; @@ -170,6 +170,24 @@ const requestUrlDescriptors: Record = { wss: createRequestUrlDescriptor("wss"), }; +function isCommonIpv4(hostname: string): boolean { + if (!commonIpv4.test(hostname)) { + return false; + } + let octet = 0; + for (const character of hostname) { + if (character === ".") { + octet = 0; + } else { + octet = octet * 10 + character.charCodeAt(0) - 48; + if (octet > 255) { + return false; + } + } + } + return true; +} + function isCommonHost(host: string): boolean { if (!commonHost.test(host)) { return false; @@ -179,13 +197,10 @@ function isCommonHost(host: string): boolean { return false; } const hostname = host.slice(0, portDelimiter < 0 ? undefined : portDelimiter); - if (!numericHost.test(hostname)) { + if (commonDomainHost.test(hostname)) { return true; } - return ( - commonIpv4.test(hostname) && - hostname.split(".").every((part) => Number(part) <= 255) - ); + return isCommonIpv4(hostname); } function createRequestContext( From 9020499d5655a9ddc73b6402a69a1be0a1c9b9e0 Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 19 Aug 2026 20:43:25 +0000 Subject: [PATCH 4/4] test(server): cover repeated and asynchronous URL access Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-8c38-777d-8af1-1f42f02050a9 Co-authored-by: Upd4ting --- src/server.ts | 15 ++------------- src/test/request-context.test.ts | 33 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/server.ts b/src/server.ts index c45fe21..73469a4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -950,12 +950,7 @@ function processRequest( const method = req.method?.toLowerCase() || "get"; try { - const execution = executeRequest( - method, - path, - pathname, - requestContext, - ); + const execution = executeRequest(method, path, pathname, requestContext); const then = getThen(execution); if (then) { return resolveThenable(execution, then).then( @@ -1004,13 +999,7 @@ export async function upgradeListener( let mustDestroySocket = false; try { - const handler = getHandler( - method, - path, - roots.websocket, - false, - pathname, - ); + const handler = getHandler(method, path, roots.websocket, false, pathname); if (!handler || Array.isArray(handler)) { mustSendResponse = true; mustDestroySocket = true; diff --git a/src/test/request-context.test.ts b/src/test/request-context.test.ts index 80d203d..80efe63 100644 --- a/src/test/request-context.test.ts +++ b/src/test/request-context.test.ts @@ -178,12 +178,43 @@ describe("Request context URL", () => { } as IncomingMessage; await assert.rejects( - requestListener(request, {} as ServerResponse, "http"), + async () => requestListener(request, {} as ServerResponse, "http"), TypeError, ); assert.equal(handlerWasCalled, false); }); + it("returns the same URL instance across repeated synchronous accesses", async () => { + register("lazy-url-identity", "handler", "/lazy/identity", (context) => { + const first = context.url; + return { + pathname: context.url.pathname, + query: context.url.searchParams.get("value"), + sameInstance: first === context.url, + }; + }); + + const body = await sendRawRequest( + "/lazy/identity?value=repeated", + "example.test", + ); + assert.deepEqual(JSON.parse(body), { + pathname: "/lazy/identity", + query: "repeated", + sameInstance: true, + }); + }); + + it("keeps lazy URL semantics in asynchronous handlers", async () => { + register("lazy-url-async", "handler", "/lazy/async", async (context) => { + await Promise.resolve(); + return context.url.href; + }); + + const body = await sendRawRequest("/lazy/async?value=ok", "example.test"); + assert.equal(body, "http://example.test/lazy/async?value=ok"); + }); + it("keeps url assignable", async () => { register( "lazy-url-assignable",