feat: optional strict response entities for the HTTP client - #1233
Draft
pjfanning wants to merge 3 commits into
Draft
feat: optional strict response entities for the HTTP client#1233pjfanning wants to merge 3 commits into
pjfanning wants to merge 3 commits into
Conversation
Motivation: Client responses are dispatched with streamed entities, so applications always have to consume or discard the entity before the connection can be reused. Applications that only ever work with fully buffered responses have to call `toStrict` on every response themselves. Modification: Add `pekko.http.client.strict-response-entity-timeout` (`off` by default) and `pekko.http.client.strict-response-entity-max-bytes` (8m by default). When a timeout is configured, `OutgoingConnectionBlueprint` collects every response entity into an `HttpEntity.Strict` before the response leaves the connection layer, which also covers the connection pool behind the host-level and request-level APIs. Entities that are already strict pass through untouched. Result: The HTTP/1.1 client can be configured to hand out strict response entities. The default behaviour is unchanged: entities stay streamed. Tests: - sbt "http-core/testOnly org.apache.pekko.http.impl.engine.client.LowLevelOutgoingConnectionSpec" - pass - sbt "http-core/testOnly org.apache.pekko.http.impl.engine.client.HttpConfigurationSpec" - pass - sbt http-core/mimaReportBinaryIssues - pass - sbt ++3.3.8 http-core/Test/compile - pass - sbt docs/paradox - pass - scalafmt --list --mode diff-ref=origin/main - no changes References: None - follow-up on making client response entity handling configurable
Member
Author
Motivation: `pekko.http.client.strict-response-entity-timeout` only affected the HTTP/1.1 client, so HTTP/2 users could not get the same behaviour. Modification: Move the strictify flow into `StreamUtils.strictifyResponseEntities`, taking a parallelism, and apply it in `Http2Blueprint.httpLayerClient` as well. The HTTP/1.1 client keeps parallelism 1, since responses on a connection are sequential anyway; the HTTP/2 client uses `max-concurrent-streams` so that a big response does not delay smaller ones on other streams. Document the HTTP/2 specific trade-offs in `reference.conf` and in the client configuration docs: responses are emitted in the order their entities complete, worst case memory is `max-concurrent-streams` * `strict-response-entity-max-bytes`, a failing entity fails the whole connection with every stream in flight on it, and entity data is read at the peer's pace rather than the application's. Result: The setting now covers both the HTTP/1.1 and the HTTP/2 client, with the HTTP/2 caveats spelled out. Tests: - sbt "http2-tests/testOnly org.apache.pekko.http.impl.engine.http2.Http2ClientSpec" - pass - sbt "http2-tests/testOnly org.apache.pekko.http.impl.engine.http2.Http2ClientServerSpec org.apache.pekko.http.impl.engine.http2.Http2PersistentClientTlsSpec org.apache.pekko.http.impl.engine.http2.Http2PersistentClientPlaintextSpec" - pass - sbt "http-core/testOnly org.apache.pekko.http.impl.engine.client.LowLevelOutgoingConnectionSpec org.apache.pekko.http.impl.engine.client.HttpConfigurationSpec" - pass - sbt http-core/mimaReportBinaryIssues - pass - sbt ++3.3.8 http-core/Test/compile http2-tests/Test/compile - pass - sbt docs/paradox - pass - scalafmt --list --mode diff-ref=upstream/main - no changes References: None - follow-up to the HTTP/1.1 support in the previous commit
Contributor
|
@pjfanning - looks promising. I wonder if there'll be a performance benefit for clients - in my experience there's no benefit from applying I don't fully understand the pekko-http HTTP/2 code, but I don't see where this collects the HTTP/2 trailers into the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Client responses are always dispatched with streamed entities, so applications have to consume or discard each response entity before the connection can be reused. Applications that only ever work with fully buffered responses end up calling
toStricton every response themselves.Note that the client already produces
HttpEntity.Strictopportunistically (when the whole body arrives together with the headers in one buffer), so "sometimes strict" is already the observable behaviour — this just makes it deterministic when asked for.Modification
Two new settings in
pekko.http.client:When a timeout is configured, response entities are collected into an
HttpEntity.Strict(reusing the existingimpl.util.ToStrictstage) before the response leaves the connection layer. Entities that are already strict pass through untouched. The shared flow lives inStreamUtils.strictifyResponseEntitiesand takes a parallelism.Two commits:
OutgoingConnectionBlueprintwith parallelism 1. BecausePoolInterfacebuilds its connections throughHttp().outgoingConnectionUsingContext, this covers the connection-level API, the host-level API andsingleRequestin one place. Responses on an HTTP/1.1 connection are sequential, so collecting one entity never holds up another.Http2Blueprint.httpLayerClientwith parallelismmax-concurrent-streams, so a big response does not hold up smaller ones on other streams.Caveats that apply to both, documented in
reference.confand in the client configuration docs:TimeoutException, one that exceedsstrict-response-entity-max-byteswith anEntityStreamException, and in both cases the connection is failed too, since the rest of the body cannot be skipped safely;HttpEntity.toStrict.HTTP/2 specific trade-offs
These are called out in a dedicated docs section, since they are not obvious:
RequestResponseAssociation, so this does not break the API, but it is an observable change. There is a test pinning this behaviour.max-concurrent-streams×strict-response-entity-max-bytes— 256 × 8 MB with the defaults. Both settings should be tuned for the responses actually expected.managedPersistentHttp2()the connection is re-established permax-persistent-attempts.Draft: opening for feedback on the setting names/shape and on whether the HTTP/2 trade-offs above are acceptable as they stand before this is marked ready.
Result
Both the HTTP/1.1 and the HTTP/2 client can be configured to hand out strict response entities. Default behaviour is unchanged: entities stay streamed.
Tests
sbt "http-core/testOnly org.apache.pekko.http.impl.engine.client.LowLevelOutgoingConnectionSpec"- pass (6 new cases: chunked, default, close-delimited, already-strict, over-limit, and streamed-by-default)sbt "http-core/testOnly org.apache.pekko.http.impl.engine.client.HttpConfigurationSpec"- pass (2 new cases for defaults and pool propagation)sbt "http2-tests/testOnly org.apache.pekko.http.impl.engine.http2.Http2ClientSpec"- pass (4 new cases: collect entity, completion-order emission, over-limit, streamed-by-default)sbt "http2-tests/testOnly org.apache.pekko.http.impl.engine.http2.Http2ClientServerSpec org.apache.pekko.http.impl.engine.http2.Http2PersistentClientTlsSpec org.apache.pekko.http.impl.engine.http2.Http2PersistentClientPlaintextSpec"- passsbt http-core/mimaReportBinaryIssues- pass (no new filters needed)sbt ++3.3.8 http-core/Test/compile http2-tests/Test/compile- passsbt docs/paradox- passscalafmt --list --mode diff-ref=upstream/main- no changesReferences
None - makes client response entity handling configurable