Skip to content

Commit 58ed112

Browse files
committed
fix: improve URL handling in getRequestURL function
1 parent 7c658fe commit 58ed112

File tree

2 files changed

+91
-3
lines changed

2 files changed

+91
-3
lines changed

src/utils.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,27 @@ function getRequestURL(req) {
312312
if (typeof req.getURL === "function") {
313313
return req.getURL();
314314
}
315-
// Fastify decodes URI by default, Our logic is based on encoded URI
316-
else if (typeof req.originalUrl !== "undefined") {
317-
return req.originalUrl;
315+
316+
// Fastify decodes URI by default, our logic is based on encoded URI.
317+
// req.originalUrl preserves the original encoded URL; req.url may be
318+
// modified by middleware (e.g. connect-history-api-fallback), in which
319+
// case we use req.url instead.
320+
if (typeof req.originalUrl !== "undefined") {
321+
// If req.url is just the decoded form of req.originalUrl (Fastify behavior),
322+
// return the original encoded URL. Otherwise middleware modified req.url.
323+
try {
324+
if (
325+
req.url === req.originalUrl ||
326+
(typeof req.url !== "undefined" &&
327+
decodeURI(req.originalUrl) === req.url)
328+
) {
329+
return req.originalUrl;
330+
}
331+
} catch {
332+
// decodeURI can throw on malformed sequences, fall through
333+
}
334+
335+
return req.url;
318336
}
319337

320338
return req.url;

test/middleware.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3473,6 +3473,76 @@ describe.each([
34733473
});
34743474
});
34753475

3476+
if (!["hapi", "hono"].includes(name)) {
3477+
describe("should work when req.url is modified by middleware to a file with encoded characters", () => {
3478+
let compiler;
3479+
3480+
const outputPath = path.resolve(
3481+
__dirname,
3482+
"./outputs/basic-test-modified-url-encoded",
3483+
);
3484+
3485+
beforeAll(async () => {
3486+
compiler = getCompiler({
3487+
...webpackConfig,
3488+
output: {
3489+
filename: "bundle.js",
3490+
path: outputPath,
3491+
},
3492+
});
3493+
3494+
[server, req, instance] = await frameworkFactory(
3495+
name,
3496+
framework,
3497+
compiler,
3498+
{},
3499+
{
3500+
setupMiddlewares: (middlewares) => {
3501+
if (name === "koa") {
3502+
middlewares.unshift(async (ctx, next) => {
3503+
ctx.url = "/file with spaces.html";
3504+
3505+
await next();
3506+
});
3507+
} else {
3508+
middlewares.unshift({
3509+
route: "/",
3510+
fn: (oldReq, res, next) => {
3511+
oldReq.url = "/file with spaces.html";
3512+
next();
3513+
},
3514+
});
3515+
}
3516+
3517+
return middlewares;
3518+
},
3519+
},
3520+
);
3521+
3522+
instance.context.outputFileSystem.mkdirSync(outputPath, {
3523+
recursive: true,
3524+
});
3525+
instance.context.outputFileSystem.writeFileSync(
3526+
path.resolve(outputPath, "file with spaces.html"),
3527+
"HTML with spaces",
3528+
);
3529+
});
3530+
3531+
afterAll(async () => {
3532+
await close(server, instance);
3533+
});
3534+
3535+
it('should return the "200" code for the "GET" request when req.url is rewritten to a path with spaces', async () => {
3536+
const response = await req.get("/any-path");
3537+
3538+
expect(response.statusCode).toBe(200);
3539+
expect(response.headers["content-type"]).toBe(
3540+
"text/html; charset=utf-8",
3541+
);
3542+
});
3543+
});
3544+
}
3545+
34763546
describe("should work and don't call the next middleware for finished or errored requests by default", () => {
34773547
let compiler;
34783548

0 commit comments

Comments
 (0)