Skip to content

Commit

Permalink
Use a single fetch handler per Router (#382)
Browse files Browse the repository at this point in the history
* Use a single fetch handler per Router

* Keep track of routes on a per-method basis.

* Add a comment.
  • Loading branch information
jeffposnick authored Mar 25, 2017
1 parent 3a0bd5c commit 7811957
Showing 1 changed file with 87 additions and 74 deletions.
161 changes: 87 additions & 74 deletions packages/sw-routing/src/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,68 +50,19 @@ import logHelper from '../../../../lib/log-helper.js';
*/
class Router {
/**
* An optional default handler will have its handle method called when a
* request doesn't have a matching route.
*
* @example
* router.setDefaultHandler({
* handler: new goog.runtimeCaching.NetworkFirst()
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
*/
setDefaultHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');

this.defaultHandler = handler;
}

/**
* If a Route throws an error while handling a request, this catch handler
* will be called to return an error case.
*
* @example
* router.setCatchHandler({
* handler: ({event, params}) => {
* return caches.match('/error-page.html');
* }
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
* Start with an empty array of routes, and set up the fetch handler.
*/
setCatchHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');

this.catchHandler = handler;
}

/**
* Register routes will take an array of Routes to register with the
* router.
*
* @example
* router.registerRoutes({
* routes: [
* new RegExpRoute({ ... }),
* new ExpressRoute({ ... }),
* new Route({ ... }),
* ]
* });
*
* @param {Object} input
* @param {Array.<Route>} input.routes An array of routes to register.
*/
registerRoutes({routes} = {}) {
assert.isArrayOfClass({routes}, Route);
constructor() {
// _routes will contain a mapping of HTTP method name ('GET', etc.) to an
// array of all the corresponding Route instances that are registered.
this._routes = new Map();

self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (!url.protocol.startsWith('http')) {
logHelper.log({
that: this,
message: 'URL does not start with HTTP and so not parsing ' +
message: 'URL does not start with HTTP and so not passing ' +
'through the router.',
data: {
request: event.request,
Expand All @@ -122,11 +73,7 @@ class Router {

let responsePromise;
let matchingRoute;
for (let route of (routes || [])) {
if (route.method !== event.request.method) {
continue;
}

for (let route of (this._routes.get(event.request.method) || [])) {
const matchResult = route.match({url, event});
if (matchResult) {
matchingRoute = route;
Expand All @@ -151,7 +98,6 @@ class Router {
params = undefined;
}

matchingRoute = route;
responsePromise = route.handler.handle({url, event, params});
break;
}
Expand All @@ -169,23 +115,90 @@ class Router {

if (responsePromise) {
event.respondWith(responsePromise
.then((response) => {
logHelper.debug({
that: this,
message: 'The router is managing a route with a response.',
data: {
route: matchingRoute,
request: event.request,
response: response,
},
});

return response;
}));
.then((response) => {
logHelper.debug({
that: this,
message: 'The router is managing a route with a response.',
data: {
route: matchingRoute,
request: event.request,
response: response,
},
});

return response;
}));
}
});
}

/**
* An optional default handler will have its handle method called when a
* request doesn't have a matching route.
*
* @example
* router.setDefaultHandler({
* handler: new goog.runtimeCaching.NetworkFirst()
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
*/
setDefaultHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');

this.defaultHandler = handler;
}

/**
* If a Route throws an error while handling a request, this catch handler
* will be called to return an error case.
*
* @example
* router.setCatchHandler({
* handler: ({event, params}) => {
* return caches.match('/error-page.html');
* }
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
*/
setCatchHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');

this.catchHandler = handler;
}

/**
* Register routes will take an array of Routes to register with the
* router.
*
* @example
* router.registerRoutes({
* routes: [
* new RegExpRoute({ ... }),
* new ExpressRoute({ ... }),
* new Route({ ... }),
* ]
* });
*
* @param {Object} input
* @param {Array.<Route>} input.routes An array of routes to register.
*/
registerRoutes({routes} = {}) {
assert.isArrayOfClass({routes}, Route);

for (let route of routes) {
if (!this._routes.has(route.method)) {
this._routes.set(route.method, []);
}

// Give precedence to the most recent route by listing it first.
this._routes.get(route.method).unshift(route);
}
}

/**
* Registers a single route with the router.
*
Expand Down

0 comments on commit 7811957

Please sign in to comment.