Socket
Socket
Sign inDemoInstall

router

Package Overview
Dependencies
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

router - npm Package Compare versions

Comparing version 0.6.2 to 1.0.0-beta.1

HISTORY.md

694

index.js

@@ -1,92 +0,632 @@

var matcher = require('./matcher');
var formatter = require('./formatter');
/*!
* router
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/
var METHODS = ['get', 'post', 'put', 'del' , 'delete', 'head', 'options'];
var HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'DELETE', 'HEAD', 'OPTIONS'];
/**
* Module dependencies.
* @private
*/
var noop = function() {};
var error = function(res) {
return function() {
res.statusCode = 404;
res.end();
};
};
var router = function() {
var methods = {};
var traps = {};
var debug = require('debug')('router')
var Layer = require('./lib/layer')
var methods = require('methods')
var mixin = require('utils-merge')
var parseUrl = require('parseurl')
var Route = require('./lib/route')
var utils = require('./lib/utils')
HTTP_METHODS.forEach(function(method) {
methods[method] = [];
});
/**
* Module variables.
* @private
*/
var route = function(req, res, next) {
var method = methods[req.method];
var trap = traps[req.method];
var index = req.url.indexOf('?');
var url = index === -1 ? req.url : req.url.substr(0, index);
var i = 0;
var slice = Array.prototype.slice
next = next || error(res);
if (!method) return next();
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
var loop = function(err) {
if (err) return next(err);
while (i < method.length) {
var route = method[i];
/**
* Expose `Router`.
*/
i++;
req.params = route.pattern(url);
if (!req.params) continue;
if (route.rewrite) {
req.url = url = route.rewrite(req.params)+(index === -1 ? '' : req.url.substr(index));
}
route.fn(req, res, loop);
return;
}
if (!trap) return next();
trap(req, res, next);
};
module.exports = Router
loop();
};
/**
* Expose `Route`.
*/
METHODS.forEach(function(method, i) {
route[method] = function(pattern, rewrite, fn) {
if (Array.isArray(pattern)) {
pattern.forEach(function(item) {
route[method](item, rewrite, fn);
});
return;
}
module.exports.Route = Route
if (!fn && !rewrite) return route[method](null, null, pattern);
if (!fn && typeof rewrite === 'string') return route[method](pattern, rewrite, route);
if (!fn && typeof rewrite === 'function') return route[method](pattern, null, rewrite);
if (!fn) return route;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {object} options
* @return {Router} which is an callable function
* @public
*/
(route.onmount || noop)(pattern, rewrite, fn);
function Router(options) {
if (!(this instanceof Router)) {
return new Router(options)
}
if (!pattern) {
traps[HTTP_METHODS[i]] = fn;
return route;
}
var opts = options || {}
methods[HTTP_METHODS[i]].push({
pattern:matcher(pattern),
rewrite:formatter(rewrite),
fn:fn
});
return route;
};
});
route.all = function(pattern, rewrite, fn) {
METHODS.forEach(function(method) {
route[method](pattern, rewrite, fn);
});
return route;
};
function router(req, res, next) {
router.handle(req, res, next)
}
return route;
};
// inherit from the correct prototype
router.__proto__ = this
module.exports = router;
router.caseSensitive = opts.caseSensitive
router.mergeParams = opts.mergeParams
router.params = {}
router.strict = opts.strict
router.stack = []
return router
}
/**
* Router prototype inherits from a Function.
*/
/* istanbul ignore next */
Router.prototype = function () {}
/**
* Map the given param placeholder `name`(s) to the given callback.
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code.
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* Just like in middleware, you must either respond to the request or call next
* to avoid stalling the request.
*
* router.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* return next(err)
* } else if (!user) {
* return next(new Error('failed to load user'))
* }
* req.user = user
* next()
* })
* })
*
* @param {string} name
* @param {function} fn
* @public
*/
Router.prototype.param = function param(name, fn) {
if (!name) {
throw new TypeError('argument name is required')
}
if (typeof name !== 'string') {
throw new TypeError('argument name must be a string')
}
if (!fn) {
throw new TypeError('argument fn is required')
}
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function')
}
if (name[0] === ':') {
name = name.substr(1)
}
var params = this.params[name]
if (!params) {
params = this.params[name] = []
}
params.push(fn)
return this
}
/**
* Dispatch a req, res into the router.
*
* @private
*/
Router.prototype.handle = function handle(req, res, callback) {
if (!callback) {
throw new TypeError('argument callback is required')
}
debug('dispatching %s %s', req.method, req.url)
var search = 1 + req.url.indexOf('?')
var pathlength = search ? search - 1 : req.url.length
var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://')
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''
var idx = 0
var removed = ''
var self = this
var slashAdded = false
var paramcalled = {}
// store options for OPTIONS request
// only used if OPTIONS request
var options = []
// middleware and routes
var stack = this.stack
// manage inter-router variables
var parentParams = req.params
var parentUrl = req.baseUrl || ''
var done = restore(callback, req, 'baseUrl', 'next', 'params')
// setup next layer
req.next = next
// for options requests, respond with a default if nothing else responds
if (req.method === 'OPTIONS') {
done = wrap(done, function onDone(old, err) {
if (err || options.length === 0) {
return old(err)
}
var methods = options.sort().join(', ')
res.setHeader('Allow', methods)
res.setHeader('Content-Length', Buffer.byteLength(methods))
res.setHeader('Content-Type', 'text/plain')
res.setHeader('X-Content-Type-Options', 'nosniff')
res.end(methods)
})
}
// setup basic req values
req.baseUrl = parentUrl
req.originalUrl = req.originalUrl || req.url
next()
function next(err) {
var layerError = err === 'route'
? null
: err
var layer = stack[idx++]
if (slashAdded) {
req.url = req.url.substr(1)
slashAdded = false
}
if (removed.length !== 0) {
req.baseUrl = parentUrl
req.url = protohost + removed + req.url.substr(protohost.length)
removed = ''
}
if (!layer) {
defer(done, layerError)
return
}
self.match_layer(layer, req, res, function (err, path) {
if (err || path === undefined) {
next(layerError || err)
return
}
// route object and not middleware
var route = layer.route
// if final route, then we support options
if (route) {
// we don't run any routes with error first
if (layerError) {
next(layerError)
return
}
var method = req.method
var has_method = route._handles_method(method)
// build up automatic options response
if (!has_method && method === 'OPTIONS') {
options.push.apply(options, route._options())
}
// don't even bother
if (!has_method && method !== 'HEAD') {
next()
return
}
// we can now dispatch to the route
req.route = route
}
// Capture one-time layer values
req.params = self.mergeParams
? mergeParams(layer.params, parentParams)
: layer.params
var layerPath = layer.path
// this should be done for the layer
self.process_params(layer, paramcalled, req, res, function (err) {
if (err) {
next(layerError || err)
return
}
if (route) {
return layer.handle_request(req, res, next)
}
trim_prefix(layer, layerError, layerPath, path)
})
})
}
function trim_prefix(layer, layerError, layerPath, path) {
var c = path[layerPath.length]
if (c && '/' !== c && '.' !== c) {
next(layerError)
return
}
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
if (layerPath.length !== 0) {
debug('trim prefix (%s) from url %s', layerPath, req.url)
removed = layerPath
req.url = protohost + req.url.substr(protohost.length + removed.length)
// Ensure leading slash
if (!fqdn && req.url[0] !== '/') {
req.url = '/' + req.url
slashAdded = true
}
// Setup base URL (no trailing slash)
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
? removed.substring(0, removed.length - 1)
: removed)
}
debug('%s %s : %s', layer.name, layerPath, req.originalUrl)
if (layerError) {
layer.handle_error(layerError, req, res, next)
} else {
layer.handle_request(req, res, next)
}
}
}
/**
* Match request to a layer.
*
* @private
*/
Router.prototype.match_layer = function match_layer(layer, req, res, done) {
var error = null
var path
try {
path = parseUrl(req).pathname
if (!layer.match(path)) {
path = undefined
}
} catch (err) {
error = err
}
done(error, path)
}
/**
* Process any parameters for the layer.
*
* @private
*/
Router.prototype.process_params = function process_params(layer, called, req, res, done) {
var params = this.params
// captured parameters from the layer, keys and values
var keys = layer.keys
// fast track
if (!keys || keys.length === 0) {
return done()
}
var i = 0
var name
var paramIndex = 0
var key
var paramVal
var paramCallbacks
var paramCalled
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err)
}
if (i >= keys.length ) {
return done()
}
paramIndex = 0
key = keys[i++]
if (!key) {
return done()
}
name = key.name
paramVal = req.params[name]
paramCallbacks = params[name]
paramCalled = called[name]
if (paramVal === undefined || !paramCallbacks) {
return param()
}
// param previously called with same value or error occurred
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
// restore value
req.params[name] = paramCalled.value
// next param
return param(paramCalled.error)
}
called[name] = paramCalled = {
error: null,
match: paramVal,
value: paramVal
}
paramCallback()
}
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++]
// store updated value
paramCalled.value = req.params[key.name]
if (err) {
// store error
paramCalled.error = err
param(err)
return
}
if (!fn) return param()
try {
fn(req, res, paramCallback, paramVal, key.name)
} catch (e) {
paramCallback(e)
}
}
param()
}
/**
* Use the given middleware function, with optional path, defaulting to "/".
*
* Use (like `.all`) will run for any http METHOD, but it will not add
* handlers for those methods so OPTIONS requests will not consider `.use`
* functions even if they could respond.
*
* The other difference is that _route_ path is stripped and not visible
* to the handler function. The main effect of this feature is that mounted
* handlers can operate without any code changes regardless of the "prefix"
* pathname.
*
* @public
*/
Router.prototype.use = function use(handler) {
var offset = 0
var path = '/'
// default path to '/'
// disambiguate router.use([handler])
if (typeof handler !== 'function') {
var arg = handler
while (Array.isArray(arg) && arg.length !== 0) {
arg = arg[0]
}
// first arg is the path
if (typeof arg !== 'function') {
offset = 1
path = handler
}
}
var callbacks = utils.flatten(slice.call(arguments, offset))
if (callbacks.length === 0) {
throw new TypeError('argument handler is required')
}
callbacks.forEach(function (fn) {
if (typeof fn !== 'function') {
throw new TypeError('argument handler must be a function')
}
// add the middleware
debug('use %s %s', path, fn.name || '<anonymous>')
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn)
layer.route = undefined
this.stack.push(layer)
}, this)
return this
}
/**
* Create a new Route for the given path.
*
* Each route contains a separate middleware stack and VERB handlers.
*
* See the Route api documentation for details on adding handlers
* and middleware to routes.
*
* @param {string} path
* @return {Route}
* @public
*/
Router.prototype.route = function route(path) {
var route = new Route(path)
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, handle)
function handle(req, res, next) {
route.dispatch(req, res, next)
}
layer.route = route
this.stack.push(layer)
return route
}
// create Router#VERB functions
methods.concat('all').forEach(function(method){
Router.prototype[method] = function (path) {
var route = this.route(path)
route[method].apply(route, slice.call(arguments, 1))
return this
}
})
/**
* Merge params with parent params
*
* @private
*/
function mergeParams(params, parent) {
if (typeof parent !== 'object' || !parent) {
return params
}
// make copy of parent for base
var obj = mixin({}, parent)
// simple non-numeric merging
if (!(0 in params) || !(0 in parent)) {
return mixin(obj, params)
}
var i = 0
var o = 0
// determine numeric gaps
while (i === o || o in parent) {
if (i in params) i++
if (o in parent) o++
}
// offset numeric indices in params before merge
for (i--; i >= 0; i--) {
params[i + o] = params[i]
// create holes for the merge when necessary
if (i < o) {
delete params[i]
}
}
return mixin(parent, params)
}
/**
* Restore obj props after function
*
* @private
*/
function restore(fn, obj) {
var props = new Array(arguments.length - 2)
var vals = new Array(arguments.length - 2)
for (var i = 0; i < props.length; i++) {
props[i] = arguments[i + 2]
vals[i] = obj[props[i]]
}
return function(err){
// restore vals
for (var i = 0; i < props.length; i++) {
obj[props[i]] = vals[i]
}
return fn.apply(this, arguments)
}
}
/**
* Wrap a function
*
* @private
*/
function wrap(old, fn) {
return function proxy() {
var args = new Array(arguments.length + 1)
args[0] = old
for (var i = 0, len = arguments.length; i < len; i++) {
args[i + 1] = arguments[i]
}
fn.apply(this, args)
}
}

45

package.json
{
"name":"router",
"version":"0.6.2",
"description":"A lean and mean http router",
"keywords":["connect","middleware","router","route","http"],
"repository": {"type": "git", "url": "git://github.com/gett/router.git"},
"contributors": [
"Mathias Buus Madsen <m@ge.tt>",
"Ian Jorgensen <i@ge.tt"
],
"dependencies": {}
"name": "router",
"description": "Simple middleware-style router",
"version": "1.0.0-beta.1",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT",
"repository": "pillarjs/router",
"dependencies": {
"debug": "~2.1.0",
"methods": "~1.1.0",
"parseurl": "~1.3.0",
"path-to-regexp": "0.1.3",
"utils-merge": "1.0.0"
},
"devDependencies": {
"after": "0.8.1",
"finalhandler": "0.3.2",
"istanbul": "0.3.2",
"mocha": "~2.0.1",
"supertest": "~0.14.0"
},
"files": [
"lib/",
"LICENSE",
"HISTORY.md",
"README.md",
"index.js"
],
"engines": {
"node": ">= 0.8"
},
"scripts": {
"test": "mocha --reporter spec --bail --check-leaks test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
}
}

@@ -1,104 +0,49 @@

# Router
# router
A lean and mean http router for [node.js](http://nodejs.org).
It is available through npm:
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]
npm install router
## Usage
Simple middleware-style router
Router does one thing and one thing only - route http requests.
## Installation
``` js
var http = require('http');
var router = require('router');
var route = router();
route.get('/', function(req, res) {
res.writeHead(200);
res.end('hello index page');
});
http.createServer(route).listen(8080); // start the server on port 8080
```sh
$ npm install router
```
If you want to grap a part of the path you can use capture groups in the pattern:
## API
``` js
route.get('/{base}', function(req, res) {
var base = req.params.base; // ex: if the path is /foo/bar, then base = foo
});
```js
var Router = require('router')
```
The capture patterns matches until the next `/` or character present after the group
This module is currently an extracted version from the Express 4.x project,
but with the main change being it can be used with a plain `http.createServer`
object or something like `connect` by removing Express-specific API calls.
``` js
route.get('/{x}x{y}', function(req, res) {
// if the path was /200x200, then req.params = {x:'200', y:'200'}
});
```
Documentation is forthcoming, but the Express 4.x documentation can be found
at http://expressjs.com/4x/api.html#router
Optional patterns are supported by adding a `?` at the end
## Testing
``` js
route.get('/{prefix}?/{top}', function(req, res) {
// matches both '/a/b' and '/b'
});
```sh
$ npm test
```
If you want to just match everything you can use a wildcard `*` which works like unix wildcards
## License
``` js
route.get('/{prefix}/*', function(req, res) {
// matches both '/a/', '/a/b', 'a/b/c' and so on.
// the value of the wildcard is available through req.params.wildcard
});
```
[MIT](LICENSE)
If the standard capture groups aren't expressive enough for you can specify an optional inline regex
``` js
route.get('/{digits}([0-9]+)', function(req, res) {
// matches both '/24' and '/424' but not '/abefest' and so on.
});
```
You can also use regular expressions and the related capture groups instead:
``` js
route.get(/^\/foo\/(\w+)/, function(req, res) {
var group = req.params[1]; // if path is /foo/bar, then group is bar
});
```
## Methods
* `route.get`: Match `GET` requests
* `route.post`: Match `POST` requests
* `route.put`: Match `PUT` requests
* `route.head`: Match `HEAD` requests
* `route.del`: Match `DELETE` requests
* `route.options`: Match `OPTIONS` requests
* `route.all`: Match all above request methods.
## Error handling
By default Router will return 404 if you no route matched. If you want to do your own thing you can give it a callback:
``` js
route(req, res, function() {
// no route was matched
res.writeHead(404);
res.end();
});
```
You can also provide a catch-all to a given route that is called if no route was matched:
``` js
route.get(function(req, res) {
// called if no other get route matched
res.writeHead(404);
res.end('no GET handler found');
});
```
[npm-image]: https://img.shields.io/npm/v/router.svg?style=flat
[npm-url]: https://npmjs.org/package/router
[node-version-image]: https://img.shields.io/node/v/router.svg?style=flat
[node-version-url]: http://nodejs.org/download/
[travis-image]: https://img.shields.io/travis/pillarjs/router.svg?style=flat
[travis-url]: https://travis-ci.org/pillarjs/router
[coveralls-image]: https://img.shields.io/coveralls/pillarjs/router.svg?style=flat
[coveralls-url]: https://coveralls.io/r/pillarjs/router?branch=master
[downloads-image]: https://img.shields.io/npm/dm/router.svg?style=flat
[downloads-url]: https://npmjs.org/package/router
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc