-
-
Notifications
You must be signed in to change notification settings - Fork 732
Description
Bug Description
PR #4837 changed the path extraction in httpNetworkFetch's internal dispatch() from url.pathname + url.search to url.href.slice(url.origin.length, ...). This is wrong when the URL contains credentials, because url.origin omits the userinfo component but url.href includes it:
const url = new URL("http://user:[email protected]:1234/path");
url.href.slice(url.origin.length); // ":[email protected]:1234/path" ΓÇö should be "/path"The garbled path fails the path must be an absolute URL or start with a slash check in lib/core/request.js, so the request never reaches the server.
Reproducible By
Run this script with undici 7.23.0+
const http = require("http");
const crypto = require("crypto");
const { WebSocket } = require("undici");
const server = http.createServer();
server.on("upgrade", (req, socket) => {
console.log("Server received upgrade request:");
console.log(" URL:", req.url);
console.log(" Authorization:", req.headers.authorization || "(none)");
const auth = req.headers.authorization;
if (!auth || auth !== "Basic " + Buffer.from("user:pass").toString("base64")) {
socket.write(
"HTTP/1.1 401 Unauthorized\r\n" +
"WWW-Authenticate: Basic realm=\"test\"\r\n" +
"Content-Length: 0\r\n" +
"\r\n"
);
socket.destroy();
return;
}
const key = req.headers["sec-websocket-key"];
const accept = crypto
.createHash("sha1")
.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
.digest("base64");
socket.write(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Accept: " + accept + "\r\n" +
"\r\n"
);
});
server.listen(0, "127.0.0.1", () => {
const { port } = server.address();
const url = "ws://user:[email protected]:" + port + "/path";
console.log("Connecting to " + url);
const ws = new WebSocket(url);
const timeout = setTimeout(() => {
console.log("FAIL: Timed out");
server.close();
process.exitCode = 1;
}, 5000);
ws.addEventListener("open", () => {
console.log("PASS: WebSocket opened successfully");
clearTimeout(timeout);
ws.close();
server.close();
});
ws.addEventListener("error", e => {
console.log("FAIL: WebSocket error:", e.error);
clearTimeout(timeout);
server.close();
process.exitCode = 1;
});
});Expected Behavior
The WebSocket connects successfully via the 401 challenge-response flow: first request without credentials gets a 401, retry with Authorization header succeeds. This works on 7.22.0.
Environment
Windows 11 (ARM64), Node v25.5.0, undici 7.24.0
Additional context
This breaks WebSocket basic auth (the useURLCredentials / 401 challenge-response flow), since the request URL retains credentials throughout.