From: Justin Wind Date: Sat, 12 Mar 2022 22:07:48 +0000 (-0800) Subject: Merge branch 'v1.2-dev' as v1.2.5 X-Git-Tag: v1.2.5 X-Git-Url: http://git.squeep.com/?p=squeep-api-dingus;a=commitdiff_plain;h=12568946a94e853c3c16974d57dd34de1ad3877c;hp=0ebfbe2181ce363e13d8b6fd10c9703897d5306f Merge branch 'v1.2-dev' as v1.2.5 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ac75dc..0a69b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ Releases and notable changes to this project are documented here. ## [Unreleased] +## [v1.2.5] - 2022-03-12 + +### Added + +- options to some helper methods, to not persist rarely-used and redundant information on contexts +- optional maximum request body size +- updated dependencies + +## [v1.2.4] - 2022-01-22 + +### Added + +- updated dependencies + ## [v1.2.3] - 2021-12-30 ### Added @@ -44,7 +58,9 @@ Releases and notable changes to this project are documented here. --- -[Unreleased]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=HEAD;hp=v1.2.3 +[Unreleased]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=HEAD;hp=v1.2.5 +[v1.2.5]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=v1.2.5;hp=v1.2.4 +[v1.2.4]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=v1.2.4;hp=v1.2.3 [v1.2.3]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=v1.2.3;hp=v1.2.2 [v1.2.2]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=v1.2.2;hp=v1.2.1 [v1.2.1]: https://git.squeep.com/?p=squeep-api-dingus;a=commitdiff;h=v1.2.1;hp=v1.2.0 diff --git a/lib/dingus.js b/lib/dingus.js index 84c1d14..51da6fa 100644 --- a/lib/dingus.js +++ b/lib/dingus.js @@ -226,11 +226,13 @@ class Dingus { /** * Intercept writes for head requests, do not send to client, * but send length, and make body available in context. + * N.B. If persisted, ctx.responseBody will be a raw buffer, be aware when logging. * @param {http.ClientRequest} req * @param {http.ServerResponse} res * @param {object} ctx + * @param {Boolean} persistResponseBody */ - static setHeadHandler(req, res, ctx) { + static setHeadHandler(req, res, ctx, persistResponseBody = false) { if (req.method === 'HEAD') { const origEnd = res.end.bind(res); const chunks = []; @@ -240,8 +242,11 @@ class Dingus { }; res.end = function (data, encoding, ...rest) { Dingus.pushBufChunk(chunks, data, encoding); - ctx.responseBody = Buffer.concat(chunks); - res.setHeader(Enum.Header.ContentLength, Buffer.byteLength(ctx.responseBody)); + const responseBody = Buffer.concat(chunks); + res.setHeader(Enum.Header.ContentLength, Buffer.byteLength(responseBody)); + if (persistResponseBody) { + ctx.responseBody = responseBody; + } return origEnd(undefined, encoding, ...rest); }; } @@ -306,21 +311,27 @@ class Dingus { /** - * Parse rawBody from ctx as contentType into parsedBody. - * @param {string} contentType - * @param {object} ctx - */ - parseBody(contentType, ctx) { + * Parse rawBody as contentType into ctx.parsedBody. + * @param {string} contentType + * @param {object} ctx + * @param {string|buffer} + */ + parseBody(contentType, ctx, rawBody) { const _scope = _fileScope('parseBody'); + if (!rawBody) { + // 1.2.4 and earlier expected rawBody on context + rawBody = ctx.rawBody; + } + switch (contentType) { case Enum.ContentType.ApplicationForm: - ctx.parsedBody = this.querystring.parse(ctx.rawBody); + ctx.parsedBody = this.querystring.parse(rawBody); break; case Enum.ContentType.ApplicationJson: try { - ctx.parsedBody = JSON.parse(ctx.rawBody); + ctx.parsedBody = JSON.parse(rawBody); } catch (e) { this.logger.debug(_scope, 'JSON parse failed', { requestId: ctx.requestId, error: e }); throw new ResponseError(Enum.ErrorResponse.BadRequest, e.message); @@ -337,12 +348,21 @@ class Dingus { /** * 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 }); @@ -357,11 +377,19 @@ class Dingus { * @param {http.ClientRequest} req * @param {http.ServerResponse} res * @param {object} ctx - */ - async ingestBody(req, res, ctx) { - ctx.rawBody = await this.bodyData(req); - const contentType = Dingus.getRequestContentType(req); - this.parseBody(contentType, ctx); + * @param {object} + * @param {Boolean} .parseEmptyBody + * @param {Boolean} .persistRawBody + */ + async ingestBody(req, res, ctx, { parseEmptyBody = true, persistRawBody = false, maximumBodySize } = {}) { + const rawBody = await this.bodyData(req, maximumBodySize); + if (persistRawBody) { + ctx.rawBody = rawBody; + } + if (rawBody || parseEmptyBody) { + const contentType = Dingus.getRequestContentType(req); + this.parseBody(contentType, ctx, rawBody); + } } diff --git a/lib/enum.js b/lib/enum.js index ef8e773..e24ed6f 100644 --- a/lib/enum.js +++ b/lib/enum.js @@ -56,6 +56,10 @@ const ErrorResponse = { statusCode: 410, errorMessage: 'Gone', }, + RequestEntityTooLarge: { + statusCode: 413, + errorMessage: 'Request Entity Too Large', + }, UnsupportedMediaType: { statusCode: 415, errorMessage: 'Unsupported Media Type', diff --git a/package-lock.json b/package-lock.json index 91aa609..8d270e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@squeep/api-dingus", - "version": "1.2.4", + "version": "1.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -236,16 +236,16 @@ } }, "@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.2.0", + "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -267,12 +267,6 @@ "ms": "2.1.2" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -285,9 +279,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -375,18 +369,18 @@ } }, "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.0.0.tgz", + "integrity": "sha512-+shXA2X7KNP7H7qNbQTJ3SA+NQc0pZDSBrdvFSRwF8sAo/ohw+ZQFD8Moc+gnz51+1eRXtEQBpKWPiQ4jsRC/w==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" } }, "@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -620,9 +614,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -805,12 +799,12 @@ "dev": true }, "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.5", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -818,10 +812,10 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", + "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -886,9 +880,9 @@ } }, "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "js-yaml": { @@ -959,15 +953,15 @@ } }, "eslint-plugin-sonarjs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.11.0.tgz", - "integrity": "sha512-ei/WuZiL0wP+qx2KrxKyZs3+eDbxiGAhFSm3GKCOOAUkg+G2ny6TSXDB2j67tvyqHefi+eoQsAgGQvz+nEtIBw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.12.0.tgz", + "integrity": "sha512-soxjK67hoYxO8hesKqXWN50GSM+oG2r35N5WnAMehetahO6zoVpv3HZbdziP0jYWNopEe6te/BFUZOYAZI+qhg==", "dev": true }, "eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -990,20 +984,20 @@ "dev": true }, "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "requires": { "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "eslint-visitor-keys": "^3.3.0" }, "dependencies": { "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true } } @@ -1118,9 +1112,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "foreground-child": { @@ -1193,9 +1187,9 @@ } }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -1636,32 +1630,32 @@ "dev": true }, "mocha": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", - "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -1674,9 +1668,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -1697,9 +1691,9 @@ "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1708,6 +1702,17 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "has-flag": { @@ -1725,6 +1730,15 @@ "argparse": "^2.0.1" } }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1749,9 +1763,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "natural-compare": { @@ -1761,27 +1775,16 @@ "dev": true }, "nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } } }, "node-preload": { @@ -2368,16 +2371,16 @@ "dev": true }, "sinon": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", - "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", + "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^8.1.0", - "@sinonjs/samsam": "^6.0.2", + "@sinonjs/fake-timers": "^9.0.0", + "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", - "nise": "^5.1.0", + "nise": "^5.1.1", "supports-color": "^7.2.0" }, "dependencies": { @@ -2619,9 +2622,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index be8ef65..84e5763 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@squeep/api-dingus", - "version": "1.2.4", + "version": "1.2.5", "description": "A minimal API server framework", "main": "index.js", "scripts": { @@ -26,13 +26,13 @@ "uuid": "^8.3.2" }, "devDependencies": { - "eslint": "^8.6.0", + "eslint": "^8.11.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-security": "^1.4.0", - "eslint-plugin-sonarjs": "^0.11.0", - "mocha": "^9.1.4", + "eslint-plugin-sonarjs": "^0.12.0", + "mocha": "^9.2.2", "nyc": "^15.1.0", "pre-commit": "^1.2.2", - "sinon": "^12.0.1" + "sinon": "^13.0.1" } } diff --git a/test/lib/dingus.js b/test/lib/dingus.js index 519feb0..356974f 100644 --- a/test/lib/dingus.js +++ b/test/lib/dingus.js @@ -269,7 +269,7 @@ describe('Dingus', function () { }; ctx = {}; }); - it('collects body without writing', function () { + it('collects response without writing', function () { Dingus.setHeadHandler(req, res, ctx); res.write(Buffer.from('foo')); res.write('baz'); @@ -277,6 +277,16 @@ describe('Dingus', function () { res.end('quux'); assert(!origWrite.called); assert(origEnd.called); + assert.deepStrictEqual(ctx.responseBody, undefined); + }); + it('collects response without writing, persists written data', function () { + Dingus.setHeadHandler(req, res, ctx, true); + res.write(Buffer.from('foo')); + res.write('baz'); + res.write(); + res.end('quux'); + assert(!origWrite.called); + assert(origEnd.called); assert.deepStrictEqual(ctx.responseBody, Buffer.from('foobazquux')); }); it('ignores non-head method', function () { @@ -514,10 +524,21 @@ describe('Dingus', function () { assert.strictEqual(e, 'foo'); } }); + it('limits size', async function () { + const p = dingus.bodyData(res, 8); + resEvents['data'](Buffer.from('foobar')); + resEvents['data'](Buffer.from('bazquux')); + try { + await p; + assert.fail(noExpectedException); + } catch (e) { + assert.strictEqual(e.statusCode, 413); + } + }); }); // bodyData describe('ingestBody', function () { - it('covers', async function () { + it('ingests json', async function () { const req = {}; const res = {}; const ctx = {}; @@ -525,6 +546,30 @@ describe('Dingus', function () { sinon.stub(Dingus, 'getRequestContentType').returns(Enum.ContentType.ApplicationJson); await dingus.ingestBody(req, res, ctx); assert.deepStrictEqual(ctx.parsedBody, { foo: 'bar' }); + assert.deepStrictEqual(ctx.rawBody, undefined); + }); + it('persists rawBody', async function () { + const req = {}; + const res = {}; + const ctx = {}; + const body = '{"foo":"bar"}'; + sinon.stub(dingus, 'bodyData').resolves(body); + sinon.stub(Dingus, 'getRequestContentType').returns(Enum.ContentType.ApplicationJson); + await dingus.ingestBody(req, res, ctx, { persistRawBody: true }); + assert.deepStrictEqual(ctx.parsedBody, { foo: 'bar' }); + assert.deepStrictEqual(ctx.rawBody, body); + }); + it('skips parsing empty body', async function () { + const req = {}; + const res = {}; + const ctx = {}; + const body = ''; + sinon.stub(dingus, 'bodyData').resolves(body); + sinon.stub(Dingus, 'getRequestContentType').returns(Enum.ContentType.ApplicationJson); + sinon.spy(dingus, 'parseBody'); + await dingus.ingestBody(req, res, ctx, { parseEmptyBody: false }); + assert.deepStrictEqual(ctx.parsedBody, undefined); + assert(dingus.parseBody.notCalled); }); }); // ingestBody