Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/delvedor/find-my-way
Browse files Browse the repository at this point in the history
  • Loading branch information
delvedor committed Mar 26, 2018
2 parents 4d99c7a + 4caea1c commit 933b63f
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 111 deletions.
7 changes: 7 additions & 0 deletions bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const FindMyWay = require('./')
const findMyWay = new FindMyWay()
findMyWay.on('GET', '/', () => true)
findMyWay.on('GET', '/user/:id', () => true)
findMyWay.on('GET', '/user/:id/static', () => true)
findMyWay.on('GET', '/customer/:name-:surname', () => true)
findMyWay.on('GET', '/at/:hour(^\\d+)h:minute(^\\d+)m', () => true)
findMyWay.on('GET', '/abc/def/ghi/lmn/opq/rst/uvz', () => true)
Expand All @@ -28,6 +29,9 @@ suite
.add('lookup long static route', function () {
findMyWay.lookup({ method: 'GET', url: '/abc/def/ghi/lmn/opq/rst/uvz' }, null)
})
.add('lookup long dynamic route', function () {
findMyWay.lookup({ method: 'GET', url: '/user/qwertyuiopasdfghjklzxcvbnm/static' }, null)
})
.add('find static route', function () {
findMyWay.find('GET', '/')
})
Expand All @@ -43,6 +47,9 @@ suite
.add('find long static route', function () {
findMyWay.find('GET', '/abc/def/ghi/lmn/opq/rst/uvz')
})
.add('find long dynamic route', function () {
findMyWay.find('GET', '/user/qwertyuiopasdfghjklzxcvbnm/static')
})
.on('cycle', function (event) {
console.log(String(event.target))
})
Expand Down
15 changes: 13 additions & 2 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
'use strict'

const http = require('http')
const router = require('./')()
const router = require('./')({
defaultRoute: (req, res) => {
res.end('not found')
}
})

router.on('GET', '/test', (req, res, params) => {
res.end('{"hello":"world"}')
})

router.on('GET', '/:test', (req, res, params) => {
res.end(JSON.stringify(params))
})

router.on('GET', '/text/hello', (req, res, params) => {
res.end('{"winter":"is here"}')
})

const server = http.createServer((req, res) => {
console.log(req.url)
router.lookup(req, res)
})

Expand Down
103 changes: 39 additions & 64 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

const assert = require('assert')
const http = require('http')
const fastDecode = require('fast-decode-uri-component')
const Node = require('./node')
const NODE_TYPES = Node.prototype.types
const httpMethods = http.METHODS
var errored = false

function Router (opts) {
if (!(this instanceof Router)) {
Expand Down Expand Up @@ -83,6 +83,7 @@ Router.prototype._on = function _on (method, path, handler, store) {
if (path.charCodeAt(i) === 58) {
var nodeType = NODE_TYPES.PARAM
j = i + 1
// add the static part of the route to the tree
this._insert(method, path.slice(0, i), 0, null, null, null, null)

// isolate the parameter name
Expand Down Expand Up @@ -118,12 +119,14 @@ Router.prototype._on = function _on (method, path, handler, store) {
if (i === len) {
return this._insert(method, path.slice(0, i), nodeType, params, handler, store, regex)
}
// add the parameter and continue with the search
this._insert(method, path.slice(0, i), nodeType, params, null, null, regex)

i--
// wildcard route
} else if (path.charCodeAt(i) === 42) {
this._insert(method, path.slice(0, i), 0, null, null, null, null)
// add the wildcard parameter
params.push('*')
return this._insert(method, path.slice(0, len), 2, params, handler, store, null)
}
Expand Down Expand Up @@ -152,8 +155,9 @@ Router.prototype._insert = function _insert (method, path, kind, params, handler
max = pathLen < prefixLen ? pathLen : prefixLen
while (len < max && path[len] === prefix[len]) len++

// the longest common prefix is smaller than the current prefix
// let's split the node and add a new child
if (len < prefixLen) {
// split the node in the radix tree and add it to the parent
node = new Node(
prefix.slice(len),
currentNode.children,
Expand All @@ -166,42 +170,41 @@ Router.prototype._insert = function _insert (method, path, kind, params, handler
}

// reset the parent
currentNode.children = [node]
currentNode.numberOfChildren = 1
currentNode.prefix = prefix.slice(0, len)
currentNode.label = currentNode.prefix[0]
currentNode.handlers = new Node.Handlers()
currentNode.kind = NODE_TYPES.STATIC
currentNode.regex = null
currentNode.wildcardChild = null
currentNode
.reset(prefix.slice(0, len))
.addChild(node)

// if the longest common prefix has the same length of the current path
// the handler should be added to the current node, to a child otherwise
if (len === pathLen) {
// add the handler to the parent node
assert(!currentNode.getHandler(method), `Method '${method}' already declared for route '${route}'`)
currentNode.setHandler(method, handler, params, store)
currentNode.kind = kind
} else {
// create a child node and add an handler to it
node = new Node(path.slice(len), [], kind, null, regex)
node = new Node(path.slice(len), {}, kind, null, regex)
node.setHandler(method, handler, params, store)
// add the child to the parent
currentNode.add(node)
currentNode.addChild(node)
}

// the longest common prefix is smaller than the path length,
// but is higher than the prefix
} else if (len < pathLen) {
// remove the prefix
path = path.slice(len)
node = currentNode.findByLabel(path[0])
// check if there is a child with the label extracted from the new path
node = currentNode.findByLabel(path)
// there is a child within the given label, we must go deepen in the tree
if (node) {
// we must go deeper in the tree
currentNode = node
continue
}
// create a new child node
node = new Node(path, [], kind, null, regex)
// there are not children within the given label, let's create a new one!
node = new Node(path, {}, kind, null, regex)
node.setHandler(method, handler, params, store)
// add the child to the parent
currentNode.add(node)
currentNode.addChild(node)

// the node already exist
} else if (handler) {
// the node already exist
assert(!currentNode.getHandler(method), `Method '${method}' already declared for route '${route}'`)
currentNode.setHandler(method, handler, params, store)
}
Expand Down Expand Up @@ -262,7 +265,7 @@ Router.prototype.off = function off (method, path) {

Router.prototype.lookup = function lookup (req, res) {
var handle = this.find(req.method, sanitizeUrl(req.url))
if (handle == null) return this._defaultRoute(req, res)
if (handle === null) return this._defaultRoute(req, res)
return handle.handler(req, res, handle.params, handle.store)
}

Expand All @@ -287,7 +290,7 @@ Router.prototype.find = function find (method, path) {
// found the route
if (pathLen === 0 || path === prefix) {
var handle = currentNode.handlers[method]
if (handle != null) {
if (handle !== null && handle !== undefined) {
var paramsObj = {}
if (handle.paramsLength > 0) {
var paramNames = handle.params
Expand All @@ -307,14 +310,14 @@ Router.prototype.find = function find (method, path) {

// search for the longest common prefix
i = pathLen < prefixLen ? pathLen : prefixLen
while (len < i && path[len] === prefix[len]) len++
while (len < i && path.charCodeAt(len) === prefix.charCodeAt(len)) len++

if (len === prefixLen) {
path = path.slice(len)
pathLen = path.length
}

var node = currentNode.find(path, method)
var node = currentNode.findChild(path, method)
if (node === null) {
node = currentNode.parametricBrother
if (node === null) {
Expand Down Expand Up @@ -350,13 +353,11 @@ Router.prototype.find = function find (method, path) {
// parametric route
if (kind === NODE_TYPES.PARAM) {
currentNode = node
i = 0
while (i < pathLen && path.charCodeAt(i) !== 47) i++
i = path.indexOf('/')
if (i === -1) i = pathLen
if (i > maxParamLength) return null
decoded = fastDecode(path.slice(0, i))
if (errored === true) {
return null
}
if (decoded === null) return null
params[pindex++] = decoded
path = path.slice(i)
continue
Expand All @@ -365,9 +366,7 @@ Router.prototype.find = function find (method, path) {
// wildcard route
if (kind === NODE_TYPES.MATCH_ALL) {
decoded = fastDecode(path)
if (errored === true) {
return null
}
if (decoded === null) return null
params[pindex] = decoded
currentNode = node
path = ''
Expand All @@ -377,13 +376,11 @@ Router.prototype.find = function find (method, path) {
// parametric(regex) route
if (kind === NODE_TYPES.REGEX) {
currentNode = node
i = 0
while (i < pathLen && path.charCodeAt(i) !== 47) i++
i = path.indexOf('/')
if (i === -1) i = pathLen
if (i > maxParamLength) return null
decoded = fastDecode(path.slice(0, i))
if (errored === true) {
return null
}
if (decoded === null) return null
if (!node.regex.test(decoded)) return null
params[pindex++] = decoded
path = path.slice(i)
Expand All @@ -403,9 +400,7 @@ Router.prototype.find = function find (method, path) {
if (i > maxParamLength) return null
}
decoded = fastDecode(path.slice(0, i))
if (errored === true) {
return null
}
if (decoded === null) return null
params[pindex++] = decoded
path = path.slice(i)
continue
Expand Down Expand Up @@ -455,23 +450,12 @@ function sanitizeUrl (url) {
return url
}

function fastDecode (path) {
errored = false
try {
return decodeURIComponent(path)
} catch (err) {
errored = true
}
}

function getWildcardNode (node, method, path, len) {
if (node === null) return null
var decoded = fastDecode(path.slice(-len))
if (errored === true) {
return null
}
if (decoded === null) return null
var handle = node.handlers[method]
if (handle != null) {
if (handle !== null && handle !== undefined) {
return {
handler: handle.handler,
params: { '*': decoded },
Expand All @@ -481,15 +465,6 @@ function getWildcardNode (node, method, path, len) {
return null
}

/**
* Returns the position of the last closing parenthese of the regexp expression.
*
* @param {String} path
* @param {Number} idx
* @returns {Number}
* @throws {TypeError} when a closing parenthese is missing
* @private
*/
function getClosingParenthensePosition (path, idx) {
// `path.indexOf()` will always return the first position of the closing parenthese,
// but it's inefficient for grouped or wrong regexp expressions.
Expand Down

0 comments on commit 933b63f

Please sign in to comment.