mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 04:22:07 +00:00
[dev] allow middleware rewrites to change origin in vc dev (#8457)
Middleware rewrites in `vc dev` were not respecting changes to the origin. This PR implements that to match prodution. --- Paired with: @TooTallNate
This commit is contained in:
@@ -101,6 +101,7 @@ import {
|
|||||||
isError,
|
isError,
|
||||||
isSpawnError,
|
isSpawnError,
|
||||||
} from '../is-error';
|
} from '../is-error';
|
||||||
|
import isURL from './is-url';
|
||||||
import { pickOverrides } from '../projects/project-settings';
|
import { pickOverrides } from '../projects/project-settings';
|
||||||
|
|
||||||
const frontendRuntimeSet = new Set(
|
const frontendRuntimeSet = new Set(
|
||||||
@@ -180,11 +181,17 @@ export default class DevServer {
|
|||||||
this.caseSensitive = false;
|
this.caseSensitive = false;
|
||||||
this.apiDir = null;
|
this.apiDir = null;
|
||||||
this.apiExtensions = new Set();
|
this.apiExtensions = new Set();
|
||||||
|
|
||||||
this.proxy = httpProxy.createProxyServer({
|
this.proxy = httpProxy.createProxyServer({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
xfwd: true,
|
xfwd: true,
|
||||||
});
|
});
|
||||||
|
this.proxy.on('proxyRes', proxyRes => {
|
||||||
|
// override "server" header, like production
|
||||||
|
proxyRes.headers['server'] = 'Vercel';
|
||||||
|
});
|
||||||
|
|
||||||
this.server = http.createServer(this.devServerHandler);
|
this.server = http.createServer(this.devServerHandler);
|
||||||
this.server.timeout = 0; // Disable timeout
|
this.server.timeout = 0; // Disable timeout
|
||||||
this.stopping = false;
|
this.stopping = false;
|
||||||
@@ -1560,15 +1567,33 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rewritePath) {
|
if (rewritePath) {
|
||||||
// TODO: add validation?
|
|
||||||
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
|
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
|
||||||
prevUrl = rewritePath;
|
prevUrl = rewritePath;
|
||||||
|
|
||||||
// Retain orginal pathname, but override query parameters from the rewrite
|
|
||||||
const beforeRewriteUrl = req.url || '/';
|
const beforeRewriteUrl = req.url || '/';
|
||||||
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
|
|
||||||
rewriteUrlParsed.search = url.parse(rewritePath).search;
|
if (isURL(rewritePath)) {
|
||||||
req.url = url.format(rewriteUrlParsed);
|
const rewriteUrlParsed = new URL(rewritePath);
|
||||||
|
|
||||||
|
// `this.address` already has localhost normalized from ip4 and ip6 values
|
||||||
|
const devServerParsed = new URL(this.address);
|
||||||
|
if (devServerParsed.origin === rewriteUrlParsed.origin) {
|
||||||
|
// remove origin, leaving the path
|
||||||
|
req.url = rewritePath.slice(rewriteUrlParsed.origin.length);
|
||||||
|
} else {
|
||||||
|
// Proxy to absolute URL with different origin
|
||||||
|
debug(`ProxyPass: ${rewritePath}`);
|
||||||
|
this.setResponseHeaders(res, requestId);
|
||||||
|
proxyPass(req, res, rewritePath, this, requestId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Retain orginal pathname, but override query parameters from the rewrite
|
||||||
|
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
|
||||||
|
rewriteUrlParsed.search = url.parse(rewritePath).search;
|
||||||
|
req.url = url.format(rewriteUrlParsed);
|
||||||
|
}
|
||||||
|
|
||||||
debug(
|
debug(
|
||||||
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
|
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
export default req => {
|
export default req => {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
const rewriteTo = url.searchParams.get('to');
|
||||||
|
if (rewriteTo) {
|
||||||
|
return new Response(null, {
|
||||||
|
headers: {
|
||||||
|
'x-middleware-rewrite': rewriteTo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (url.pathname === '/') {
|
if (url.pathname === '/') {
|
||||||
// Pass-through "index.html" page
|
// Pass-through "index.html" page
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
|
|||||||
@@ -457,9 +457,42 @@ test(
|
|||||||
await testPath(200, '/another', '<h1>Another</h1>');
|
await testPath(200, '/another', '<h1>Another</h1>');
|
||||||
await testPath(200, '/another.html', '<h1>Another</h1>');
|
await testPath(200, '/another.html', '<h1>Another</h1>');
|
||||||
await testPath(200, '/foo', '<h1>Another</h1>');
|
await testPath(200, '/foo', '<h1>Another</h1>');
|
||||||
|
// different origin
|
||||||
|
await testPath(200, '?to=http://example.com', /Example Domain/);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('[vercel dev] Middleware rewrites with same origin', async () => {
|
||||||
|
const directory = fixture('middleware-rewrite');
|
||||||
|
const { dev, port, readyResolver } = await testFixture(directory);
|
||||||
|
|
||||||
|
try {
|
||||||
|
dev.unref();
|
||||||
|
await readyResolver;
|
||||||
|
|
||||||
|
let response = await fetch(
|
||||||
|
`http://localhost:${port}?to=http://localhost:${port}`
|
||||||
|
);
|
||||||
|
validateResponseHeaders(response);
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
`http://localhost:${port}?to=http://127.0.0.1:${port}`
|
||||||
|
);
|
||||||
|
validateResponseHeaders(response);
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
|
||||||
|
|
||||||
|
response = await fetch(`http://localhost:${port}?to=http://[::1]:${port}`);
|
||||||
|
validateResponseHeaders(response);
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
|
||||||
|
} finally {
|
||||||
|
await dev.kill('SIGTERM');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'[vercel dev] Middleware that rewrites with custom query params',
|
'[vercel dev] Middleware that rewrites with custom query params',
|
||||||
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
|
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user