Skip to content

Commit

Permalink
fix(azure): fix cookie format normalization (#1753)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
tobiasdiez and pi0 authored Sep 26, 2023
1 parent 3f7faa5 commit aee0204
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 8 deletions.
62 changes: 54 additions & 8 deletions src/runtime/utils.azure.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,63 @@
import type { Cookie } from "@azure/functions";
import { parse } from "cookie-es";
import { splitCookiesString } from "h3";
import { joinHeaders } from "./utils";

export function getAzureParsedCookiesFromHeaders(
headers: Record<string, number | string | string[] | undefined>
) {
const c = String(headers["set-cookie"]);
if (!c || c.length === 0) {
): Cookie[] {
const setCookieHeader = headers["set-cookie"];
if (
!setCookieHeader ||
typeof setCookieHeader === "number" ||
setCookieHeader.length === 0
) {
return [];
}
const cookies = splitCookiesString(joinHeaders(c)).map((cookie) =>
parse(cookie)
);
return cookies as unknown as Cookie[];
const azureCookies: Cookie[] = [];
for (const setCookieStr of splitCookiesString(setCookieHeader)) {
const setCookie = Object.entries(parse(setCookieStr));
if (setCookie.length === 0) {
continue;
}
const [[key, value], ..._setCookieOptions] = setCookie;
const setCookieOptions = Object.fromEntries(
_setCookieOptions.map(([k, v]) => [k.toLowerCase(), v])
);
const cookieObject: Cookie = {
name: key,
value,
domain: setCookieOptions.domain,
path: setCookieOptions.path,
expires: parseNumberOrDate(setCookieOptions.expires),
sameSite: setCookieOptions.samesite as "Lax" | "Strict" | "None",
maxAge: parseNumber(setCookieOptions.maxAge),
secure: setCookieStr.includes("Secure") ? true : undefined,
httpOnly: setCookieStr.includes("HttpOnly") ? true : undefined,
};
azureCookies.push(cookieObject);
}
return azureCookies;
}

function parseNumberOrDate(expires: string) {
const expiresAsNumber = parseNumber(expires);
if (expiresAsNumber !== undefined) {
return expiresAsNumber;
}
// Convert to Date if possible
const expiresAsDate = new Date(expires);
if (!Number.isNaN(expiresAsDate.getTime())) {
return expiresAsDate;
}
}

function parseNumber(maxAge: string) {
if (!maxAge) {
return undefined;
}
// Convert to number if possible
const maxAgeAsNumber = Number(maxAge);
if (!Number.isNaN(maxAgeAsNumber)) {
return maxAgeAsNumber;
}
}
91 changes: 91 additions & 0 deletions test/unit/azure.utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { describe, it, expect } from "vitest";
import { getAzureParsedCookiesFromHeaders } from "../../src/runtime/utils.azure";

describe("getAzureParsedCookiesFromHeaders", () => {
it("returns empty array if no cookies", () => {
expect(getAzureParsedCookiesFromHeaders({})).toMatchObject([]);
});
it("returns empty array if no set-cookie header", () => {
expect(
getAzureParsedCookiesFromHeaders({ "set-cookie": undefined })
).toMatchObject([]);
});
it("returns empty array if empty set-cookie header", () => {
expect(
getAzureParsedCookiesFromHeaders({ "set-cookie": " " })
).toMatchObject([]);
});
it("returns single cookie", () => {
expect(
getAzureParsedCookiesFromHeaders({ "set-cookie": "foo=bar" })
).toMatchObject([
{
name: "foo",
value: "bar",
},
]);
});
it('returns cookie with "expires" attribute', () => {
expect(
getAzureParsedCookiesFromHeaders({
"set-cookie": "foo=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT",
})
).toMatchObject([
{
name: "foo",
value: "bar",
expires: new Date("1970-01-01T00:00:00.000Z"),
},
]);
});
it("returns a complex cookie", () => {
expect(
getAzureParsedCookiesFromHeaders({
"set-cookie": [
"session=xyz; Path=/; Expires=Sun, 24 Mar 2024 09:13:27 GMT; HttpOnly; SameSite=Strict",
],
})
).toMatchObject([
{
name: "session",
value: "xyz",
expires: new Date("2024-03-24T09:13:27.000Z"),
path: "/",
sameSite: "Strict",
httpOnly: true,
},
]);
});
it("returns multiple cookies", () => {
expect(
getAzureParsedCookiesFromHeaders({
"set-cookie": ["foo=bar", "baz=qux"],
})
).toMatchObject([
{
name: "foo",
value: "bar",
},
{
name: "baz",
value: "qux",
},
]);
});
it("returns multiple cookies given as string", () => {
expect(
getAzureParsedCookiesFromHeaders({
"set-cookie": "foo=bar, baz=qux",
})
).toMatchObject([
{
name: "foo",
value: "bar",
},
{
name: "baz",
value: "qux",
},
]);
});
});

0 comments on commit aee0204

Please sign in to comment.