Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const http = require("node:http");

const server = http.createServer((req, res) => {
// Set response headers
res.setHeader("Content-Type", "text/plain");
res.setHeader("X-Powered-By", "Node.js");
res.setHeader("Cache-Control", ["no-cache", "yes-cache"]);
res.appendHeader("Cache-Control", "maybe-cache");
res.appendHeader("Cache-Control", ["please-cache", "please-dont-cache"]);
res.setHeader("Set-Cookie", ["a=b", "c=d"]);
res.appendHeader("Set-Cookie", "e=f");
res.appendHeader("Set-Cookie", ["g=h", "i=j"]);
res.setHeader("Abc", ["list-one", "list-two"]);
res.setHeader("Abc", ["list-three", "list-four"]);

// Write response
res.statusCode = 200;
res.end("Hello World\n");
});

const PORT = 0;
server.listen(PORT, async () => {
const port = server.address().port;
console.log(`Server running`);

// Test the server response headers using fetch
try {
const response = await fetch(`http://localhost:${port}/`);
console.log("Response status: " + response.status);

// Check headers
console.log("Headers test results:");
response.headers.delete("date");
for (const [key, value] of response.headers.entries()) {
console.log(`${key}: ${value}`);
}

const body = await response.text();
console.log("Body:", body);
process.exit(0);
} catch (error) {
console.error("Error testing server:", error);
process.exit(1);
} finally {
// Uncomment to close server after test
// server.close();
}
});
28 changes: 25 additions & 3 deletions src/bun.js/bindings/NodeHTTP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,8 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPGetHeader, (JSGlobalObject * globalObject, CallFr
return JSValue::encode(jsUndefined());
}

JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFrame* callFrame))
template<bool append>
EncodedJSValue jsHTTPSetOrAppendHeader(JSGlobalObject* globalObject, CallFrame* callFrame)
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
Expand Down Expand Up @@ -1396,7 +1397,11 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFr

auto value = item.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
impl->set(name, value);
if constexpr (append) {
impl->append(name, value);
} else {
impl->set(name, value);
}
RETURN_IF_EXCEPTION(scope, {});
}
for (unsigned i = 1; i < length; ++i) {
Expand All @@ -1414,7 +1419,11 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFr

auto value = valueValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
impl->set(name, value);
if constexpr (append) {
impl->append(name, value);
} else {
impl->set(name, value);
}
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsUndefined());
}
Expand All @@ -1423,13 +1432,26 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFr
return JSValue::encode(jsUndefined());
}

JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
return jsHTTPSetOrAppendHeader<false>(globalObject, callFrame);
}

JSC_DEFINE_HOST_FUNCTION(jsHTTPAppendHeader, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
return jsHTTPSetOrAppendHeader<true>(globalObject, callFrame);
}

JSValue createNodeHTTPInternalBinding(Zig::GlobalObject* globalObject)
{
auto* obj = constructEmptyObject(globalObject);
VM& vm = globalObject->vm();
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setHeader"_s)),
JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "appendHeader"_s)),
JSC::JSFunction::create(vm, globalObject, 3, "appendHeader"_s, jsHTTPAppendHeader, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getHeader"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), 0);
Expand Down
5 changes: 4 additions & 1 deletion src/js/internal/http.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const {
getHeader,
setHeader,
appendHeader,
Headers,
assignHeaders: assignHeadersFast,
setRequestTimeout,
Expand All @@ -12,7 +13,8 @@ const {
setServerIdleTimeout,
} = $cpp("NodeHTTP.cpp", "createNodeHTTPInternalBinding") as {
getHeader: (headers: Headers, name: string) => string | undefined;
setHeader: (headers: Headers, name: string, value: string) => void;
setHeader: (headers: Headers, name: string, value: string | string[]) => void;
appendHeader: (headers: Headers, name: string, value: string | string[]) => void;
Headers: (typeof globalThis)["Headers"];
assignHeaders: (object: any, req: Request, headersTuple: any) => boolean;
setRequestTimeout: (req: Request, timeout: number) => boolean;
Expand Down Expand Up @@ -421,6 +423,7 @@ export {
runSymbol,
serverSymbol,
setHeader,
appendHeader,
setIsNextIncomingMessageHTTPS,
setRequestTimeout,
setServerCustomOptions,
Expand Down
19 changes: 4 additions & 15 deletions src/js/node/_http_outgoing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const {
kHandle,
getHeader,
setHeader,
appendHeader,
Headers,
getRawKeys,
} = require("internal/http");
Expand Down Expand Up @@ -205,13 +206,8 @@ const OutgoingMessagePrototype = {
validateString(name, "name");
validateHeaderValue(name, value);
var headers = (this[headersSymbol] ??= new Headers());
if ($isJSArray(value)) {
for (let i = 0; i < value.length; i++) {
headers.append(name, value[i]);
}
} else {
headers.append(name, value);
}

appendHeader(headers, name, value);
return this;
},

Expand Down Expand Up @@ -269,14 +265,7 @@ const OutgoingMessagePrototype = {
validateHeaderName(name);
validateHeaderValue(name, value);
const headers = (this[headersSymbol] ??= new Headers());
if ($isJSArray(value)) {
if (value.length > 0) setHeader(headers, name, value[0]);
for (let i = 1; i < value.length; i++) {
headers.append(name, value[i]);
}
} else {
setHeader(headers, name, value);
}
setHeader(headers, name, value);
return this;
},
setHeaders(headers) {
Expand Down
57 changes: 57 additions & 0 deletions test/js/node/http/fixtures/node-http-client-headers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,60 @@ await execute("set user agent and accept (array 1)", {
await execute("set user agent and accept (flat array)", {
headers: ["user-agent", "my new user agent", "accept", "text/html", "host", "example.com"],
});

async function server() {
const { promise, resolve, reject } = Promise.withResolvers();

const server = http.createServer((req, res) => {
// Set response headers
res.setHeader("Content-Type", "text/plain");
res.setHeader("X-Powered-By", "Node.js");
res.setHeader("Cache-Control", ["no-cache", "yes-cache"]);
res.appendHeader("Cache-Control", "maybe-cache");
res.appendHeader("Cache-Control", ["please-cache", "please-dont-cache"]);
res.setHeader("Set-Cookie", ["a=b", "c=d"]);
res.appendHeader("Set-Cookie", "e=f");
res.appendHeader("Set-Cookie", ["g=h", "i=j"]);
res.setHeader("Abc", ["list-one", "list-two"]);
res.setHeader("Abc", ["list-three", "list-four"]);

// Write response
res.statusCode = 200;
res.end("Hello World\n");
});

const PORT = 0;
server.listen(PORT, async () => {
const port = server.address().port;
console.log(`Server running`);

// Test the server response headers using fetch
try {
const response = await fetch(`http://localhost:${port}/`);
console.log("Response status: " + response.status);

// Check headers
console.log("Headers test results:");
for (const [key, value] of [...response.headers.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
if (key === "date") continue;
if (key === "keep-alive") continue;
if (key === "connection") continue;
console.log(`${key}: ${value}`);
}

const body = await response.text();
console.log("Body:", body);
resolve();
} catch (error) {
console.error("Error testing server:", error);
reject(error);
} finally {
// Uncomment to close server after test
// server.close();
}
});
await promise;
server.close();
}
console.log("%<test>server</test>");
await server();
119 changes: 0 additions & 119 deletions test/js/node/http/node-http-client-headers-array.js

This file was deleted.