Skip to content

fix(node): parse HTTP absolute-form request URLs - #81

Open
bun-unsafe wants to merge 1 commit into
middleapi:mainfrom
bun-unsafe:fix/node-absolute-form-url
Open

fix(node): parse HTTP absolute-form request URLs#81
bun-unsafe wants to merge 1 commit into
middleapi:mainfrom
bun-unsafe:fix/node-absolute-form-url

Conversation

@bun-unsafe

Copy link
Copy Markdown

Problem

toStandardUrl treated any req.url that does not start with / as a relative path and prefixed /.

HTTP/1.1 absolute-form request targets (common from proxies) look like http://127.0.0.1:3000/ping. That became /http://127.0.0.1:3000/ping, so consumers such as oRPC's Node RPCHandler failed to match the procedure and returned 404.

Origin-form (/ping) was already correct. The Fetch adapter already uses URL.pathname + search + hash.

Fix

If the request target is an http: or https: URL, parse it with URL and return pathname + search + hash, matching the Fetch adapter.

Unparseable values and non-http schemes still get the previous /${url} behavior (for example base/base).

Testing

vitest run packages/node/src/url.test.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor suggestion inline.

Reviewed changes

  • fromAbsoluteHttpUrl(url) — new helper in packages/node/src/url.ts that parses http:/https: absolute-form request targets with new URL and returns pathname + search + hash, matching the Fetch adapter (packages/fetch/src/url.ts), returning undefined for unparseable input or non-http schemes.
  • toStandardUrl(req) — origin-form (/…) URLs now pass through untouched; absolute-form URLs go through the helper; everything else keeps the previous /${url} fallback.
  • Tests — five new exact-match cases in packages/node/src/url.test.ts covering absolute-form canonicalization (path, query, hash), scheme case-insensitivity, and absolute-form via originalUrl.

The change is correctly shaped: Node hands req.url the raw request-target, so only absolute-form is affected, and I mutation-checked that reverting the fix fails the new tests. I also verified the fallback paths empirically (non-http schemes like ftp: and CONNECT authority-form host:443 still map to /${url}). The only gap is a test for the scheme-guard branch, covered inline.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

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')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth pinning the scheme-guard fallback branch: every new case here is an http(s) URL, so parsed.protocol !== 'http:' && parsed.protocol !== 'https:' in fromAbsoluteHttpUrl is the only branch still uncovered — new URL('ftp://example.com/x') parses fine, and dropping that guard would silently change it from /ftp://example.com/x to /x with no test noticing. Adding expect(toStandardUrl({ url: 'ftp://example.com/x' } as any)).toBe('/ftp://example.com/x') would lock in the documented "non-http schemes still get the previous /${url} behavior".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant