Repository: nodejs/undici (JavaScript)

Issue:
Fetch request concatenates Cookie headers with comma instead of semicolon
### Version

19.6.0

### Platform

Darwin MBP.local 22.2.0 Darwin Kernel Version 22.2.0: Fri Nov 11 02:08:47 PST 2022; root:xnu-8792.61.2~4/RELEASE_X86_64 x86_64

### Subsystem

_No response_

### What steps will reproduce the bug?

When specifying multiple `Cookie` headers for a fetch request, the headers will be concatenated using commas instead of semicolons. While this is _technically_ compliant with the WHATWG spec, that spec is based around browser use and causes a risk of incorrect handling by the receiving server.

Example:

```js
fetch('http://localhost:8080/', {
  headers: [
    ['cookie', 'FOO=lorem-ipsum-dolor-sit-amet'],
    ['cookie', 'BAR=the-quick-brown-fox']
  ]
});
```

### How often does it reproduce? Is there a required condition?

_No response_

### What is the expected behavior?

Cookies are separated by semicolons (`;`).

```http
GET / HTTP/1.1
host: localhost:8080
connection: keep-alive
cookie: FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox
...
```

### What do you see instead?

Cookies are separated by commas (`,`).

```http
GET / HTTP/1.1
host: localhost:8080
connection: keep-alive
cookie: FOO=lorem-ipsum-dolor-sit-amet, BAR=the-quick-brown-fox
...
```

### Additional information

Similar issue but on the receiving end: https://github.com/nodejs/node/issues/11256


Discussion:
> ### What is the expected behavior?
> ```httpspec
> GET / HTTP/1.1
> host: localhost:8080
> connection: keep-alive
> cookie: FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox
> ...
> ```
> 
> ### What do you see instead?
> ```httpspec
> GET / HTTP/1.1
> host: localhost:8080
> connection: keep-alive
> cookie: FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox
> ...
> ```

Both look exactly similar, is it because of a missed copy-pasting?
Node behavior with `http.request` with  cookies is to use `;` separator.
Undici `fetch` behavior is to use `,` separator.

> Both look exactly similar, is it because of a missed copy-pasting?

Apologies, that was a mistake! 😞 I've edited the original post to reflect this.



Patch:
diff --git a/lib/fetch/headers.js b/lib/fetch/headers.js
index f3955f2a47a..0febd114db5 100644
--- a/lib/fetch/headers.js
+++ b/lib/fetch/headers.js
@@ -104,7 +104,11 @@ class HeadersList {
 
     // 2. Append (name, value) to list.
     if (exists) {
-      this[kHeadersMap].set(lowercaseName, { name: exists.name, value: `${exists.value}, ${value}` })
+      const delimiter = lowercaseName === 'cookie' ? '; ' : ', '
+      this[kHeadersMap].set(lowercaseName, {
+        name: exists.name,
+        value: `${exists.value}${delimiter}${value}`
+      })
     } else {
       this[kHeadersMap].set(lowercaseName, { name, value })
     }


Test patch:
diff --git a/test/fetch/cookies.js b/test/fetch/cookies.js
index d17a8827df5..18b001d8644 100644
--- a/test/fetch/cookies.js
+++ b/test/fetch/cookies.js
@@ -48,3 +48,22 @@ test('Can send cookies to a server with fetch - issue #1463', async (t) => {
 
   t.end()
 })
+
+test('Cookie header is delimited with a semicolon rather than a comma - issue #1905', async (t) => {
+  t.plan(1)
+
+  const server = createServer((req, res) => {
+    t.equal(req.headers.cookie, 'FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox')
+    res.end()
+  }).listen(0)
+
+  t.teardown(server.close.bind(server))
+  await once(server, 'listening')
+
+  await fetch(`http://localhost:${server.address().port}`, {
+    headers: [
+      ['cookie', 'FOO=lorem-ipsum-dolor-sit-amet'],
+      ['cookie', 'BAR=the-quick-brown-fox']
+    ]
+  })
+})