| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 | /*! * connect * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2015 Douglas Christopher Wilson * MIT Licensed */'use strict';/** * Module dependencies. * @private */var debug = require('debug')('connect:dispatcher');var EventEmitter = require('events').EventEmitter;var finalhandler = require('finalhandler');var http = require('http');var merge = require('utils-merge');var parseUrl = require('parseurl');/** * Module exports. * @public */module.exports = createServer;/** * Module variables. * @private */var env = process.env.NODE_ENV || 'development';var proto = {};/* istanbul ignore next */var defer = typeof setImmediate === 'function'  ? setImmediate  : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }/** * Create a new connect server. * * @return {function} * @public */function createServer() {  function app(req, res, next){ app.handle(req, res, next); }  merge(app, proto);  merge(app, EventEmitter.prototype);  app.route = '/';  app.stack = [];  return app;}/** * Utilize the given middleware `handle` to the given `route`, * defaulting to _/_. This "route" is the mount-point for the * middleware, when given a value other than _/_ the middleware * is only effective when that segment is present in the request's * pathname. * * For example if we were to mount a function at _/admin_, it would * be invoked on _/admin_, and _/admin/settings_, however it would * not be invoked for _/_, or _/posts_. * * @param {String|Function|Server} route, callback or server * @param {Function|Server} callback or server * @return {Server} for chaining * @public */proto.use = function use(route, fn) {  var handle = fn;  var path = route;  // default route to '/'  if (typeof route !== 'string') {    handle = route;    path = '/';  }  // wrap sub-apps  if (typeof handle.handle === 'function') {    var server = handle;    server.route = path;    handle = function (req, res, next) {      server.handle(req, res, next);    };  }  // wrap vanilla http.Servers  if (handle instanceof http.Server) {    handle = handle.listeners('request')[0];  }  // strip trailing slash  if (path[path.length - 1] === '/') {    path = path.slice(0, -1);  }  // add the middleware  debug('use %s %s', path || '/', handle.name || 'anonymous');  this.stack.push({ route: path, handle: handle });  return this;};/** * Handle server requests, punting them down * the middleware stack. * * @private */proto.handle = function handle(req, res, out) {  var index = 0;  var protohost = getProtohost(req.url) || '';  var removed = '';  var slashAdded = false;  var stack = this.stack;  // final function handler  var done = out || finalhandler(req, res, {    env: env,    onerror: logerror  });  // store the original URL  req.originalUrl = req.originalUrl || req.url;  function next(err) {    if (slashAdded) {      req.url = req.url.substr(1);      slashAdded = false;    }    if (removed.length !== 0) {      req.url = protohost + removed + req.url.substr(protohost.length);      removed = '';    }    // next callback    var layer = stack[index++];    // all done    if (!layer) {      defer(done, err);      return;    }    // route data    var path = parseUrl(req).pathname || '/';    var route = layer.route;    // skip this layer if the route doesn't match    if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {      return next(err);    }    // skip if route match does not border "/", ".", or end    var c = path.length > route.length && path[route.length];    if (c && c !== '/' && c !== '.') {      return next(err);    }    // trim off the part of the url that matches the route    if (route.length !== 0 && route !== '/') {      removed = route;      req.url = protohost + req.url.substr(protohost.length + removed.length);      // ensure leading slash      if (!protohost && req.url[0] !== '/') {        req.url = '/' + req.url;        slashAdded = true;      }    }    // call the layer handle    call(layer.handle, route, err, req, res, next);  }  next();};/** * Listen for connections. * * This method takes the same arguments * as node's `http.Server#listen()`. * * HTTP and HTTPS: * * If you run your application both as HTTP * and HTTPS you may wrap them individually, * since your Connect "server" is really just * a JavaScript `Function`. * *      var connect = require('connect') *        , http = require('http') *        , https = require('https'); * *      var app = connect(); * *      http.createServer(app).listen(80); *      https.createServer(options, app).listen(443); * * @return {http.Server} * @api public */proto.listen = function listen() {  var server = http.createServer(this);  return server.listen.apply(server, arguments);};/** * Invoke a route handle. * @private */function call(handle, route, err, req, res, next) {  var arity = handle.length;  var error = err;  var hasError = Boolean(err);  debug('%s %s : %s', handle.name || '<anonymous>', route, req.originalUrl);  try {    if (hasError && arity === 4) {      // error-handling middleware      handle(err, req, res, next);      return;    } else if (!hasError && arity < 4) {      // request-handling middleware      handle(req, res, next);      return;    }  } catch (e) {    // replace the error    error = e;  }  // continue  next(error);}/** * Log error using console.error. * * @param {Error} err * @private */function logerror(err) {  if (env !== 'test') console.error(err.stack || err.toString());}/** * Get get protocol + host for a URL. * * @param {string} url * @private */function getProtohost(url) {  if (url.length === 0 || url[0] === '/') {    return undefined;  }  var searchIndex = url.indexOf('?');  var pathLength = searchIndex !== -1    ? searchIndex    : url.length;  var fqdnIndex = url.substr(0, pathLength).indexOf('://');  return fqdnIndex !== -1    ? url.substr(0, url.indexOf('/', 3 + fqdnIndex))    : undefined;}
 |