test: resolve paths against `server.url` instead of joining with a slash

RO
1mo ago
+75-69

Behaviour change: none

Problem

  • server.url is a URL whose href ends with /. So ${server.url}/path is http://host//path, with two slashes. 12 test files and 3 fixtures build a request URL this way.
  • These requests reach /path only because fetch() drops the second slash: URL::parse_with (src/url/lib.rs) shortens a pathname that starts with //. Node and curl send //path.
  • docs/runtime/http/server.mdx printed server.url as http://localhost:3000, without the slash.

Fix

  • Each join becomes new URL("/path", server.url), or server.url for the root. Every assertion stays.
  • The docs page prints server.url.href with its slash, and shows how to build the URL of a path.
  • Correct because no request line changes: fetch sends /path for both spellings today.
  • Verified: the 13 test files that use these joins pass on a debug build of main (463 tests).

Background

  • A temporary log in HTTPClient::build_request recorded each request whose path starts with // while 437 test files ran. 13 test files sent one by accident, all through this join.
  • With a fetch that sends the path as written, 24 tests in 7 of these files fail. The routes and handlers of the test servers do not match //path.
  • A draft of that fetch change is on the branch robobun/91e8acaa/fetch-keep-leading-slashes. It is not final and has no PR: it needs a maintainer decision first. The question is in a comment below.
Notes

Files. cookie.test.ts, async-iterator-stream.test.ts, bun-serve-html.test.ts, bun-serve-static.test.ts, bun-server.test.ts and its js-sink-sourmap-fixture/index.mjs, node-http-maxHeaderSize.test.ts and max-header-size-fixture.ts, abortsignal-leak-fixture.ts, fetch-args.test.ts, fetch.brotli.test.ts, bun-add.test.ts, and the regression tests 18547, 24817 and test-21049.

test-21049.test.ts compared response.url with the same ${server.url}/redirect string. The request URL and the expectation now share one constant, new URL("/redirect", server.url).href.

bun-add.test.ts named a tarball dependency ${server.url.href}/booop-0.0.1.tgz. It is now ${server.url.href}booop-0.0.1.tgz, in the dependency and in the expected package.json.

What the draft branch does. It deletes the loop for every caller of URL::parse, so the HTTP client sends the path of the URL on the first request, on each followed redirect, in the proxy absolute form and in the HTTP/2 and HTTP/3 :path. It repairs three places where bun wrote // itself and the loop hid it: href_without_auth for a root registry with credentials (the change of #38812), a registry typed http://host//, and a bun.lock tarball URL from the package-lock.json migration. FileSystemRouter#match(url) and server.fetch(path) keep their output. The branch holds the two commits of this PR and one more. A review of the draft found more work in its install half, so it can still change. #33496 adds a rule that keeps // for signed S3 URLs only. With the loop gone, that rule is not needed.