Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow routes to specify the idle socket timeout in addition to the payload timeout #73730

Merged
merged 32 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6ce7f1e
Route options timeout -> timeout.payload
kobelb Jul 29, 2020
4e3b566
timeout.idleSocket can now be specified per route
kobelb Jul 29, 2020
26541ba
Removing nested ternary
kobelb Jul 29, 2020
4048150
Fixing integration tests
kobelb Jul 29, 2020
8267a2c
Trying to actually fix the integration tests. Existing tests are hitting
kobelb Jul 29, 2020
80e6259
Merge remote-tracking branch 'upstream/master' into core/idle-socket-…
kobelb Aug 5, 2020
66cf715
Fixing payload post timeout integration test
kobelb Aug 5, 2020
1a26b10
Fixing PUT and DELETE payload sending too long tests
kobelb Aug 5, 2020
c03a0ce
Fixing type-script errors
kobelb Aug 5, 2020
b5b7026
GET routes can't specify the payload timeout, they can't accept payloads
kobelb Aug 5, 2020
7429fea
Removing some redundancy in the tests
kobelb Aug 5, 2020
4f7e9e1
Adding 'Transfer-Encoding: chunked' to the POST test
kobelb Aug 5, 2020
828eab8
Fixing POST/GET/PUT quick tests
kobelb Aug 5, 2020
c092cbe
Adding idleSocket timeout test
kobelb Aug 5, 2020
2c939ea
Removing unnecessary `isSafeMethod` call
kobelb Aug 5, 2020
d300b9f
Updating documentation
kobelb Aug 5, 2020
84e06fa
Removing PUT/DELETE integration tests
kobelb Aug 6, 2020
3d8901a
Working around the HapiJS bug
kobelb Aug 6, 2020
afbb7e8
Deleting unused type import
kobelb Aug 6, 2020
c4a3e15
The socket can be undefined...
kobelb Aug 6, 2020
87634ff
Merge branch 'master' into core/idle-socket-timeout-2
elasticmachine Aug 10, 2020
20ff93f
Update src/core/server/http/http_server.ts
kobelb Aug 10, 2020
0e8fd23
Merge remote-tracking branch 'upstream/master' into core/idle-socket-…
kobelb Aug 10, 2020
dc430b9
Adding payload timeout functional tests
kobelb Aug 10, 2020
31dd497
Adding idle socket timeout functional tests
kobelb Aug 10, 2020
25b6db0
Adding better comments, using ?? instead of ||
kobelb Aug 10, 2020
d4e7d2a
Fixing the plugin fixture TS
kobelb Aug 10, 2020
2862387
Fixing some typescript errors
kobelb Aug 11, 2020
4c6a15d
Merge remote-tracking branch 'upstream/master' into core/idle-socket-…
kobelb Aug 11, 2020
0b0c488
Fixing plugin fixture tsconfig.json
kobelb Aug 11, 2020
2bd6bb9
Merge branch 'master' into core/idle-socket-timeout-2
elasticmachine Aug 11, 2020
44ab6dc
Merge branch 'master' into core/idle-socket-timeout-2
elasticmachine Aug 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export interface RouteConfigOptions<Method extends RouteMethod>
| [authRequired](./kibana-plugin-core-server.routeconfigoptions.authrequired.md) | <code>boolean &#124; 'optional'</code> | Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource if has valid credentials or no credentials at all. Can be useful when we grant access to a resource but want to identify a user if possible.<!-- -->Defaults to <code>true</code> if an auth mechanism is registered. |
| [body](./kibana-plugin-core-server.routeconfigoptions.body.md) | <code>Method extends 'get' &#124; 'options' ? undefined : RouteConfigOptionsBody</code> | Additional body options [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md)<!-- -->. |
| [tags](./kibana-plugin-core-server.routeconfigoptions.tags.md) | <code>readonly string[]</code> | Additional metadata tag strings to attach to the route. |
| [timeout](./kibana-plugin-core-server.routeconfigoptions.timeout.md) | <code>number</code> | Timeouts for processing durations. Response timeout is in milliseconds. Default value: 2 minutes |
| [timeout](./kibana-plugin-core-server.routeconfigoptions.timeout.md) | <code>{</code><br/><code> payload?: Method extends 'get' &#124; 'options' ? undefined : number;</code><br/><code> idleSocket?: number;</code><br/><code> }</code> | Defines per-route timeouts. |
| [xsrfRequired](./kibana-plugin-core-server.routeconfigoptions.xsrfrequired.md) | <code>Method extends 'get' ? never : boolean</code> | Defines xsrf protection requirements for a route: - true. Requires an incoming POST/PUT/DELETE request to contain <code>kbn-xsrf</code> header. - false. Disables xsrf protection.<!-- -->Set to true by default |

Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

## RouteConfigOptions.timeout property

Timeouts for processing durations. Response timeout is in milliseconds. Default value: 2 minutes
Defines per-route timeouts.

<b>Signature:</b>

```typescript
timeout?: number;
timeout?: {
payload?: Method extends 'get' | 'options' ? undefined : number;
idleSocket?: number;
};
```
314 changes: 219 additions & 95 deletions src/core/server/http/http_server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ test('exposes route details of incoming request to a route handler', async () =>
authRequired: true,
xsrfRequired: false,
tags: [],
timeout: {},
},
});
});
Expand Down Expand Up @@ -906,6 +907,9 @@ test('exposes route details of incoming request to a route handler (POST + paylo
authRequired: true,
xsrfRequired: true,
tags: [],
timeout: {
payload: 10000,
},
body: {
parse: true, // hapi populates the default
maxBytes: 1024, // hapi populates the default
Expand Down Expand Up @@ -993,129 +997,249 @@ describe('body options', () => {
});

describe('timeout options', () => {
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a POST', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);
describe('payload timeout', () => {
test('POST routes set the payload timeout', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);

const router = new Router('', logger, enhanceWithContext);
router.post(
{
path: '/',
validate: false,
options: {
timeout: {
payload: 300000,
},
},
},
(context, req, res) => {
try {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
} catch (err) {
return res.internalError({ body: err.message });
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener)
.post('/')
.send({ test: 1 })
.expect(200, {
timeout: {
payload: 300000,
},
});
});

const router = new Router('', logger, enhanceWithContext);
router.post(
{
path: '/',
validate: false,
options: { timeout: 300000 },
},
(context, req, res) => {
try {
return res.ok({ body: { timeout: req.route.options.timeout } });
} catch (err) {
return res.internalError({ body: err.message });
test('DELETE routes set the payload timeout', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);

const router = new Router('', logger, enhanceWithContext);
router.delete(
{
path: '/',
validate: false,
options: {
timeout: {
payload: 300000,
},
},
},
(context, req, res) => {
try {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
} catch (err) {
return res.internalError({ body: err.message });
}
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener).post('/').send({ test: 1 }).expect(200, {
timeout: 300000,
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener)
.delete('/')
.expect(200, {
timeout: {
payload: 300000,
},
});
});
});

test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a GET', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);
test('PUT routes set the payload timeout and automatically adjusts the idle socket timeout', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);

const router = new Router('', logger, enhanceWithContext);
router.put(
{
path: '/',
validate: false,
options: {
timeout: {
payload: 300000,
},
},
},
(context, req, res) => {
try {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
} catch (err) {
return res.internalError({ body: err.message });
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener)
.put('/')
.expect(200, {
timeout: {
payload: 300000,
},
});
});

const router = new Router('', logger, enhanceWithContext);
router.get(
{
path: '/',
validate: false,
options: { timeout: 300000 },
},
(context, req, res) => {
try {
return res.ok({ body: { timeout: req.route.options.timeout } });
} catch (err) {
return res.internalError({ body: err.message });
test('PATCH routes set the payload timeout and automatically adjusts the idle socket timeout', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);

const router = new Router('', logger, enhanceWithContext);
router.patch(
{
path: '/',
validate: false,
options: {
timeout: {
payload: 300000,
},
},
},
(context, req, res) => {
try {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
} catch (err) {
return res.internalError({ body: err.message });
}
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener).get('/').expect(200, {
timeout: 300000,
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener)
.patch('/')
.expect(200, {
timeout: {
payload: 300000,
},
});
});
});

test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a DELETE', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);
describe('idleSocket timeout', () => {
test('uses server socket timeout when not specified in the route', async () => {
const { registerRouter, server: innerServer } = await server.setup({
...config,
socketTimeout: 11000,
});

const router = new Router('', logger, enhanceWithContext);
router.delete(
{
path: '/',
validate: false,
options: { timeout: 300000 },
},
(context, req, res) => {
try {
return res.ok({ body: { timeout: req.route.options.timeout } });
} catch (err) {
return res.internalError({ body: err.message });
const router = new Router('', logger, enhanceWithContext);
router.get(
{
path: '/',
validate: { body: schema.any() },
},
(context, req, res) => {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener).delete('/').expect(200, {
timeout: 300000,
);
registerRouter(router);

await server.start();
await supertest(innerServer.listener)
.get('/')
.send()
.expect(200, {
timeout: {
idleSocket: 11000,
},
});
});
});

test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a PUT', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);
test('sets the socket timeout when specified in the route', async () => {
const { registerRouter, server: innerServer } = await server.setup({
...config,
socketTimeout: 11000,
});

const router = new Router('', logger, enhanceWithContext);
router.put(
{
path: '/',
validate: false,
options: { timeout: 300000 },
},
(context, req, res) => {
try {
return res.ok({ body: { timeout: req.route.options.timeout } });
} catch (err) {
return res.internalError({ body: err.message });
const router = new Router('', logger, enhanceWithContext);
router.get(
{
path: '/',
validate: { body: schema.any() },
options: { timeout: { idleSocket: 12000 } },
},
(context, req, res) => {
return res.ok({
body: {
timeout: req.route.options.timeout,
},
});
}
}
);
registerRouter(router);
await server.start();
await supertest(innerServer.listener).put('/').expect(200, {
timeout: 300000,
);
registerRouter(router);

await server.start();
await supertest(innerServer.listener)
.get('/')
.send()
.expect(200, {
timeout: {
idleSocket: 12000,
},
});
});
});

test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a PATCH', async () => {
const { registerRouter, server: innerServer } = await server.setup(config);
test(`idleSocket timeout can be smaller than the payload timeout`, async () => {
const { registerRouter } = await server.setup(config);

const router = new Router('', logger, enhanceWithContext);
router.patch(
router.post(
{
path: '/',
validate: false,
options: { timeout: 300000 },
validate: { body: schema.any() },
options: {
timeout: {
payload: 1000,
idleSocket: 10,
},
},
},
(context, req, res) => {
try {
return res.ok({ body: { timeout: req.route.options.timeout } });
} catch (err) {
return res.internalError({ body: err.message });
}
return res.ok({ body: { timeout: req.route.options.timeout } });
}
);

registerRouter(router);

await server.start();
await supertest(innerServer.listener).patch('/').expect(200, {
timeout: 300000,
});
});
});

Expand Down
Loading