Merge branch 'v2.1-dev'
[squeep-api-dingus] / lib / dingus.js
index 9bc428282b94c3f187563266377a370c65b38ccf..f060c18f00d251f610a97929fdce663c9f01de89 100644 (file)
@@ -19,7 +19,8 @@ const Router = require('./router');
 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,
@@ -34,6 +35,8 @@ const defaultOptions = {
   querystring,
 };
 
+const cookieSplitRE = /; */;
+
 class Dingus {
   /**
    * @param {Object} logger object which implements logging methods
@@ -117,6 +120,9 @@ class Dingus {
   /**
    * 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 
@@ -163,7 +169,7 @@ class Dingus {
 
 
   /**
-   * 
+   * Sets ctx.clientAddress and ctx.clientProtocol.
    * @param {http.ClientRequest} req 
    * @param {http.ServerResponse} res 
    * @param {object} ctx 
@@ -175,14 +181,40 @@ class Dingus {
 
 
   /**
-   * 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);
   }
 
 
@@ -410,6 +442,7 @@ class Dingus {
 
   /**
    * Read and parse request body data.
+   * Sets ctx.parsedBody, and optionally ctx.rawBody.
    * @param {http.ClientRequest} req
    * @param {http.ServerResponse} res
    * @param {object} ctx
@@ -453,6 +486,7 @@ class Dingus {
 
   /**
    * 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 
@@ -474,7 +508,7 @@ class Dingus {
 
 
   /**
-   * Inserts an encoding
+   * Inserts an encoding into Content-Encoding header.
    * @param {http.ServerResponse} res
    * @param {string} encoding
    */
@@ -519,8 +553,8 @@ class Dingus {
    */
   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}`;
@@ -528,7 +562,7 @@ class Dingus {
 
     const [stat, data] = await this._readFileInfo(metaFilePath);
     if (!stat) {
-      return;
+      return added;
     }
 
     const lineBreakRE = /\r\n|\n|\r/;
@@ -541,8 +575,10 @@ class Dingus {
         const result = headerParseRE.exec(line);
         const { groups: header } = result;
         res.setHeader(header.name, header.value);
+        added = true;
       }
     });
+    return added;
   }
 
 
@@ -635,7 +671,7 @@ class Dingus {
     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 });