4 * Copyright(c) 2010 Sencha Inc.
5 * Copyright(c) 2011 TJ Holowaychuk
10 * Module dependencies.
13 var http = require('http')
14 , parse = require('url').parse
15 , assert = require('assert');
19 var env = process.env.NODE_ENV || 'development';
22 * Initialize a new `Server` with the given `middleware`.
26 * var server = connect.createServer(
29 * , connect.static(__dirname + '/public')
32 * @params {Array} middleware
37 var Server = exports.Server = function HTTPServer(middleware) {
39 middleware.forEach(function(fn){
42 http.Server.call(this, this.handle);
46 * Inherit from `http.Server.prototype`.
49 Server.prototype.__proto__ = http.Server.prototype;
52 * Utilize the given middleware `handle` to the given `route`,
53 * defaulting to _/_. This "route" is the mount-point for the
54 * middleware, when given a value other than _/_ the middleware
55 * is only effective when that segment is present in the request's
58 * For example if we were to mount a function at _/admin_, it would
59 * be invoked on _/admin_, and _/admin/settings_, however it would
60 * not be invoked for _/_, or _/posts_.
62 * This is effectively the same as passing middleware to `connect.createServer()`,
63 * however provides a progressive api.
67 * var server = connect.createServer();
68 * server.use(connect.favicon());
69 * server.use(connect.logger());
70 * server.use(connect.static(__dirname + '/public'));
72 * If we wanted to prefix static files with _/public_, we could
73 * "mount" the `static()` middleware:
75 * server.use('/public', connect.static(__dirname + '/public'));
77 * This api is chainable, meaning the following is valid:
79 * connect.createServer()
80 * .use(connect.favicon())
81 * .use(connect.logger())
82 * .use(connect.static(__dirname + '/public'))
85 * @param {String|Function} route or handle
86 * @param {Function} handle
91 Server.prototype.use = function(route, handle){
94 // default route to '/'
95 if ('string' != typeof route) {
101 if (arguments.length > 2) {
102 return Array.prototype.slice.call(arguments, 1).forEach(function(fn){
108 if ('function' == typeof handle.handle) {
110 server.route = route;
111 handle = function(req, res, next) {
112 server.handle(req, res, next);
116 // wrap vanilla http.Servers
117 if (handle instanceof http.Server) {
118 handle = handle.listeners('request')[0];
121 // normalize route to not trail with slash
122 if ('/' == route[route.length - 1]) {
123 route = route.substr(0, route.length - 1);
126 // add the middleware
127 this.stack.push({ route: route, handle: handle });
134 * Handle server requests, punting them down
135 * the middleware stack.
140 Server.prototype.handle = function(req, res, out) {
141 var writeHead = res.writeHead
147 req.url = removed + req.url;
148 req.originalUrl = req.originalUrl || req.url;
151 var layer = stack[index++];
155 // but wait! we have a parent
156 if (out) return out(err);
158 // otherwise send a proper error message to the browser.
160 var msg = 'production' == env
161 ? 'Internal Server Error'
162 : err.stack || err.toString();
164 // output to stderr in a non-test env
165 if ('test' != env) console.error(err.stack || err.toString());
167 res.statusCode = 500;
168 res.setHeader('Content-Type', 'text/plain');
171 res.statusCode = 404;
172 res.setHeader('Content-Type', 'text/plain');
173 res.end('Cannot ' + req.method + ' ' + req.url);
179 var pathname = parse(req.url).pathname;
180 if (undefined == pathname) pathname = '/';
182 // skip this layer if the route doesn't match.
183 if (0 != pathname.indexOf(layer.route)) return next(err);
185 var nextChar = pathname[layer.route.length];
186 if (nextChar && '/' != nextChar && '.' != nextChar) return next(err);
188 // Call the layer handler
189 // Trim off the part of the url that matches the route
190 removed = layer.route;
191 req.url = req.url.substr(removed.length);
193 // Ensure leading slash
194 if ('/' != req.url[0]) req.url = '/' + req.url;
196 var arity = layer.handle.length;
199 layer.handle(err, req, res, next);
203 } else if (arity < 4) {
204 layer.handle(req, res, next);
209 if (e instanceof assert.AssertionError) {
210 console.error(e.stack + '\n');