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:
parent
0425dabb84
commit
07613a6bf2
2 changed files with 140 additions and 7 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
Loading…
Reference in a new issue