mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
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>
This commit is contained in:
@@ -37,6 +37,8 @@ const NODE_HTTP_WARNING =
|
||||
"WARN: Agent is mostly unused in Bun's implementation of http. If you see strange behavior, this is probably the cause.";
|
||||
|
||||
var _globalAgent;
|
||||
var _defaultHTTPSAgent;
|
||||
var kInternalRequest = Symbol("kInternalRequest");
|
||||
|
||||
var FakeSocket = class Socket {
|
||||
on() {
|
||||
@@ -97,6 +99,8 @@ export class Agent extends EventEmitter {
|
||||
this.#scheduling = options.scheduling || "lifo";
|
||||
this.#maxTotalSockets = options.maxTotalSockets;
|
||||
this.#totalSocketCount = 0;
|
||||
this.#defaultPort = options.defaultPort || 80;
|
||||
this.#protocol = options.protocol || "http:";
|
||||
}
|
||||
|
||||
get defaultPort() {
|
||||
@@ -226,7 +230,7 @@ export class Server extends EventEmitter {
|
||||
}
|
||||
|
||||
address() {
|
||||
return this.#server.hostname;
|
||||
return this.#server?.hostname;
|
||||
}
|
||||
|
||||
listen(port, host, onListen) {
|
||||
@@ -322,14 +326,21 @@ function destroyBodyStreamNT(bodyStream) {
|
||||
}
|
||||
|
||||
var defaultIncomingOpts = { type: "request" };
|
||||
|
||||
function getDefaultHTTPSAgent() {
|
||||
return (_defaultHTTPSAgent ??= new Agent({ defaultPort: 443, protocol: "https:" }));
|
||||
}
|
||||
|
||||
export class IncomingMessage extends Readable {
|
||||
constructor(req, { type = "request" } = defaultIncomingOpts) {
|
||||
constructor(req, defaultIncomingOpts) {
|
||||
const method = req.method;
|
||||
|
||||
super();
|
||||
|
||||
const url = new URL(req.url);
|
||||
|
||||
var { type = "request", [kInternalRequest]: nodeReq } = defaultIncomingOpts || {};
|
||||
|
||||
this.#noBody =
|
||||
type === "request" // TODO: Add logic for checking for body on response
|
||||
? "GET" === method ||
|
||||
@@ -349,6 +360,7 @@ export class IncomingMessage extends Readable {
|
||||
this.#fakeSocket = undefined;
|
||||
|
||||
this.url = url.pathname + url.search;
|
||||
this.#nodeReq = nodeReq;
|
||||
assignHeaders(this, req);
|
||||
}
|
||||
|
||||
@@ -363,6 +375,11 @@ export class IncomingMessage extends Readable {
|
||||
#req;
|
||||
url;
|
||||
#type;
|
||||
#nodeReq;
|
||||
|
||||
get req() {
|
||||
return this.#nodeReq;
|
||||
}
|
||||
|
||||
_construct(callback) {
|
||||
// TODO: streaming
|
||||
@@ -373,7 +390,6 @@ export class IncomingMessage extends Readable {
|
||||
|
||||
const contentLength = this.#req.headers.get("content-length");
|
||||
const length = contentLength ? parseInt(contentLength, 10) : 0;
|
||||
|
||||
if (length === 0) {
|
||||
this.#noBody = true;
|
||||
callback();
|
||||
@@ -898,6 +914,7 @@ export class ClientRequest extends OutgoingMessage {
|
||||
#protocol;
|
||||
#method;
|
||||
#port;
|
||||
#useDefaultPort;
|
||||
#joinDuplicateHeaders;
|
||||
#maxHeaderSize;
|
||||
#agent = _globalAgent;
|
||||
@@ -967,17 +984,21 @@ export class ClientRequest extends OutgoingMessage {
|
||||
var method = this.#method,
|
||||
body = this.#body;
|
||||
|
||||
this.#fetchRequest = fetch(`${this.#protocol}//${this.#host}:${this.#port}${this.#path}`, {
|
||||
method,
|
||||
headers: this.getHeaders(),
|
||||
body: body && method !== "GET" && method !== "HEAD" && method !== "OPTIONS" ? body : undefined,
|
||||
redirect: "manual",
|
||||
verbose: Boolean(__DEBUG__),
|
||||
signal: this[kAbortController].signal,
|
||||
})
|
||||
this.#fetchRequest = fetch(
|
||||
`${this.#protocol}//${this.#host}${this.#useDefaultPort ? "" : ":" + this.#port}${this.#path}`,
|
||||
{
|
||||
method,
|
||||
headers: this.getHeaders(),
|
||||
body: body && method !== "GET" && method !== "HEAD" && method !== "OPTIONS" ? body : undefined,
|
||||
redirect: "manual",
|
||||
verbose: Boolean(__DEBUG__),
|
||||
signal: this[kAbortController].signal,
|
||||
},
|
||||
)
|
||||
.then(response => {
|
||||
var res = (this.#res = new IncomingMessage(response, {
|
||||
type: "response",
|
||||
[kInternalRequest]: this,
|
||||
}));
|
||||
this.emit("response", res);
|
||||
})
|
||||
@@ -1008,7 +1029,12 @@ export class ClientRequest extends OutgoingMessage {
|
||||
|
||||
if (typeof input === "string") {
|
||||
const urlStr = input;
|
||||
input = urlToHttpOptions(new URL(urlStr));
|
||||
try {
|
||||
var urlObject = new URL(urlStr);
|
||||
} catch (e) {
|
||||
throw new TypeError(`Invalid URL: ${urlStr}`);
|
||||
}
|
||||
input = urlToHttpOptions(urlObject);
|
||||
} else if (input && typeof input === "object" && input instanceof URL) {
|
||||
// url.URL instance
|
||||
input = urlToHttpOptions(input);
|
||||
@@ -1025,8 +1051,29 @@ export class ClientRequest extends OutgoingMessage {
|
||||
options = ObjectAssign(input || {}, options);
|
||||
}
|
||||
|
||||
const defaultAgent = options._defaultAgent || Agent.globalAgent;
|
||||
var defaultAgent = options._defaultAgent || Agent.globalAgent;
|
||||
|
||||
const protocol = (this.#protocol = options.protocol ||= defaultAgent.protocol);
|
||||
switch (this.#agent?.protocol) {
|
||||
case undefined: {
|
||||
break;
|
||||
}
|
||||
case "http:": {
|
||||
if (protocol === "https:") {
|
||||
defaultAgent = this.#agent = getDefaultHTTPSAgent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
case "https:": {
|
||||
if (protocol === "https") {
|
||||
defaultAgent = this.#agent = Agent.globalAgent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.path) {
|
||||
const path = String(options.path);
|
||||
@@ -1044,7 +1091,10 @@ export class ClientRequest extends OutgoingMessage {
|
||||
// throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol);
|
||||
}
|
||||
|
||||
this.#port = options.port || options.defaultPort || this.#agent?.defaultPort || (protocol === "https:" ? 443 : 80);
|
||||
const defaultPort = protocol === "https:" ? 443 : 80;
|
||||
|
||||
this.#port = options.port || options.defaultPort || this.#agent?.defaultPort || defaultPort;
|
||||
this.#useDefaultPort = this.#port === defaultPort;
|
||||
const host =
|
||||
(this.#host =
|
||||
options.host =
|
||||
|
||||
@@ -598,54 +598,42 @@ pub const Platform = enum {
|
||||
break :brk array;
|
||||
};
|
||||
|
||||
pub const default_conditions_strings = .{
|
||||
.browser = @as(string, "browser"),
|
||||
.import = @as(string, "import"),
|
||||
.worker = @as(string, "worker"),
|
||||
.require = @as(string, "require"),
|
||||
.node = @as(string, "node"),
|
||||
.default = @as(string, "default"),
|
||||
.bun = @as(string, "bun"),
|
||||
.bun_macro = @as(string, "bun_macro"),
|
||||
.module = @as(string, "module"), // used in tslib
|
||||
.development = @as(string, "development"),
|
||||
.production = @as(string, "production"),
|
||||
};
|
||||
|
||||
pub const DefaultConditions: std.EnumArray(Platform, []const string) = brk: {
|
||||
var array = std.EnumArray(Platform, []const string).initUndefined();
|
||||
|
||||
array.set(Platform.node, &[_]string{
|
||||
default_conditions_strings.node,
|
||||
default_conditions_strings.module,
|
||||
"node",
|
||||
"module",
|
||||
});
|
||||
|
||||
var listc = [_]string{
|
||||
default_conditions_strings.browser,
|
||||
default_conditions_strings.module,
|
||||
"browser",
|
||||
"module",
|
||||
};
|
||||
array.set(Platform.browser, &listc);
|
||||
array.set(
|
||||
Platform.bun,
|
||||
&[_]string{
|
||||
default_conditions_strings.bun,
|
||||
default_conditions_strings.worker,
|
||||
default_conditions_strings.module,
|
||||
default_conditions_strings.node,
|
||||
default_conditions_strings.browser,
|
||||
"bun",
|
||||
"worker",
|
||||
"module",
|
||||
"node",
|
||||
"default",
|
||||
"browser",
|
||||
},
|
||||
);
|
||||
array.set(
|
||||
Platform.bun_macro,
|
||||
&[_]string{
|
||||
default_conditions_strings.bun,
|
||||
default_conditions_strings.worker,
|
||||
default_conditions_strings.module,
|
||||
default_conditions_strings.node,
|
||||
default_conditions_strings.browser,
|
||||
"bun",
|
||||
"worker",
|
||||
"module",
|
||||
"node",
|
||||
"default",
|
||||
"browser",
|
||||
},
|
||||
);
|
||||
// array.set(Platform.bun_macro, [_]string{ default_conditions_strings.bun_macro, default_conditions_strings.browser, default_conditions_strings.default, },);
|
||||
// array.set(Platform.bun_macro, [_]string{ "bun_macro", "browser", "default", },);
|
||||
|
||||
// Original comment:
|
||||
// The neutral platform is for people that don't want esbuild to try to
|
||||
@@ -868,9 +856,8 @@ pub const ESMConditions = struct {
|
||||
try import_condition_map.ensureTotalCapacity(defaults.len + 1);
|
||||
try require_condition_map.ensureTotalCapacity(defaults.len + 1);
|
||||
|
||||
import_condition_map.putAssumeCapacityNoClobber(Platform.default_conditions_strings.import, {});
|
||||
require_condition_map.putAssumeCapacityNoClobber(Platform.default_conditions_strings.require, {});
|
||||
default_condition_amp.putAssumeCapacityNoClobber(Platform.default_conditions_strings.default, {});
|
||||
import_condition_map.putAssumeCapacity("import", {});
|
||||
require_condition_map.putAssumeCapacity("require", {});
|
||||
|
||||
for (defaults) |default| {
|
||||
default_condition_amp.putAssumeCapacityNoClobber(default, {});
|
||||
@@ -878,6 +865,8 @@ pub const ESMConditions = struct {
|
||||
require_condition_map.putAssumeCapacityNoClobber(default, {});
|
||||
}
|
||||
|
||||
default_condition_amp.putAssumeCapacity("default", {});
|
||||
|
||||
return ESMConditions{
|
||||
.default = default_condition_amp,
|
||||
.import = import_condition_map,
|
||||
|
||||
@@ -1,605 +0,0 @@
|
||||
// @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 () => {
|
||||
const 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");
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("request & response body streaming (large)", async () => {
|
||||
const bodyBlob = new Blob(["hello world", "hello world".repeat(9000)]);
|
||||
|
||||
const input = await bodyBlob.text();
|
||||
|
||||
const 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);
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("request & response body streaming (small)", async () => {
|
||||
const bodyBlob = new Blob(["hello world", "hello world".repeat(4)]);
|
||||
|
||||
const input = await bodyBlob.text();
|
||||
|
||||
const 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);
|
||||
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", () => {
|
||||
let server;
|
||||
let serverPort;
|
||||
let timer: Timer | null = null;
|
||||
beforeAll(() => {
|
||||
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:${serverPort}/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) => {
|
||||
serverPort = port;
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
if (timer) clearTimeout(timer);
|
||||
});
|
||||
|
||||
it("check for expected fields", done => {
|
||||
const req = request({ host: "localhost", port: serverPort, method: "GET" }, res => {
|
||||
res.on("end", () => {
|
||||
done();
|
||||
});
|
||||
res.on("error", err => 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 => {
|
||||
const req = request(`http://localhost:${serverPort}`, 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 => {
|
||||
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 => {
|
||||
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("Posting\nPOST\nHello World");
|
||||
done();
|
||||
});
|
||||
res.on("error", err => done(err));
|
||||
});
|
||||
req.write("Posting\n");
|
||||
req.end();
|
||||
});
|
||||
|
||||
it("should noop request.setSocketKeepAlive without error", () => {
|
||||
const req = request(`http://localhost:${serverPort}`);
|
||||
req.setSocketKeepAlive(true, 1000);
|
||||
req.end();
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it("should allow us to set timeout with request.setTimeout or `timeout` in options", done => {
|
||||
const createDone = createDoneDotAll(done);
|
||||
const req1Done = createDone();
|
||||
const req2Done = createDone();
|
||||
|
||||
// const start = Date.now();
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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 => {
|
||||
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("should abort and close the server", done => {
|
||||
const server = createServer((req, res) => {
|
||||
res.writeHead(200, { "Content-Type": "text/plain" });
|
||||
res.end("Hello World");
|
||||
});
|
||||
|
||||
//force timeout to not hang tests
|
||||
const interval = setTimeout(() => {
|
||||
expect(false).toBe(true);
|
||||
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", () => {
|
||||
let server;
|
||||
let url;
|
||||
beforeAll(async () => {
|
||||
server = createServer((req, res) => {
|
||||
res.writeHead(200, { "Content-Type": "text/plain" });
|
||||
res.end("Hello World");
|
||||
});
|
||||
url = await listen(server);
|
||||
});
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
it("should make a standard GET request, like request", done => {
|
||||
get(url, res => {
|
||||
let data = "";
|
||||
res.setEncoding("utf8");
|
||||
res.on("data", chunk => {
|
||||
data += chunk;
|
||||
});
|
||||
res.on("end", () => {
|
||||
expect(data).toBe("Hello World");
|
||||
done();
|
||||
});
|
||||
res.on("error", err => done(err));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Agent", () => {
|
||||
let server;
|
||||
let dummyReq;
|
||||
let dummyAgent;
|
||||
beforeAll(() => {
|
||||
dummyAgent = new Agent();
|
||||
server = createServer((req, res) => {
|
||||
res.writeHead(200, { "Content-Type": "text/plain" });
|
||||
res.end("Hello World");
|
||||
});
|
||||
server.listen({ port: 0 }, (_, host, port) => {
|
||||
// Setup request after server is listening
|
||||
dummyReq = request(
|
||||
{
|
||||
host,
|
||||
port,
|
||||
agent: dummyAgent,
|
||||
},
|
||||
res => {},
|
||||
);
|
||||
dummyReq.on("error", () => {});
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
dummyReq.end();
|
||||
server.close();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
agent.keepSocketAlive(dummyReq.socket);
|
||||
});
|
||||
|
||||
it("should provide globalAgent", () => {
|
||||
expect(globalAgent instanceof Agent).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ClientRequest.signal", () => {
|
||||
let server;
|
||||
let server_port;
|
||||
let server_host;
|
||||
beforeAll(() => {
|
||||
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;
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
it("should attempt to make a standard GET request and abort", done => {
|
||||
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", () => {
|
||||
expect(true).toBeFalsy();
|
||||
done();
|
||||
});
|
||||
res.on("error", _ => {
|
||||
expect(true).toBeFalsy();
|
||||
done();
|
||||
});
|
||||
}).on("error", err => {
|
||||
expect(err?.name).toBe("AbortError");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
624
test/js/node/http/node-http.test.ts
Normal file
624
test/js/node/http/node-http.test.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user