// @ts-nocheck import { createServer, request, get, Agent, globalAgent, Server } from "node:http"; import { createTest } from "node-harness"; const { describe, expect, it, beforeAll, afterAll, createDoneDotAll } = createTest(import.meta.path); function listen(server: Server): Promise { return new Promise((resolve, reject) => { server.listen({ port: 0 }, (err, hostname, port) => { if (err) { reject(err); } else { resolve(new URL(`http://${hostname}:${port}`)); } }); setTimeout(() => reject("Timed out"), 5000); }); } describe("node:http", () => { describe("createServer", async () => { it("hello world", async () => { try { var server = createServer((req, res) => { expect(req.url).toBe("/hello?world"); res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello World"); }); const url = await listen(server); const res = await fetch(new URL("/hello?world", url)); expect(await res.text()).toBe("Hello World"); } catch (e) { throw e; } finally { server.close(); } }); it("request & response body streaming (large)", async () => { try { const bodyBlob = new Blob(["hello world", "hello world".repeat(9000)]); const input = await bodyBlob.text(); var server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); req.on("data", chunk => { res.write(chunk); }); req.on("end", () => { res.end(); }); }); const url = await listen(server); const res = await fetch(url, { method: "POST", body: bodyBlob, }); const out = await res.text(); expect(out).toBe(input); } finally { server.close(); } }); it("request & response body streaming (small)", async () => { try { const bodyBlob = new Blob(["hello world", "hello world".repeat(4)]); const input = await bodyBlob.text(); var server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); req.on("data", chunk => { res.write(chunk); }); req.on("end", () => { res.end(); }); }); const url = await listen(server); const res = await fetch(url, { method: "POST", body: bodyBlob, }); const out = await res.text(); expect(out).toBe(input); } finally { server.close(); } }); it("listen should return server", async () => { const server = createServer(); const listenResponse = server.listen(0); expect(listenResponse instanceof Server).toBe(true); expect(listenResponse).toBe(server); listenResponse.close(); }); }); describe("request", () => { function runTest(done: Function, callback: (server: Server, port: number, done: (err?: Error) => void) => void) { var timer; var server = createServer((req, res) => { const reqUrl = new URL(req.url!, `http://${req.headers.host}`); if (reqUrl.pathname) { if (reqUrl.pathname === "/redirect") { // Temporary redirect res.writeHead(301, { Location: `http://localhost:${server.port}/redirected`, }); res.end("Got redirect!\n"); return; } if (reqUrl.pathname === "/redirected") { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Not Found"); return; } if (reqUrl.pathname === "/lowerCaseHeaders") { res.writeHead(200, { "content-type": "text/plain", "X-Custom-Header": "custom_value" }); res.end("Hello World"); return; } if (reqUrl.pathname.includes("timeout")) { if (timer) clearTimeout(timer); timer = setTimeout(() => { res.end("Hello World"); timer = null; }, 3000); return; } if (reqUrl.pathname === "/pathTest") { res.end("Path correct!\n"); return; } } res.writeHead(200, { "Content-Type": "text/plain" }); if (req.headers["x-test"]) { res.write(`x-test: ${req.headers["x-test"]}\n`); } // Check for body if (req.method === "POST") { req.on("data", chunk => { res.write(chunk); }); req.on("end", () => { res.write("POST\n"); res.end("Hello World"); }); } else { if (req.headers["X-Test"] !== undefined) { res.write(`X-Test: test\n`); } res.write("Maybe GET maybe not\n"); res.end("Hello World"); } }); server.listen({ port: 0 }, (_, __, port) => { var _done = (...args) => { server.close(); done(...args); }; callback(server, port, _done); }); } // it.only("check for expected fields", done => { // runTest((server, port) => { // const req = request({ host: "localhost", port, method: "GET" }, res => { // console.log("called"); // res.on("end", () => { // console.log("here"); // server.close(); // done(); // }); // res.on("error", err => { // server.close(); // done(err); // }); // }); // expect(req.path).toEqual("/"); // expect(req.method).toEqual("GET"); // expect(req.host).toEqual("localhost"); // expect(req.protocol).toEqual("http:"); // req.end(); // }); // }); it("should make a standard GET request when passed string as first arg", done => { runTest(done, (server, port, done) => { const req = request(`http://localhost:${port}`, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Maybe GET maybe not\nHello World"); done(); }); res.on("error", err => done(err)); }); req.end(); }); }); it("should make a https:// GET request when passed string as first arg", done => { const req = request("https://example.com", res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toContain("This domain is for use in illustrative examples in documents"); done(); }); res.on("error", err => done(err)); }); req.end(); }); it("should make a POST request when provided POST method, even without a body", done => { runTest(done, (server, serverPort, done) => { const req = request({ host: "localhost", port: serverPort, method: "POST" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("POST\nHello World"); done(); }); res.on("error", err => done(err)); }); req.end(); }); }); it("should correctly handle a POST request with a body", done => { runTest(done, (server, port, done) => { const req = request({ host: "localhost", port, method: "POST" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Posting\nPOST\nHello World"); done(); }); res.on("error", err => done(err)); }); req.write("Posting\n"); req.end(); }); }); it("should noop request.setSocketKeepAlive without error", done => { runTest(done, (server, port, done) => { const req = request(`http://localhost:${port}`); req.setSocketKeepAlive(true, 1000); req.end(); expect(true).toBe(true); done(); }); }); it("should allow us to set timeout with request.setTimeout or `timeout` in options", done => { runTest(done, (server, serverPort, done) => { const createDone = createDoneDotAll(done); const req1Done = createDone(); const req2Done = createDone(); const req1 = request( { host: "localhost", port: serverPort, path: "/timeout", timeout: 500, }, res => { req1Done(new Error("Should not have received response")); }, ); req1.on("timeout", () => req1Done()); const req2 = request( { host: "localhost", port: serverPort, path: "/timeout", }, res => { req2Done(new Error("Should not have received response")); }, ); req2.setTimeout(500, () => { req2Done(); }); req1.end(); req2.end(); }); }); it("should correctly set path when path provided", done => { runTest(done, (server, serverPort, done) => { const createDone = createDoneDotAll(done); const req1Done = createDone(); const req2Done = createDone(); const req1 = request(`http://localhost:${serverPort}/pathTest`, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Path correct!\n"); req1Done(); }); res.on("error", err => req1Done(err)); }); const req2 = request(`http://localhost:${serverPort}`, { path: "/pathTest" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Path correct!\n"); req2Done(); }); res.on("error", err => req2Done(err)); }); req1.end(); req2.end(); expect(req1.path).toBe("/pathTest"); expect(req2.path).toBe("/pathTest"); }); }); it("should emit response when response received", done => { runTest(done, (server, serverPort, done) => { const req = request(`http://localhost:${serverPort}`); req.on("response", res => { expect(res.statusCode).toBe(200); done(); }); req.end(); }); }); // NOTE: Node http.request doesn't follow redirects by default it("should handle redirects properly", done => { runTest(done, (server, serverPort, done) => { const req = request(`http://localhost:${serverPort}/redirect`, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Got redirect!\n"); done(); }); res.on("error", err => done(err)); }); req.end(); }); }); it("should correctly attach headers to request", done => { runTest(done, (server, serverPort, done) => { const req = request({ host: "localhost", port: serverPort, headers: { "X-Test": "test" } }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("x-test: test\nMaybe GET maybe not\nHello World"); done(); }); res.on("error", err => done(err)); }); req.end(); expect(req.getHeader("X-Test")).toBe("test"); }); }); it("should correct casing of method param", done => { runTest(done, (server, serverPort, done) => { const req = request({ host: "localhost", port: serverPort, method: "get" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Maybe GET maybe not\nHello World"); done(); }); res.on("error", err => done(err)); }); req.end(); }); }); it("should allow for port as a string", done => { runTest(done, (server, serverPort, done) => { const req = request({ host: "localhost", port: `${serverPort}`, method: "GET" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Maybe GET maybe not\nHello World"); done(); }); res.on("error", err => done(err)); }); req.end(); }); }); it("should allow us to pass a URL object", done => { runTest(done, (server, serverPort, done) => { const req = request(new URL(`http://localhost:${serverPort}`), { method: "POST" }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Hello WorldPOST\nHello World"); done(); }); res.on("error", err => done(err)); }); req.write("Hello World"); req.end(); }); }); it("should ignore body when method is GET/HEAD/OPTIONS", done => { runTest(done, (server, serverPort, done) => { const createDone = createDoneDotAll(done); const methods = ["GET", "HEAD", "OPTIONS"]; const dones = {}; for (const method of methods) { dones[method] = createDone(); } for (const method of methods) { const req = request(`http://localhost:${serverPort}`, { method }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe(method === "GET" ? "Maybe GET maybe not\nHello World" : ""); dones[method](); }); res.on("error", err => dones[method](err)); }); req.write("BODY"); req.end(); } }); }); it("should return response with lowercase headers", done => { runTest(done, (server, serverPort, done) => { const req = request(`http://localhost:${serverPort}/lowerCaseHeaders`, res => { console.log(res.headers); expect(res.headers["content-type"]).toBe("text/plain"); expect(res.headers["x-custom-header"]).toBe("custom_value"); done(); }); req.end(); }); }); }); describe("signal", () => { it.skip("should abort and close the server", done => { const server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello World"); }); const interval = setTimeout(() => { server.close(); done(); }, 100); const signal = AbortSignal.timeout(30); signal.addEventListener("abort", () => { clearTimeout(interval); expect(true).toBe(true); done(); }); server.listen({ signal, port: 0 }); }); }); describe("get", () => { it("should make a standard GET request, like request", async done => { const server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello World"); }); const url = await listen(server); get(url, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { expect(data).toBe("Hello World"); server.close(); done(); }); res.on("error", err => { server.close(); done(err); }); }); }); }); describe("Agent", () => { let dummyAgent; beforeAll(() => { dummyAgent = new Agent(); }); it("should be a class", () => { expect(Agent instanceof Function).toBe(true); }); it("should have a default maxSockets of Infinity", () => { expect(dummyAgent.maxSockets).toBe(Infinity); }); it("should have a keepAlive value", () => { expect(dummyAgent.keepAlive).toBe(false); }); it("should noop keepSocketAlive", () => { const agent = new Agent({ keepAlive: true }); // @ts-ignore expect(agent.keepAlive).toBe(true); const server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello World"); agent.keepSocketAlive(request({ host: "localhost", port: server.address().port, method: "GET" })); server.end(); }); }); it("should provide globalAgent", () => { expect(globalAgent instanceof Agent).toBe(true); }); }); describe("ClientRequest.signal", () => { it("should attempt to make a standard GET request and abort", done => { let server_port; let server_host; const server = createServer((req, res) => { Bun.sleep(10).then(() => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello World"); }); }); server.listen({ port: 0 }, (_err, host, port) => { server_port = port; server_host = host; get(`http://${server_host}:${server_port}`, { signal: AbortSignal.timeout(5) }, res => { let data = ""; res.setEncoding("utf8"); res.on("data", chunk => { data += chunk; }); res.on("end", () => { server.close(); done(); }); res.on("error", _ => { server.close(); done(); }); }).on("error", err => { expect(err?.name).toBe("AbortError"); server.close(); done(); }); }); }); }); });