Skip to content

Commit 48f5e83

Browse files
authored
Fix external rewrite (#299)
* fix rewrites not working * fix middleware response next 14
1 parent ea5e302 commit 48f5e83

File tree

4 files changed

+63
-24
lines changed

4 files changed

+63
-24
lines changed

packages/open-next/src/adapters/plugins/routing/default.replacement.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,16 @@ export const processInternalEvent: ProcessInternalEvent = async (
5959
internalEvent = middleware;
6060
}
6161

62-
let isExternalRewrite = false;
63-
// First rewrite to be applied
64-
const beforeRewrites = handleRewrites(
65-
internalEvent,
66-
RoutesManifest.rewrites.beforeFiles,
67-
);
68-
internalEvent = beforeRewrites.internalEvent;
69-
isExternalRewrite = beforeRewrites.isExternalRewrite;
70-
62+
let isExternalRewrite = middleware.externalRewrite ?? false;
63+
if (!isExternalRewrite) {
64+
// First rewrite to be applied
65+
const beforeRewrites = handleRewrites(
66+
internalEvent,
67+
RoutesManifest.rewrites.beforeFiles,
68+
);
69+
internalEvent = beforeRewrites.internalEvent;
70+
isExternalRewrite = beforeRewrites.isExternalRewrite;
71+
}
7172
const isStaticRoute = RoutesManifest.routes.static.some((route) =>
7273
new RegExp(route.regex).test(event.rawPath),
7374
);

packages/open-next/src/adapters/plugins/routing/util.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
22
import crypto from "crypto";
3+
import { ServerResponse } from "http";
34

45
import { BuildId, HtmlPages } from "../../config/index.js";
56
import { IncomingMessage } from "../../http/request.js";
@@ -36,14 +37,28 @@ export async function proxyRequest(
3637
});
3738

3839
await new Promise<void>((resolve, reject) => {
39-
proxy.on("proxyRes", () => {
40-
resolve();
40+
proxy.on("proxyRes", (proxyRes: ServerResponse) => {
41+
const body: Uint8Array[] = [];
42+
proxyRes.on("data", function (chunk) {
43+
body.push(chunk);
44+
});
45+
proxyRes.on("end", function () {
46+
const newBody = Buffer.concat(body).toString();
47+
debug(`Proxying response`, {
48+
headers: proxyRes.getHeaders(),
49+
body: newBody,
50+
});
51+
res.end(newBody);
52+
resolve();
53+
});
4154
});
4255

4356
proxy.on("error", (err: any) => {
4457
reject(err);
4558
});
4659

60+
debug(`Proxying`, { url: req.url, headers: req.headers });
61+
4762
proxy.web(req, res, {
4863
target: req.url,
4964
headers: req.headers,

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

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { ServerlessResponse } from "../http/response.js";
77
import {
88
convertRes,
99
getMiddlewareMatch,
10+
isExternal,
1011
loadMiddlewareManifest,
1112
} from "./util.js";
1213

1314
const middlewareManifest = loadMiddlewareManifest(NEXT_DIR);
1415

16+
//NOTE: we should try to avoid importing stuff from next as much as possible
17+
// every release of next could break this
1518
const { run } = require("next/dist/server/web/sandbox");
16-
const { pipeReadable } = require("next/dist/server/pipe-readable");
1719
const { getCloneableBody } = require("next/dist/server/body-streams");
1820
const {
1921
signalFromNodeResponse,
@@ -23,13 +25,18 @@ const middleMatch = getMiddlewareMatch(middlewareManifest);
2325

2426
type MiddlewareOutputEvent = InternalEvent & {
2527
responseHeaders?: Record<string, string | string[]>;
28+
externalRewrite?: boolean;
2629
};
2730

2831
// NOTE: As of Nextjs 13.4.13+, the middleware is handled outside the next-server.
2932
// OpenNext will run the middleware in a sandbox and set the appropriate req headers
3033
// and res.body prior to processing the next-server.
3134
// @returns undefined | res.end()
3235

36+
interface MiddlewareResult {
37+
response: Response;
38+
}
39+
3340
// if res.end() is return, the parent needs to return and not process next server
3441
export async function handleMiddleware(
3542
internalEvent: InternalEvent,
@@ -65,7 +72,7 @@ export async function handleMiddleware(
6572
initialUrl.search = new URLSearchParams(urlQuery).toString();
6673
const url = initialUrl.toString();
6774

68-
const result = await run({
75+
const result: MiddlewareResult = await run({
6976
distDir: NEXT_DIR,
7077
name: middlewareInfo.name || "/",
7178
paths: middlewareInfo.paths || [],
@@ -129,7 +136,7 @@ export async function handleMiddleware(
129136
statusCode: res.statusCode,
130137
headers: {
131138
...resHeaders,
132-
Location: location,
139+
Location: location ?? "",
133140
},
134141
isBase64Encoded: false,
135142
};
@@ -139,22 +146,34 @@ export async function handleMiddleware(
139146
// NOTE: the header was added to `req` from above
140147
const rewriteUrl = responseHeaders.get("x-middleware-rewrite");
141148
let rewritten = false;
149+
let externalRewrite = false;
142150
let middlewareQueryString = internalEvent.query;
143151
if (rewriteUrl) {
144-
const rewriteUrlObject = new URL(rewriteUrl);
145-
req.url = rewriteUrlObject.pathname;
146-
//reset qs
147-
middlewareQueryString = {};
148-
rewriteUrlObject.searchParams.forEach((v: string, k: string) => {
149-
middlewareQueryString[k] = v;
150-
});
151-
rewritten = true;
152+
if (isExternal(rewriteUrl, req.headers.host)) {
153+
req.url = rewriteUrl;
154+
rewritten = true;
155+
externalRewrite = true;
156+
} else {
157+
const rewriteUrlObject = new URL(rewriteUrl);
158+
req.url = rewriteUrlObject.pathname;
159+
//reset qs
160+
middlewareQueryString = {};
161+
rewriteUrlObject.searchParams.forEach((v: string, k: string) => {
162+
middlewareQueryString[k] = v;
163+
});
164+
rewritten = true;
165+
}
152166
}
153167

154168
// If the middleware returned a `NextResponse`, pipe the body to res. This will return
155169
// the body immediately to the client.
156170
if (result.response.body) {
157-
await pipeReadable(result.response.body, res);
171+
// transfer response body to res
172+
const arrayBuffer = await result.response.arrayBuffer();
173+
const buffer = Buffer.from(arrayBuffer);
174+
res.end(buffer);
175+
176+
// await pipeReadable(result.response.body, res);
158177
return {
159178
type: internalEvent.type,
160179
...convertRes(res),
@@ -174,5 +193,6 @@ export async function handleMiddleware(
174193
query: middlewareQueryString,
175194
cookies: internalEvent.cookies,
176195
remoteAddress: internalEvent.remoteAddress,
196+
externalRewrite,
177197
};
178198
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { isBinaryContentType } from "../binary";
55
import { ServerlessResponse } from "../http/response";
66
import { MiddlewareManifest } from "../types/next-types";
77

8-
export function isExternal(url?: string) {
8+
export function isExternal(url?: string, host?: string) {
99
if (!url) return false;
1010
const pattern = /^https?:\/\//;
11+
if (host) {
12+
return pattern.test(url) && !url.includes(host);
13+
}
1114
return pattern.test(url);
1215
}
1316

0 commit comments

Comments
 (0)