From f37fbc2b9d88488fdc1dfa987ca768211838b75d Mon Sep 17 00:00:00 2001 From: Ben Schmidt Date: Sat, 25 Apr 2020 00:07:53 +1000 Subject: [PATCH] Support setting http.Server properties through config. --- package-lock.json | 2 +- package.json | 2 +- src/simple-web.js | 21 ++++++++++++ test/simple-web-test.js | 73 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index e28fd7a..5566198 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "koa-simple-web", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a22c20a..141be21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "koa-simple-web", - "version": "1.3.0", + "version": "1.4.0", "description": "A simple web server using Koa", "main": "src/simple-web.js", "scripts": { diff --git a/src/simple-web.js b/src/simple-web.js index 2195e03..87c26b2 100644 --- a/src/simple-web.js +++ b/src/simple-web.js @@ -2,9 +2,23 @@ const Koa = require("koa"); +const PASSTHROUGH_CONFIG_KEYS = [ + "maxHeadersCount", + "headersTimeout", + "timeout", + "keepAliveTimeout" +]; + /** * @typedef {Object} SimpleWebServerConfig + * + * See this bug related to the timeouts: https://github.com/nodejs/node/issues/27363 + * * @property {number} port The port to listen on + * @property [number] maxHeadersCount {@link https://nodejs.org/docs/latest/api/http.html#http_server_maxheaderscount} + * @property [number] headersTimeout {@link https://nodejs.org/docs/latest/api/http.html#http_server_headerstimeout} + * @property [number] timeout {@link https://nodejs.org/docs/latest/api/http.html#http_server_timeout} + * @property [number] keepAliveTimeout {@link https://nodejs.org/docs/latest/api/http.html#http_server_keepalivetimeout} */ /** @@ -27,6 +41,13 @@ class SimpleWeb { return new Promise((resolve, reject) => { if (!this._server) { this._server = this._web.listen(this._config.port); + + PASSTHROUGH_CONFIG_KEYS.forEach((key) => { + if (typeof this._config[key] !== 'undefined') { + this._server[key] = this._config[key]; + } + }); + this._server.on("listening", resolve); } else { diff --git a/test/simple-web-test.js b/test/simple-web-test.js index 3befb7f..b562a2b 100644 --- a/test/simple-web-test.js +++ b/test/simple-web-test.js @@ -3,18 +3,30 @@ const Router = require("koa-router"); const superagent = require("superagent"); -const { assertThat, equalTo, is } = require("hamjest"); +const { assertThat, equalTo, is, not, defined } = require("hamjest"); const { hasHeader, hasStatusCode } = require("superjest"); const SimpleWeb = require("../src/simple-web"); -const port = process.env.PORT || 8080; +const PORT = process.env.PORT || 8080; +const MAX_HEADERS_COUNT = 1111; +const HEADERS_TIMEOUT = 2222; +const TIMEOUT = 3333; +const KEEP_ALIVE_TIMEOUT = 4444; + +const config = { + port: PORT, + maxHeadersCount: MAX_HEADERS_COUNT, + headersTimeout: HEADERS_TIMEOUT, + timeout: TIMEOUT, + keepAliveTimeout: KEEP_ALIVE_TIMEOUT +}; describe("simple web", function() { let web; beforeEach(async function() { - web = new SimpleWeb({ port: port }); + web = new SimpleWeb(config); }); afterEach(async function() { @@ -50,6 +62,53 @@ describe("simple web", function() { }) }); + describe("config", function() { + ["maxHeadersCount", "headersTimeout", "timeout", "keepAliveTimeout"].forEach((key) => { + it(`should set ${key}`, async function() { + // Configuration should be synchronous, not wait for the promise to settle + const promise = web.start(); + + try { + assertThat(web._server[key], is(config[key])); + } + finally { + await promise; + } + }); + + it(`should set ${key} to zero`, async function() { + const alteredConfig = Object.assign({}, config); + alteredConfig[key] = 0; + + web = new SimpleWeb(alteredConfig); + const promise = web.start(); + + try { + assertThat(web._server[key], is(0)); + } + finally { + await promise; + } + }); + + it(`should not set ${key} if undefined`, async function() { + const alteredConfig = Object.assign({}, config); + delete alteredConfig[key]; + + web = new SimpleWeb(alteredConfig); + const promise = web.start(); + + try { + assertThat(web._server[key], defined()); + assertThat(web._server[key], not(config[key])); + } + finally { + await promise; + } + }); + }); + }); + describe("routes", function() { beforeEach(async function() { web.route(givenRootRoute()); @@ -58,7 +117,7 @@ describe("simple web", function() { }); it("should mount routes", function(done) { - superagent.get(`http://localhost:${port}`) + superagent.get(`http://localhost:${PORT}`) .end((error, response) => { assertThat(response, hasStatusCode(200)); assertThat(response.text, is("OK")); @@ -68,7 +127,7 @@ describe("simple web", function() { }); it("should only allow defined methods", function(done) { - superagent.post(`http://localhost:${port}`) + superagent.post(`http://localhost:${PORT}`) .end((error, response) => { assertThat(response, hasStatusCode(405)); @@ -97,7 +156,7 @@ describe("simple web", function() { }); it("should allow arbitrary middleware", function(done) { - superagent.get(`http://localhost:${port}`) + superagent.get(`http://localhost:${PORT}`) .end((error, response) => { assertThat(response, hasStatusCode(200)); assertThat(response, hasHeader("x-foo", equalTo("bar"))); @@ -127,7 +186,7 @@ describe("simple web", function() { return name; }); - superagent.get(`http://localhost:${port}/name`) + superagent.get(`http://localhost:${PORT}/name`) .end((error, response) => { assertThat(response, hasStatusCode(200)); assertThat(response.text, is(name));