-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add ALPN/HTTP2 support to Connect #1485
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
Comments
Talking through this a bit on IRC, here's some clarification. A user likely has various ways they want to declare when to use HTTP/1 vs HTTP/2. A major way to do is with TLS' ALPN mechanism, but that seems best handled in the Some variants a user may need:
A key part of ALPN is that if a client chooses to use it, it must advertise the protocols it can speak, such as This means the connector needs some way of telling the There is also a question around how a user configures what variant they need. There is the It might be sufficient to not have extra configuration on the |
I was initially thinking that the best route would be something like a This would require configuration of both the TLS connector and the client if you want to use only HTTP/1 but that seems fine since I think that'd be a pretty rare use case? |
Here's what I think a reasonable route forward is: Public API changes: pub struct Protocol(...);
impl Protocol {
const HTTP1_1: Protocol = Protocol(...);
const HTTP2: Protocol = Protocol(...);
}
impl Connected {
pub fn negotiated_protocol(self, protocol: Protocol) -> Self { ... }
} If a connector indicates that it has negotiated a protocol, Hyper has to respect that. If HTTP/1.1 is negotiated but the client was configured with http2_only, it'll have to return an error. The pool implementation will need to change, since it currently depends on knowing up-front which protocol will be used. The constraint that only a single HTTP/2 connection attempt can exist at any time since we don't know if the connection will be using HTTP/2. It seems like a reasonable approach is that the pool's key type is just the authority, and the value is an enum of either a single HTTP/2 connection, or a set of idle HTTP/1.1 connections. The HTTP/2 mode is preferred, so if a new connection comes in that negotiated HTTP/2, it'll replace any existing HTTP/1.1 connections. Does this all seem plausible? I can start working on the implementation if so. |
The caveats you mention sound about right. An alternative to exposing a new type would be to just add Does it make sense at all for the |
Yeah, negotiated_http1 and negotiated_http2 seem reasonable to me as well. I'm not sure it'll be all that useful for the connector to know that http2_only was set. It seems pretty weird to have a server that supports h2 but prioritizes http/1.1 over it. |
Yea, that'd be odd. There could conceptually be an Perhaps that concern can just be postponed, and people can configure their HTTPS connector how to do ALPN in the first place. |
Yeah I think you'll need to explicitly configure ALPN for hyper-openssl at least for it to be used at all initially. |
The changes required in the connection pool are pretty significant, so I want to write them out and make sure it seems reasonable before making them. There are a couple of constraints we want to enforce:
The pool currently just tracks the set of pending HTTP/2 connections to guarantee constraint 1, and you ask the pool for permission before making a new connection. This becomes more complicated now since we don't know up-front what protocol we're going to be using until we get part way through the connection process. There are a couple of options:
Imagine we're spawning off N requests at the same time to a new host. Option 1 is non-ideal in the HTTP/2 case since you'll perform N TCP and TLS handshakes, then throw away N-1 connections and perform a single HTTP/2 handshake. Option 2 is non-ideal in the HTTP/1 case since you'll have N-1 requests queue up behind a single request that performs the first TCP and TLS handshake, realizes the server speaks HTTP/1, and then the other N-1 requests can create their own connections. On the whole, option 2 seems preferable. This does mean we now need to track what protocol a host speaks. In particular, there's a distinction between a host we've never connected to and a host that has all of its HTTP/1 connections currently checked out. For simplicity, I'd like to just track that information "forever", even after idle connections have been cleared out. |
"We don't want to allow multiple live HTTP/2 connections to a single host." |
Sure, there could be an option to open a second connection if the first has no remaining request slots. |
I have seen a few other HTTP2 client libraries which easily allow opening multiple h2 connections, but I'm wary about doing that (at least, about making it "easy" to accidentally do so). Note the spec pleads that clients shouldn't do that:
Of course, since the connection pool is used by a |
"Of course, since the connection pool is used by a Client, a user could by pass it in hyper by just making a new Client..." |
Let me just write this down here to remind me to read the issue if there's going to be any changes: I'd really appreciate if the new |
Looking over what Golang's client does, it opts for your option 1, which is to just open a bunch of connections if there isn't an existing HTTP2 connection. If after ALPN, it is HTTP2, then the extras are all closed up and only the first connection is kept. That seems simpler to me, and less likely to cause problems for people who were successfully using the client for HTTP/1 already. For @pimeys concern, there's a couple things we could do to prevent that:
I also think the HTTP/1-and-HTTP/2-and-HTTP1Upgrade option can be removed from consideration. It turns out to be very seldom implemented, and browsers don't bother, since there exist some servers that naively hate upon seeing any |
That sounds reasonable to me. |
Live test passing, at least on linux, so far. github related: hyperium/hyper#1574 hyperium/hyper#1485
The
Connect
trait allows to provide information to the connector in theDestination
type. It should be augmented to allow requesting ALPN for HTTP2, and the returnedConnected
type should include a way for the connector to inform theClient
whether HTTP2 was negotiated.The text was updated successfully, but these errors were encountered: