@cloudflare/pages-shared 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -192,38 +192,47 @@ export async function generateHandler<
192
192
  staticRedirectsMatcher() || generateRedirectsMatcher()({ request })[0];
193
193
 
194
194
  if (match) {
195
- const { status, to } = match;
196
- const destination = new URL(to, request.url);
197
- const location =
198
- destination.origin === new URL(request.url).origin
199
- ? `${destination.pathname}${destination.search || search}${
200
- destination.hash
201
- }`
202
- : `${destination.href}${destination.search ? "" : search}${
203
- destination.hash
204
- }`;
205
- switch (status) {
206
- case 301:
207
- return new MovedPermanentlyResponse(location, undefined, {
208
- preventLeadingDoubleSlash: false,
209
- });
210
- case 303:
211
- return new SeeOtherResponse(location, undefined, {
212
- preventLeadingDoubleSlash: false,
213
- });
214
- case 307:
215
- return new TemporaryRedirectResponse(location, undefined, {
216
- preventLeadingDoubleSlash: false,
217
- });
218
- case 308:
219
- return new PermanentRedirectResponse(location, undefined, {
220
- preventLeadingDoubleSlash: false,
221
- });
222
- case 302:
223
- default:
224
- return new FoundResponse(location, undefined, {
225
- preventLeadingDoubleSlash: false,
226
- });
195
+ if (match.status === 200) {
196
+ // A 200 redirect means that we are proxying to a different asset, for example,
197
+ // a request with url /users/12345 could be pointed to /users/id.html. In order to
198
+ // do this, we overwrite the pathname, and instead match for assets with that url,
199
+ // and importantly, do not use the regular redirect handler - as the url visible to
200
+ // the user does not change
201
+ pathname = new URL(match.to, request.url).pathname;
202
+ } else {
203
+ const { status, to } = match;
204
+ const destination = new URL(to, request.url);
205
+ const location =
206
+ destination.origin === new URL(request.url).origin
207
+ ? `${destination.pathname}${destination.search || search}${
208
+ destination.hash
209
+ }`
210
+ : `${destination.href}${destination.search ? "" : search}${
211
+ destination.hash
212
+ }`;
213
+ switch (status) {
214
+ case 301:
215
+ return new MovedPermanentlyResponse(location, undefined, {
216
+ preventLeadingDoubleSlash: false,
217
+ });
218
+ case 303:
219
+ return new SeeOtherResponse(location, undefined, {
220
+ preventLeadingDoubleSlash: false,
221
+ });
222
+ case 307:
223
+ return new TemporaryRedirectResponse(location, undefined, {
224
+ preventLeadingDoubleSlash: false,
225
+ });
226
+ case 308:
227
+ return new PermanentRedirectResponse(location, undefined, {
228
+ preventLeadingDoubleSlash: false,
229
+ });
230
+ case 302:
231
+ default:
232
+ return new FoundResponse(location, undefined, {
233
+ preventLeadingDoubleSlash: false,
234
+ });
235
+ }
227
236
  }
228
237
  }
229
238
 
@@ -3,7 +3,7 @@ export const HEADERS_VERSION = 2;
3
3
  export const ANALYTICS_VERSION = 1;
4
4
  export const ROUTES_JSON_VERSION = 1;
5
5
 
6
- export const PERMITTED_STATUS_CODES = new Set([301, 302, 303, 307, 308]);
6
+ export const PERMITTED_STATUS_CODES = new Set([200, 301, 302, 303, 307, 308]);
7
7
  export const HEADER_SEPARATOR = ":";
8
8
  export const MAX_LINE_LENGTH = 2000;
9
9
  export const MAX_HEADER_RULES = 100;
@@ -6,7 +6,7 @@ import {
6
6
  SPLAT_REGEX,
7
7
  PLACEHOLDER_REGEX,
8
8
  } from "./constants";
9
- import { validateUrl } from "./validateURL";
9
+ import { validateUrl, urlHasHost } from "./validateURL";
10
10
  import type {
11
11
  InvalidRedirectRule,
12
12
  ParsedRedirects,
@@ -104,7 +104,20 @@ export function parseRedirects(input: string): ParsedRedirects {
104
104
  invalid.push({
105
105
  line,
106
106
  lineNumber: i + 1,
107
- message: `Valid status codes are 301, 302 (default), 303, 307, or 308. Got ${str_status}.`,
107
+ message: `Valid status codes are 200, 301, 302 (default), 303, 307, or 308. Got ${str_status}.`,
108
+ });
109
+ continue;
110
+ }
111
+
112
+ // We want to always block the `/* /index.html` redirect - this will cause TOO_MANY_REDIRECTS errors as
113
+ // the asset server will redirect it back to `/`, removing the `/index.html`. This is the case for regular
114
+ // redirects, as well as proxied (200) rewrites. We only want to run this on relative urls
115
+ if (/\/\*?$/.test(from) && /\/index(.html)?$/.test(to) && !urlHasHost(to)) {
116
+ invalid.push({
117
+ line,
118
+ lineNumber: i + 1,
119
+ message:
120
+ "Infinite loop detected in this rule and has been ignored. This will cause a redirect to strip `.html` or `/index` and end up triggering this rule again. Please fix or remove this rule to silence this warning.",
108
121
  });
109
122
  continue;
110
123
  }
@@ -119,6 +132,17 @@ export function parseRedirects(input: string): ParsedRedirects {
119
132
  }
120
133
  seen_paths.add(from);
121
134
 
135
+ if (status === 200) {
136
+ if (urlHasHost(to)) {
137
+ invalid.push({
138
+ line,
139
+ lineNumber: i + 1,
140
+ message: `Proxy (200) redirects can only point to relative paths. Got ${to}`,
141
+ });
142
+ continue;
143
+ }
144
+ }
145
+
122
146
  rules.push({ from, to, status, lineNumber: i + 1 });
123
147
  }
124
148
 
@@ -55,3 +55,8 @@ export const validateUrl = (
55
55
  : 'URLs should either be relative (e.g. begin with a forward-slash), or use HTTPS (e.g. begin with "https://").',
56
56
  ];
57
57
  };
58
+
59
+ export function urlHasHost(token: string): boolean {
60
+ const host = URL_REGEX.exec(token);
61
+ return Boolean(host && host.groups && host.groups.host);
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/pages-shared",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cloudflare/workers-sdk.git",