Issue 1:
Hello!

This is pretty specific, but I was trying to use Deno as a test runner to test a web server that serves different content based on the Host header (NGINX server blocks / Apache VHosts), as Deno's batteries-included makes it very convenient.

However, currently my testing code doesn't work using the built-in fetch nor when using the node:http compat module's request() (or any library that depends on it, like npm:got), because they silently ignore the host header. Trying to do the same thing in Node works.

It makes sense that fetch should by default ignore the Host header like browsers do. However there should be an option to allow it as mentioned in the issues linked below, and the node:http module compat should allow it.

I tried to do it myself, but I don't know Rust and got overwhelmed. For the record, this is where the Host (and Content Length) header is skipped:

deno/ext/fetch/lib.rs

Lines 307 to 309
in
92ba46c

if !matches!(name, HOST | CONTENT_LENGTH) {

header_map.append(name, v);

}

Relevant issues / previous discussions:

#6868 (Jul 24, 2020) proposes an allowAllHeaders option to Deno.createHttpClient

As noted in the comments:

The problem is fetch() will always behave differently in a server context and a client context. You have all the situations with same origin and cross origin which just don't make sense in a server context. I am still just for documenting it. As you state, Node's implementation just passes forbidden as well.

#7307 (Sep 1, 2020) talks about adding non-standard features to fetch via HttpClient.

#11017 (Jun 17, 2021) claims that the "Host" header should always be set by reqwest / hyper to align with browser fetch.

#14909 (Jun 19, 2022) reports the lack of a way to set the "Host" header. Closed and marked as duplicate of #11017.

#14910 (Jun 19, 2022) by the same person about the Websocket API, closed for the same reason.

#16840 (Nov 26, 2022) asks for the allowAllHeaders proposed in the first issue.

Tests

Deno-only repro (from masx200 in #14909):

import { serve } from "https://deno.land/std@0.144.0/http/mod.ts";
import { assertEquals } from "https://deno.land/std@0.144.0/testing/asserts.ts";

Deno.test("http-request-host", async () => {
const ac = new AbortController();
const { signal } = ac;
const p = serve(async (req) => new Response(req.url), {
signal,
port: 5000,
});
const response = await fetch("http://127.0.0.1:5000/", {
headers: { host: "localhost.hello.world:9900" },
});
const text = await response.text();
assertEquals(text, "http://localhost.hello.world:9900/");
ac.abort();
await p;
});

Deno & Node repro (my own code, under the Unlicense (feel free to reuse, re-license etc) if that matters). Run it with deno run --allow-env=HOST,PORT --allow-net=localhost:8007 main.mjs and node main.mjs.

import assert from 'node:assert/strict';
import http from "node:http";

const isDeno = typeof Deno !== "undefined";
const host = (isDeno ? Deno.env.get("HOST") : process.env.HOST) || "localhost";
const port = (isDeno ? Deno.env.get("PORT") : process.env.PORT) || 8007;

// Set up a server that replies with the received Host header
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end(`Host header: "${req.headers.host}"`);
});
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});

const CustomHostHeader = "test.localhost"

// Query our server
http.get(
{ host, port, headers: { "Host": CustomHostHeader } },
(response) => {
// Read the body...
let body = "";
response.on("data", d => body += d);

response.on("end", () => {
console.log("body received:", body);

try {
assert.equal(body, `Host header: "${CustomHostHeader}"`);
console.log("works!");
} catch(e) {
console.log("assertion error (deno):", e)
}
finally {
server.close();
}
});
}
);

Thanks for your time!

Issue 2:
Only happening occasionally - only observed on windows.

test fetchPostBodyTypedArray_permW0N1E0R0
HttpUser: connection closed
at maybeError (deno/js/errors.ts:38:12)
at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)
test result: FAILED. 7 passed; 1 failed; 0 ignored; 0 measured; 206 filtered out
Error: There were 1 test failures.
at setTimeout (file:///C:/deno/js/testing/testing.ts:110:13)
at fire (deno/js/timers.ts:131:3)

ex: https://ci.appveyor.com/project/deno/deno/builds/20541747