|  | /*! | 
|  | * 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 fqdnIndex = url.indexOf('://') | 
|  |  | 
|  | return fqdnIndex !== -1 && url.lastIndexOf('?', fqdnIndex) === -1 | 
|  | ? url.substr(0, url.indexOf('/', 3 + fqdnIndex)) | 
|  | : undefined; | 
|  | } |