Skip to content

Commit 0d46a3a

Browse files
author
Eran Hammer
committed
refactor router
1 parent a0d89a3 commit 0d46a3a

File tree

4 files changed

+136
-184
lines changed

4 files changed

+136
-184
lines changed

lib/router.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Load modules
2+
3+
var Route = require('./route');
4+
var Utils = require('./utils');
5+
6+
7+
// Declare internals
8+
9+
var internals = {};
10+
11+
12+
module.exports = internals.Router = function (server) {
13+
14+
this.server = server;
15+
this.table = {}; // Array per HTTP method, including * for catch-all
16+
17+
this.notfound = new Route({
18+
method: 'notfound',
19+
path: '/{p*}',
20+
config: {
21+
auth: { mode: 'none' }, // In case defaults are set otherwise
22+
handler: 'notFound'
23+
}
24+
}, server);
25+
26+
if (server.settings.cors) {
27+
this.cors = new Route({
28+
path: '/{p*}',
29+
method: 'options',
30+
config: {
31+
auth: { mode: 'none' }, // In case defaults are set otherwise
32+
handler: function (request) {
33+
34+
request.reply({});
35+
}
36+
}
37+
}, server);
38+
}
39+
};
40+
41+
42+
internals.Router.prototype.route = function (request) {
43+
44+
// Lookup route
45+
46+
var method = (request.method === 'head' ? 'get' : request.method);
47+
48+
var routes = this.table[method] || [];
49+
for (var i = 0, il = routes.length; i<il;++i) {
50+
var route = routes[i];
51+
if (route.match(request)) {
52+
return route;
53+
}
54+
}
55+
56+
// CORS
57+
58+
if (method === 'options' &&
59+
this.cors) {
60+
61+
return this.cors;
62+
}
63+
64+
// *
65+
66+
routes = this.table['*'] || [];
67+
for (i = 0, il = routes.length; i < il; ++i) {
68+
var route = routes[i];
69+
if (route.match(request)) {
70+
return route;
71+
}
72+
}
73+
74+
// Not found
75+
76+
return this.notfound;
77+
};
78+
79+
80+
internals.Router.prototype.add = function (configs) {
81+
82+
var self = this;
83+
84+
Utils.assert(configs, 'Routes configs must exist');
85+
86+
configs = (configs instanceof Array ? configs : [configs]);
87+
88+
var methods = {};
89+
configs.forEach(function (config) {
90+
91+
var route = new Route(config, self.server); // Do no use config beyond this point, use route members
92+
93+
self.table[route.method] = self.table[route.method] || [];
94+
95+
// Check for existing route with same fingerprint
96+
97+
methods[route.method] = true;
98+
self.table[route.method].forEach(function (existing) {
99+
100+
Utils.assert(route.fingerprint !== existing.fingerprint, 'New route: ' + route.path + ' conflicts with existing: ' + existing.path);
101+
});
102+
103+
self.table[route.method].push(route);
104+
});
105+
106+
Object.keys(methods).forEach(function (method) {
107+
108+
self.table[method].sort(Route.sort);
109+
});
110+
};
111+
112+
113+
internals.Router.prototype.routingTable = function () {
114+
115+
var self = this;
116+
117+
var table = [];
118+
Object.keys(this.table).forEach(function (method) {
119+
120+
self.table[method].forEach(function (route) {
121+
122+
table.push(route);
123+
});
124+
});
125+
126+
return table;
127+
};

lib/server.js

Lines changed: 6 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var Catbox = require('catbox');
1111
var Defaults = require('./defaults');
1212
var Boom = require('boom');
1313
var Request = require('./request');
14-
var Route = require('./route');
14+
var Router = require('./router');
1515
var Schema = require('./schema');
1616
var Views = require('./views');
1717
var Utils = require('./utils');
@@ -23,8 +23,6 @@ var Utils = require('./utils');
2323
var internals = {};
2424

2525

26-
// Create and configure server instance
27-
2826
module.exports = internals.Server = function (/* host, port, options */) { // all optional
2927

3028
var self = this;
@@ -113,18 +111,7 @@ module.exports = internals.Server = function (/* host, port, options */) {
113111

114112
Utils.assert(!this.settings.router.routeDefaults || !this.settings.router.routeDefaults.handler, 'Route defaults cannot include a handler');
115113

116-
this._router = {
117-
table: {} // Array per HTTP method, including * for catch-all
118-
};
119-
120-
this._router.notfound = new Route({
121-
method: 'notfound',
122-
path: '/{p*}',
123-
config: {
124-
auth: { mode: 'none' }, // In case defaults are set otherwise
125-
handler: 'notFound'
126-
}
127-
}, this);
114+
this._router = new Router(this);
128115

129116
// Plugin interface (pack)
130117

@@ -161,20 +148,6 @@ module.exports = internals.Server = function (/* host, port, options */) {
161148
if (this.settings.cors) {
162149
this.settings.cors._headers = (this.settings.cors.headers || []).concat(this.settings.cors.additionalHeaders || []).join(', ');
163150
this.settings.cors._methods = (this.settings.cors.methods || []).concat(this.settings.cors.additionalMethods || []).join(', ');
164-
165-
var optionsConfig = {
166-
path: '/{p*}',
167-
method: 'options',
168-
config: {
169-
auth: { mode: 'none' }, // In case defaults are set otherwise
170-
handler: function (request) {
171-
172-
request.reply({});
173-
}
174-
}
175-
};
176-
177-
this._router.cors = new Route(optionsConfig, this);
178151
}
179152

180153
// Initialize cache engine
@@ -232,87 +205,15 @@ internals.Server.prototype._dispatch = function (options) {
232205

233206
// Lookup route
234207

235-
var method = (request.method === 'head' ? 'get' : request.method);
236-
237-
var routes = self._router.table[method];
238-
if (routes) {
239-
for (var i = 0, il = routes.length; i < il; ++i) {
240-
var route = routes[i];
241-
if (route.match(request)) {
242-
return request._execute(route);
243-
}
244-
}
245-
}
246-
247-
// CORS
248-
249-
if (method === 'options' &&
250-
self.settings.cors) {
251-
252-
return request._execute(self._router.cors);
253-
}
254-
255-
// *
256-
257-
routes = self._router.table['*'];
258-
if (routes) {
259-
for (i = 0, il = routes.length; i < il; ++i) {
260-
var route = routes[i];
261-
if (route.match(request)) {
262-
return request._execute(route);
263-
}
264-
}
265-
}
266-
267-
// Not found
268-
269-
return request._execute(self._router.notfound);
208+
request._execute(self._router.route(request));
270209
});
271210
};
272211
};
273212

274213

275-
// Find a route match
276-
277-
internals.Server.prototype._match = function (method, path) {
278-
279-
Utils.assert(method, 'The method parameter must be provided');
280-
Utils.assert(path, 'The path parameter must be provided');
281-
282-
// Lookup route
214+
internals.Server.prototype.routingTable = function () {
283215

284-
method = method.toLowerCase();
285-
method = (method === 'head' ? 'get' : method);
286-
var routes = this._router.table[method];
287-
288-
if (routes) {
289-
for (var i = 0, il = routes.length; i < il; ++i) {
290-
var route = routes[i];
291-
if (route.test(path)) {
292-
return route;
293-
}
294-
}
295-
}
296-
297-
return null;
298-
};
299-
300-
301-
internals.Server.prototype._routeTable = function () {
302-
303-
var self = this;
304-
305-
var flattenedRoutes = [];
306-
307-
Object.keys(this._router.table).forEach(function (method) {
308-
309-
self._router.table[method].forEach(function (route) {
310-
311-
flattenedRoutes.push(route);
312-
});
313-
});
314-
315-
return flattenedRoutes;
216+
return this._router.routingTable();
316217
};
317218

318219

@@ -408,34 +309,7 @@ internals.Server.prototype.ext = function (event, func) {
408309

409310
internals.Server.prototype.route = internals.Server.prototype.addRoute = internals.Server.prototype.addRoutes = function (configs) {
410311

411-
var self = this;
412-
413-
Utils.assert(configs, 'Routes configs must exist');
414-
415-
configs = (configs instanceof Array ? configs : [configs]);
416-
417-
var methods = {};
418-
configs.forEach(function (config) {
419-
420-
var route = new Route(config, self); // Do no use config beyond this point, use route members
421-
422-
self._router.table[route.method] = self._router.table[route.method] || [];
423-
424-
// Check for existing route with same fingerprint
425-
426-
methods[route.method] = true;
427-
var routes = self._router.table[route.method];
428-
for (var ri = 0, rl = routes.length; ri < rl; ++ri) {
429-
Utils.assert(route.fingerprint !== routes[ri].fingerprint, 'New route: ' + route.path + ' conflicts with existing: ' + routes[ri].path);
430-
}
431-
432-
routes.push(route);
433-
});
434-
435-
Object.keys(methods).forEach(function (method) {
436-
437-
self._router.table[method].sort(Route.sort);
438-
});
312+
this._router.add(configs);
439313
};
440314

441315

test/integration/proxy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ describe('Proxy', function () {
224224
callback(new Error());
225225
};
226226

227-
var route = _server._match('get', '/proxyerror');
227+
var route = _server._router.route({ method: 'get', path: '/proxyerror' });
228228
route.proxy.httpClient = requestStub;
229229

230230
makeRequest({ path: '/proxyerror', method: 'get' }, function (rawRes) {

test/unit/server.js

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -162,55 +162,6 @@ describe('Server', function () {
162162
done();
163163
});
164164

165-
describe('#_match', function () {
166-
167-
it('throws an error when the method parameter is null', function (done) {
168-
169-
var fn = function () {
170-
171-
var server = new Hapi.Server('0.0.0.0', 0);
172-
server._match(null, '/test');
173-
};
174-
expect(fn).to.throw(Error, 'The method parameter must be provided');
175-
done();
176-
});
177-
178-
it('throws an error when the path parameter is null', function (done) {
179-
180-
var fn = function () {
181-
182-
var server = new Hapi.Server('0.0.0.0', 0);
183-
server._match('POST', null);
184-
};
185-
expect(fn).to.throw(Error, 'The path parameter must be provided');
186-
done();
187-
});
188-
189-
it('returns null when no routes are added', function (done) {
190-
191-
var server = new Hapi.Server('0.0.0.0', 0);
192-
var result = server._match('GET', '/test');
193-
194-
expect(result).to.not.exist;
195-
done();
196-
});
197-
198-
it('returns the route when there is a match', function (done) {
199-
200-
var server = new Hapi.Server('0.0.0.0', 0);
201-
server.route({
202-
method: 'GET',
203-
path: '/test',
204-
handler: function () { }
205-
});
206-
var result = server._match('GET', '/test');
207-
208-
expect(result).exist;
209-
expect(result.path).to.equal('/test');
210-
done();
211-
});
212-
});
213-
214165
describe('#start', function () {
215166

216167
it('doesn\'t throw an error', function (done) {
@@ -558,7 +509,7 @@ describe('Server', function () {
558509
});
559510
});
560511

561-
describe('#_routeTable', function () {
512+
describe('#routingTable', function () {
562513

563514
it('returns an array of the current routes', function (done) {
564515

@@ -567,7 +518,7 @@ describe('Server', function () {
567518
server.route({ path: '/test/', method: 'get', handler: function () { } });
568519
server.route({ path: '/test/{p}/end', method: 'get', handler: function () { } });
569520

570-
var routes = server._routeTable();
521+
var routes = server.routingTable();
571522

572523
expect(routes.length).to.equal(2);
573524
expect(routes[0].path).to.equal('/test/');

0 commit comments

Comments
 (0)