Skip to content

Commit 30a1160

Browse files
authored
[fix] don't decode URL when finding matching route (#2354)
1 parent ba8cddc commit 30a1160

File tree

9 files changed

+29
-17
lines changed

9 files changed

+29
-17
lines changed

.changeset/two-tigers-cry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
[fix] don't decode URL when finding matching route

packages/kit/src/core/create_manifest_data/index.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,10 @@ function get_pattern(segments, add_trailing_slash) {
388388
.map((part) => {
389389
return part.dynamic
390390
? '([^/]+?)'
391-
: part.content
392-
.normalize()
393-
.replace(/\?/g, '%3F')
394-
.replace(/#/g, '%23')
395-
.replace(/%5B/g, '[')
396-
.replace(/%5D/g, ']')
397-
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
391+
: encodeURIComponent(part.content.normalize()).replace(
392+
/[.*+?^${}()|[\]\\]/g,
393+
'\\$&'
394+
);
398395
})
399396
.join('');
400397
})

packages/kit/src/core/create_manifest_data/index.spec.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,20 @@ test('creates routes with layout', () => {
132132
]);
133133
});
134134

135-
test('encodes invalid characters', () => {
135+
test('encoding of characters', () => {
136136
const { components, routes } = create('samples/encoding');
137137

138138
// had to remove ? and " because windows
139139

140140
// const quote = 'samples/encoding/".svelte';
141141
const hash = 'samples/encoding/#.svelte';
142+
const potato = 'samples/encoding/土豆.svelte';
142143
// const question_mark = 'samples/encoding/?.svelte';
143144

144145
assert.equal(components, [
145146
layout,
146147
error,
148+
potato,
147149
// quote,
148150
hash
149151
// question_mark
@@ -152,6 +154,7 @@ test('encodes invalid characters', () => {
152154
assert.equal(
153155
routes.map((p) => p.pattern),
154156
[
157+
/^\/%E5%9C%9F%E8%B1%86\/?$/,
155158
// /^\/%22\/?$/,
156159
/^\/%23\/?$/
157160
// /^\/%3F\/?$/

packages/kit/src/core/create_manifest_data/test/samples/encoding/土豆.svelte

Whitespace-only changes.

packages/kit/src/runtime/client/renderer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,8 @@ export class Renderer {
541541
* @param {boolean} no_cache
542542
* @returns {Promise<import('./types').NavigationResult | undefined>} undefined if fallthrough
543543
*/
544-
async _load({ route, info: { path, decoded_path, query } }, no_cache) {
545-
const key = `${decoded_path}?${query}`;
544+
async _load({ route, info: { path, query } }, no_cache) {
545+
const key = `${path}?${query}`;
546546

547547
if (!no_cache) {
548548
const cached = this.cache.get(key);
@@ -552,7 +552,7 @@ export class Renderer {
552552
const [pattern, a, b, get_params] = route;
553553
const params = get_params
554554
? // the pattern is for the route which we've already matched to this path
555-
get_params(/** @type {RegExpExecArray} */ (pattern.exec(decoded_path)))
555+
get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
556556
: {};
557557

558558
const changed = this.current.page && {

packages/kit/src/runtime/client/router.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,12 @@ export class Router {
170170
if (this.owns(url)) {
171171
const path = url.pathname.slice(this.base.length) || '/';
172172

173-
const decoded_path = decodeURI(path);
174-
const routes = this.routes.filter(([pattern]) => pattern.test(decoded_path));
173+
const routes = this.routes.filter(([pattern]) => pattern.test(path));
175174

176175
const query = new URLSearchParams(url.search);
177176
const id = `${path}?${query}`;
178177

179-
return { id, routes, path, decoded_path, query };
178+
return { id, routes, path, query };
180179
}
181180
}
182181

packages/kit/src/runtime/client/types.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export type NavigationInfo = {
55
id: string;
66
routes: CSRRoute[];
77
path: string;
8-
decoded_path: string;
98
query: URLSearchParams;
109
};
1110

packages/kit/src/runtime/server/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ export async function respond(incoming, options, state = {}) {
5454
});
5555
}
5656

57-
const decoded = decodeURI(request.path);
5857
for (const route of options.manifest.routes) {
59-
const match = route.pattern.exec(decoded);
58+
const match = route.pattern.exec(request.path);
6059
if (!match) continue;
6160

6261
const response =

packages/kit/test/apps/basics/src/routes/encoded/_tests.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ export default function (test) {
99
assert.equal(decodeURI(await page.innerHTML('h3')), '/encoded/苗条');
1010
});
1111

12+
test('visits a route with a doubly encoded space', '/encoded/test%2520me', async ({ page }) => {
13+
assert.equal(await page.innerHTML('h2'), '/encoded/test%2520me: test%20me');
14+
assert.equal(await page.innerHTML('h3'), '/encoded/test%2520me: test%20me');
15+
});
16+
17+
test('visits a route with an encoded slash', '/encoded/AC%2fDC', async ({ page }) => {
18+
assert.equal(await page.innerHTML('h2'), '/encoded/AC%2fDC: AC/DC');
19+
assert.equal(await page.innerHTML('h3'), '/encoded/AC%2fDC: AC/DC');
20+
});
21+
1222
test(
1323
'visits a dynamic route with non-ASCII character',
1424
'/encoded',

0 commit comments

Comments
 (0)