From 31dd497099615f4529fa6b9e6f42a46443a3d5dc Mon Sep 17 00:00:00 2001 From: kobelb Date: Mon, 10 Aug 2020 16:20:05 -0700 Subject: [PATCH] Adding idle socket timeout functional tests --- .../http/integration_tests/router.test.ts | 1 - .../server/plugin.ts | 51 ++++++++ .../test_suites/core/route.ts | 119 +++++++++++++++--- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 40747aa3581a..e19c348511f1 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -400,7 +400,6 @@ describe('Options', () => { }, }, async (context, req, res) => { - await new Promise((resolve) => setTimeout(resolve, 2000)); return res.ok({}); } ); diff --git a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts index 10ffffcd6d41..41c262872d21 100644 --- a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts @@ -18,6 +18,7 @@ */ import { Plugin, CoreSetup } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; export interface PluginARequestContext { ping: () => Promise; @@ -66,6 +67,56 @@ export class CorePluginRouteTimeoutsPlugin implements Plugin { return res.ok({}); } ); + + router.post( + { + options: { + body: { + accepts: ['application/json'], + }, + timeout: { idleSocket: 10 }, + }, + path: '/short_idle_socket_timeout', + validate: { + body: schema.maybe( + schema.object({ + responseDelay: schema.maybe(schema.number()), + }) + ), + }, + }, + async (context, req, res) => { + if (req.body.responseDelay) { + await new Promise((resolve) => setTimeout(resolve, req.body.responseDelay)); + } + return res.ok({}); + } + ); + + router.post( + { + options: { + body: { + accepts: ['application/json'], + }, + timeout: { idleSocket: 5000 }, + }, + path: '/longer_idle_socket_timeout', + validate: { + body: schema.maybe( + schema.object({ + responseDelay: schema.maybe(schema.number()), + }) + ), + }, + }, + async (context, req, res) => { + if (req.body.responseDelay) { + await new Promise((resolve) => setTimeout(resolve, req.body.responseDelay)); + } + return res.ok({}); + } + ); } public start() {} diff --git a/test/plugin_functional/test_suites/core/route.ts b/test/plugin_functional/test_suites/core/route.ts index 20daf84cdf0c..becde49a6971 100644 --- a/test/plugin_functional/test_suites/core/route.ts +++ b/test/plugin_functional/test_suites/core/route.ts @@ -26,26 +26,27 @@ export default function ({ getService }: PluginFunctionalProviderContext) { describe('route', function () { describe('timeouts', function () { - describe('payload', function () { - const writeBodyCharAtATime = (request: Test, body: string, interval: number) => { - return new Promise((resolve, reject) => { - let i = 0; - const intervalId = setInterval(() => { - if (i < body.length) { - request.write(body[i++]); - } else { - clearInterval(intervalId); - request.end((err, res) => { - resolve(res); - }); - } - }, interval); - request.on('error', (err) => { + const writeBodyCharAtATime = (request: Test, body: string, interval: number) => { + return new Promise((resolve, reject) => { + let i = 0; + const intervalId = setInterval(() => { + if (i < body.length) { + request.write(body[i++]); + } else { clearInterval(intervalId); - reject(err); - }); + request.end((err, res) => { + resolve(res); + }); + } + }, interval); + request.on('error', (err) => { + clearInterval(intervalId); + reject(err); }); - }; + }); + }; + + describe('payload', function () { it(`should timeout if POST payload sending is too slow`, async () => { // start the request const request = supertest @@ -86,6 +87,88 @@ export default function ({ getService }: PluginFunctionalProviderContext) { ); }); }); + + describe('idle socket', function () { + it('should timeout if payload sending has too long of an idle period', async function () { + // start the request + const request = supertest + .post('/short_idle_socket_timeout') + .set('Content-Type', 'application/json') + .set('Transfer-Encoding', 'chunked') + .set('kbn-xsrf', 'true'); + + const result = writeBodyCharAtATime(request, '{"responseDelay":100}', 20); + + await result.then( + (res) => { + expect(res).to.be(undefined); + }, + (err) => { + expect(err.message).to.be('socket hang up'); + } + ); + }); + + it('should not timeout if payload sending does not have too long of an idle period', async function () { + // start the request + const request = supertest + .post('/longer_idle_socket_timeout') + .set('Content-Type', 'application/json') + .set('Transfer-Encoding', 'chunked') + .set('kbn-xsrf', 'true'); + + const result = writeBodyCharAtATime(request, '{"responseDelay":0}', 10); + + await result.then( + (res) => { + expect(res).to.have.property('statusCode', 200); + }, + (err) => { + expect(err).to.be(undefined); + } + ); + }); + + it('should timeout if servers response is too slow', async function () { + // start the request + const request = supertest + .post('/short_idle_socket_timeout') + .set('Content-Type', 'application/json') + .set('Transfer-Encoding', 'chunked') + .set('kbn-xsrf', 'true'); + + const result = writeBodyCharAtATime(request, '{"responseDelay":100}', 0); + + await result.then( + (res) => { + expect(res).to.be(undefined); + }, + (err) => { + expect(err.message).to.be('socket hang up'); + } + ); + }); + + it('should not timeout if servers response is fast enough', async function () { + // start the request + const request = supertest + .post('/longer_idle_socket_timeout') + .set('Content-Type', 'application/json') + .set('Transfer-Encoding', 'chunked') + .set('kbn-xsrf', 'true'); + + const result = writeBodyCharAtATime(request, '{"responseDelay":100}', 0); + + await result.then( + (res) => { + expect(res).to.have.property('statusCode', 200); + }, + (err) => { + expect(err).to.be(undefined); + } + ); + }); + }); }); }); }