undici
undici copied to clipboard
`fetch` throws UND_ERR_SOCKET error
Bug Description
A request on this page of the Daily Mail throws an UND_ERR_SOCKET error. There is no problem with https.
Reproducible By
Execute this script:
import https from "node:https";
import zlib from "node:zlib";
const testWithHttps = (url) => {
return new Promise((resolve, reject) => {
const req = https.request(new URL(url), (res) => {
const gunzip = zlib.createGunzip();
res.pipe(gunzip);
const buffer = [];
gunzip.on("data", (chunk) => buffer.push(chunk))
.on("error", (err) => reject(err))
.on("end", () => resolve(buffer.join("")));
});
req.on("error", (err) => reject(err));
req.setHeader("Accept", "*/*");
req.setHeader("Accept-Encoding", "br, gzip, deflate");
req.setHeader("Accept-Language", "*");
req.setHeader("Sec-Fetch-Mode", "cors");
req.setHeader("User-Agent", "undici");
req.end();
});
};
const testWithFetch = async (url) => {
const response = await fetch(url);
return response.text();
};
console.log("PTS (https://ptsv2.com/t/undici)");
let url = "https://ptsv2.com/t/undici/post";
console.log("HTTPS: " + (await testWithHttps(url)).substring(0, 100));
console.log("FETCH: " + (await testWithFetch(url)).substring(0, 100));
console.log("\nDaily Mail");
url = "https://www.dailymail.co.uk/sciencetech/article-8057229" +
"/Scientists-create-stunning-gifs-Mars-sand" +
"-dunes-understand-conditions-impact-them.html";
console.log("HTTPS: " + (await testWithHttps(url)).substring(0, 100));
console.log("FETCH: " + (await testWithFetch(url)).substring(0, 100));
Expected Behavior
PTS (https://ptsv2.com/t/undici)
HTTPS: Thank you for this dump. I hope you have a lovely day!
(node:22125) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
FETCH: Thank you for this dump. I hope you have a lovely day!
Daily Mail
HTTPS: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-tr
FETCH: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-tr
Logs & Screenshots
PTS (https://ptsv2.com/t/undici)
HTTPS: Thank you for this dump. I hope you have a lovely day!
(node:22125) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
FETCH: Thank you for this dump. I hope you have a lovely day!
Daily Mail
HTTPS: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-tr
node:internal/deps/undici/undici:6266
fetchParams.controller.controller.error(new TypeError("terminated", {
^
TypeError: terminated
at Fetch.onAborted (node:internal/deps/undici/undici:6266:53)
at Fetch.emit (node:events:527:28)
at Fetch.terminate (node:internal/deps/undici/undici:5522:14)
at Object.onError (node:internal/deps/undici/undici:6357:36)
at Request.onError (node:internal/deps/undici/undici:2023:31)
at errorRequest (node:internal/deps/undici/undici:3949:17)
at TLSSocket.onSocketClose (node:internal/deps/undici/undici:3411:9)
at TLSSocket.emit (node:events:539:35)
at node:net:715:12
at TCP.done (node:_tls_wrap:581:7) {
[cause]: SocketError: closed
at TLSSocket.onSocketClose (node:internal/deps/undici/undici:3399:35)
at TLSSocket.emit (node:events:539:35)
at node:net:715:12
at TCP.done (node:_tls_wrap:581:7) {
code: 'UND_ERR_SOCKET',
socket: {
localAddress: undefined,
localPort: undefined,
remoteAddress: undefined,
remotePort: undefined,
remoteFamily: 'IPvundefined',
timeout: undefined,
bytesWritten: 294,
bytesRead: 83018
}
}
}
Node.js v18.1.0
Environment
- Ubuntu: 20.04.4 LTS
- Node: v18.1.0
- npm: 8.8.0
Additional context
In test case, I modified the https headers to have the same values as fetch. You can see the requests (with their headers) on this page of PTS.
Could you reproduce with a self-hosted server?
@mcollina I reproduce the same error with a secure HTTP2 server.
// node server.js
import fs from "node:fs/promises";
import http2 from "node:http2";
// openssl genrsa -out key.pem
// openssl req -new -key key.pem -out csr.pem
// openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
// rm csr.pem
const server = http2.createSecureServer({
key: await fs.readFile("key.pem"),
cert: await fs.readFile("cert.pem"),
});
server.on("error", (err) => console.log(err));
server.on("stream", (stream) => stream.end("foo"));
server.listen(11011);
// NODE_TLS_REJECT_UNAUTHORIZED='0' node client.js
import http2 from "node:http2";
const testWithHttp2 = (url) => {
return new Promise((resolve, reject) => {
const client = http2.connect(url);
const req = client.request();
const buffer = [];
req.on("data", (chunk) => buffer.push(chunk));
req.on("error", (err) => reject(err));
req.on("end", () => {
resolve(buffer.join(""));
client.close();
});
req.end();
});
};
const testWithFetch = async (url) => {
const response = await fetch(url);
return response.text();
};
let url = "https://localhost:11011/";
console.log("HTTP2: " + (await testWithHttp2(url)));
console.log("FETCH: " + (await testWithFetch(url)));
(node:23659) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
HTTP2: foo
(node:23659) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
node:internal/deps/undici/undici:6266
fetchParams.controller.controller.error(new TypeError("terminated", {
^
TypeError: terminated
at Fetch.onAborted (node:internal/deps/undici/undici:6266:53)
at Fetch.emit (node:events:527:28)
at Fetch.terminate (node:internal/deps/undici/undici:5522:14)
at Object.onError (node:internal/deps/undici/undici:6357:36)
at Request.onError (node:internal/deps/undici/undici:2023:31)
at errorRequest (node:internal/deps/undici/undici:3949:17)
at TLSSocket.onSocketClose (node:internal/deps/undici/undici:3411:9)
at TLSSocket.emit (node:events:539:35)
at node:net:715:12
at TCP.done (node:_tls_wrap:581:7) {
[cause]: SocketError: closed
at TLSSocket.onSocketClose (node:internal/deps/undici/undici:3399:35)
at TLSSocket.emit (node:events:539:35)
at node:net:715:12
at TCP.done (node:_tls_wrap:581:7) {
code: 'UND_ERR_SOCKET',
socket: {
localAddress: undefined,
localPort: undefined,
remoteAddress: undefined,
remotePort: undefined,
remoteFamily: 'IPvundefined',
timeout: undefined,
bytesWritten: 176,
bytesRead: 239
}
}
}
Node.js v18.1.0
With a unsecure HTTP2 server, fetch throws a HPE_INVALID_CONSTANT error:
node:internal/deps/undici/undici:5575
p.reject(Object.assign(new TypeError("fetch failed"), { cause: response.error }));
^
TypeError: fetch failed
at Object.processResponse (node:internal/deps/undici/undici:5575:34)
at node:internal/deps/undici/undici:5901:42
at node:internal/process/task_queues:140:7
at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8) {
cause: HTTPParserError: Expected HTTP/
at Parser.execute (node:internal/deps/undici/undici:3075:19)
at Parser.readMore (node:internal/deps/undici/undici:3034:16)
at Socket.onSocketReadable (node:internal/deps/undici/undici:3364:14)
at Socket.emit (node:events:527:28)
at emitReadable_ (node:internal/streams/readable:590:12)
at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
code: 'HPE_INVALID_CONSTANT',
data: '\x00\x00\x00\x04\x00\x00\x00\x00\x00'
}
}
Node.js v18.1.0
There is issues and pull request to add support for HTTP/2:
- https://github.com/nodejs/undici/issues/399
- https://github.com/nodejs/undici/issues/902
- https://github.com/nodejs/undici/pull/720
- https://github.com/nodejs/undici/pull/1014
undici doesn't support http2 yet