Skip to content

Commit 16976b9

Browse files
authored
refactor(core): Replace Tokio I/O abstraction (#909)
1 parent 5f6723a commit 16976b9

60 files changed

Lines changed: 539 additions & 1449 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ stream = ["tokio/fs", "dep:tokio-util", "dep:sync_wrapper"]
5555
socks = ["dep:tokio-socks"]
5656

5757
# Enable WebSocket support.
58-
ws = ["dep:async-tungstenite", "futures-util/std", "futures-util/io"]
58+
ws = ["dep:tokio-tungstenite"]
5959

6060
# Enable webpki-roots for TLS certificate validation.
6161
webpki-roots = ["dep:webpki-root-certs"]
@@ -92,20 +92,17 @@ socket2 = { version = "0.6.0", features = ["all"] }
9292
ipnet = "2.11.0"
9393
schnellru = { version = "0.2.4", default-features = false }
9494
ahash = { version = "0.8.12", default-features = false }
95-
96-
## runtime
95+
boring2 = { version = "5.0.0-alpha.4", features = ["pq-experimental"] }
96+
tokio-boring2 = { version = "5.0.0-alpha.4", features = ["pq-experimental"] }
97+
brotli = "8.0.1"
98+
flate2 = "1.1.2"
99+
zstd = "0.13.3"
97100
tokio = { version = "1.47.1", default-features = false, features = [
98101
"net",
99102
"time",
100103
"rt",
101104
] }
102105

103-
## tls
104-
boring2 = { version = "5.0.0-alpha.4", features = ["pq-experimental"] }
105-
tokio-boring2 = { version = "5.0.0-alpha.4", features = ["pq-experimental"] }
106-
brotli = "8.0.1"
107-
flate2 = "1.1.2"
108-
zstd = "0.13.3"
109106

110107
# Optional deps...
111108

@@ -139,7 +136,9 @@ tokio-util = { version = "0.7.16", default-features = false, optional = true }
139136
tokio-socks = { version = "0.5.2", optional = true }
140137

141138
## websocket
142-
async-tungstenite = { version = "0.31.0", optional = true }
139+
tokio-tungstenite = { version = "0.27.0", default-features = false, features = [
140+
"handshake",
141+
], optional = true }
143142

144143
## hickory-dns
145144
hickory-resolver = { version = "0.25.2", optional = true }
@@ -162,12 +161,12 @@ system-configuration = { version = "0.6.1", optional = true }
162161
libc = "0.2.173"
163162

164163
[dev-dependencies]
165-
hyper = { version = "1.1.0", default-features = false, features = [
164+
hyper = { version = "1.7.0", default-features = false, features = [
166165
"http1",
167166
"http2",
168167
"server",
169168
] }
170-
hyper-util = { version = "0.1.13", features = [
169+
hyper-util = { version = "0.1.16", features = [
171170
"http1",
172171
"http2",
173172
"server-auto",

src/client/http/connect/conn.rs

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@ use pin_project_lite::pin_project;
88
#[cfg(unix)]
99
use tokio::net::UnixStream;
1010
use tokio::{
11-
io::{AsyncRead, AsyncWrite},
11+
io::{AsyncRead, AsyncWrite, ReadBuf},
1212
net::TcpStream,
1313
};
1414
use tokio_boring2::SslStream;
1515

1616
use super::{AsyncConnWithInfo, TlsInfoFactory};
1717
use crate::{
18-
core::{
19-
client::connect::{Connected, Connection},
20-
rt::{Read, ReadBufCursor, TokioIo, Write},
21-
},
18+
core::client::connect::{Connected, Connection},
2219
tls::{MaybeHttpsStream, TlsInfo},
2320
};
2421

@@ -43,7 +40,7 @@ pin_project! {
4340
/// It is mainly used internally to abstract over different connection types.
4441
pub struct TlsConn<T> {
4542
#[pin]
46-
inner: TokioIo<SslStream<T>>,
43+
inner: SslStream<T>,
4744
}
4845
}
4946

@@ -65,48 +62,49 @@ impl Connection for Conn {
6562
}
6663
}
6764

68-
impl Read for Conn {
65+
impl AsyncRead for Conn {
66+
#[inline]
6967
fn poll_read(
7068
self: Pin<&mut Self>,
7169
cx: &mut Context,
72-
buf: ReadBufCursor<'_>,
70+
buf: &mut ReadBuf<'_>,
7371
) -> Poll<io::Result<()>> {
74-
let this = self.project();
75-
Read::poll_read(this.inner, cx, buf)
72+
AsyncRead::poll_read(self.project().inner, cx, buf)
7673
}
7774
}
7875

79-
impl Write for Conn {
76+
impl AsyncWrite for Conn {
77+
#[inline]
8078
fn poll_write(
8179
self: Pin<&mut Self>,
8280
cx: &mut Context,
8381
buf: &[u8],
8482
) -> Poll<Result<usize, io::Error>> {
85-
let this = self.project();
86-
Write::poll_write(this.inner, cx, buf)
83+
AsyncWrite::poll_write(self.project().inner, cx, buf)
8784
}
8885

86+
#[inline]
8987
fn poll_write_vectored(
9088
self: Pin<&mut Self>,
9189
cx: &mut Context<'_>,
9290
bufs: &[IoSlice<'_>],
9391
) -> Poll<Result<usize, io::Error>> {
94-
let this = self.project();
95-
Write::poll_write_vectored(this.inner, cx, bufs)
92+
AsyncWrite::poll_write_vectored(self.project().inner, cx, bufs)
9693
}
9794

95+
#[inline]
9896
fn is_write_vectored(&self) -> bool {
9997
self.inner.is_write_vectored()
10098
}
10199

100+
#[inline]
102101
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), io::Error>> {
103-
let this = self.project();
104-
Write::poll_flush(this.inner, cx)
102+
AsyncWrite::poll_flush(self.project().inner, cx)
105103
}
106104

105+
#[inline]
107106
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), io::Error>> {
108-
let this = self.project();
109-
Write::poll_shutdown(this.inner, cx)
107+
AsyncWrite::poll_shutdown(self.project().inner, cx)
110108
}
111109
}
112110

@@ -119,29 +117,27 @@ where
119117
/// Creates a new `TlsConn` wrapping the provided `SslStream`.
120118
#[inline(always)]
121119
pub fn new(inner: SslStream<T>) -> Self {
122-
Self {
123-
inner: TokioIo::new(inner),
124-
}
120+
Self { inner }
125121
}
126122
}
127123

128124
// ===== impl TcpStream =====
129125

130126
impl Connection for TlsConn<TcpStream> {
131127
fn connected(&self) -> Connected {
132-
let connected = self.inner.inner().get_ref().connected();
133-
if self.inner.inner().ssl().selected_alpn_protocol() == Some(b"h2") {
128+
let connected = self.inner.get_ref().connected();
129+
if self.inner.ssl().selected_alpn_protocol() == Some(b"h2") {
134130
connected.negotiated_h2()
135131
} else {
136132
connected
137133
}
138134
}
139135
}
140136

141-
impl Connection for TlsConn<TokioIo<MaybeHttpsStream<TcpStream>>> {
137+
impl Connection for TlsConn<MaybeHttpsStream<TcpStream>> {
142138
fn connected(&self) -> Connected {
143-
let connected = self.inner.inner().get_ref().connected();
144-
if self.inner.inner().ssl().selected_alpn_protocol() == Some(b"h2") {
139+
let connected = self.inner.get_ref().connected();
140+
if self.inner.ssl().selected_alpn_protocol() == Some(b"h2") {
145141
connected.negotiated_h2()
146142
} else {
147143
connected
@@ -154,8 +150,8 @@ impl Connection for TlsConn<TokioIo<MaybeHttpsStream<TcpStream>>> {
154150
#[cfg(unix)]
155151
impl Connection for TlsConn<UnixStream> {
156152
fn connected(&self) -> Connected {
157-
let connected = self.inner.inner().get_ref().connected();
158-
if self.inner.inner().ssl().selected_alpn_protocol() == Some(b"h2") {
153+
let connected = self.inner.get_ref().connected();
154+
if self.inner.ssl().selected_alpn_protocol() == Some(b"h2") {
159155
connected.negotiated_h2()
160156
} else {
161157
connected
@@ -164,65 +160,66 @@ impl Connection for TlsConn<UnixStream> {
164160
}
165161

166162
#[cfg(unix)]
167-
impl Connection for TlsConn<TokioIo<MaybeHttpsStream<UnixStream>>> {
163+
impl Connection for TlsConn<MaybeHttpsStream<UnixStream>> {
168164
fn connected(&self) -> Connected {
169-
let connected = self.inner.inner().get_ref().connected();
170-
if self.inner.inner().ssl().selected_alpn_protocol() == Some(b"h2") {
165+
let connected = self.inner.get_ref().connected();
166+
if self.inner.ssl().selected_alpn_protocol() == Some(b"h2") {
171167
connected.negotiated_h2()
172168
} else {
173169
connected
174170
}
175171
}
176172
}
177173

178-
impl<T: AsyncRead + AsyncWrite + Unpin> Read for TlsConn<T> {
174+
impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for TlsConn<T> {
175+
#[inline]
179176
fn poll_read(
180177
self: Pin<&mut Self>,
181178
cx: &mut Context,
182-
buf: ReadBufCursor<'_>,
179+
buf: &mut ReadBuf<'_>,
183180
) -> Poll<tokio::io::Result<()>> {
184-
let this = self.project();
185-
Read::poll_read(this.inner, cx, buf)
181+
AsyncRead::poll_read(self.project().inner, cx, buf)
186182
}
187183
}
188184

189-
impl<T: AsyncRead + AsyncWrite + Unpin> Write for TlsConn<T> {
185+
impl<T: AsyncRead + AsyncWrite + Unpin> AsyncWrite for TlsConn<T> {
186+
#[inline]
190187
fn poll_write(
191188
self: Pin<&mut Self>,
192189
cx: &mut Context,
193190
buf: &[u8],
194191
) -> Poll<Result<usize, tokio::io::Error>> {
195-
let this = self.project();
196-
Write::poll_write(this.inner, cx, buf)
192+
AsyncWrite::poll_write(self.project().inner, cx, buf)
197193
}
198194

195+
#[inline]
199196
fn poll_write_vectored(
200197
self: Pin<&mut Self>,
201198
cx: &mut Context<'_>,
202199
bufs: &[IoSlice<'_>],
203200
) -> Poll<Result<usize, io::Error>> {
204-
let this = self.project();
205-
Write::poll_write_vectored(this.inner, cx, bufs)
201+
AsyncWrite::poll_write_vectored(self.project().inner, cx, bufs)
206202
}
207203

204+
#[inline]
208205
fn is_write_vectored(&self) -> bool {
209206
self.inner.is_write_vectored()
210207
}
211208

209+
#[inline]
212210
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), tokio::io::Error>> {
213-
let this = self.project();
214-
Write::poll_flush(this.inner, cx)
211+
AsyncWrite::poll_flush(self.project().inner, cx)
215212
}
216213

214+
#[inline]
217215
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), tokio::io::Error>> {
218-
let this = self.project();
219-
Write::poll_shutdown(this.inner, cx)
216+
AsyncWrite::poll_shutdown(self.project().inner, cx)
220217
}
221218
}
222219

223220
impl<T> TlsInfoFactory for TlsConn<T>
224221
where
225-
TokioIo<SslStream<T>>: TlsInfoFactory,
222+
SslStream<T>: TlsInfoFactory,
226223
{
227224
fn tls_info(&self) -> Option<TlsInfo> {
228225
self.inner.tls_info()

src/client/http/connect/connector.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99
};
1010

1111
use http::{Uri, uri::Scheme};
12+
use tokio::io::{AsyncRead, AsyncWrite};
1213
use tower::{
1314
Service, ServiceBuilder, ServiceExt,
1415
timeout::TimeoutLayer,
@@ -24,12 +25,9 @@ use super::{
2425
#[cfg(unix)]
2526
use crate::core::client::connect::UnixConnector;
2627
use crate::{
27-
core::{
28-
client::{
29-
ConnectExtra, ConnectRequest,
30-
connect::{Connection, proxy},
31-
},
32-
rt::{Read, TokioIo, Write},
28+
core::client::{
29+
ConnectExtra, ConnectRequest,
30+
connect::{Connection, proxy},
3331
},
3432
dns::DynResolver,
3533
error::{BoxError, TimedOut, map_timeout_to_connector_error},
@@ -411,8 +409,6 @@ impl ConnectorService {
411409
// The tunnel connector will first establish a CONNECT tunnel,
412410
// then perform the TLS handshake over the tunneled stream.
413411
let tunneled = tunnel.call(uri).await?;
414-
let tunneled = TokioIo::new(tunneled);
415-
let tunneled = TokioIo::new(tunneled);
416412

417413
// Wrap the established tunneled stream with TLS.
418414
let established_conn = EstablishedConn::new(req, tunneled);
@@ -448,8 +444,6 @@ impl ConnectorService {
448444
// The tunnel connector will first establish a CONNECT tunnel,
449445
// then perform the TLS handshake over the tunneled stream.
450446
let tunneled = tunnel.call(uri).await?;
451-
let tunneled = TokioIo::new(tunneled);
452-
let tunneled = TokioIo::new(tunneled);
453447

454448
// Wrap the established tunneled stream with TLS.
455449
let established_conn = EstablishedConn::new(req, tunneled);
@@ -521,7 +515,7 @@ impl ConnectorService {
521515
S: Service<Uri, Response = T> + Send,
522516
S::Error: Into<BoxError>,
523517
S::Future: Unpin + Send + 'static,
524-
T: Read + Write + Connection + Unpin + std::fmt::Debug + Sync + Send + 'static,
518+
T: AsyncRead + AsyncWrite + Connection + Unpin + std::fmt::Debug + Sync + Send + 'static,
525519
{
526520
// Prefer TLS options from metadata, fallback to default
527521
let tls = extra

src/client/http/connect/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ mod connector;
33
mod tls_info;
44
mod verbose;
55

6+
use tokio::io::{AsyncRead, AsyncWrite};
7+
68
pub(super) use self::{conn::Conn, connector::Connector, tls_info::TlsInfoFactory};
7-
use crate::core::{
8-
client::{ConnectRequest, connect::Connection},
9-
rt::{Read, Write},
10-
};
9+
use crate::core::client::{ConnectRequest, connect::Connection};
1110

1211
/// A wrapper type for [`ConnectRequest`] used to erase its concrete type.
1312
///
@@ -22,14 +21,14 @@ pub struct Unnameable(pub(super) ConnectRequest);
2221
/// - [`Read`] + [`Write`]: For I/O operations
2322
/// - [`Connection`]: For connection metadata
2423
/// - [`Send`] + [`Sync`] + [`Unpin`] + `'static`: For async/await compatibility
25-
trait AsyncConn: Read + Write + Connection + Send + Sync + Unpin + 'static {}
24+
trait AsyncConn: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static {}
2625

2726
/// An async connection that can also provide TLS information.
2827
///
2928
/// This extends [`AsyncConn`] with the ability to extract TLS certificate information
3029
/// when available. Useful for connections that may be either plain TCP or TLS-encrypted.
3130
trait AsyncConnWithInfo: AsyncConn + TlsInfoFactory {}
3231

33-
impl<T> AsyncConn for T where T: Read + Write + Connection + Send + Sync + Unpin + 'static {}
32+
impl<T> AsyncConn for T where T: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static {}
3433

3534
impl<T> AsyncConnWithInfo for T where T: AsyncConn + TlsInfoFactory {}

0 commit comments

Comments
 (0)