diff options
author | Luca Casonato <hello@lcas.dev> | 2021-07-16 16:31:16 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-16 16:31:16 +0200 |
commit | 91bbd1a6d5ec21595efe790ff482c7a96b7fafb4 (patch) | |
tree | adc03226d56ffa498e8b744b79ddbde354c59e64 | |
parent | 2e57476fbbed240d9636561a05e53d454f232aa0 (diff) |
fix(ext/http): correctly concat cookie headers (#11422)
Cookie headers should not be concatenated by commas, rather by
semicolons.
-rw-r--r-- | cli/tests/unit/http_test.ts | 27 | ||||
-rw-r--r-- | extensions/http/lib.rs | 37 |
2 files changed, 60 insertions, 4 deletions
diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index 21325ff77..bf351cd48 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -662,3 +662,30 @@ unitTest({ perms: { net: true } }, async function httpServerWebSocket() { await def; await promise; }); + +unitTest({ perms: { net: true } }, async function httpCookieConcatenation() { + const promise = (async () => { + const listener = Deno.listen({ port: 4501 }); + for await (const conn of listener) { + const httpConn = Deno.serveHttp(conn); + for await (const { request, respondWith } of httpConn) { + assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(await request.text(), ""); + assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo"); + respondWith(new Response("ok")); + } + break; + } + })(); + + const resp = await fetch("http://127.0.0.1:4501/", { + headers: [ + ["connection", "close"], + ["cookie", "foo=bar"], + ["cookie", "bar=foo"], + ], + }); + const text = await resp.text(); + assertEquals(text, "ok"); + await promise; +}); diff --git a/extensions/http/lib.rs b/extensions/http/lib.rs index a8d92ab46..a20e74c03 100644 --- a/extensions/http/lib.rs +++ b/extensions/http/lib.rs @@ -217,12 +217,41 @@ async fn op_http_request_next( let req = request_resource.request; let method = req.method().to_string(); + // We treat cookies specially, because we don't want them to get them + // mangled by the `Headers` object in JS. What we do is take all cookie + // headers and concat them into a single cookie header, seperated by + // semicolons. + let mut total_cookie_length = 0; + let mut cookies = vec![]; + let mut headers = Vec::with_capacity(req.headers().len()); for (name, value) in req.headers().iter() { - let name: &[u8] = name.as_ref(); - let value = value.as_bytes(); - headers - .push((ByteString(name.to_owned()), ByteString(value.to_owned()))); + if name == hyper::header::COOKIE { + let bytes = value.as_bytes(); + total_cookie_length += bytes.len(); + cookies.push(bytes); + } else { + let name: &[u8] = name.as_ref(); + let value = value.as_bytes(); + headers + .push((ByteString(name.to_owned()), ByteString(value.to_owned()))); + } + } + + if !cookies.is_empty() { + let cookie_count = cookies.len(); + total_cookie_length += (cookie_count * 2) - 2; + let mut bytes = Vec::with_capacity(total_cookie_length); + for (i, cookie) in cookies.into_iter().enumerate() { + bytes.extend(cookie); + if i != cookie_count - 1 { + bytes.extend("; ".as_bytes()); + } + } + headers.push(( + ByteString("cookie".as_bytes().to_owned()), + ByteString(bytes), + )); } let url = { |