feat(http2): add a pooled, multiplexed HTTP/2 http.Client - #1956
feat(http2): add a pooled, multiplexed HTTP/2 http.Client#1956demolaf wants to merge 34 commits into
Conversation
brianquinlan
left a comment
There was a problem hiding this comment.
I'll have more review feedback tomorrow. Also, this PR adds (preliminary) conformance tests: #1960
You might want to add them to this PR.
…xConcurrentStreams
e8b8590 to
39b75f9
Compare
|
@brianquinlan @mosuem I'm deferring the TODOs in the conformance tests to a later PR since this is already quite a lot, sounds good? |
| @@ -0,0 +1,34 @@ | |||
| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | |||
There was a problem hiding this comment.
Let's make this example more useful for the user. Maybe copy the example from package:cupertino_http or something.
There was a problem hiding this comment.
I added an example showing 100 concurrent requests but it's not really a great example since http/1.1 can handle that faster (if uncapped). The only notable difference would be memory usage since http/1.1 would open TCP+TLS connections per request.
| /// `true` accepts a certificate that failed normal verification (expired, | ||
| /// self-signed, wrong host, ...). It exists for tests and trusted private | ||
| /// networks - do not use it to accept arbitrary certificates in production. | ||
| class Http2Client extends BaseClient { |
There was a problem hiding this comment.
Maybe marks this experimental for now:
https://api.flutter.dev/flutter/meta/experimental-constant.html
There was a problem hiding this comment.
that makes sense to do since we still have more pending feature work to do per the conformance tests
|
|
||
| List<int>? bodyBytes; | ||
|
|
||
| Future<StreamedResponse> attempt() async { |
There was a problem hiding this comment.
This is awkward to read. Do we need to define this function, which is only used once?
There was a problem hiding this comment.
yeah i agree, it's actually used twice, the initial attempt and then in the .catchError block.
| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | ||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
There was a problem hiding this comment.
Maybe provide an example on how to use this library. But I like the approach.
|
|
||
| class _PooledResource<T> { | ||
| _PooledResource(this.future); | ||
| final Future<T> future; |
There was a problem hiding this comment.
I think that these need to be better named/documented:
future refers to the creation of the resource, right?
inFlight refers to the number of concurrent requests of the resource, right?
failed indicates that creating the resource failed, right?
Maybe:
create
inFlightCount
createFailed
And a comment?
This would be more clear if T were ClientConnection but maybe the tests would then be too hard to write.
There was a problem hiding this comment.
done, renamed all three. also dropped the generic — ClientPool is ClientConnection-specific now, and the pool tests use a generated MockClientConnection.
| /// its most recent SETTINGS_MAX_CONCURRENT_STREAMS (RFC 7540 6.5.2), or | ||
| /// `null` if it hasn't advertised a limit. | ||
| /// | ||
| /// Deliberately not on [ClientTransportConnection]: that class can only be |
There was a problem hiding this comment.
@mosuem maybe we should add it there anyway - there are no "implements ClientTransportConnection" on GitHub but we could also bump semver.
There was a problem hiding this comment.
Yes, just add it there and bump semver.
There was a problem hiding this comment.
done — moved it to ClientTransportConnection and bumped to 3.1.0-wip
90b0813 to
af53e7f
Compare
a5cea15 to
a05fb28
Compare
Closes #1385
Adds
Http2Client, a pooled, multiplexedhttp.Clientbacked by HTTP/2 connections, plus theClientPoolit's built on — fixesdart:io'sHttpClientopening one connection per concurrent request. Ported and generalized from a downstream implementation built for firebase/firebase-admin-dart#305.ClientPool: most-full-first packing, idle GC, failure retirement, and a lease API so a slot can outlive the call that claimed itHttp2Client: pools perhost:port, caps concurrent handshakes globally, multi-host safe (e.g. asgoogleapis_auth'sbaseClient)SETTINGS_MAX_CONCURRENT_STREAMSallows, and doesn't use a connection at all until that frame has arrived — seeHttp2Client.settingsTimeoutClientTransportConnection.peerMaxConcurrentStreams. This is a new member on an implementable class, hence the minor version bump