const Template = require('./template');
// For logging.
-const _fileScope = common.fileScope(__filename);
+const { fileScope } = require('@squeep/log-helper');
+const _fileScope = fileScope(__filename);
const defaultOptions = {
ignoreTrailingSlash: true,
querystring,
};
+const cookieSplitRE = /; */;
+
class Dingus {
/**
* @param {Object} logger object which implements logging methods
/**
* Common header tagging for all requests.
* Add our own identifier, and persist any external transit identifiers.
+ * Sets requestId on ctx to a new uuid.
+ * If X-Request-Id or X-Correlation-Id exist on incoming headers, sets them
+ * on outgoing headers and sets on ctx.
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {object} ctx
/**
- *
+ * Sets ctx.clientAddress and ctx.clientProtocol.
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {object} ctx
/**
- * Called before every request handler.
+ * Sets ctx.cookie from Cookie header.
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {object} ctx
*/
+ static ingestCookie(req, res, ctx) {
+ ctx.cookie = {};
+ req.getHeader(Enum.Header.Cookie)?.split(cookieSplitRE).forEach((cookie) => {
+ const [ name, value ] = common.splitFirst(cookie, '=', null).map((x) => {
+ try {
+ return decodeURIComponent(x.trim());
+ } catch (e) {
+ return x;
+ }
+ });
+ if (name && !(name in ctx.cookie)) {
+ const isQuoted = value?.startsWith('"') && value.endsWith('"');
+ ctx.cookie[name] = isQuoted ? value.slice(1, -1) : value; // eslint-disable-line security/detect-object-injection
+ }
+ });
+ }
+
+
+ /**
+ * Called before every request handler.
+ * Sets tracking identifiers and client information on ctx.
+ * @param {http.ClientRequest} req
+ * @param {http.ServerResponse} res
+ * @param {object} ctx
+ */
async preHandler(req, res, ctx) {
- Dingus.tagContext(req, res, ctx);
+ this.constructor.tagContext(req, res, ctx);
this.clientAddressContext(req, res, ctx);
+ this.constructor.ingestCookie(req, res, ctx);
}
/**
* Read and parse request body data.
+ * Sets ctx.parsedBody, and optionally ctx.rawBody.
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {object} ctx
/**
* Set the best content type for the response.
+ * Sets ctx.responseType, and Content-Type header.
* @param {string[]} responseTypes default first
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
/**
- * Inserts an encoding
+ * Inserts an encoding into Content-Encoding header.
* @param {http.ServerResponse} res
* @param {string} encoding
*/
*/
async _serveFileMetaHeaders(res, directory, fileName) {
const _scope = _fileScope('_serveFileMetaHeaders');
- this.logger.debug(_scope, 'called', { directory, fileName });
+ let added = false;
const metaPrefix = '.';
const metaSuffix = '.meta';
const metaFileName = `${metaPrefix}${fileName}${metaSuffix}`;
const [stat, data] = await this._readFileInfo(metaFilePath);
if (!stat) {
- return;
+ return added;
}
const lineBreakRE = /\r\n|\n|\r/;
const result = headerParseRE.exec(line);
const { groups: header } = result;
res.setHeader(header.name, header.value);
+ added = true;
}
});
+ return added;
}
res.setHeader(Enum.Header.CacheControl, 'public');
if (this.staticMetadata) {
- await this._serveFileMetaHeaders(res, directory, fileName);
+ ctx.metaHeaders = await this._serveFileMetaHeaders(res, directory, fileName);
}
this.logger.debug(_scope, 'serving file', { filePath, contentType });