Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50665 views
1
var events = require('events'),
2
qs = require('querystring'),
3
util = require('util'),
4
director = require('../../director'),
5
responses = require('./responses');
6
7
//
8
// ### Expose all HTTP methods and responses
9
//
10
exports.methods = require('./methods');
11
Object.keys(responses).forEach(function (name) {
12
exports[name] = responses[name];
13
})
14
15
//
16
// ### function Router (routes)
17
// #### @routes {Object} **Optional** Routing table for this instance.
18
// Constuctor function for the HTTP Router object responsible for building
19
// and dispatching from a given routing table.
20
//
21
var Router = exports.Router = function (routes) {
22
//
23
// ### Extend the `Router` prototype with all of the RFC methods.
24
//
25
this.params = {};
26
this.routes = {};
27
this.methods = ['on', 'after', 'before'];
28
this.scope = [];
29
this._methods = {};
30
this.recurse = 'forward';
31
this._attach = [];
32
33
this.extend(exports.methods.concat(['before', 'after']));
34
this.configure();
35
this.mount(routes || {});
36
};
37
38
//
39
// Inherit from `director.Router`.
40
//
41
util.inherits(Router, director.Router);
42
43
//
44
// ### function on (method, path, route)
45
// #### @method {string} **Optional** Method to use
46
// #### @path {string} Path to set this route on.
47
// #### @route {Array|function} Handler for the specified method and path.
48
// Adds a new `route` to this instance for the specified `method`
49
// and `path`.
50
//
51
Router.prototype.on = function (method, path) {
52
var args = Array.prototype.slice.call(arguments, 2),
53
route = args.pop(),
54
options = args.pop();
55
56
if (options && options.stream) {
57
route.stream = true;
58
}
59
60
director.Router.prototype.on.call(this, method, path, route);
61
};
62
63
//
64
// ### function attach (func)
65
// ### @func {function} Function to execute on `this` before applying to router function
66
// Ask the router to attach objects or manipulate `this` object on which the
67
// function passed to the http router will get applied
68
Router.prototype.attach = function (func) {
69
this._attach.push(func);
70
};
71
72
//
73
// ### function dispatch (method, path)
74
// #### @req {http.ServerRequest} Incoming request to dispatch.
75
// #### @res {http.ServerResponse} Outgoing response to dispatch.
76
// #### @callback {function} **Optional** Continuation to respond to for async scenarios.
77
// Finds a set of functions on the traversal towards
78
// `method` and `path` in the core routing table then
79
// invokes them based on settings in this instance.
80
//
81
Router.prototype.dispatch = function (req, res, callback) {
82
//
83
// Dispatch `HEAD` requests to `GET`
84
//
85
var method = req.method === 'HEAD' ? 'get' : req.method.toLowerCase(),
86
url = decodeURI(req.url.split('?', 1)[0]),
87
fns = this.traverse(method, url, this.routes, ''),
88
thisArg = { req: req, res: res },
89
self = this,
90
runlist,
91
stream,
92
error;
93
94
if (this._attach) {
95
for (var i in this._attach) {
96
this._attach[i].call(thisArg);
97
}
98
}
99
100
if (!fns || fns.length === 0) {
101
error = new exports.NotFound('Could not find path: ' + req.url)
102
if (typeof this.notfound === 'function') {
103
this.notfound.call(thisArg, callback);
104
}
105
else if (callback) {
106
callback.call(thisArg, error, req, res);
107
}
108
return false;
109
}
110
111
if (this.recurse === 'forward') {
112
fns = fns.reverse();
113
}
114
115
runlist = this.runlist(fns);
116
stream = runlist.some(function (fn) { return fn.stream === true });
117
118
function parseAndInvoke() {
119
error = self.parse(req);
120
if (error) {
121
if (callback) {
122
callback.call(thisArg, error, req, res);
123
}
124
return false;
125
}
126
127
self.invoke(runlist, thisArg, callback);
128
}
129
130
if (!stream) {
131
//
132
// If there is no streaming required on any of the functions on the
133
// way to `path`, then attempt to parse the fully buffered request stream
134
// once it has emitted the `end` event.
135
//
136
if (req.readable) {
137
//
138
// If the `http.ServerRequest` is still readable, then await
139
// the end event and then continue
140
//
141
req.once('end', parseAndInvoke)
142
}
143
else {
144
//
145
// Otherwise, just parse the body now.
146
//
147
parseAndInvoke();
148
}
149
}
150
else {
151
this.invoke(runlist, thisArg, callback);
152
}
153
154
return true;
155
};
156
157
//
158
// ### @parsers {Object}
159
// Lookup table of parsers to use when attempting to
160
// parse incoming responses.
161
//
162
Router.prototype.parsers = {
163
'application/x-www-form-urlencoded': qs.parse,
164
'application/json': JSON.parse
165
};
166
167
//
168
// ### function parse (req)
169
// #### @req {http.ServerResponse|BufferedStream} Incoming HTTP request to parse
170
// Attempts to parse `req.body` using the value found at `req.headers['content-type']`.
171
//
172
Router.prototype.parse = function (req) {
173
function mime(req) {
174
var str = req.headers['content-type'] || '';
175
return str.split(';')[0];
176
}
177
178
var parser = this.parsers[mime(req)],
179
body;
180
181
if (parser) {
182
req.body = req.body || '';
183
184
if (req.chunks) {
185
req.chunks.forEach(function (chunk) {
186
req.body += chunk;
187
});
188
}
189
190
try {
191
req.body = req.body && req.body.length
192
? parser(req.body)
193
: {};
194
}
195
catch (err) {
196
return new exports.BadRequest('Malformed data');
197
}
198
}
199
};
200
201
202