Bug Description
When the connection to the proxy server is closed with UND_ERR_SOCKET using ProxyAgent an infinite loop is created.
This is actually a double bug; I found this while investigating why ProxyAgent doesn't work with a basic http proxy built with node:http:
import { ok } from "node:assert";
import { createServer, request as httpRequest } from "node:http";
import { request as httpsRequest } from "node:https";
import { Client, ProxyAgent, request } from "undici";
const server = createServer((req, res) => {
ok(req.url);
const targetUrl = new URL(req.url);
req.pipe(
(targetUrl.protocol === "https:" ? httpsRequest : httpRequest)(
{
method: req.method,
hostname: targetUrl.hostname,
port: targetUrl.port,
path: targetUrl.pathname + targetUrl.search,
headers: req.headers,
},
(proxiedRes) => {
ok(proxiedRes.statusCode);
res.writeHead(
proxiedRes.statusCode,
proxiedRes.statusMessage,
proxiedRes.headers,
);
proxiedRes.pipe(res);
},
).once("error", res.destroy.bind(res)),
);
}).listen();
const address = server.address();
ok(address && typeof address === "object");
// This works fine:
console.log(
await new Client(`http://localhost:${address.port}`)
.request({
method: "GET",
path: "http://example.com",
headers: { host: "example.com" },
})
.then((res) => res.body.text()),
);
// This doesn't work and creates an infinite loop trying to connect to proxy url
// For instance, listening to server.on("connection", console.log) logs infinite times
console.log(
await request("http://example.com", {
dispatcher: new ProxyAgent(`http://localhost:${address.port}`),
}).then((res) => res.body.text()),
);
Now here there are two questions:
- Why is the connection closing in the first place? Spent hours debugging but honestly couldn't find the reason
- Why is it looping until a connection is established? This creates a significant overhead both on the client and on the proxy server that is flooded by requests
The error is caught here where there is already something about "avoiding a loop in client.js#connect"
|
} catch (err) { |
|
if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') { |
|
// Throw a custom error to avoid loop in client.js#connect |
|
callback(new SecureProxyConnectionError(err)) |
|
} else { |
|
callback(err) |
|
} |
|
} |
Reproducible By
Example above
Expected Behavior
If the connection is closed, the error should be thrown immediately instead of trying infinite times.
Also, I think the example above should be successful considering that using a Client works
Logs & Screenshots
The error (added throw err to avoid loop)
SocketError: other side closed
at Socket.onHttpSocketEnd (D:\-\node_modules\undici\lib\dispatcher\client-h1.js:902:22)
at Socket.emit (node:events:530:35)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
code: 'UND_ERR_SOCKET',
socket: {
localAddress: '::1',
localPort: 60534,
remoteAddress: '::1',
remotePort: 60533,
remoteFamily: 'IPv6',
timeout: undefined,
bytesWritten: 73,
bytesRead: 0
}
}
Environment
Node v22, all undici versions with ProxyAgent
Additional context
Using ProxyAgent with a "real" proxy works (even if it seems a bit slow?), so I'm not sure what is it expecting from my custom server that is different from a normal proxy
Bug Description
When the connection to the proxy server is closed with
UND_ERR_SOCKETusingProxyAgentan infinite loop is created.This is actually a double bug; I found this while investigating why
ProxyAgentdoesn't work with a basic http proxy built withnode:http:Now here there are two questions:
The error is caught here where there is already something about "avoiding a loop in client.js#connect"
undici/lib/dispatcher/proxy-agent.js
Lines 97 to 104 in 1cfe094
Reproducible By
Example above
Expected Behavior
If the connection is closed, the error should be thrown immediately instead of trying infinite times.
Also, I think the example above should be successful considering that using a
ClientworksLogs & Screenshots
The error (added
throw errto avoid loop)SocketError: other side closed at Socket.onHttpSocketEnd (D:\-\node_modules\undici\lib\dispatcher\client-h1.js:902:22) at Socket.emit (node:events:530:35) at endReadableNT (node:internal/streams/readable:1698:12) at process.processTicksAndRejections (node:internal/process/task_queues:90:21) { code: 'UND_ERR_SOCKET', socket: { localAddress: '::1', localPort: 60534, remoteAddress: '::1', remotePort: 60533, remoteFamily: 'IPv6', timeout: undefined, bytesWritten: 73, bytesRead: 0 } }Environment
Node v22, all undici versions with
ProxyAgentAdditional context
Using
ProxyAgentwith a "real" proxy works (even if it seems a bit slow?), so I'm not sure what is it expecting from my custom server that is different from a normal proxy