aboutsummaryrefslogtreecommitdiff
path: root/test/js/node/http/node-http.test.ts
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-04-17 07:13:01 -0700
committerGravatar GitHub <noreply@github.com> 2023-04-17 07:13:01 -0700
commit983d9428a67781f07476dd3eb76bd1bb31592486 (patch)
treeace3aaf0795a1e7e9f09253120df363be91202b0 /test/js/node/http/node-http.test.ts
parentfc539c278e59377c7992cdd0e806a709116209e1 (diff)
downloadbun-983d9428a67781f07476dd3eb76bd1bb31592486.tar.gz
bun-983d9428a67781f07476dd3eb76bd1bb31592486.tar.zst
bun-983d9428a67781f07476dd3eb76bd1bb31592486.zip
Get axios working (#2673)
* Revive node:http tests * Fix a couple bugs in node:http * possibly breaking: use `"browser"` exports condition last * Make URL validation error better --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'test/js/node/http/node-http.test.ts')
-rw-r--r--test/js/node/http/node-http.test.ts624
1 files changed, 624 insertions, 0 deletions
diff --git a/test/js/node/http/node-http.test.ts b/test/js/node/http/node-http.test.ts
new file mode 100644
index 000000000..fd2673728
--- /dev/null
+++ b/test/js/node/http/node-http.test.ts
@@ -0,0 +1,624 @@
+// @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<URL> {
+ 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();
+ });
+ });
+ });
+ });
+});