diff --git a/packages/node/src/url.test.ts b/packages/node/src/url.test.ts index 270664d..bb6dc96 100644 --- a/packages/node/src/url.test.ts +++ b/packages/node/src/url.test.ts @@ -6,4 +6,9 @@ it('toStandardUrl', () => { expect(toStandardUrl({ url: '/foo?bar=1#baz' } as any)).toBe('/foo?bar=1#baz') expect(toStandardUrl({ url: '/', originalUrl: '/foo?bar=2#baz' } as any)).toBe('/foo?bar=2#baz') expect(toStandardUrl({ url: 'base' } as any)).toBe('/base') + expect(toStandardUrl({ url: 'http://127.0.0.1:3000/ping' } as any)).toBe('/ping') + expect(toStandardUrl({ url: 'http://example.com/foo?bar=1' } as any)).toBe('/foo?bar=1') + expect(toStandardUrl({ url: 'https://example.com/foo#h' } as any)).toBe('/foo#h') + expect(toStandardUrl({ url: 'HTTP://EXAMPLE.COM/Foo' } as any)).toBe('/Foo') + expect(toStandardUrl({ url: '/', originalUrl: 'http://127.0.0.1:80/foo?x=1' } as any)).toBe('/foo?x=1') }) diff --git a/packages/node/src/url.ts b/packages/node/src/url.ts index 6d43b6f..cd86b72 100644 --- a/packages/node/src/url.ts +++ b/packages/node/src/url.ts @@ -1,8 +1,34 @@ import type { StandardUrl } from '@standardserver/core' import type { NodeHttpRequest } from './types' +function fromAbsoluteHttpUrl(url: string): StandardUrl | undefined { + try { + const parsed = new URL(url) + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + return undefined + } + + const pathname = `${parsed.pathname.startsWith('/') ? '' : '/'}${parsed.pathname}` as `/${string}` + return `${pathname}${parsed.search}${parsed.hash}` + } + catch { + return undefined + } +} + export function toStandardUrl(req: NodeHttpRequest): StandardUrl { // prefer originalUrl over url, especially useful in express.js middleware const url = req.originalUrl ?? req.url ?? '/' - return `${url.startsWith('/') ? '' : '/'}${url}` as `/${string}` + + if (url.startsWith('/')) { + return url as StandardUrl + } + + // RFC 9112 absolute-form. Fetch adapter uses URL.pathname + search + hash. + const fromAbsolute = fromAbsoluteHttpUrl(url) + if (fromAbsolute !== undefined) { + return fromAbsolute + } + + return `/${url}` as StandardUrl }