Skip to content

Commit 918e651

Browse files
committed
refactor: internalEvent.url is a full URL
1 parent 674dce6 commit 918e651

File tree

19 files changed

+114
-148
lines changed

19 files changed

+114
-148
lines changed

packages/open-next/src/adapters/edge-adapter.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import type { OpenNextHandlerOptions } from "types/overrides";
88
// We import it like that so that the edge plugin can replace it
99
import { NextConfig } from "../adapters/config";
1010
import { createGenericHandler } from "../core/createGenericHandler";
11-
import {
12-
convertBodyToReadableStream,
13-
convertToQueryString,
14-
} from "../core/routing/util";
11+
import { convertBodyToReadableStream } from "../core/routing/util";
1512

1613
globalThis.__openNextAls = new AsyncLocalStorage();
1714

@@ -25,13 +22,6 @@ const defaultHandler = async (
2522
return runWithOpenNextRequestContext(
2623
{ isISRRevalidation: false, waitUntil: options?.waitUntil },
2724
async () => {
28-
const host = internalEvent.headers.host
29-
? `https://${internalEvent.headers.host}`
30-
: "http://localhost:3000";
31-
const initialUrl = new URL(internalEvent.rawPath, host);
32-
initialUrl.search = convertToQueryString(internalEvent.query);
33-
const url = initialUrl.toString();
34-
3525
// @ts-expect-error - This is bundled
3626
const handler = await import("./middleware.mjs");
3727

@@ -43,7 +33,7 @@ const defaultHandler = async (
4333
i18n: NextConfig.i18n,
4434
trailingSlash: NextConfig.trailingSlash,
4535
},
46-
url,
36+
url: internalEvent.url,
4737
body: convertBodyToReadableStream(
4838
internalEvent.method,
4939
internalEvent.body,

packages/open-next/src/adapters/middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const defaultHandler = async (
9090
internalEvent: {
9191
...result.internalEvent,
9292
rawPath: "/500",
93-
url: "/500",
93+
url: new URL("/500", new URL(result.internalEvent.url)).href,
9494
method: "GET",
9595
},
9696
// On error we need to rewrite to the 500 page which is an internal rewrite

packages/open-next/src/core/requestHandler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export async function openNextHandler(
102102
rawPath: "/500",
103103
method: "GET",
104104
headers: {},
105-
url: "/500",
105+
url: new URL("/500", new URL(internalEvent.url)).href,
106106
query: {},
107107
cookies: {},
108108
remoteAddress: "",
@@ -146,12 +146,13 @@ export async function openNextHandler(
146146

147147
const preprocessedEvent = routingResult.internalEvent;
148148
debug("preprocessedEvent", preprocessedEvent);
149+
const { search, pathname, hash } = new URL(preprocessedEvent.url);
149150
const reqProps = {
150151
method: preprocessedEvent.method,
151-
url: preprocessedEvent.url,
152+
url: `${pathname}${search}${hash}`,
152153
//WORKAROUND: We pass this header to the serverless function to mimic a prefetch request which will not trigger revalidation since we handle revalidation differently
153154
// There is 3 way we can handle revalidation:
154-
// 1. We could just let the revalidation go as normal, but due to race condtions the revalidation will be unreliable
155+
// 1. We could just let the revalidation go as normal, but due to race conditions the revalidation will be unreliable
155156
// 2. We could alter the lastModified time of our cache to make next believe that the cache is fresh, but this could cause issues with stale data since the cdn will cache the stale data as if it was fresh
156157
// 3. OUR CHOICE: We could pass a purpose prefetch header to the serverless function to make next believe that the request is a prefetch request and not trigger revalidation (This could potentially break in the future if next changes the behavior of prefetch requests)
157158
headers: { ...headers, purpose: "prefetch" },

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,10 @@ export function handleRewrites<T extends RewriteDefinition>(
245245
internalEvent: {
246246
...event,
247247
rawPath: rewrittenUrl,
248-
url: `${rewrittenUrl}${convertToQueryString(finalQuery)}`,
248+
url: new URL(
249+
`${rewrittenUrl}${convertToQueryString(finalQuery)}`,
250+
new URL(event.url),
251+
).href,
249252
},
250253
__rewrite: rewrite,
251254
isExternalRewrite,
@@ -365,7 +368,10 @@ export function fixDataPage(
365368
...internalEvent,
366369
rawPath: newPath,
367370
query,
368-
url: `${newPath}${convertToQueryString(query)}`,
371+
url: new URL(
372+
`${newPath}${convertToQueryString(query)}`,
373+
new URL(internalEvent.url),
374+
).href,
369375
};
370376
}
371377
return internalEvent;
@@ -397,7 +403,7 @@ export function handleFallbackFalse(
397403
event: {
398404
...internalEvent,
399405
rawPath: "/404",
400-
url: "/404",
406+
url: new URL("/404", new URL(internalEvent.url)).href,
401407
headers: {
402408
...internalEvent.headers,
403409
"x-invoke-status": "404",

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

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,9 @@ export async function handleMiddleware(
5656
const hasMatch = middleMatch.some((r) => r.test(normalizedPath));
5757
if (!hasMatch) return internalEvent;
5858

59-
// Retrieve the protocol:
60-
// - In lambda, the url only contains the rawPath and the query - default to https
61-
// - In cloudflare, the protocol is usually http in dev and https in production
62-
const protocol = internalEvent.url.startsWith("http://") ? "http:" : "https:";
63-
64-
const host = headers.host
65-
? `${protocol}//${headers.host}`
66-
: "http://localhost:3000";
67-
68-
const initialUrl = new URL(normalizedPath, host);
59+
const initialUrl = new URL(normalizedPath, new URL(internalEvent.url));
6960
initialUrl.search = convertToQueryString(internalEvent.query);
70-
const url = initialUrl.toString();
61+
const url = initialUrl.href;
7162

7263
const middleware = await middlewareLoader();
7364

@@ -131,12 +122,7 @@ export async function handleMiddleware(
131122
// the redirected url and end the response.
132123
if (statusCode >= 300 && statusCode < 400) {
133124
resHeaders.location =
134-
responseHeaders
135-
.get("location")
136-
?.replace(
137-
"http://localhost:3000",
138-
`${protocol}//${internalEvent.headers.host}`,
139-
) ?? resHeaders.location;
125+
responseHeaders.get("location") ?? resHeaders.location;
140126
// res.setHeader("Location", location);
141127
return {
142128
body: emptyReadableStream(),
@@ -162,7 +148,7 @@ export async function handleMiddleware(
162148
isExternalRewrite = true;
163149
} else {
164150
const rewriteUrlObject = new URL(rewriteUrl);
165-
newUrl = rewriteUrlObject.pathname;
151+
newUrl = rewriteUrlObject.href;
166152

167153
// Reset the query params if the middleware is a rewrite
168154
if (middlewareQueryString.__nextDataReq) {
@@ -199,7 +185,9 @@ export async function handleMiddleware(
199185
responseHeaders: resHeaders,
200186
url: newUrl,
201187
rawPath: rewritten
202-
? (newUrl ?? internalEvent.rawPath)
188+
? newUrl
189+
? new URL(newUrl).pathname
190+
: internalEvent.rawPath
203191
: internalEvent.rawPath,
204192
type: internalEvent.type,
205193
headers: { ...internalEvent.headers, ...reqHeaders },

packages/open-next/src/core/routingHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export default async function routingHandler(
174174
internalEvent = {
175175
...internalEvent,
176176
rawPath: "/404",
177-
url: "/404",
177+
url: new URL("/404", new URL(internalEvent.url)).href,
178178
headers: {
179179
...internalEvent.headers,
180180
"x-middleware-response-cache-control":

packages/open-next/src/overrides/converters/aws-apigw-v1.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ async function convertFromAPIGatewayProxyEvent(
5757
event: APIGatewayProxyEvent,
5858
): Promise<InternalEvent> {
5959
const { path, body, httpMethod, requestContext, isBase64Encoded } = event;
60+
const headers = normalizeAPIGatewayProxyEventHeaders(event);
6061
return {
6162
type: "core",
6263
method: httpMethod,
6364
rawPath: path,
64-
url: path + normalizeAPIGatewayProxyEventQueryParams(event),
65+
url: `https://${headers.host ?? "on"}${path}${normalizeAPIGatewayProxyEventQueryParams(event)}`,
6566
body: Buffer.from(body ?? "", isBase64Encoded ? "base64" : "utf8"),
66-
headers: normalizeAPIGatewayProxyEventHeaders(event),
67+
headers,
6768
remoteAddress: requestContext.identity.sourceIp,
6869
query: removeUndefinedFromQuery(
6970
event.multiValueQueryStringParameters ?? {},

packages/open-next/src/overrides/converters/aws-apigw-v2.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,14 @@ async function convertFromAPIGatewayProxyEventV2(
7575
event: APIGatewayProxyEventV2,
7676
): Promise<InternalEvent> {
7777
const { rawPath, rawQueryString, requestContext } = event;
78+
const headers = normalizeAPIGatewayProxyEventV2Headers(event);
7879
return {
7980
type: "core",
8081
method: requestContext.http.method,
8182
rawPath,
82-
url: rawPath + (rawQueryString ? `?${rawQueryString}` : ""),
83+
url: `https://${headers.host ?? "on"}${rawPath}${rawQueryString ? `?${rawQueryString}` : ""}`,
8384
body: normalizeAPIGatewayProxyEventV2Body(event),
84-
headers: normalizeAPIGatewayProxyEventV2Headers(event),
85+
headers,
8586
remoteAddress: requestContext.http.sourceIp,
8687
query: removeUndefinedFromQuery(convertToQuery(rawQueryString)),
8788
cookies:

packages/open-next/src/overrides/converters/aws-cloudfront.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,29 @@ function normalizeCloudFrontRequestEventHeaders(
8282
async function convertFromCloudFrontRequestEvent(
8383
event: CloudFrontRequestEvent,
8484
): Promise<InternalEvent> {
85-
const { method, uri, querystring, body, headers, clientIp } =
86-
event.Records[0].cf.request;
87-
85+
const {
86+
method,
87+
uri,
88+
querystring,
89+
body,
90+
headers: cfHeaders,
91+
clientIp,
92+
} = event.Records[0].cf.request;
93+
const headers = normalizeCloudFrontRequestEventHeaders(cfHeaders);
8894
return {
8995
type: "core",
9096
method,
9197
rawPath: uri,
92-
url: uri + (querystring ? `?${querystring}` : ""),
98+
url: `https://${headers.host ?? "on"}${uri}${querystring ? `?${querystring}` : ""}`,
9399
body: Buffer.from(
94100
body?.data ?? "",
95101
body?.encoding === "base64" ? "base64" : "utf8",
96102
),
97-
headers: normalizeCloudFrontRequestEventHeaders(headers),
103+
headers,
98104
remoteAddress: clientIp,
99105
query: convertToQuery(querystring),
100106
cookies:
101-
headers.cookie?.reduce(
107+
cfHeaders.cookie?.reduce(
102108
(acc, cur) => {
103109
const { key = "", value } = cur;
104110
acc[key] = value;

packages/open-next/src/overrides/converters/edge.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,7 @@ const converter: Converter<InternalEvent, InternalResult | MiddlewareResult> = {
5959
},
6060
convertTo: async (result) => {
6161
if ("internalEvent" in result) {
62-
let url = result.internalEvent.url;
63-
if (!result.isExternalRewrite) {
64-
if (result.origin) {
65-
url = `${result.origin.protocol}://${result.origin.host}${
66-
result.origin.port ? `:${result.origin.port}` : ""
67-
}${url}`;
68-
} else {
69-
url = `https://${result.internalEvent.headers.host}${url}`;
70-
}
71-
}
72-
73-
const request = new Request(url, {
62+
const request = new Request(result.internalEvent.url, {
7463
body: result.internalEvent.body,
7564
method: result.internalEvent.method,
7665
headers: {

packages/open-next/src/overrides/converters/node.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ const converter: Converter = {
1616
});
1717
});
1818

19-
const url = new URL(req.url!, `http://${req.headers.host}`);
19+
const url = new URL(req.url!, `http://${req.headers.host ?? "on"}`);
2020
const query = Object.fromEntries(url.searchParams.entries());
2121
return {
2222
type: "core",
2323
method: req.method ?? "GET",
2424
rawPath: url.pathname,
25-
url: url.pathname + url.search,
25+
url: url.href,
2626
body,
2727
headers: Object.fromEntries(
2828
Object.entries(req.headers ?? {})

packages/open-next/src/overrides/wrappers/cloudflare-node.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,7 @@ const handler: WrapperHandler<InternalEvent, InternalResult> =
2828
}
2929

3030
const internalEvent = await converter.convertFrom(request);
31-
32-
// TODO:
33-
// The edge converter populate event.url with the url including the origin.
34-
// This is required for middleware to keep track of the protocol (i.e. http with wrangler dev).
35-
// However the server expects that the origin is not included.
36-
const url = new URL(internalEvent.url);
37-
(internalEvent.url as string) = url.href.slice(url.origin.length);
31+
const url = new URL(request.url);
3832

3933
const { promise: promiseResponse, resolve: resolveResponse } =
4034
Promise.withResolvers<Response>();

packages/open-next/src/types/open-next.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type BaseEventOrResult<T extends string = string> = {
2222
export type InternalEvent = {
2323
readonly method: string;
2424
readonly rawPath: string;
25+
// Full URL - starts with "https://on/" when the host is not available
2526
readonly url: string;
2627
readonly body?: Buffer;
2728
readonly headers: Record<string, string>;

packages/tests-unit/tests/converters/aws-apigw-v1.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe("convertFrom", () => {
8787
type: "core",
8888
method: "POST",
8989
rawPath: "/",
90-
url: "/",
90+
url: "https://on/",
9191
body: Buffer.from('{"message":"Hello, world!"}'),
9292
headers: {
9393
"content-type": "application/json",
@@ -126,7 +126,7 @@ describe("convertFrom", () => {
126126
type: "core",
127127
method: "POST",
128128
rawPath: "/",
129-
url: "/",
129+
url: "https://on/",
130130
body: Buffer.from('{"message":"Hello, world!"}'),
131131
headers: {
132132
test: "test1,test2",
@@ -167,7 +167,7 @@ describe("convertFrom", () => {
167167
type: "core",
168168
method: "POST",
169169
rawPath: "/",
170-
url: "/?test=test",
170+
url: "https://on/?test=test",
171171
body: Buffer.from('{"message":"Hello, world!"}'),
172172
headers: {},
173173
remoteAddress: "::1",
@@ -208,7 +208,7 @@ describe("convertFrom", () => {
208208
type: "core",
209209
method: "POST",
210210
rawPath: "/",
211-
url: "/",
211+
url: "https://on/",
212212
body: Buffer.from('{"message":"Hello, world!"}'),
213213
headers: {
214214
"content-type": "application/json",
@@ -251,7 +251,7 @@ describe("convertFrom", () => {
251251
type: "core",
252252
method: "GET",
253253
rawPath: "/",
254-
url: "/",
254+
url: "https://on/",
255255
body: Buffer.from("Hello, world!"),
256256
headers: {
257257
"content-type": "application/json",

packages/tests-unit/tests/converters/aws-apigw-v2.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ describe("convertFrom", () => {
126126
type: "core",
127127
method: "POST",
128128
rawPath: "/",
129-
url: "/",
129+
url: "https://on/",
130130
body: Buffer.from('{"message":"Hello, world!"}'),
131131
headers: {
132132
"content-type": "application/json",
@@ -163,7 +163,7 @@ describe("convertFrom", () => {
163163
type: "core",
164164
method: "POST",
165165
rawPath: "/",
166-
url: "/",
166+
url: "https://on/",
167167
body: Buffer.from('{"message":"Hello, world!"}'),
168168
headers: {
169169
"content-type": "application/json",
@@ -204,7 +204,7 @@ describe("convertFrom", () => {
204204
type: "core",
205205
method: "POST",
206206
rawPath: "/",
207-
url: "/?hello=world&foo=1&foo=2",
207+
url: "https://on/?hello=world&foo=1&foo=2",
208208
body: Buffer.from('{"message":"Hello, world!"}'),
209209
headers: {
210210
"content-type": "application/json",
@@ -246,7 +246,7 @@ describe("convertFrom", () => {
246246
type: "core",
247247
method: "POST",
248248
rawPath: "/",
249-
url: "/",
249+
url: "https://on/",
250250
body: Buffer.from('{"message":"Hello, world!"}'),
251251
headers: {
252252
"content-type": "application/json",

0 commit comments

Comments
 (0)