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 | , utils = require('./utils');
|
17 |
|
18 | // environment
|
19 |
|
20 | var env = process.env.NODE_ENV || 'development';
|
21 |
|
22 | /**
|
23 | * Initialize a new `Server` with the given `middleware`.
|
24 | *
|
25 | * Examples:
|
26 | *
|
27 | * var server = connect.createServer(
|
28 | * connect.favicon()
|
29 | * , connect.logger()
|
30 | * , connect.static(__dirname + '/public')
|
31 | * );
|
32 | *
|
33 | * @params {Array} middleware
|
34 | * @return {Server}
|
35 | * @api public
|
36 | */
|
37 |
|
38 | var Server = exports.Server = function HTTPServer(middleware) {
|
39 | this.stack = [];
|
40 | middleware.forEach(function(fn){
|
41 | this.use(fn);
|
42 | }, this);
|
43 | http.Server.call(this, this.handle);
|
44 | };
|
45 |
|
46 | /**
|
47 | * Inherit from `http.Server.prototype`.
|
48 | */
|
49 |
|
50 | Server.prototype.__proto__ = http.Server.prototype;
|
51 |
|
52 | /**
|
53 | * Utilize the given middleware `handle` to the given `route`,
|
54 | * defaulting to _/_. This "route" is the mount-point for the
|
55 | * middleware, when given a value other than _/_ the middleware
|
56 | * is only effective when that segment is present in the request's
|
57 | * pathname.
|
58 | *
|
59 | * For example if we were to mount a function at _/admin_, it would
|
60 | * be invoked on _/admin_, and _/admin/settings_, however it would
|
61 | * not be invoked for _/_, or _/posts_.
|
62 | *
|
63 | * This is effectively the same as passing middleware to `connect.createServer()`,
|
64 | * however provides a progressive api.
|
65 | *
|
66 | * Examples:
|
67 | *
|
68 | * var server = connect.createServer();
|
69 | * server.use(connect.favicon());
|
70 | * server.use(connect.logger());
|
71 | * server.use(connect.static(__dirname + '/public'));
|
72 | *
|
73 | * If we wanted to prefix static files with _/public_, we could
|
74 | * "mount" the `static()` middleware:
|
75 | *
|
76 | * server.use('/public', connect.static(__dirname + '/public'));
|
77 | *
|
78 | * This api is chainable, meaning the following is valid:
|
79 | *
|
80 | * connect.createServer()
|
81 | * .use(connect.favicon())
|
82 | * .use(connect.logger())
|
83 | * .use(connect.static(__dirname + '/public'))
|
84 | * .listen(3000);
|
85 | *
|
86 | * @param {String|Function} route or handle
|
87 | * @param {Function} handle
|
88 | * @return {Server}
|
89 | * @api public
|
90 | */
|
91 |
|
92 | Server.prototype.use = function(route, handle){
|
93 | this.route = '/';
|
94 |
|
95 | // default route to '/'
|
96 | if ('string' != typeof route) {
|
97 | handle = route;
|
98 | route = '/';
|
99 | }
|
100 |
|
101 | // wrap sub-apps
|
102 | if ('function' == typeof handle.handle) {
|
103 | var server = handle;
|
104 | server.route = route;
|
105 | handle = function(req, res, next) {
|
106 | server.handle(req, res, next);
|
107 | };
|
108 | }
|
109 |
|
110 | // wrap vanilla http.Servers
|
111 | if (handle instanceof http.Server) {
|
112 | handle = handle.listeners('request')[0];
|
113 | }
|
114 |
|
115 | // normalize route to not trail with slash
|
116 | if ('/' == route[route.length - 1]) {
|
117 | route = route.substr(0, route.length - 1);
|
118 | }
|
119 |
|
120 | // add the middleware
|
121 | this.stack.push({ route: route, handle: handle });
|
122 |
|
123 | // allow chaining
|
124 | return this;
|
125 | };
|
126 |
|
127 | /**
|
128 | * Handle server requests, punting them down
|
129 | * the middleware stack.
|
130 | *
|
131 | * @api private
|
132 | */
|
133 |
|
134 | Server.prototype.handle = function(req, res, out) {
|
135 | var writeHead = res.writeHead
|
136 | , stack = this.stack
|
137 | , removed = ''
|
138 | , index = 0;
|
139 |
|
140 | function next(err) {
|
141 | var layer, path, c;
|
142 | req.url = removed + req.url;
|
143 | req.originalUrl = req.originalUrl || req.url;
|
144 | removed = '';
|
145 |
|
146 | layer = stack[index++];
|
147 |
|
148 | // all done
|
149 | if (!layer || res.headerSent) {
|
150 | // but wait! we have a parent
|
151 | if (out) return out(err);
|
152 |
|
153 | // error
|
154 | if (err) {
|
155 | var msg = 'production' == env
|
156 | ? 'Internal Server Error'
|
157 | : err.stack || err.toString();
|
158 |
|
159 | // output to stderr in a non-test env
|
160 | if ('test' != env) console.error(err.stack || err.toString());
|
161 |
|
162 | // unable to respond
|
163 | if (res.headerSent) return req.socket.destroy();
|
164 |
|
165 | res.statusCode = 500;
|
166 | res.setHeader('Content-Type', 'text/plain');
|
167 | if ('HEAD' == req.method) return res.end();
|
168 | res.end(msg);
|
169 | } else {
|
170 | res.statusCode = 404;
|
171 | res.setHeader('Content-Type', 'text/plain');
|
172 | if ('HEAD' == req.method) return res.end();
|
173 | res.end('Cannot ' + req.method + ' ' + utils.escape(req.originalUrl));
|
174 | }
|
175 | return;
|
176 | }
|
177 |
|
178 | try {
|
179 | path = parse(req.url).pathname;
|
180 | if (undefined == path) path = '/';
|
181 |
|
182 | // skip this layer if the route doesn't match.
|
183 | if (0 != path.indexOf(layer.route)) return next(err);
|
184 |
|
185 | c = path[layer.route.length];
|
186 | if (c && '/' != c && '.' != c) 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 | }; |
\ | No newline at end of file |