Skip to content

node:http — res.headers['set-cookie'] collapses to a single value instead of an array #5079

@proggeramlug

Description

@proggeramlug

Summary

A response's set-cookie header must surface as a string array on IncomingMessage.headers, even for a single cookie. Perry collapses duplicate response headers into one value, so res.headers['set-cookie'] returns only the last cookie string.

Repro

import http from "node:http";

const server = http.createServer((_req, res) => {
  res.setHeader("Set-Cookie", ["a=1", "b=2"]);
  res.end("ok");
});
server.listen(0, () => {
  const port = (server.address() as any).port;
  http.get({ port }, (res: any) => {
    console.log("set-cookie", JSON.stringify(res.headers["set-cookie"]));
    res.on("data", () => {});
    res.on("end", () => server.close(() => console.log("closed")));
  });
});
setTimeout(() => {}, 1000);
# node v26
set-cookie ["a=1","b=2"]
closed

# perry
set-cookie "b=2"
closed

Root cause

IncomingMessageHandle.headers (crates/perry-ext-http/src/lib.rs) is a HashMap<String, String>, built by inserting each (k, v) pair from the reqwest response — duplicates clobber. Node's rule (per RFC 7230 §3.2.2 / the _http_incoming.js matchKnownFields logic):

  • set-cookiealways accumulated into an array;
  • a small set of single-value headers (content-length, content-type, host, authorization, …) → first value wins;
  • everything else → duplicates joined with , .

Fix sketch

Change IncomingMessageHandle to preserve header multiplicity (e.g. Vec<(String, String)> or a small multimap) and apply Node's combination rules in the res.headers getter (js_http_response_headers). headersDistinct / rawHeaders already round-trip the raw pairs, so the underlying data is available; this is about the combined-headers view. Touches the pub struct and its build sites across perry-ext-http (client) and the perry-ext-http-server server-side IncomingMessage, so both paths should be updated together.

Found while fixing client transport errors (#5078); deferred there to keep that PR focused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions