strictAccept: true,
selfBaseUrl: '',
staticMetadata: true,
+ staticPath: undefined, // no reasonable default
trustProxy: true,
querystring,
};
* @param {string} urlPath
* @param {fn} handler
*/
- on(method, urlPath, handler) {
- this.router.on(method, urlPath, handler);
+ on(method, urlPath, handler, ...handlerArgs) {
+ this.router.on(method, urlPath, handler, handlerArgs);
}
const { pathPart, queryParams } = this._splitUrl(req.url);
ctx.queryParams = queryParams;
- let handler;
+ let handler, handlerArgs = [];
try {
- handler = this.router.lookup(req.method, pathPart, ctx);
+ ({ handler, handlerArgs } = this.router.lookup(req.method, pathPart, ctx));
} catch (e) {
if (e instanceof DingusError) {
switch (e.message) {
default:
this.logger.error(_scope, 'unknown dingus error', { error: e });
handler = this.handlerInternalServerError.bind(this);
- }
+ }
} else if (e instanceof URIError) {
handler = this.handlerBadRequest.bind(this);
} else {
try {
await this.preHandler(req, res, ctx);
- return await handler(req, res, ctx);
+ return await handler(req, res, ctx, ...handlerArgs);
} catch (e) {
ctx.error = e;
this.sendErrorResponse(e, req, res, ctx);
/**
* Return all body data from a request.
* @param {http.ClientRequest} req
+ * @param {Number=} maximumBodySize
*/
- async bodyData(req) {
+ async bodyData(req, maximumBodySize) {
const _scope = _fileScope('bodyData');
return new Promise((resolve, reject) => {
const body = [];
- req.on('data', (chunk) => body.push(chunk));
+ let length = 0;
+ req.on('data', (chunk) => {
+ body.push(chunk);
+ length += Buffer.byteLength(chunk);
+ if (maximumBodySize && length > maximumBodySize) {
+ this.logger.debug(_scope, 'body data exceeded limit', { length, maximumBodySize });
+ reject(new ResponseError(Enum.ErrorResponse.RequestEntityTooLarge));
+ }
+ });
req.on('end', () => resolve(Buffer.concat(body).toString()));
req.on('error', (e) => {
this.logger.error(_scope, 'failed', { error: e });
const _scope = _fileScope('serveFile');
this.logger.debug(_scope, 'called', { req: common.requestLogData(req), ctx });
+ // Require a directory field.
+ if (!directory) {
+ this.logger.debug(_scope, 'rejected unset directory', { fileName });
+ return this.handlerNotFound(req, res, ctx);
+ }
+
// Normalize the supplied path, as encoded path-navigation may have been (maliciously) present.
fileName = path.normalize(fileName);
}
+ /**
+ * @param {http.ClientRequest} req
+ * @param {http.ServerResponse} res
+ * @param {object} ctx
+ * @param {String} file - override ctx.params.file
+ */
+ async handlerGetStaticFile(req, res, ctx, file) {
+ Dingus.setHeadHandler(req, res, ctx);
+
+ // Set a default response type to handle any errors; will be re-set to serve actual static content type.
+ this.setResponseType(this.responseTypes, req, res, ctx);
+
+ await this.serveFile(req, res, ctx, this.staticPath, file || ctx.params.file);
+ }
+
+
+ /**
+ * @param {http.ClientRequest} req
+ * @param {http.ServerResponse} res
+ * @param {Object} ctx
+ * @param {String} newPath
+ * @param {Number} statusCode
+ */
+ async handlerRedirect(req, res, ctx, newPath, statusCode = 307) {
+ this.setResponseType(this.responseTypes, req, res, ctx);
+ res.setHeader(Enum.Header.Location, newPath);
+ res.statusCode = statusCode;
+ res.end();
+ }
+
+
/**
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {http.ServerResponse} res
* @param {object} ctx
*/
- async handlerBadRequest(req, res, ctx) {
+ async handlerBadRequest(req, res, ctx) {
this.setResponseType(this.responseTypes, req, res, ctx);
throw new ResponseError(Enum.ErrorResponse.BadRequest);
}
* @param {http.ServerResponse} res
* @param {object} ctx
*/
- async handlerInternalServerError(req, res, ctx) {
+ async handlerInternalServerError(req, res, ctx) {
this.setResponseType(this.responseTypes, req, res, ctx);
throw new ResponseError(Enum.ErrorResponse.InternalServerError);
}