fix(ext/node): work correctly with wrapper Response objects, use correct rawHeaders structure (#29056)

Fixes https://github.com/denoland/deno/issues/28022

Basically drizzle-kit studio uses hono with the node-server adapter.
That creates wrapper objects for responses that forward property getters
to the underlying response (the one we provided). However, in deno.serve
we were assuming that the response was actually the same response we
initially gave and crashed when it wasn't. instead, just call the
property getters if we can't find the inner response.

The raw headers bug is that we were exposing the `rawHeaders` field on
`Incoming` as a `Headers` object, instead it's supposed to be a flat
array of the header keys + values. I.e. `["Content-Type:",
"application/json", "Host:", "http://localhost"]`
This commit is contained in:
Nathan Whitaker 2025-04-29 17:14:29 -07:00 committed by GitHub
parent c22d17824b
commit 05574665b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 253 additions and 8 deletions

View file

@ -58,6 +58,7 @@ import {
ResponsePrototype,
toInnerResponse,
} from "ext:deno_fetch/23_response.js";
import { headerListFromHeaders } from "ext:deno_fetch/20_headers.js";
import {
abortRequest,
fromInnerRequest,
@ -500,8 +501,15 @@ function fastSyncResponseOrStream(
return;
}
const stream = respBody.streamOrStatic;
const body = stream.body;
let stream;
let body;
if (respBody.streamOrStatic) {
stream = respBody.streamOrStatic;
body = stream.body;
} else {
stream = respBody;
body = respBody;
}
if (body !== undefined) {
// We ensure the response has not been consumed yet in the caller of this
// function.
@ -632,8 +640,19 @@ function mapToCallback(context, callback, onError) {
return;
}
const status = inner.status;
const headers = inner.headerList;
let status;
let headers;
let body;
if (inner) {
status = inner.status;
headers = inner.headerList;
body = inner.body;
} else {
status = response.status;
headers = headerListFromHeaders(response.headers);
body = response.body;
}
if (headers && headers.length > 0) {
if (headers.length == 1) {
op_http_set_response_header(req, headers[0][0], headers[0][1]);
@ -642,7 +661,7 @@ function mapToCallback(context, callback, onError) {
}
}
fastSyncResponseOrStream(req, inner.body, status, innerRequest);
fastSyncResponseOrStream(req, body, status, innerRequest);
};
if (TRACING_ENABLED) {

View file

@ -1780,6 +1780,8 @@ Object.defineProperty(ServerResponse.prototype, "connection", {
),
});
const kRawHeaders = Symbol("rawHeaders");
// TODO(@AaronO): optimize
export class IncomingMessageForServer extends NodeReadable {
#headers: Record<string, string>;
@ -1819,7 +1821,7 @@ export class IncomingMessageForServer extends NodeReadable {
this.method = "";
this.socket = socket;
this.upgrade = null;
this.rawHeaders = [];
this[kRawHeaders] = [];
socket?.on("error", (e) => {
if (this.listenerCount("error") > 0) {
this.emit("error", e);
@ -1842,7 +1844,7 @@ export class IncomingMessageForServer extends NodeReadable {
get headers() {
if (!this.#headers) {
this.#headers = {};
const entries = headersEntries(this.rawHeaders);
const entries = headersEntries(this[kRawHeaders]);
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
this.#headers[entry[0]] = entry[1];
@ -1855,6 +1857,16 @@ export class IncomingMessageForServer extends NodeReadable {
this.#headers = val;
}
get rawHeaders() {
const entries = headersEntries(this[kRawHeaders]);
const out = new Array(entries.length * 2);
for (let i = 0; i < entries.length; i++) {
out[i * 2] = entries[i][0];
out[i * 2 + 1] = entries[i][1];
}
return out;
}
// connection is deprecated, but still tested in unit test.
get connection() {
return this.socket;
@ -1959,7 +1971,7 @@ export class ServerImpl extends EventEmitter {
req.upgrade =
request.headers.get("connection")?.toLowerCase().includes("upgrade") &&
request.headers.get("upgrade");
req.rawHeaders = request.headers;
req[kRawHeaders] = request.headers;
if (req.upgrade && this.listenerCount("upgrade") > 0) {
const { conn, response } = upgradeHttpRaw(request);