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 | // wrap sub-apps
|
101 | if ('function' == typeof handle.handle) {
|
102 | var server = handle;
|
103 | server.route = route;
|
104 | handle = function(req, res, next) {
|
105 | server.handle(req, res, next);
|
106 | };
|
107 | }
|
108 |
|
109 | // wrap vanilla http.Servers
|
110 | if (handle instanceof http.Server) {
|
111 | handle = handle.listeners('request')[0];
|
112 | }
|
113 |
|
114 | // normalize route to not trail with slash
|
115 | if ('/' == route[route.length - 1]) {
|
116 | route = route.substr(0, route.length - 1);
|
117 | }
|
118 |
|
119 | // add the middleware
|
120 | this.stack.push({ route: route, handle: handle });
|
121 |
|
122 | // allow chaining
|
123 | return this;
|
124 | };
|
125 |
|
126 | /**
|
127 | * Handle server requests, punting them down
|
128 | * the middleware stack.
|
129 | *
|
130 | * @api private
|
131 | */
|
132 |
|
133 | Server.prototype.handle = function(req, res, out) {
|
134 | var writeHead = res.writeHead
|
135 | , stack = this.stack
|
136 | , removed = ''
|
137 | , index = 0;
|
138 |
|
139 | function next(err) {
|
140 | var layer, path, c;
|
141 | req.url = removed + req.url;
|
142 | req.originalUrl = req.originalUrl || req.url;
|
143 | removed = '';
|
144 |
|
145 | layer = stack[index++];
|
146 |
|
147 | // all done
|
148 | if (!layer || res.headerSent) {
|
149 | // but wait! we have a parent
|
150 | if (out) return out(err);
|
151 |
|
152 | // error
|
153 | if (err) {
|
154 | var msg = 'production' == env
|
155 | ? 'Internal Server Error'
|
156 | : err.stack || err.toString();
|
157 |
|
158 | // output to stderr in a non-test env
|
159 | if ('test' != env) console.error(err.stack || err.toString());
|
160 |
|
161 | // unable to respond
|
162 | if (res.headerSent) return req.socket.destroy();
|
163 |
|
164 | res.statusCode = 500;
|
165 | res.setHeader('Content-Type', 'text/plain');
|
166 | if ('HEAD' == req.method) return res.end();
|
167 | res.end(msg);
|
168 | } else {
|
169 | res.statusCode = 404;
|
170 | res.setHeader('Content-Type', 'text/plain');
|
171 | if ('HEAD' == req.method) return res.end();
|
172 | res.end('Cannot ' + req.method + ' ' + req.url);
|
173 | }
|
174 | return;
|
175 | }
|
176 |
|
177 | try {
|
178 | path = parse(req.url).pathname;
|
179 | if (undefined == path) path = '/';
|
180 |
|
181 | // skip this layer if the route doesn't match.
|
182 | if (0 != path.indexOf(layer.route)) return next(err);
|
183 |
|
184 | c = path[layer.route.length];
|
185 | if (c && '/' != c && '.' != c) return next(err);
|
186 |
|
187 | // Call the layer handler
|
188 | // Trim off the part of the url that matches the route
|
189 | removed = layer.route;
|
190 | req.url = req.url.substr(removed.length);
|
191 |
|
192 | // Ensure leading slash
|
193 | if ('/' != req.url[0]) req.url = '/' + req.url;
|
194 |
|
195 | var arity = layer.handle.length;
|
196 | if (err) {
|
197 | if (arity === 4) {
|
198 | layer.handle(err, req, res, next);
|
199 | } else {
|
200 | next(err);
|
201 | }
|
202 | } else if (arity < 4) {
|
203 | layer.handle(req, res, next);
|
204 | } else {
|
205 | next();
|
206 | }
|
207 | } catch (e) {
|
208 | if (e instanceof assert.AssertionError) {
|
209 | console.error(e.stack + '\n');
|
210 | next(e);
|
211 | } else {
|
212 | next(e);
|
213 | }
|
214 | }
|
215 | }
|
216 | next();
|
217 | }; |
\ | No newline at end of file |