find-my-way
Advanced tools
Comparing version 6.0.0 to 6.1.0
@@ -77,2 +77,4 @@ import { IncomingMessage, ServerResponse } from 'http'; | ||
ignoreDuplicateSlashes?: boolean; | ||
allowUnsafeRegex?: boolean; | ||
@@ -79,0 +81,0 @@ |
44
index.js
@@ -86,2 +86,3 @@ 'use strict' | ||
this.ignoreTrailingSlash = opts.ignoreTrailingSlash || false | ||
this.ignoreDuplicateSlashes = opts.ignoreDuplicateSlashes || false | ||
this.maxParamLength = opts.maxParamLength || 100 | ||
@@ -125,2 +126,6 @@ this.allowUnsafeRegex = opts.allowUnsafeRegex || false | ||
if (this.ignoreDuplicateSlashes) { | ||
path = removeDuplicateSlashes(path) | ||
} | ||
if (this.ignoreTrailingSlash) { | ||
@@ -304,2 +309,6 @@ path = trimLastSlash(path) | ||
if (this.ignoreDuplicateSlashes) { | ||
path = removeDuplicateSlashes(path) | ||
} | ||
if (this.ignoreTrailingSlash) { | ||
@@ -347,4 +356,12 @@ path = trimLastSlash(path) | ||
// This must be run before sanitizeUrl as the resulting function | ||
// .sliceParameter must be constructed with same URL string used | ||
// throughout the rest of this function. | ||
if (this.ignoreDuplicateSlashes) { | ||
path = removeDuplicateSlashes(path) | ||
} | ||
let sanitizedUrl | ||
let querystring | ||
let shouldDecodeParam | ||
@@ -355,2 +372,3 @@ try { | ||
querystring = sanitizedUrl.querystring | ||
shouldDecodeParam = sanitizedUrl.shouldDecodeParam | ||
} catch (error) { | ||
@@ -456,6 +474,4 @@ return this._onBadUrl(path) | ||
let param = originPath.slice(pathIndex) | ||
const firstPercentIndex = param.indexOf('%') | ||
if (firstPercentIndex !== -1) { | ||
param = safeDecodeURIComponent(param, firstPercentIndex) | ||
if (shouldDecodeParam) { | ||
param = safeDecodeURIComponent(param) | ||
} | ||
@@ -473,16 +489,10 @@ | ||
if (currentNode.kind === NODE_TYPES.PARAMETRIC) { | ||
let paramEndIndex = pathIndex | ||
let firstPercentIndex = -1 | ||
for (; paramEndIndex < pathLen; paramEndIndex++) { | ||
const charCode = path.charCodeAt(paramEndIndex) | ||
if (charCode === 47) { | ||
break | ||
} else if (firstPercentIndex === -1 && charCode === 37) { | ||
firstPercentIndex = paramEndIndex - pathIndex | ||
} | ||
let paramEndIndex = originPath.indexOf('/', pathIndex) | ||
if (paramEndIndex === -1) { | ||
paramEndIndex = pathLen | ||
} | ||
let param = originPath.slice(pathIndex, paramEndIndex) | ||
if (firstPercentIndex !== -1) { | ||
param = safeDecodeURIComponent(param, firstPercentIndex) | ||
if (shouldDecodeParam) { | ||
param = safeDecodeURIComponent(param) | ||
} | ||
@@ -590,2 +600,6 @@ | ||
function removeDuplicateSlashes (path) { | ||
return path.replace(/\/\/+/g, '/') | ||
} | ||
function trimLastSlash (path) { | ||
@@ -592,0 +606,0 @@ if (path.length > 1 && path.charCodeAt(path.length - 1) === 47) { |
@@ -39,2 +39,4 @@ 'use strict' | ||
let shouldDecode = false | ||
let shouldDecodeParam = false | ||
let querystring = '' | ||
@@ -52,2 +54,3 @@ | ||
} else { | ||
shouldDecodeParam = true | ||
// %25 - encoded % char. We need to encode one more time to prevent double decoding | ||
@@ -71,6 +74,9 @@ if (highCharCode === 50 && lowCharCode === 53) { | ||
const decodedPath = shouldDecode ? decodeURI(path) : path | ||
return { path: decodedPath, querystring } | ||
return { path: decodedPath, querystring, shouldDecodeParam } | ||
} | ||
function safeDecodeURIComponent (uriComponent, startIndex) { | ||
function safeDecodeURIComponent (uriComponent) { | ||
const startIndex = uriComponent.indexOf('%') | ||
if (startIndex === -1) return uriComponent | ||
let decoded = '' | ||
@@ -77,0 +83,0 @@ let lastIndex = startIndex |
{ | ||
"name": "find-my-way", | ||
"version": "6.0.0", | ||
"version": "6.1.0", | ||
"description": "Crazy fast http radix based router", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -73,2 +73,16 @@ # find-my-way | ||
Duplicate slashes can be ignored by supplying the `ignoreDuplicateSlashes` option: | ||
```js | ||
const router = require('find-my-way')({ | ||
ignoreDuplicateSlashes: true | ||
}) | ||
function handler (req, res, params) { | ||
res.end('foo') | ||
} | ||
// maps "/foo", "//foo", "///foo", etc to `handler` | ||
router.on('GET', '////foo', handler) | ||
``` | ||
Note that when `ignoreTrailingSlash` and `ignoreDuplicateSlashes` are both set to true, duplicate slashes will first be removed and then trailing slashes will, meaning `//a//b//c//` will be converted to `/a/b/c`. | ||
You can set a custom length for parameters in parametric *(standard, regex and multi)* routes by using `maxParamLength` option, the default value is 100 characters.<br/> | ||
@@ -75,0 +89,0 @@ *If the maximum length limit is reached, the default route will be invoked.* |
@@ -31,2 +31,14 @@ 'use strict' | ||
test('Method should be a string [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
try { | ||
findMyWay.on(0, '/test', () => {}) | ||
t.fail('method shoukd be a string') | ||
} catch (e) { | ||
t.equal(e.message, 'Method should be a string') | ||
} | ||
}) | ||
test('Method should be a string (array)', t => { | ||
@@ -56,2 +68,14 @@ t.plan(1) | ||
test('Method should be a string (array) [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
try { | ||
findMyWay.on(['GET', 0], '/test', () => {}) | ||
t.fail('method shoukd be a string') | ||
} catch (e) { | ||
t.equal(e.message, 'Method should be a string') | ||
} | ||
}) | ||
test('Path should be a string', t => { | ||
@@ -81,2 +105,14 @@ t.plan(1) | ||
test('Path should be a string [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
try { | ||
findMyWay.on('GET', 0, () => {}) | ||
t.fail('path should be a string') | ||
} catch (e) { | ||
t.equal(e.message, 'Path should be a string') | ||
} | ||
}) | ||
test('The path could not be empty', t => { | ||
@@ -106,2 +142,14 @@ t.plan(1) | ||
test('The path could not be empty [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
try { | ||
findMyWay.on('GET', '', () => {}) | ||
t.fail('The path could not be empty') | ||
} catch (e) { | ||
t.equal(e.message, 'The path could not be empty') | ||
} | ||
}) | ||
test('The first character of a path should be `/` or `*`', t => { | ||
@@ -131,2 +179,14 @@ t.plan(1) | ||
test('The first character of a path should be `/` or `*` [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
try { | ||
findMyWay.on('GET', 'a', () => {}) | ||
t.fail('The first character of a path should be `/` or `*`') | ||
} catch (e) { | ||
t.equal(e.message, 'The first character of a path should be `/` or `*`') | ||
} | ||
}) | ||
test('Handler should be a function', t => { | ||
@@ -239,2 +299,48 @@ t.plan(1) | ||
test('Method already declared [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(2) | ||
t.test('without duplicate slashes', t => { | ||
t.plan(2) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
findMyWay.on('GET', '/test', () => {}) | ||
try { | ||
findMyWay.on('GET', '/test', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test\' with constraints \'{}\'') | ||
} | ||
try { | ||
findMyWay.on('GET', '//test', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test\' with constraints \'{}\'') | ||
} | ||
}) | ||
t.test('with duplicate slashes', t => { | ||
t.plan(2) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
findMyWay.on('GET', '//test', () => {}) | ||
try { | ||
findMyWay.on('GET', '/test', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test\' with constraints \'{}\'') | ||
} | ||
try { | ||
findMyWay.on('GET', '//test', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test\' with constraints \'{}\'') | ||
} | ||
}) | ||
}) | ||
test('Method already declared nested route', t => { | ||
@@ -318,1 +424,51 @@ t.plan(1) | ||
}) | ||
test('Method already declared nested route [ignoreDuplicateSlashes=true]', t => { | ||
t.plan(2) | ||
t.test('without duplicate slashes', t => { | ||
t.plan(2) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
findMyWay.on('GET', '/test', () => {}) | ||
findMyWay.on('GET', '/test/hello', () => {}) | ||
findMyWay.on('GET', '/test/world', () => {}) | ||
try { | ||
findMyWay.on('GET', '/test/hello', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test/hello\' with constraints \'{}\'') | ||
} | ||
try { | ||
findMyWay.on('GET', '/test//hello', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test/hello\' with constraints \'{}\'') | ||
} | ||
}) | ||
t.test('with duplicate slashes', t => { | ||
t.plan(2) | ||
const findMyWay = FindMyWay({ ignoreDuplicateSlashes: true }) | ||
findMyWay.on('GET', '/test/', () => {}) | ||
findMyWay.on('GET', '/test//hello', () => {}) | ||
findMyWay.on('GET', '/test//world', () => {}) | ||
try { | ||
findMyWay.on('GET', '/test/hello', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test/hello\' with constraints \'{}\'') | ||
} | ||
try { | ||
findMyWay.on('GET', '/test//hello', () => {}) | ||
t.fail('method already declared') | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/test/hello\' with constraints \'{}\'') | ||
} | ||
}) | ||
}) |
@@ -171,2 +171,29 @@ 'use strict' | ||
test('off removes all routes when ignoreDuplicateSlashes is true', t => { | ||
t.plan(6) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: true | ||
}) | ||
findMyWay.on('GET', '//test1', () => {}) | ||
t.equal(findMyWay.routes.length, 1) | ||
findMyWay.on('GET', '/test2', () => {}) | ||
t.equal(findMyWay.routes.length, 2) | ||
findMyWay.off('GET', '/test1') | ||
t.equal(findMyWay.routes.length, 1) | ||
t.equal( | ||
findMyWay.routes.filter((r) => r.path === '/test2').length, | ||
1 | ||
) | ||
t.equal( | ||
findMyWay.routes.filter((r) => r.path === '//test2').length, | ||
0 | ||
) | ||
findMyWay.off('GET', '//test2') | ||
t.equal(findMyWay.routes.length, 0) | ||
}) | ||
test('deregister a route without children', t => { | ||
@@ -173,0 +200,0 @@ t.plan(2) |
@@ -128,5 +128,6 @@ 'use strict' | ||
test('derigister a route with optional param', (t) => { | ||
test('Optional Parameter with ignoreDuplicateSlashes = true', (t) => { | ||
t.plan(4) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: true, | ||
defaultRoute: (req, res) => { | ||
@@ -137,2 +138,53 @@ t.fail('Should not be defaultRoute') | ||
findMyWay.on('GET', '/test/hello/:optional?', (req, res, params) => { | ||
if (params.optional) { | ||
t.equal(params.optional, 'foo') | ||
} else { | ||
t.equal(params.optional, undefined) | ||
} | ||
}) | ||
findMyWay.lookup({ method: 'GET', url: '/test//hello', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test/hello', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test/hello/foo', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test//hello//foo', headers: {} }, null) | ||
}) | ||
test('Optional Parameter with ignoreDuplicateSlashes = false', (t) => { | ||
t.plan(4) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: false, | ||
defaultRoute: (req, res) => { | ||
if (req.url === '/test//hello') { | ||
t.same(req.params, undefined) | ||
} else if (req.url === '/test//hello/foo') { | ||
t.same(req.params, undefined) | ||
} | ||
} | ||
}) | ||
findMyWay.on('GET', '/test/hello/:optional?', (req, res, params) => { | ||
if (req.url === '/test/hello/') { | ||
t.same(params, { optional: '' }) | ||
} else if (req.url === '/test/hello') { | ||
t.same(params, {}) | ||
} else if (req.url === '/test/hello/foo') { | ||
t.same(params, { optional: 'foo' }) | ||
} | ||
}) | ||
findMyWay.lookup({ method: 'GET', url: '/test//hello', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test/hello', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test/hello/foo', headers: {} }, null) | ||
findMyWay.lookup({ method: 'GET', url: '/test//hello/foo', headers: {} }, null) | ||
}) | ||
test('deregister a route with optional param', (t) => { | ||
t.plan(4) | ||
const findMyWay = FindMyWay({ | ||
defaultRoute: (req, res) => { | ||
t.fail('Should not be defaultRoute') | ||
} | ||
}) | ||
findMyWay.on('GET', '/a/:param/b/:optional?', (req, res, params) => {}) | ||
@@ -139,0 +191,0 @@ |
@@ -7,5 +7,5 @@ 'use strict' | ||
t.test('path params match', (t) => { | ||
t.plan(12) | ||
t.plan(24) | ||
const findMyWay = FindMyWay({ ignoreTrailingSlash: true }) | ||
const findMyWay = FindMyWay({ ignoreTrailingSlash: true, ignoreDuplicateSlashes: true }) | ||
@@ -24,8 +24,16 @@ const b1Path = function b1StaticPath () {} | ||
t.equal(findMyWay.find('GET', '/ab1/').handler, b1Path) | ||
t.equal(findMyWay.find('GET', '//ab1').handler, b1Path) | ||
t.equal(findMyWay.find('GET', '//ab1//').handler, b1Path) | ||
t.equal(findMyWay.find('GET', '/ab2').handler, b2Path) | ||
t.equal(findMyWay.find('GET', '/ab2/').handler, b2Path) | ||
t.equal(findMyWay.find('GET', '//ab2').handler, b2Path) | ||
t.equal(findMyWay.find('GET', '//ab2//').handler, b2Path) | ||
t.equal(findMyWay.find('GET', '/ac').handler, cPath) | ||
t.equal(findMyWay.find('GET', '/ac/').handler, cPath) | ||
t.equal(findMyWay.find('GET', '//ac').handler, cPath) | ||
t.equal(findMyWay.find('GET', '//ac//').handler, cPath) | ||
t.equal(findMyWay.find('GET', '/foo').handler, paramPath) | ||
t.equal(findMyWay.find('GET', '/foo/').handler, paramPath) | ||
t.equal(findMyWay.find('GET', '//foo').handler, paramPath) | ||
t.equal(findMyWay.find('GET', '//foo//').handler, paramPath) | ||
@@ -39,2 +47,10 @@ const noTrailingSlashRet = findMyWay.find('GET', '/abcdef') | ||
t.same(trailingSlashRet.params, { pam: 'abcdef' }) | ||
const noDuplicateSlashRet = findMyWay.find('GET', '/abcdef') | ||
t.equal(noDuplicateSlashRet.handler, paramPath) | ||
t.same(noDuplicateSlashRet.params, { pam: 'abcdef' }) | ||
const duplicateSlashRet = findMyWay.find('GET', '//abcdef') | ||
t.equal(duplicateSlashRet.handler, paramPath) | ||
t.same(duplicateSlashRet.params, { pam: 'abcdef' }) | ||
}) |
@@ -251,2 +251,151 @@ 'use strict' | ||
test('maps two routes when duplicate slashes should be trimmed', t => { | ||
t.plan(21) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: true | ||
}) | ||
findMyWay.on('GET', '//test', (req, res, params) => { | ||
t.ok(req) | ||
t.ok(res) | ||
t.ok(params) | ||
res.end('test') | ||
}) | ||
findMyWay.on('GET', '/othertest', (req, res, params) => { | ||
t.ok(req) | ||
t.ok(res) | ||
t.ok(params) | ||
res.end('othertest') | ||
}) | ||
const server = http.createServer((req, res) => { | ||
findMyWay.lookup(req, res) | ||
}) | ||
server.listen(0, err => { | ||
t.error(err) | ||
server.unref() | ||
const baseURL = 'http://localhost:' + server.address().port | ||
http.get(baseURL + '//test', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'test') | ||
}) | ||
http.get(baseURL + '/test', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'test') | ||
}) | ||
http.get(baseURL + '/othertest', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'othertest') | ||
}) | ||
http.get(baseURL + '//othertest', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'othertest') | ||
}) | ||
}) | ||
}) | ||
test('does not trim duplicate slashes when ignoreDuplicateSlashes is false', t => { | ||
t.plan(7) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: false | ||
}) | ||
findMyWay.on('GET', '//test', (req, res, params) => { | ||
t.ok(req) | ||
t.ok(res) | ||
t.ok(params) | ||
res.end('test') | ||
}) | ||
const server = http.createServer((req, res) => { | ||
findMyWay.lookup(req, res) | ||
}) | ||
server.listen(0, err => { | ||
t.error(err) | ||
server.unref() | ||
const baseURL = 'http://localhost:' + server.address().port | ||
http.get(baseURL + '//test', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'test') | ||
}) | ||
http.get(baseURL + '/test', async (res) => { | ||
t.equal(res.statusCode, 404) | ||
}) | ||
}) | ||
}) | ||
test('does map // when ignoreDuplicateSlashes is true', t => { | ||
t.plan(11) | ||
const findMyWay = FindMyWay({ | ||
ignoreDuplicateSlashes: true | ||
}) | ||
findMyWay.on('GET', '/', (req, res, params) => { | ||
t.ok(req) | ||
t.ok(res) | ||
t.ok(params) | ||
res.end('test') | ||
}) | ||
const server = http.createServer((req, res) => { | ||
findMyWay.lookup(req, res) | ||
}) | ||
server.listen(0, err => { | ||
t.error(err) | ||
server.unref() | ||
const baseURL = 'http://localhost:' + server.address().port | ||
http.get(baseURL + '/', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'test') | ||
}) | ||
http.get(baseURL + '//', async (res) => { | ||
let body = '' | ||
for await (const chunk of res) { | ||
body += chunk | ||
} | ||
t.equal(res.statusCode, 200) | ||
t.same(body, 'test') | ||
}) | ||
}) | ||
}) | ||
test('versioned routes', t => { | ||
@@ -253,0 +402,0 @@ t.plan(3) |
@@ -16,2 +16,3 @@ import { expectType } from 'tsd' | ||
ignoreTrailingSlash: true, | ||
ignoreDuplicateSlashes: true, | ||
allowUnsafeRegex: false, | ||
@@ -90,2 +91,3 @@ caseSensitive: false, | ||
ignoreTrailingSlash: true, | ||
ignoreDuplicateSlashes: true, | ||
allowUnsafeRegex: false, | ||
@@ -92,0 +94,0 @@ caseSensitive: false, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
306931
80
7721
625