diff --git a/src/router.js b/src/router.js index d4ec4fef..2eb109c2 100644 --- a/src/router.js +++ b/src/router.js @@ -174,7 +174,7 @@ module.exports = class Router extends EventEmitter { } routes.push(route); // normal routes optimization - if(canBeOptimized(route.path) && route.pattern !== '/*' && !this.parent && this.get('case sensitive routing') && this.uwsApp) { + if(!route.complex && canBeOptimized(route.path) && !this.parent && this.get('case sensitive routing') && this.uwsApp) { if(supportedUwsMethods.has(method)) { const optimizedPath = this._optimizeRoute(route, this._routes); if(optimizedPath) { diff --git a/src/utils.js b/src/utils.js index 78c63814..4decfa95 100644 --- a/src/utils.js +++ b/src/utils.js @@ -52,14 +52,25 @@ function patternToRegex(pattern, isPrefix = false) { } let wildcardIndex = 0; - let regexPattern = pattern - .replaceAll('.', '\\.') - .replaceAll('-', '\\-') - .replaceAll(/(\*|\(.*?\))/g, (match) => `(?<_wc${wildcardIndex++}>${match.startsWith('(') ? match.slice(1, -1) : match.replaceAll('*', '.*')})`) // Convert * to .* and stuff in parentheses to capture group - .replace(/\/:(\w+)(\(.+?\))?\??/g, (match, param, regex) => { - const optional = match.endsWith('?'); - return `\\/${optional ? '?' : ''}(?<${param}>${regex ? regex + '($|\\/)' : '[^/]+'})${optional ? '?' : ''}`; - }); // Convert :param to capture group + let regexPattern = ''; + const captureGroupTest = /(\/|[.-]+):(\w+)(?:\((.+?)\))?\??/g; + let offset = 0; + while (true) { + const result = captureGroupTest.exec(pattern); + // Process last preceding part if matched, or final part if match ended + regexPattern += pattern.substring(offset, result?.index ?? pattern.length) + .replaceAll('.', '\\.') + .replaceAll('-', '\\-') + .replaceAll(/(\*|\(.*?\))/g, (match) => // Convert * to .* and stuff in parentheses to capture group + `(?<_wc${wildcardIndex++}>${match.startsWith('(') ? match.slice(1, -1) : match.replaceAll('*', '.*')})` + ); + if (!result) break; + const [match, prefix, param, regex] = result; + const optional = match.endsWith('?'); + // Convert :param to capture group + regexPattern += `${optional ? '(' : ''}${prefix}(?<${param}>${regex ? regex + '(?=$|\/)' : '[^/]+'})${optional ? ')?' : ''}`; + offset = result.index + match.length; + } return new RegExp(`^${regexPattern}${isPrefix ? '(?=$|\/)' : '$'}`); } diff --git a/tests/tests/routers/multi-params.js b/tests/tests/routers/multi-params.js index 203c63c5..7f672b7c 100644 --- a/tests/tests/routers/multi-params.js +++ b/tests/tests/routers/multi-params.js @@ -4,6 +4,10 @@ const express = require("express"); const app = express(); +app.get("/api/1.0/info/:subdir(ab|cd)?/:item([0-9A-Za-z-_]{6}.[^.])/:empty(.{0})?/details", (req, res) => { + res.json({path: req.path, params: req.params}); +}); + app.get("/api/1.0/projects/:project_id/user/:user_id", (req, res) => { res.json({path: req.path, params: req.params}); }); @@ -12,6 +16,10 @@ app.get("/api/1.0/projects/:project_id/users", (req, res) => { res.json({path: req.path, params: req.params}); }); +app.use((req, res, next) => { + res.send('404'); +}); + app.listen(13333, async () => { console.log("Server is running at http://localhost:13333"); @@ -22,6 +30,21 @@ app.listen(13333, async () => { fetch("http://localhost:13333/api/1.0/projects/456/user/789").then((res) => res.json() ), + fetch("http://localhost:13333/api/1.0/info/AAAAAA.2//details").then((res) => + res.json() + ), + fetch("http://localhost:13333/api/1.0/info/zzzzzz+2//details").then((res) => + res.json() + ), + fetch("http://localhost:13333/api/1.0/info/ab/Ab_23-+$/details").then((res) => + res.json() + ), + fetch("http://localhost:13333/api/1.0/info/cd//AAAAAA.a///details").then((res) => + res.json() + ), + fetch("http://localhost:13333/api/1.0/info/Ab%20c.+//details").then((res) => + res.json() + ), ]); console.log(responses); diff --git a/tests/tests/routing/special-characters.js b/tests/tests/routing/special-characters.js index b2dec870..dd2980e1 100644 --- a/tests/tests/routing/special-characters.js +++ b/tests/tests/routing/special-characters.js @@ -12,6 +12,10 @@ app.get('/hi-bye*a', (req, res) => { res.send('hi-bye'); }); +app.get('/test/:from--:to', (req, res) => { + res.send(`from: ${req.params.from}, to: ${req.params.to}`); +}); + app.listen(13333, async () => { console.log('Server is running on port 13333'); @@ -23,6 +27,9 @@ app.listen(13333, async () => { res = await fetch('http://localhost:13333/test/hiAbyeaa'); console.log(await res.text()); + + res = await fetch('http://localhost:13333/test/123--xyz'); + console.log(await res.text()); process.exit(0); })