You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Extract TcpConnector and Connector trait from hyper #2078
I feel like we need some crate (in-between tokio and hyper) to provide a layer of connectors.
In hyper everything's about http, but we do need a TcpConnector and a Connector trait to be available for better composability in our networking libraries.
The rationale is the "connecting" isn't just about HTTP, it's about all the connection-based protocols. It abstracts really nicely and allows us to really use the transport protocols interchangeably.
There's an emerging anti-pattern that I observed is a couple of codebases - to use HttpConnector to establish non-http TCP stream. I don't think it's right, and that's why I classify it as an anti-pattern. Unfortunately, even the authors do recommend this: #1445.
I propose we extract TcpConnector from HttpConnector, and keep the HttpConnector as a thin layer atop of TcpConnector, providing functionality like using URI as an argument (and enforce_http and so on). We can put the happy eyeballs and DNS resolution support in a layer between TcpConnector and HttpConnector (HttpConnector<HappyEyeballsConnector<TcpConnector, DomainResolver>>).
What do you think?
P.S. Maybe this is not the right place for this issue, but can start here and move somewhere else if needed.
changed the title [-]Extract `TcpConnector` and `Connector` trait from hyper.[/-][+]Extract `TcpConnector` and `Connector` trait from hyper[/+]on Dec 13, 2019
Note that in the near future, we might want to support QUIC as an alternative to TCP. So any forward-looking attempt at this should take that into consideration.
FWIW I think tower's Service trait could be used for this use case given sensible arguments.
I'd say tower services is an abstraction that can be applied atop of the connectors to make them play nice with the tower infrastructure. I'm afraid not every connector can be easily described as one request-response interaction. The readiness can be a non-trivial thing, so we might it might not naturally fil into the service trait too. TCP, HTTP fit nicely though, but I'm not sure the same applies to TLS - I'd try to avoid sacrificing configurability to fit the service trait.
An example comes to mind: SOCKS5 authorization with interactive user credentials input. Here we need to split the connection into two phases: first the handshake, and then auth negotiation.
And, in general, I'm worried about things like this - where you need multiple phases to complete the connection.
Don't anyone else think impl AsyncRead + AsyncWrite is a bit too strict? We'll probably make it a trait type, so why add more restrictions. Websocket for example also doesn't fit, but it's a connection-based protocol.
I figured the protocols that require connection and protocols that provide streaming read/write interface are separate sets, and they partially overlap.
@djc I feel like quic is a bit unique here compared to TCP in general. Not sure, how quic fits into the AsyncRead + AsyncWrite interface?
QUIC is different from TCP in this regard, yes. (I'm not sure if the word unique is useful here, since QUIC is intended to be a general purpose alternative to TCP.) QUIC connections indeed don't fit in with the AsyncRead + AsyncWrite world, since they conceptually provide a bundle of byte streams rather than a single byte stream (and QUIC byte streams can be unidirectional or bidirectional, but that's probably less important).
We have avoided exposing a wrapper trait or even a feature flag in the AWS SDKs for configuring aws-smithy-http-client with a custom TCP connector because we don't want to end up bifurcated on hyper-util versions in one of our stable crates.
I would definitely recommend against exposing a dependency on those traits/types. I already regret that some crates depend on them to plug into the legacy client. We'll eventually have 0.2 or 0.3, and that will "break" dependent APIs.
I think a future exists where those traits aren't needed:
Connect is basically just impl Service with types filled in. With the composable pool work, I've considered "connectors" to be impl Service<Dst, Response = impl Service<HttpRequest>.
The Connection trait and its Connected type don't provide much. We could likely get similar functionality with different service helpers. Like, if it's useful to attach extensions to a Response, that's a MapResponse layer.
For things like hyper-tls, when we can kill the legacy client, I'd make it just accept an S: Service<Dst>, instead of using HttpConnector. I think the existing HttpConnector as basically a better TcpStream::connect is worth having somewhere. Where exactly is not that interesting, since I'd recommend making things generic over any connector.
Activity
[-]Extract `TcpConnector` and `Connector` trait from hyper.[/-][+]Extract `TcpConnector` and `Connector` trait from hyper[/+]djc commentedon Dec 14, 2019
Note that in the near future, we might want to support QUIC as an alternative to TCP. So any forward-looking attempt at this should take that into consideration.
FWIW I think tower's Service trait could be used for this use case given sensible arguments.
MOZGIII commentedon Dec 14, 2019
I'd say tower services is an abstraction that can be applied atop of the connectors to make them play nice with the tower infrastructure. I'm afraid not every connector can be easily described as one request-response interaction. The readiness can be a non-trivial thing, so we might it might not naturally fil into the service trait too. TCP, HTTP fit nicely though, but I'm not sure the same applies to TLS - I'd try to avoid sacrificing configurability to fit the service trait.
seanmonstar commentedon Dec 16, 2019
So the thinking we've had for a while is that the
Servicetrait fits the pattern well (there's aMakeConnectiontrait).What do you think doesn't fit into the pattern of
async connect(Dst) -> Result<impl AsyncRead + AsyncWrite, Error>?MOZGIII commentedon Dec 16, 2019
An example comes to mind: SOCKS5 authorization with interactive user credentials input. Here we need to split the connection into two phases: first the handshake, and then auth negotiation.
And, in general, I'm worried about things like this - where you need multiple phases to complete the connection.
djc commentedon Dec 16, 2019
For one thing, that doesn't support QUIC.
LucioFranco commentedon Dec 16, 2019
@djc I feel like
quicis a bit unique here compared to TCP in general. Not sure, how quic fits into theAsyncRead + AsyncWriteinterface?MOZGIII commentedon Dec 16, 2019
Don't anyone else think
impl AsyncRead + AsyncWriteis a bit too strict? We'll probably make it a trait type, so why add more restrictions. Websocket for example also doesn't fit, but it's a connection-based protocol.MOZGIII commentedon Dec 16, 2019
I figured the protocols that require connection and protocols that provide streaming read/write interface are separate sets, and they partially overlap.
djc commentedon Dec 17, 2019
QUIC is different from TCP in this regard, yes. (I'm not sure if the word unique is useful here, since QUIC is intended to be a general purpose alternative to TCP.) QUIC connections indeed don't fit in with the
AsyncRead + AsyncWriteworld, since they conceptually provide a bundle of byte streams rather than a single byte stream (and QUIC byte streams can be unidirectional or bidirectional, but that's probably less important).alikor commentedon Mar 17, 2023
So any news on QUIC support?
djc commentedon Mar 17, 2023
@alikor for HTTP/3 support in Hyper, #1818 is probably a better place to follow.
(Not sure what the role of this issue should be in the context of the impending 1.0 release?)
aajtodd commentedon May 19, 2026
Certainly seems like the ecosystem could use a stable version of
Connect,Connection, andConnected.We have avoided exposing a wrapper trait or even a feature flag in the AWS SDKs for configuring
aws-smithy-http-clientwith a custom TCP connector because we don't want to end up bifurcated on hyper-util versions in one of our stable crates.seanmonstar commentedon May 19, 2026
I would definitely recommend against exposing a dependency on those traits/types. I already regret that some crates depend on them to plug into the legacy client. We'll eventually have 0.2 or 0.3, and that will "break" dependent APIs.
I think a future exists where those traits aren't needed:
Connectis basically justimpl Servicewith types filled in. With the composable pool work, I've considered "connectors" to beimpl Service<Dst, Response = impl Service<HttpRequest>.Connectiontrait and itsConnectedtype don't provide much. We could likely get similar functionality with different service helpers. Like, if it's useful to attach extensions to aResponse, that's aMapResponselayer.For things like hyper-tls, when we can kill the legacy client, I'd make it just accept an
S: Service<Dst>, instead of usingHttpConnector. I think the existingHttpConnectoras basically a betterTcpStream::connectis worth having somewhere. Where exactly is not that interesting, since I'd recommend making things generic over any connector.