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

Conversation

kobelb
Copy link
Contributor

@kobelb kobelb commented Jul 29, 2020

Summary

An attempt to make #72362 work with #73103.

Route definitions can now specify the idleSocket timeout and the payload timeout. There are still a few "hazards" to this interface until hapijs/hapi#4123 merges, which is exercised by the unit tests. This PR works around hapijs/hapi#4123 by specifying a socket timeout in the route definition when the payload timeout is specified, and then uses the onPreAuth lifecycle event of the route to set the real timeouts.

Dev Docs

Route definitions can now specify the idleSocket timeout in addition to the payload timeout.

Resolves #73557

if (timeout?.idleSocket) {
socketTimeout = timeout.idleSocket;
} else if (payloadTimeout) {
// Hapi server requires the socket to be greater than payload settings so we add 1 millisecond
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's rather confusing that socketTimeout is completely ignored. a couple of options:

  • use a higher value Math.max(socketTimeout, payloadTimeout + 1)
  • do not rely on Hapi and set socket.setTimeout(...) in a request interceptor manually

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's rather confusing that socketTimeout is completely ignored

If the route specifies the timeout.idleSocket it will always be used. Even if it leads to an error being thrown if it's smaller than the payload timeout...

use a higher value Math.max(socketTimeout, payloadTimeout + 1)

If the route definition tries to explicitly set the idle socket timeout to be smaller than the payload timeout, it feels less surprising to consumers to just throw an explicit error.

do not rely on Hapi and set socket.setTimeout(...) in a request interceptor manually

I'm hopeful that this will be a temporary requirement, and hapijs/hapi#4123 will merge shortly. The only route that I'm aware of that will need to set the idle socket timeout is Fleet, which is adjusting the timeout to be super long to support long-polling, so they won't hit us leaking HapiJS constraints.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if hapijs/hapi#4123 is going to be backported to Hapi v17?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if hapijs/hapi#4123 is going to be backported to Hapi v17?

I don't... I just asked for clarification in hapijs/hapi#4123 (comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per hapijs/hapi#4123 (comment)

If this lands, it will only be for v20. If you need support for an older version, you'll have to check on a commercial license - #4114.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3d8901a works around the HapiJS constraints by setting a socket timeout on the route definition when a payload timeout is specified, and then uses the onPreAuth lifecycle event to set the actual socket timeout... It's not possible to do so in the onRequest lifecycle event because the route hasn't been looked up yet per https://hapi.dev/api/?v=19.2.0#request-lifecycle

@@ -164,7 +164,16 @@ export class HttpServer {
const { authRequired, tags, body = {}, timeout } = route.options;
const { accepts: allow, maxBytes, output, parse } = body;
// Hapi does not allow timeouts on payloads to be specified for 'head' or 'get' requests
const payloadTimeout = isSafeMethod(route.method) || timeout == null ? undefined : timeout;
const payloadTimeout = isSafeMethod(route.method) ? undefined : timeout?.payload;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename payload --> payloadParsing maybe?

/**
* Milliseconds to receive the payload
*/
payload?: Method extends 'get' | 'options' ? undefined : number;
Copy link
Contributor Author

@kobelb kobelb Aug 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per #73730 (comment), were you suggesting that we rename payload to payloadParsing? I don't think just calling this the payload timeout is super expressive, but I'm afraid of reusing "parsing" here as that might make users think it relates to

parse?: boolean | 'gunzip';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to payloadParsing not being correct and maybe confusing since this timeout does not have to do with parsing. I actually prefer the current naming, though if I had my way, we'd stop using the word payload and always use body since that is what the HTTP spec calls it.

@kobelb kobelb marked this pull request as ready for review August 6, 2020 17:14
@kobelb kobelb requested review from a team as code owners August 6, 2020 17:14
@kobelb
Copy link
Contributor Author

kobelb commented Aug 10, 2020

@elasticmachine merge upstream

Copy link
Contributor

@joshdover joshdover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is some differing behavior in the socket behavior when using 'fake requests', I think it would be good to have at least 1-2 functional tests that use real HTTP connections.

/**
* Milliseconds to receive the payload
*/
payload?: Method extends 'get' | 'options' ? undefined : number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to payloadParsing not being correct and maybe confusing since this timeout does not have to do with parsing. I actually prefer the current naming, though if I had my way, we'd stop using the word payload and always use body since that is what the HTTP spec calls it.

Comment on lines +200 to 202
payload: [allow, maxBytes, output, parse, timeout?.payload].some(
(v) => typeof v !== 'undefined'
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(slightly out of scope) Do we really need this .some check to return undefined instead of an object of undefined values? I would expect Hapi is using Joi under the hood to populate the defaults here anyways.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is... per #50783 (comment) HapiJS doesn't let us set the payload option for a GET or HEAD route definition...

src/core/server/http/http_server.ts Outdated Show resolved Hide resolved
src/core/server/http/http_server.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@FrankHassanabad FrankHassanabad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked over the changes to security solutions and this looks good to me 👍

@kobelb
Copy link
Contributor Author

kobelb commented Aug 11, 2020

@elasticmachine merge upstream

@kobelb
Copy link
Contributor Author

kobelb commented Aug 17, 2020

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@roncohen
Copy link
Contributor

thank you for working on this!

gmmorris added a commit to gmmorris/kibana that referenced this pull request Aug 19, 2020
* master: (112 commits)
  [Ingest Manager] Fix agent config rollout rate limit to use constants (elastic#75364)
  Update Node.js to version 10.22.0 (elastic#75254)
  [ML] Anomaly Explorer / Single Metric Viewer: Fix error reporting for annotations. (elastic#74953)
  [Discover] Fix histogram cloud tests (elastic#75268)
  Uiactions to navigate to visualize or maps (elastic#74121)
  Use prefix search invis editor field/agg combo box (elastic#75290)
  Fix docs in trigger alerting UI (elastic#75363)
  [SIEM] Fixes search bar Cypress test (elastic#74833)
  Add libnss3.so to Dockerfile template (reporting) (elastic#75370)
  [Discover] Create field_button and add popovers to sidebar (elastic#73226)
  [Reporting] Network Policy: Do not throw from the intercept handler (elastic#75105)
  [Reporting] Increase capture.timeouts.openUrl to 1 minute (elastic#75207)
  Allow routes to specify the idle socket timeout in addition to the payload timeout (elastic#73730)
  [src/dev/build] remove node-version from snapshots (elastic#75303)
  [ENDPOINT] Reintroduced tabs to endpoint management and migrated pages to use common security components (elastic#74886)
  [Canvas] Remove dependency on legacy expressions APIs (elastic#74885)
  Skip failing test in CI (elastic#75266)
  [Task Manager] time out work when it overruns in poller (elastic#74980)
  [Drilldowns] misc improvements & fixes (elastic#75276)
  Small README note on bumping memory for builds (elastic#75247)
  ...
kobelb added a commit that referenced this pull request Aug 19, 2020
…yload timeout (#73730) (#75350)

* Route options timeout -> timeout.payload

* timeout.idleSocket can now be specified per route

* Removing nested ternary

* Fixing integration tests

* Trying to actually fix the integration tests. Existing tests are hitting
idle socket timeout, not the payload timeout

* Fixing payload post timeout integration test

* Fixing PUT and DELETE payload sending too long tests

* Fixing type-script errors

* GET routes can't specify the payload timeout, they can't accept payloads

* Removing some redundancy in the tests

* Adding 'Transfer-Encoding: chunked' to the POST test

* Fixing POST/GET/PUT quick tests

* Adding idleSocket timeout test

* Removing unnecessary `isSafeMethod` call

* Updating documentation

* Removing PUT/DELETE integration tests

* Working around the HapiJS bug

* Deleting unused type import

* The socket can be undefined...

This occurs when using @hapi/shot directly or indirectly via
Server.inject. In these scenarios, there isn't a socket. This can also
occur when a "fake request" is used by the hacky background jobs:
Reporting and Alerting...

* Update src/core/server/http/http_server.ts

Co-authored-by: Josh Dover <[email protected]>

* Adding payload timeout functional tests

* Adding idle socket timeout functional tests

* Adding better comments, using ?? instead of ||

* Fixing the plugin fixture TS

* Fixing some typescript errors

* Fixing plugin fixture tsconfig.json

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Josh Dover <[email protected]>

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Josh Dover <[email protected]>
@mshustov mshustov mentioned this pull request Dec 15, 2020
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New value added to drive a business result release_note:plugin_api_changes Contains a Plugin API changes section for the breaking plugin API changes section. v7.10.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unit test needed for exercising attachment upload timeouts
7 participants