Skip to content

Commit bfa1a8c

Browse files
authored
Fix external redirect trailing (#598)
* Fix External redirects issues without trailing slash #591 * fix some incorrect unit test redirect * Create fuzzy-camels-check.md
1 parent 4ffd8dd commit bfa1a8c

File tree

5 files changed

+46
-13
lines changed

5 files changed

+46
-13
lines changed

.changeset/fuzzy-camels-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
Fix external redirect trailing

packages/open-next/src/core/routing/matcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export function handleRewrites<T extends RewriteDefinition>(
229229
}
230230
rewrittenUrl = isExternalRewrite
231231
? `${protocol}//${rewrittenHost}${rewrittenPath}`
232-
: `/${rewrittenPath}`;
232+
: `${rewrittenPath}`;
233233
// Should we merge the query params or use only the ones from the rewrite?
234234
finalQuery = {
235235
...query,

packages/open-next/src/core/routing/util.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function getUrlParts(url: string, isExternal: boolean) {
4747
const match = url.match(regex);
4848
return {
4949
hostname: "",
50-
pathname: match?.[1] ?? url,
50+
pathname: match?.[1] ? `/${match[1]}` : url,
5151
protocol: "",
5252
queryString: match?.[2] ?? "",
5353
};
@@ -61,7 +61,7 @@ export function getUrlParts(url: string, isExternal: boolean) {
6161
return {
6262
protocol: match[1] ?? "https:",
6363
hostname: match[2],
64-
pathname: match[3],
64+
pathname: match[3] ?? "",
6565
queryString: match[4]?.slice(1) ?? "",
6666
};
6767
}

packages/tests-unit/tests/core/routing/matcher.test.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,31 @@ describe("handleRedirects", () => {
240240
{
241241
source: "/:path+",
242242
destination: "/new/:path+",
243-
internal: true,
243+
locale: false,
244244
statusCode: 308,
245-
regex: "^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))/$",
245+
regex: "^(?!/_next)(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))(?:/)?$",
246246
},
247247
]);
248248

249-
expect(result).toBeUndefined();
249+
expect(result.headers.Location).toBe("/new/api-route");
250+
});
251+
252+
it("should redirect matching nested path", () => {
253+
const event = createEvent({
254+
url: "/api-route/secret",
255+
});
256+
257+
const result = handleRedirects(event, [
258+
{
259+
source: "/:path+",
260+
destination: "/new/:path+",
261+
locale: false,
262+
statusCode: 308,
263+
regex: "^(?!/_next)(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))(?:/)?$",
264+
},
265+
]);
266+
267+
expect(result.headers.Location).toBe("/new/api-route/secret");
250268
});
251269

252270
it("should not redirect unmatched path", () => {
@@ -258,9 +276,9 @@ describe("handleRedirects", () => {
258276
{
259277
source: "/foo/",
260278
destination: "/bar",
261-
internal: true,
262-
statusCode: 308,
263-
regex: "^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))/$",
279+
locale: false,
280+
statusCode: 307,
281+
regex: "^(?!/_next)/foo/(?:/)?$",
264282
},
265283
]);
266284

packages/tests-unit/tests/core/routing/util.test.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ describe("getUrlParts", () => {
119119
it("returns url parts for /", () => {
120120
expect(getUrlParts("/", false)).toEqual({
121121
hostname: "",
122-
pathname: "", // TODO: This behaviour is inconsistent with external pathname
122+
pathname: "/",
123123
protocol: "",
124124
queryString: "",
125125
});
@@ -128,16 +128,16 @@ describe("getUrlParts", () => {
128128
it("returns url parts", () => {
129129
expect(getUrlParts("/relative", false)).toEqual({
130130
hostname: "",
131-
pathname: "relative",
132-
protocol: "", // TODO: This behaviour is inconsistent with external pathname
131+
pathname: "/relative",
132+
protocol: "",
133133
queryString: "",
134134
});
135135
});
136136

137137
it("returns url parts with query string", () => {
138138
expect(getUrlParts("/relative/path?query=1", false)).toEqual({
139139
hostname: "",
140-
pathname: "relative/path", // TODO: This behaviour is inconsistent with external pathname
140+
pathname: "/relative/path",
141141
protocol: "",
142142
queryString: "query=1",
143143
});
@@ -162,6 +162,16 @@ describe("getUrlParts", () => {
162162
});
163163
});
164164

165+
// For reference https://github.com/opennextjs/opennextjs-aws/issues/591
166+
it("returns url parts for / without trailing slash", () => {
167+
expect(getUrlParts("http://localhost", true)).toEqual({
168+
hostname: "localhost",
169+
pathname: "",
170+
protocol: "http:",
171+
queryString: "",
172+
});
173+
});
174+
165175
it("returns url parts", () => {
166176
expect(getUrlParts("https://localhost/relative", true)).toEqual({
167177
hostname: "localhost",

0 commit comments

Comments
 (0)