diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts index 71900ab982f3d..b70680594151b 100644 --- a/src/core/server/http/http_tools.ts +++ b/src/core/server/http/http_tools.ts @@ -103,6 +103,10 @@ interface ListenerOptions { export function createServer(serverOptions: ServerOptions, listenerOptions: ListenerOptions) { const server = new Server(serverOptions); + // remove fix + test as soon as update node.js to v12.19 https://github.com/elastic/kibana/pull/61587 + server.listener.headersTimeout = + listenerOptions.keepaliveTimeout + 2 * server.listener.headersTimeout; + server.listener.keepAliveTimeout = listenerOptions.keepaliveTimeout; server.listener.setTimeout(listenerOptions.socketTimeout); server.listener.on('timeout', (socket) => { diff --git a/src/core/server/http/integration_tests/request.test.ts b/src/core/server/http/integration_tests/request.test.ts index 0170e94867c06..3d0eba6de632e 100644 --- a/src/core/server/http/integration_tests/request.test.ts +++ b/src/core/server/http/integration_tests/request.test.ts @@ -313,7 +313,6 @@ describe('KibanaRequest', () => { expect(resp3.body).toEqual({ requestId: 'gamma' }); }); }); - describe('request uuid', () => { it('generates a UUID', async () => { const { server: innerServer, createRouter } = await server.setup(setupDeps); diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index b4d91926f13f4..412396644648e 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -50,6 +50,8 @@ configService.atPath.mockReturnValue( allowFromAnyIp: true, ipAllowlist: [], }, + keepaliveTimeout: 120_000, + socketTimeout: 120_000, } as any) ); diff --git a/tasks/config/run.js b/tasks/config/run.js index f77dfd402386b..b3c0659b8001d 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -175,7 +175,7 @@ module.exports = function () { '--config', 'test/server_integration/http/ssl_redirect/config.js', '--config', - 'test/server_integration/http/cache/config.js', + 'test/server_integration/http/platform/config.ts', '--config', 'test/server_integration/http/ssl_with_p12/config.js', '--config', diff --git a/test/server_integration/http/cache/index.js b/test/server_integration/http/platform/cache.ts similarity index 92% rename from test/server_integration/http/cache/index.js rename to test/server_integration/http/platform/cache.ts index 5299ce361327e..e39990ca001be 100644 --- a/test/server_integration/http/cache/index.js +++ b/test/server_integration/http/platform/cache.ts @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ - +import { FtrProviderContext } from '../../services/types'; // eslint-disable-next-line import/no-default-export -export default function ({ getService }) { +export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); describe('kibana server cache-control', () => { diff --git a/test/server_integration/http/cache/config.js b/test/server_integration/http/platform/config.ts similarity index 76% rename from test/server_integration/http/cache/config.js rename to test/server_integration/http/platform/config.ts index de20bc6fc1f14..00577e092a26a 100644 --- a/test/server_integration/http/cache/config.js +++ b/test/server_integration/http/platform/config.ts @@ -16,16 +16,18 @@ * specific language governing permissions and limitations * under the License. */ +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; -export default async function ({ readConfigFile }) { +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { const httpConfig = await readConfigFile(require.resolve('../../config')); return { - testFiles: [require.resolve('./')], + testFiles: [require.resolve('./cache'), require.resolve('./headers')], services: httpConfig.get('services'), servers: httpConfig.get('servers'), junit: { - reportName: 'Http Cache-Control Integration Tests', + reportName: 'Kibana Platform Http Integration Tests', }, esTestCluster: httpConfig.get('esTestCluster'), kbnTestServer: httpConfig.get('kbnTestServer'), diff --git a/test/server_integration/http/platform/headers.ts b/test/server_integration/http/platform/headers.ts new file mode 100644 index 0000000000000..260bc37bd1328 --- /dev/null +++ b/test/server_integration/http/platform/headers.ts @@ -0,0 +1,82 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import Http from 'http'; +import Url from 'url'; +import { FtrProviderContext } from '../../services/types'; + +// @ts-ignore +import getUrl from '../../../../src/test_utils/get_url'; + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); +const oneSec = 1_000; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const config = getService('config'); + + describe('headers timeout ', () => { + it('issue-73849', async () => { + const agent = new Http.Agent({ + keepAlive: true, + }); + const { protocol, hostname, port } = Url.parse(getUrl.baseUrl(config.get('servers.kibana'))); + + function performRequest() { + return new Promise((resolve, reject) => { + const req = Http.request( + { + protocol, + hostname, + port, + path: '/', + method: 'GET', + agent, + }, + function (res) { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => resolve(data)); + } + ); + + req.on('socket', (socket) => { + socket.write('GET / HTTP/1.1\r\n'); + setTimeout(() => { + socket.write('Host: localhost\r\n'); + }, oneSec); + setTimeout(() => { + // headersTimeout doesn't seem to fire if request headers are sent in one packet. + socket.write('\r\n'); + req.end(); + }, 2 * oneSec); + }); + + req.on('error', reject); + }); + } + + await performRequest(); + const defaultHeadersTimeout = 40 * oneSec; + await delay(defaultHeadersTimeout + oneSec); + await performRequest(); + }); + }); +} diff --git a/test/server_integration/services/types.d.ts b/test/server_integration/services/types.d.ts new file mode 100644 index 0000000000000..c79c8db57d7df --- /dev/null +++ b/test/server_integration/services/types.d.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; +import { services as kibanaCommonServices } from '../../common/services'; +import { services as kibanaApiIntegrationServices } from '../../api_integration/services'; + +export type FtrProviderContext = GenericFtrProviderContext< + typeof kibanaCommonServices & { supertest: typeof kibanaApiIntegrationServices.supertest }, + {} +>;