X-Git-Url: http://git.squeep.com/?p=squeep-api-dingus;a=blobdiff_plain;f=lib%2Fdingus.js;fp=lib%2Fdingus.js;h=9bc428282b94c3f187563266377a370c65b38ccf;hp=0f2af748eca503201a9c2e08be97e1bae31462ba;hb=fc65c40bd5bb594df4776d411a4b7cd6fb462596;hpb=6207a1ec646ffe93d8dabc4c74d330348aa61a49 diff --git a/lib/dingus.js b/lib/dingus.js index 0f2af74..9bc4282 100644 --- a/lib/dingus.js +++ b/lib/dingus.js @@ -13,7 +13,7 @@ const querystring = require('querystring'); const common = require('./common'); const ContentNegotiation = require('./content-negotiation'); const Enum = require('./enum'); -const { DingusError, ResponseError, RouterNoMethodError, RouterNoPathError } = require('./errors'); +const { ResponseError, RouterNoPathError, RouterNoMethodError } = require('./errors'); const { extensionToMime } = require('./mime-helper'); const Router = require('./router'); const Template = require('./template'); @@ -29,6 +29,8 @@ const defaultOptions = { staticMetadata: true, staticPath: undefined, // No reasonable default trustProxy: true, + intrinsicHeadMethod: true, + intrinsicHeadPersistBody: false, querystring, }; @@ -42,6 +44,8 @@ class Dingus { * @param {string} options.selfBaseUrl for constructing links * @param {Boolean} options.staticMetadata serve static headers with static files * @param {Boolean} options.trustProxy trust some header data to be provided by proxy + * @param {Boolean} options.intrinsicHeadMethod handle HEAD requests automatically if not specified as a route method + * @param {Boolean} options.intrinsicHeadPersistBody include un-sent body on ctx for automatic HEAD requests * @param {Object} options.querystring alternate qs parser to use */ constructor(logger = console, options = {}) { @@ -253,13 +257,14 @@ class Dingus { /** - * Dispatch the handler for a request - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {object} ctx + * Resolve the handler to invoke for a request. + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + * @param {object} ctx + * @returns {object} */ - async dispatch(req, res, ctx = {}) { - const _scope = _fileScope('dispatch'); + _determineHandler(req, res, ctx) { + const _scope = _fileScope('_determineHandler'); const { pathPart, queryParams } = this._splitUrl(req.url); ctx.queryParams = queryParams; @@ -268,21 +273,59 @@ class Dingus { try { ({ handler, handlerArgs } = this.router.lookup(req.method, pathPart, ctx)); } catch (e) { - if (e instanceof RouterNoPathError) { + if (e instanceof URIError) { + handler = this.handlerBadRequest.bind(this); + } else if (e instanceof RouterNoPathError) { handler = this.handlerNotFound.bind(this); } else if (e instanceof RouterNoMethodError) { - handler = this.handlerMethodNotAllowed.bind(this); - } else if (e instanceof DingusError) { - this.logger.error(_scope, 'unknown dingus error', { error: e }); + if (this.intrinsicHeadMethod && req.method === 'HEAD') { + ({ handler, handlerArgs } = this._determineHeadHandler(req, res, ctx, pathPart)); + } else { + handler = this.handlerMethodNotAllowed.bind(this); + } + } else { + this.logger.error(_scope, 'unexpected error', { error: e }); handler = this.handlerInternalServerError.bind(this); - } else if (e instanceof URIError) { - handler = this.handlerBadRequest.bind(this); + } + } + return { handler, handlerArgs }; + } + + + /** + * For intrinsic HEAD requests, resolve the handler to invoke. + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + * @param {object} ctx + * @param {string} pathPart + * @returns {object} + */ + _determineHeadHandler(req, res, ctx, pathPart) { + const _scope = _fileScope('_determineHeadHandler'); + let handler, handlerArgs = []; + try { + ({ handler, handlerArgs } = this.router.lookup('GET', pathPart, ctx)); + Dingus.setHeadHandler(req, res, ctx, this.intrinsicHeadPersistBody); + } catch (e) { + if (e instanceof RouterNoMethodError) { + handler = this.handlerMethodNotAllowed.bind(this); } else { - this.logger.error(_scope, 'lookup failure', { error: e }); + this.logger.error(_scope, 'unexpected error', { error: e }); handler = this.handlerInternalServerError.bind(this); } } + return { handler, handlerArgs }; + } + + /** + * Dispatch the handler for a request + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + * @param {object} ctx + */ + async dispatch(req, res, ctx = {}) { + const { handler, handlerArgs } = this._determineHandler(req, res, ctx); try { await this.preHandler(req, res, ctx); return await handler(req, res, ctx, ...handlerArgs);