Files
bun.sh/test/js/node/http/node-http-parser.test.ts
Dylan Conway ce8767cdc8 add HTTPParser (#20049)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-05-31 16:21:08 -07:00

151 lines
4.7 KiB
TypeScript

import { describe, expect, test } from "bun:test";
const { HTTPParser, ConnectionsList } = process.binding("http_parser");
const kOnHeaders = HTTPParser.kOnHeaders;
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete;
describe("HTTPParser.prototype.close", () => {
test("does not double free", () => {
const parser = new HTTPParser();
expect(parser.close()).toBeUndefined();
expect(parser.close()).toBeUndefined();
});
test("does not segfault calling other methods after close", () => {
const parser = new HTTPParser();
parser.close();
// implementation was freed, test each method
expect(parser.close()).toBeUndefined();
expect(parser.free()).toBeUndefined();
expect(parser.remove()).toBeUndefined();
expect(parser.execute()).toBeUndefined();
expect(parser.finish()).toBeUndefined();
expect(parser.initialize()).toBeUndefined();
expect(parser.pause()).toBeUndefined();
expect(parser.resume()).toBeUndefined();
expect(parser.consume()).toBeUndefined();
expect(parser.unconsume()).toBeUndefined();
expect(parser.getCurrentBuffer()).toBeUndefined();
expect(parser.duration()).toBeUndefined();
expect(parser.headersCompleted()).toBeUndefined();
});
});
describe("HTTPParser.prototype.finish", () => {
test("returns error for invalid state", async () => {
const parser = new HTTPParser();
parser.initialize(HTTPParser.REQUEST, {});
const input = Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
const { promise, resolve } = Promise.withResolvers();
parser[kOnHeadersComplete] = function () {
expect(this.finish()).toMatchObject({
code: "HPE_INVALID_EOF_STATE",
reason: "Invalid EOF state",
bytesParsed: 0,
});
resolve();
};
parser.execute(input);
await promise;
expect(parser.finish()).toBeUndefined();
expect(parser.execute(Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"))).toBe(37);
});
test("basic", async () => {
const parser = new HTTPParser();
parser.initialize(HTTPParser.REQUEST, {});
expect(parser.finish()).toBeUndefined();
});
});
test("HTTPParser.prototype.getCurrentBuffer", async () => {
const parser = new HTTPParser();
parser.initialize(HTTPParser.REQUEST, {});
const input = Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
const { promise, resolve } = Promise.withResolvers();
parser[kOnHeaders] = function () {
expect(this.getCurrentBuffer()).toEqual(input);
resolve();
};
expect(parser.getCurrentBuffer()).toEqual(Buffer.from(""));
parser.execute(input);
await promise;
});
test("HTTPParser.prototype.duration", async () => {
const parser = new HTTPParser();
parser.initialize(HTTPParser.REQUEST, {});
const input = Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
const { promise, resolve } = Promise.withResolvers();
parser[kOnHeadersComplete] = function () {
expect(this.duration()).toBeGreaterThan(0);
resolve();
};
parser.execute(input);
await promise;
});
test("HTTPParser.prototype.headersCompleted", () => {
const paresr = new HTTPParser();
paresr.initialize(HTTPParser.REQUEST, {});
expect(paresr.headersCompleted()).toBe(false);
paresr.execute(Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"));
expect(paresr.headersCompleted()).toBe(true);
});
describe("ConnectionsList", () => {
test("basic operations", () => {
const list = new ConnectionsList();
expect(list.all()).toEqual([]);
expect(list.idle()).toEqual([]);
expect(list.active()).toEqual([]);
expect(list.expired()).toEqual([]);
});
test("works with HTTPParser", () => {
const p1 = new HTTPParser();
p1.name = "parser1";
const p2 = new HTTPParser();
p2.name = "parser2";
const p3 = new HTTPParser();
p3.name = "parser3";
const p4 = new HTTPParser();
p4.name = "parser4";
const list = new ConnectionsList();
p1.initialize(HTTPParser.REQUEST, {}, 0, 0, list);
p2.initialize(HTTPParser.REQUEST, {}, 0, 0, list);
p3.initialize(HTTPParser.REQUEST, {}, 0, 0, list);
p4.initialize(HTTPParser.REQUEST, {}, 0, 0, list);
expect(list.all()).toEqual([p1, p2, p3, p4]);
expect(list.idle()).toEqual([]);
expect(list.active()).toEqual([p1, p2, p3, p4]);
expect(list.expired()).toEqual([]);
p2.remove();
expect(list.all()).toEqual([p1, p3, p4]);
p3.execute(Buffer.from("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"));
expect(list.all()).toEqual([p1, p4, p3]);
p1.close();
p1.remove();
// p1 is still in the list after remove because close
// frees the implementation causing remove to not be able
// to remove it.
expect(list.all()).toEqual([p1, p4, p3]);
});
});