summaryrefslogtreecommitdiff
path: root/packages/integrations/netlify/src
diff options
context:
space:
mode:
authorGravatar Erika <3019731+Princesseuh@users.noreply.github.com> 2023-01-06 18:01:54 +0100
committerGravatar GitHub <noreply@github.com> 2023-01-06 12:01:54 -0500
commit2f6745019ac25785032ac3659c2433b6e224f383 (patch)
tree4c104600c0450ad6fa1bdb7180bd97a2f7b60971 /packages/integrations/netlify/src
parent23937fbbc9fc5647d155dbe418c7c2afd4814c06 (diff)
downloadastro-2f6745019ac25785032ac3659c2433b6e224f383.tar.gz
astro-2f6745019ac25785032ac3659c2433b6e224f383.tar.zst
astro-2f6745019ac25785032ac3659c2433b6e224f383.zip
Drop Node 14 in CI for Node 16 and add Node 18 to the matrix (#5768)
* ci(node): Move CI to Node 16 and add Node 18 to the matrix * fix(netlify): Fix set-cookie not working on Node 18 * fix(netlify): Handle if `set-cookie` is already somehow an array (apparently it can?) * test(node): Fix `toPromise` to match Astro's * fix(tests): Use the actual underlying ArrayBuffer instance to create the buffer in toPromise * chore: changeset
Diffstat (limited to 'packages/integrations/netlify/src')
-rw-r--r--packages/integrations/netlify/src/netlify-functions.ts117
1 files changed, 105 insertions, 12 deletions
diff --git a/packages/integrations/netlify/src/netlify-functions.ts b/packages/integrations/netlify/src/netlify-functions.ts
index fe6ce7a15..57b042af9 100644
--- a/packages/integrations/netlify/src/netlify-functions.ts
+++ b/packages/integrations/netlify/src/netlify-functions.ts
@@ -104,19 +104,29 @@ export const createExports = (manifest: SSRManifest, args: Args) => {
// The fetch API does not have a way to get multiples of a single header, but instead concatenates
// them. There are non-standard ways to do it, and node-fetch gives us headers.raw()
// See https://github.com/whatwg/fetch/issues/973 for discussion
- if (response.headers.has('set-cookie') && 'raw' in response.headers) {
- // Node fetch allows you to get the raw headers, which includes multiples of the same type.
- // This is needed because Set-Cookie *must* be called for each cookie, and can't be
- // concatenated together.
- type HeadersWithRaw = Headers & {
- raw: () => Record<string, string[]>;
- };
-
- const rawPacked = (response.headers as HeadersWithRaw).raw();
- if ('set-cookie' in rawPacked) {
- fnResponse.multiValueHeaders = {
- 'set-cookie': rawPacked['set-cookie'],
+ if (response.headers.has('set-cookie')) {
+ if ('raw' in response.headers) {
+ // Node fetch allows you to get the raw headers, which includes multiples of the same type.
+ // This is needed because Set-Cookie *must* be called for each cookie, and can't be
+ // concatenated together.
+ type HeadersWithRaw = Headers & {
+ raw: () => Record<string, string[]>;
};
+
+ const rawPacked = (response.headers as HeadersWithRaw).raw();
+ if ('set-cookie' in rawPacked) {
+ fnResponse.multiValueHeaders = {
+ 'set-cookie': rawPacked['set-cookie'],
+ };
+ }
+ } else {
+ const cookies = response.headers.get('set-cookie');
+
+ if (cookies) {
+ fnResponse.multiValueHeaders = {
+ 'set-cookie': Array.isArray(cookies) ? cookies : splitCookiesString(cookies),
+ };
+ }
}
}
@@ -135,3 +145,86 @@ export const createExports = (manifest: SSRManifest, args: Args) => {
return { handler };
};
+
+/*
+ From: https://github.com/nfriedly/set-cookie-parser/blob/5cae030d8ef0f80eec58459e3583d43a07b984cb/lib/set-cookie.js#L144
+ Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
+ that are within a single set-cookie field-value, such as in the Expires portion.
+ This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
+ Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
+ React Native's fetch does this for *every* header, including set-cookie.
+ Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
+ Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
+*/
+function splitCookiesString(cookiesString: string): string[] {
+ if (Array.isArray(cookiesString)) {
+ return cookiesString;
+ }
+ if (typeof cookiesString !== 'string') {
+ return [];
+ }
+
+ let cookiesStrings = [];
+ let pos = 0;
+ let start;
+ let ch;
+ let lastComma;
+ let nextStart;
+ let cookiesSeparatorFound;
+
+ function skipWhitespace() {
+ while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
+ pos += 1;
+ }
+ return pos < cookiesString.length;
+ }
+
+ function notSpecialChar() {
+ ch = cookiesString.charAt(pos);
+
+ return ch !== '=' && ch !== ';' && ch !== ',';
+ }
+
+ while (pos < cookiesString.length) {
+ start = pos;
+ cookiesSeparatorFound = false;
+
+ while (skipWhitespace()) {
+ ch = cookiesString.charAt(pos);
+ if (ch === ',') {
+ // ',' is a cookie separator if we have later first '=', not ';' or ','
+ lastComma = pos;
+ pos += 1;
+
+ skipWhitespace();
+ nextStart = pos;
+
+ while (pos < cookiesString.length && notSpecialChar()) {
+ pos += 1;
+ }
+
+ // currently special character
+ if (pos < cookiesString.length && cookiesString.charAt(pos) === '=') {
+ // we found cookies separator
+ cookiesSeparatorFound = true;
+ // pos is inside the next cookie, so back up and return it.
+ pos = nextStart;
+ cookiesStrings.push(cookiesString.substring(start, lastComma));
+ start = pos;
+ } else {
+ // in param ',' or param separator ';',
+ // we continue from that comma
+ pos = lastComma + 1;
+ }
+ } else {
+ pos += 1;
+ }
+ }
+
+ if (!cookiesSeparatorFound || pos >= cookiesString.length) {
+ cookiesStrings.push(cookiesString.substring(start, cookiesString.length));
+ }
+ }
+
+ return cookiesStrings;
+}