1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

fix(node/http): support all .writeHead() signatures (#24469)

Implement the missing `.writeHead()` signatures from Node's
`ServerResponse` class that we didn't support.

Fixes https://github.com/denoland/deno/issues/24468
This commit is contained in:
Marvin Hagemeister 2024-07-09 17:46:10 +02:00 committed by GitHub
parent 0425dabb84
commit 07613a6bf2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 140 additions and 7 deletions

View file

@ -3,7 +3,7 @@
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
import { core } from "ext:core/mod.js";
import { core, primordials } from "ext:core/mod.js";
import {
op_fetch_response_upgrade,
op_fetch_send,
@ -68,6 +68,7 @@ import { resourceForReadableStream } from "ext:deno_web/06_streams.js";
import { TcpConn } from "ext:deno_net/01_net.js";
const { internalRidSymbol } = core;
const { ArrayIsArray } = primordials;
enum STATUS_CODES {
/** RFC 7231, 6.2.1 */
@ -1458,20 +1459,65 @@ export class ServerResponse extends NodeWritable {
getHeaderNames() {
return Object.keys(this.#headers);
}
getHeaders() {
getHeaders(): Record<string, string | number | string[]> {
// @ts-ignore Ignore null __proto__
return { __proto__: null, ...this.#headers };
}
hasHeader(name: string) {
return Object.hasOwn(this.#headers, name);
}
writeHead(status: number, headers: Record<string, string> = {}) {
writeHead(
status: number,
statusMessage?: string,
headers?:
| Record<string, string | number | string[]>
| Array<[string, string]>,
): this;
writeHead(
status: number,
headers?:
| Record<string, string | number | string[]>
| Array<[string, string]>,
): this;
writeHead(
status: number,
statusMessageOrHeaders?:
| string
| Record<string, string | number | string[]>
| Array<[string, string]>,
maybeHeaders?:
| Record<string, string | number | string[]>
| Array<[string, string]>,
): this {
this.statusCode = status;
for (const k in headers) {
if (Object.hasOwn(headers, k)) {
this.setHeader(k, headers[k]);
let headers = null;
if (typeof statusMessageOrHeaders === "string") {
this.statusMessage = statusMessageOrHeaders;
if (maybeHeaders !== undefined) {
headers = maybeHeaders;
}
} else if (statusMessageOrHeaders !== undefined) {
headers = statusMessageOrHeaders;
}
if (headers !== null) {
if (ArrayIsArray(headers)) {
headers = headers as Array<[string, string]>;
for (let i = 0; i < headers.length; i++) {
this.appendHeader(headers[i][0], headers[i][1]);
}
} else {
headers = headers as Record<string, string>;
for (const k in headers) {
if (Object.hasOwn(headers, k)) {
this.setHeader(k, headers[k]);
}
}
}
}
return this;
}

View file

@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import EventEmitter from "node:events";
import http, { type RequestOptions } from "node:http";
import http, { type RequestOptions, type ServerResponse } from "node:http";
import url from "node:url";
import https from "node:https";
import net from "node:net";
@ -142,6 +142,93 @@ Deno.test("[node/http] chunked response", async () => {
}
});
Deno.test("[node/http] .writeHead()", async (t) => {
async function testWriteHead(
onRequest: (res: ServerResponse) => void,
onResponse: (res: Response) => void,
) {
const { promise, resolve } = Promise.withResolvers<void>();
const server = http.createServer((_req, res) => {
onRequest(res);
res.end();
});
server.listen(async () => {
const res = await fetch(
// deno-lint-ignore no-explicit-any
`http://127.0.0.1:${(server.address() as any).port}/`,
);
await res.body?.cancel();
onResponse(res);
server.close(() => resolve());
});
await promise;
}
await t.step("send status code", async () => {
await testWriteHead(
(res) => res.writeHead(404),
(res) => {
assertEquals(res.status, 404);
},
);
});
// TODO(@marvinhagemeister): hyper doesn't support custom status text
// await t.step("send status + custom status text", async () => {
// await testWriteHead(
// (res) => res.writeHead(404, "some text"),
// (res) => {
// assertEquals(res.status, 404);
// assertEquals(res.statusText, "some text");
// },
// );
// });
await t.step("send status + custom status text + headers obj", async () => {
await testWriteHead(
(res) => res.writeHead(404, "some text", { foo: "bar" }),
(res) => {
assertEquals(res.status, 404);
// TODO(@marvinhagemeister): hyper doesn't support custom
// status text
// assertEquals(res.statusText, "some text");
assertEquals(res.headers.get("foo"), "bar");
},
);
});
await t.step("send status + headers obj", async () => {
await testWriteHead(
(res) => {
res.writeHead(200, {
foo: "bar",
bar: ["foo1", "foo2"],
foobar: 1,
});
},
(res) => {
assertEquals(res.status, 200);
assertEquals(res.headers.get("foo"), "bar");
assertEquals(res.headers.get("bar"), "foo1, foo2");
assertEquals(res.headers.get("foobar"), "1");
},
);
});
await t.step("send status + headers array", async () => {
await testWriteHead(
(res) => res.writeHead(200, [["foo", "bar"]]),
(res) => {
assertEquals(res.status, 200);
assertEquals(res.headers.get("foo"), "bar");
},
);
});
});
// Test empty chunks: https://github.com/denoland/deno/issues/17194
Deno.test("[node/http] empty chunk in the middle of response", async () => {
const { promise, resolve } = Promise.withResolvers<void>();