Skip to content

Commit

Permalink
fix: split cookie headers (#1452)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
Hebilicious and pi0 authored Jul 19, 2023
1 parent 88831dc commit 592476a
Show file tree
Hide file tree
Showing 23 changed files with 276 additions and 188 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"unstorage": "^1.8.0"
},
"devDependencies": {
"@azure/functions": "^3.5.1",
"@cloudflare/workers-types": "^4.20230710.1",
"@types/aws-lambda": "^8.10.119",
"@types/etag": "^1.8.1",
Expand Down
31 changes: 31 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion src/runtime/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { createHooks, Hookable } from "hookable";
import type { NitroRuntimeHooks } from "./types";
import { useRuntimeConfig } from "./config";
import { cachedEventHandler } from "./cache";
import { normalizeFetchResponse } from "./utils";
import { createRouteRulesHandler, getRouteRulesForPath } from "./route-rules";
import type { $Fetch, NitroFetchRequest } from "nitropack";
import { plugins } from "#internal/nitro/virtual/plugins";
Expand Down Expand Up @@ -48,12 +49,18 @@ function createNitroApp(): NitroApp {

// Create local fetch callers
const localCall = createCall(toNodeListener(h3App) as any);
const localFetch = createLocalFetch(localCall, globalThis.fetch);
const _localFetch = createLocalFetch(localCall, globalThis.fetch);
const localFetch = (...args: Parameters<typeof _localFetch>) => {
return _localFetch(...args).then((response) =>
normalizeFetchResponse(response)
);
};
const $fetch = createFetch({
fetch: localFetch,
Headers,
defaults: { baseURL: config.app.baseURL },
});

// @ts-ignore
globalThis.$fetch = $fetch;

Expand Down
36 changes: 9 additions & 27 deletions src/runtime/entries/aws-lambda.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {
APIGatewayProxyEvent,
APIGatewayProxyEventHeaders,
APIGatewayProxyEventV2,
APIGatewayProxyResult,
APIGatewayProxyResultV2,
Expand All @@ -9,6 +8,10 @@ import type {
import "#internal/nitro/virtual/polyfill";
import { withQuery } from "ufo";
import { nitroApp } from "../app";
import {
normalizeLambdaIncomingHeaders,
normalizeLambdaOutgoingHeaders,
} from "../utils.lambda";

export async function handler(
event: APIGatewayProxyEvent,
Expand Down Expand Up @@ -44,51 +47,30 @@ export async function handler(
event,
url,
context,
headers: normalizeIncomingHeaders(event.headers),
headers: normalizeLambdaIncomingHeaders(event.headers),
method,
query,
body: event.body, // TODO: handle event.isBase64Encoded
});

// Lambda v2 https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html#http-api-develop-integrations-lambda.v2
if ("cookies" in event || "rawPath" in event) {
const outgoingCookies = r.headers["set-cookie"];
const cookies = Array.isArray(outgoingCookies)
? outgoingCookies
: outgoingCookies?.split(",") || [];
: outgoingCookies?.split(/,\s?/) || [];

return {
cookies,
statusCode: r.status,
headers: normalizeOutgoingHeaders(r.headers, true),
headers: normalizeLambdaOutgoingHeaders(r.headers, true),
body: r.body.toString(),
};
}

return {
statusCode: r.status,
headers: normalizeOutgoingHeaders(r.headers),
headers: normalizeLambdaOutgoingHeaders(r.headers),
body: r.body.toString(),
};
}

function normalizeIncomingHeaders(headers?: APIGatewayProxyEventHeaders) {
return Object.fromEntries(
Object.entries(headers || {}).map(([key, value]) => [
key.toLowerCase(),
value!,
])
);
}

function normalizeOutgoingHeaders(
headers: Record<string, string | string[] | undefined>,
stripCookies = false
) {
const entries = stripCookies
? Object.entries(headers).filter(([key]) => !["set-cookie"].includes(key))
: Object.entries(headers);

return Object.fromEntries(
entries.map(([k, v]) => [k, Array.isArray(v) ? v.join(",") : v!])
);
}
9 changes: 7 additions & 2 deletions src/runtime/entries/azure-functions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import "#internal/nitro/virtual/polyfill";
import type { HttpRequest, HttpResponse } from "@azure/functions";
import { nitroApp } from "../app";
import { getAzureParsedCookiesFromHeaders } from "../utils.azure";
import { normalizeLambdaOutgoingHeaders } from "../utils.lambda";

export async function handle(context, req) {
export async function handle(context: { res: HttpResponse }, req: HttpRequest) {
const url = "/" + (req.params.url || "");

const { body, status, statusText, headers } = await nitroApp.localCall({
Expand All @@ -14,7 +17,9 @@ export async function handle(context, req) {

context.res = {
status,
headers,
// cookies https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=typescript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#http-response
cookies: getAzureParsedCookiesFromHeaders(headers),
headers: normalizeLambdaOutgoingHeaders(headers, true),
body: body ? body.toString() : statusText,
};
}
9 changes: 7 additions & 2 deletions src/runtime/entries/azure.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import "#internal/nitro/virtual/polyfill";
import type { HttpResponse, HttpRequest } from "@azure/functions";
import { parseURL } from "ufo";
import { nitroApp } from "../app";
import { getAzureParsedCookiesFromHeaders } from "../utils.azure";
import { normalizeLambdaOutgoingHeaders } from "../utils.lambda";

export async function handle(context, req) {
export async function handle(context: { res: HttpResponse }, req: HttpRequest) {
let url: string;
if (req.headers["x-ms-original-url"]) {
// This URL has been proxied as there was no static file matching it.
Expand All @@ -24,7 +27,9 @@ export async function handle(context, req) {

context.res = {
status,
headers,
// cookies https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=typescript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#http-response
cookies: getAzureParsedCookiesFromHeaders(headers),
headers: normalizeLambdaOutgoingHeaders(headers, true),
body: body ? body.toString() : statusText,
};
}
5 changes: 1 addition & 4 deletions src/runtime/entries/bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ const server = Bun.serve({
body = await request.arrayBuffer();
}

const response = await nitroApp.localFetch(url.pathname + url.search, {
url: url.pathname + url.search,
return nitroApp.localFetch(url.pathname + url.search, {
host: url.hostname,
protocol: url.protocol,
headers: request.headers,
method: request.method,
redirect: request.redirect,
body,
});

return response;
},
});

Expand Down
25 changes: 5 additions & 20 deletions src/runtime/entries/cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,22 @@ async function handleEvent(event: FetchEvent) {
body = Buffer.from(await event.request.arrayBuffer());
}

const r = await nitroApp.localCall({
event,
return nitroApp.localFetch(url.pathname + url.search, {
context: {
// https://developers.cloudflare.com/workers//runtime-apis/request#incomingrequestcfproperties
cf: (event.request as any).cf,
waitUntil: (promise) => event.waitUntil(promise),
cloudflare: {
event,
},
},
url: url.pathname + url.search,
host: url.hostname,
protocol: url.protocol,
headers: Object.fromEntries(event.request.headers.entries()),
headers: event.request.headers,
method: event.request.method,
redirect: event.request.redirect,
body,
});

return new Response(r.body, {
// @ts-ignore TODO: Should be HeadersInit instead of string[][]
headers: normalizeOutgoingHeaders(r.headers),
status: r.status,
statusText: r.statusText,
});
}

function assetsCacheControl(_request) {
Expand All @@ -69,12 +63,3 @@ const baseURLModifier = (request: Request) => {
const url = withoutBase(request.url, useRuntimeConfig().app.baseURL);
return mapRequestToAsset(new Request(url, request));
};

function normalizeOutgoingHeaders(
headers: Record<string, string | string[] | undefined>
) {
return Object.entries(headers).map(([k, v]) => [
k,
Array.isArray(v) ? v.join(",") : v,
]);
}
21 changes: 2 additions & 19 deletions src/runtime/entries/deno-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,12 @@ async function handleRequest(request: Request) {
body = await request.arrayBuffer();
}

const r = await nitroApp.localCall({
url: url.pathname + url.search,
return nitroApp.localFetch(url.pathname + url.search, {
host: url.hostname,
protocol: url.protocol,
headers: Object.fromEntries(request.headers.entries()),
headers: request.headers,
method: request.method,
redirect: request.redirect,
body,
});

return new Response(r.body || undefined, {
// @ts-ignore TODO: Should be HeadersInit instead of string[][]
headers: normalizeOutgoingHeaders(r.headers),
status: r.status,
statusText: r.statusText,
});
}

function normalizeOutgoingHeaders(
headers: Record<string, string | string[] | undefined>
) {
return Object.entries(headers).map(([k, v]) => [
k,
Array.isArray(v) ? v.join(",") : v,
]);
}
23 changes: 2 additions & 21 deletions src/runtime/entries/deno-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,14 @@ async function handler(request: Request) {
body = await request.arrayBuffer();
}

const r = await nitroApp.localCall({
url: url.pathname + url.search,
return nitroApp.localFetch(url.pathname + url.search, {
host: url.hostname,
protocol: url.protocol,
headers: Object.fromEntries(request.headers.entries()),
headers: request.headers,
method: request.method,
redirect: request.redirect,
body,
});

// TODO: fix in runtime/static
const responseBody = r.status === 304 ? null : r.body;
return new Response(responseBody, {
// @ts-ignore TODO: Should be HeadersInit instead of string[][]
headers: normalizeOutgoingHeaders(r.headers),
status: r.status,
statusText: r.statusText,
});
}

function normalizeOutgoingHeaders(
headers: Record<string, string | string[] | undefined>
) {
return Object.entries(headers).map(([k, v]) => [
k,
Array.isArray(v) ? v.join(",") : v,
]);
}

export default {};
Loading

0 comments on commit 592476a

Please sign in to comment.