]> git.cworth.org Git - obsolete/notmuch-web/blob - node_modules/express/node_modules/connect/lib/http.js
Install the "express" node module via npm
[obsolete/notmuch-web] / node_modules / express / node_modules / connect / lib / http.js
1
2 /*!
3  * Connect - HTTPServer
4  * Copyright(c) 2010 Sencha Inc.
5  * Copyright(c) 2011 TJ Holowaychuk
6  * MIT Licensed
7  */
8
9 /**
10  * Module dependencies.
11  */
12
13 var http = require('http')
14   , parse = require('url').parse
15   , assert = require('assert');
16
17 // environment
18
19 var env = process.env.NODE_ENV || 'development';
20
21 /**
22  * Initialize a new `Server` with the given `middleware`.
23  *
24  * Examples:
25  *
26  *     var server = connect.createServer(
27  *         connect.favicon()
28  *       , connect.logger()
29  *       , connect.static(__dirname + '/public')
30  *     );
31  *
32  * @params {Array} middleware 
33  * @return {Server}
34  * @api public
35  */
36
37 var Server = exports.Server = function HTTPServer(middleware) {
38   this.stack = [];
39   middleware.forEach(function(fn){
40     this.use(fn);
41   }, this);
42   http.Server.call(this, this.handle);
43 };
44
45 /**
46  * Inherit from `http.Server.prototype`.
47  */
48
49 Server.prototype.__proto__ = http.Server.prototype;
50
51 /**
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
56  * pathname.
57  *
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_.
61  *
62  * This is effectively the same as passing middleware to `connect.createServer()`,
63  * however provides a progressive api.
64  *
65  * Examples:
66  *
67  *      var server = connect.createServer();
68  *      server.use(connect.favicon());
69  *      server.use(connect.logger());
70  *      server.use(connect.static(__dirname + '/public'));
71  *
72  * If we wanted to prefix static files with _/public_, we could
73  * "mount" the `static()` middleware:
74  *
75  *      server.use('/public', connect.static(__dirname + '/public'));
76  *
77  * This api is chainable, meaning the following is valid:
78  *
79  *      connect.createServer()
80  *        .use(connect.favicon())
81  *        .use(connect.logger())
82  *        .use(connect.static(__dirname + '/public'))
83  *        .listen(3000);
84  *
85  * @param {String|Function} route or handle
86  * @param {Function} handle
87  * @return {Server}
88  * @api public
89  */
90
91 Server.prototype.use = function(route, handle){
92   this.route = '/';
93
94   // default route to '/'
95   if ('string' != typeof route) {
96     handle = route;
97     route = '/';
98   }
99
100   // multiples
101   if (arguments.length > 2) {
102     return Array.prototype.slice.call(arguments, 1).forEach(function(fn){
103       this.use(route, fn);
104     }, this);
105   }
106
107   // wrap sub-apps
108   if ('function' == typeof handle.handle) {
109     var server = handle;
110     server.route = route;
111     handle = function(req, res, next) {
112       server.handle(req, res, next);
113     };
114   }
115
116   // wrap vanilla http.Servers
117   if (handle instanceof http.Server) {
118     handle = handle.listeners('request')[0];
119   }
120
121   // normalize route to not trail with slash
122   if ('/' == route[route.length - 1]) {
123     route = route.substr(0, route.length - 1);
124   }
125
126   // add the middleware
127   this.stack.push({ route: route, handle: handle });
128
129   // allow chaining
130   return this;
131 };
132
133 /**
134  * Handle server requests, punting them down
135  * the middleware stack.
136  *
137  * @api private
138  */
139
140 Server.prototype.handle = function(req, res, out) {
141   var writeHead = res.writeHead
142     , stack = this.stack
143     , removed = ''
144     , index = 0;
145
146   function next(err) {
147     req.url = removed + req.url;
148     req.originalUrl = req.originalUrl || req.url;
149     removed = '';
150
151     var layer = stack[index++];
152
153     // all done
154     if (!layer) {
155       // but wait! we have a parent
156       if (out) return out(err);
157
158       // otherwise send a proper error message to the browser.
159       if (err) {
160         var msg = 'production' == env
161           ? 'Internal Server Error'
162           : err.stack || err.toString();
163
164         // output to stderr in a non-test env
165         if ('test' != env) console.error(err.stack || err.toString());
166
167         res.statusCode = 500;
168         res.setHeader('Content-Type', 'text/plain');
169         res.end(msg);
170       } else {
171         res.statusCode = 404;
172         res.setHeader('Content-Type', 'text/plain');
173         res.end('Cannot ' + req.method + ' ' + req.url);
174       }
175       return;
176     }
177
178     try {
179       var pathname = parse(req.url).pathname;
180       if (undefined == pathname) pathname = '/';
181
182       // skip this layer if the route doesn't match.
183       if (0 != pathname.indexOf(layer.route)) return next(err);
184
185       var nextChar = pathname[layer.route.length];
186       if (nextChar && '/' != nextChar && '.' != nextChar) return next(err);
187
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);
192
193       // Ensure leading slash
194       if ('/' != req.url[0]) req.url = '/' + req.url;
195
196       var arity = layer.handle.length;
197       if (err) {
198         if (arity === 4) {
199           layer.handle(err, req, res, next);
200         } else {
201           next(err);
202         }
203       } else if (arity < 4) {
204         layer.handle(req, res, next);
205       } else {
206         next();
207       }
208     } catch (e) {
209       if (e instanceof assert.AssertionError) {
210         console.error(e.stack + '\n');
211         next(e);
212       } else {
213         next(e);
214       }
215     }
216   }
217   next();
218 };