diff --git a/lib/route.js b/lib/route.js index 6acdb77..a8a938f 100644 --- a/lib/route.js +++ b/lib/route.js @@ -35,9 +35,23 @@ function Route(spec) { Route.prototype = Object.create(null); +/** + * Escape asterisks, open parantheses and closed parantheses. + * This is necessary because they are valid parts of an URL + * but reserved characters in route-parser. + * @param {string} path the path to escape + * @return {string} The escaped path, containing %2A instead + * of *, %28 instead of ( and %29 instead of ) + */ +function escape(path) { + return path.replace(/\*/g, '%2A') + .replace(/\(/g, '%28') + .replace(/\)/g, '%29'); +} + /** * Match a path against this route, returning the matched parameters if - * it matches, false if not. + * it matches, false if not. Escapes asterisks in route. * @example * var route = new Route('/this/is/my/route') * route.match('/this/is/my/route') // -> {} @@ -50,7 +64,8 @@ Route.prototype = Object.create(null); */ Route.prototype.match = function (path) { var re = RegexpVisitor.visit(this.ast); - var matched = re.match(path); + var escapedPath = escape(path); + var matched = re.match(escapedPath); return matched !== null ? matched : false; }; diff --git a/test/test.js b/test/test.js index 446787e..7b6fbd0 100644 --- a/test/test.js +++ b/test/test.js @@ -78,6 +78,16 @@ describe('Route', function () { var route = RouteParser('/*a/foo/*b'); assert.deepEqual(route.match('/zoo/woo/foo/bar/baz'), { a: 'zoo/woo', b: 'bar/baz' }); }); + + it('escapes asterisk in route', function () { + var route = RouteParser('/foo?bar=%2Atest&*a=splat'); + assert.deepEqual(route.match('/foo?bar=*test&marker=splat'), { a: 'marker' }); + }); + + it('escapes multiple asterisks', function () { + var route = RouteParser('/foo?bar=%2Atest&*a=splat&foo=%2Abar'); + assert.deepEqual(route.match('/foo?bar=*test&marker=splat&foo=*bar'), { a: 'marker' }); + }); }); describe('mixed', function () { @@ -105,6 +115,10 @@ describe('Route', function () { var route = RouteParser('/things/(option/:first)'); assert.deepEqual(route.match('/things/option/bar'), { first: 'bar' }); }); + it('escapes parantheses in route', function () { + var route = RouteParser('/foo/(option/:first)'); + assert.deepEqual(route.match('/foo/option/bar()'), { first: 'bar()' }); + }); describe('nested', function () { it('allows nested', function () {