wreq-rb 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +1922 -397
- data/LICENSE +203 -0
- data/README.md +19 -15
- data/ext/wreq_rb/Cargo.toml +4 -6
- data/ext/wreq_rb/src/client.rs +41 -48
- data/lib/wreq-rb/version.rb +1 -1
- data/patches/0001-add-transfer-size-tracking.patch +76 -67
- data/vendor/wreq/Cargo.toml +119 -71
- data/vendor/wreq/README.md +25 -20
- data/vendor/wreq/bench/http1.rs +25 -0
- data/vendor/wreq/bench/http1_over_tls.rs +25 -0
- data/vendor/wreq/bench/http2.rs +25 -0
- data/vendor/wreq/bench/http2_over_tls.rs +25 -0
- data/vendor/wreq/bench/support/bench.rs +91 -0
- data/vendor/wreq/bench/support/client.rs +217 -0
- data/vendor/wreq/bench/support/server.rs +188 -0
- data/vendor/wreq/bench/support.rs +56 -0
- data/vendor/wreq/examples/cert_store.rs +4 -4
- data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
- data/vendor/wreq/examples/http2_websocket.rs +2 -2
- data/vendor/wreq/examples/keylog.rs +3 -3
- data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
- data/vendor/wreq/examples/rt.rs +23 -0
- data/vendor/wreq/src/client/body.rs +23 -61
- data/vendor/wreq/src/client/emulate.rs +119 -0
- data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
- data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
- data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
- data/vendor/wreq/src/client/layer/config.rs +27 -6
- data/vendor/wreq/src/client/layer/decoder.rs +9 -4
- data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
- data/vendor/wreq/src/client/layer/redirect.rs +4 -5
- data/vendor/wreq/src/client/layer/retry.rs +8 -5
- data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
- data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
- data/vendor/wreq/src/client/layer/timeout.rs +24 -74
- data/vendor/wreq/src/client/layer.rs +1 -2
- data/vendor/wreq/src/client/multipart.rs +137 -154
- data/vendor/wreq/src/client/request.rs +202 -118
- data/vendor/wreq/src/client/response.rs +46 -45
- data/vendor/wreq/src/client/upgrade.rs +15 -0
- data/vendor/wreq/src/client/ws.rs +73 -25
- data/vendor/wreq/src/client.rs +1655 -17
- data/vendor/wreq/src/config.rs +11 -11
- data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
- data/vendor/wreq/src/conn/descriptor.rs +143 -0
- data/vendor/wreq/src/conn/http.rs +484 -0
- data/vendor/wreq/src/conn/net/io.rs +75 -0
- data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
- data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
- data/vendor/wreq/src/conn/net/tcp.rs +561 -0
- data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
- data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
- data/vendor/wreq/src/conn/net/uds.rs +11 -0
- data/vendor/wreq/src/conn/net.rs +130 -0
- data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
- data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
- data/vendor/wreq/src/conn/tls_info.rs +47 -0
- data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
- data/vendor/wreq/src/cookie.rs +302 -142
- data/vendor/wreq/src/dns/gai/compio.rs +77 -0
- data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
- data/vendor/wreq/src/dns/gai.rs +14 -164
- data/vendor/wreq/src/dns/hickory.rs +16 -23
- data/vendor/wreq/src/dns/resolve.rs +7 -41
- data/vendor/wreq/src/dns.rs +90 -7
- data/vendor/wreq/src/error.rs +57 -31
- data/vendor/wreq/src/ext.rs +25 -0
- data/vendor/wreq/src/group.rs +211 -0
- data/vendor/wreq/src/header.rs +100 -112
- data/vendor/wreq/src/lib.rs +124 -73
- data/vendor/wreq/src/proxy.rs +6 -20
- data/vendor/wreq/src/redirect.rs +1 -1
- data/vendor/wreq/src/rt.rs +208 -0
- data/vendor/wreq/src/sync.rs +97 -98
- data/vendor/wreq/src/tls/compress.rs +124 -0
- data/vendor/wreq/src/tls/conn/ext.rs +54 -45
- data/vendor/wreq/src/tls/conn/service.rs +14 -18
- data/vendor/wreq/src/tls/conn.rs +169 -241
- data/vendor/wreq/src/tls/keylog.rs +68 -5
- data/vendor/wreq/src/tls/session.rs +205 -0
- data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
- data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
- data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
- data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
- data/vendor/wreq/src/tls.rs +489 -25
- data/vendor/wreq/src/trace.rs +0 -12
- data/vendor/wreq/src/util.rs +1 -1
- data/vendor/wreq/tests/badssl.rs +10 -10
- data/vendor/wreq/tests/client.rs +3 -9
- data/vendor/wreq/tests/cookie.rs +6 -8
- data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
- data/vendor/wreq/tests/multipart.rs +43 -1
- data/vendor/wreq/tests/proxy.rs +1 -1
- data/vendor/wreq/tests/support/layer.rs +1 -0
- metadata +49 -71
- data/patches/0002-add-cancel-connections.patch +0 -181
- data/vendor/wreq/src/client/conn/conn.rs +0 -231
- data/vendor/wreq/src/client/conn/http.rs +0 -1023
- data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
- data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
- data/vendor/wreq/src/client/core/body/length.rs +0 -118
- data/vendor/wreq/src/client/core/body.rs +0 -34
- data/vendor/wreq/src/client/core/common/buf.rs +0 -149
- data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
- data/vendor/wreq/src/client/core/common/watch.rs +0 -76
- data/vendor/wreq/src/client/core/common.rs +0 -3
- data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
- data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
- data/vendor/wreq/src/client/core/conn.rs +0 -11
- data/vendor/wreq/src/client/core/dispatch.rs +0 -299
- data/vendor/wreq/src/client/core/error.rs +0 -435
- data/vendor/wreq/src/client/core/ext.rs +0 -201
- data/vendor/wreq/src/client/core/http1.rs +0 -178
- data/vendor/wreq/src/client/core/http2.rs +0 -483
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
- data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
- data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
- data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
- data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
- data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
- data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
- data/vendor/wreq/src/client/core/proto.rs +0 -58
- data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
- data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
- data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
- data/vendor/wreq/src/client/core/rt.rs +0 -25
- data/vendor/wreq/src/client/core/upgrade.rs +0 -267
- data/vendor/wreq/src/client/core.rs +0 -16
- data/vendor/wreq/src/client/emulation.rs +0 -161
- data/vendor/wreq/src/client/http/client/error.rs +0 -142
- data/vendor/wreq/src/client/http/client/exec.rs +0 -29
- data/vendor/wreq/src/client/http/client/extra.rs +0 -77
- data/vendor/wreq/src/client/http/client/util.rs +0 -104
- data/vendor/wreq/src/client/http.rs +0 -1629
- data/vendor/wreq/src/client/layer/config/options.rs +0 -156
- data/vendor/wreq/src/client/layer/cookie.rs +0 -161
- data/vendor/wreq/src/hash.rs +0 -143
- data/vendor/wreq/src/tls/conn/cache.rs +0 -123
- data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
- data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
- data/vendor/wreq/src/tls/options.rs +0 -464
- /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
- /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
- /data/vendor/wreq/src/{client/conn → conn}/verbose.rs +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//! Compio-based TCP connector.
|
|
2
|
+
|
|
3
|
+
#![allow(dead_code)]
|
|
4
|
+
|
|
5
|
+
use std::{future::Future, io, net::SocketAddr, pin::Pin, time::Duration};
|
|
6
|
+
|
|
7
|
+
use compio::net::{TcpSocket, TcpStream};
|
|
8
|
+
use wreq_rt::rt::compio::{future::SendFuture, io::CompioIO};
|
|
9
|
+
|
|
10
|
+
use super::BoxConnecting;
|
|
11
|
+
use crate::conn::{
|
|
12
|
+
Connected, Connection, http::HttpInfo, net::io::CompioConnection, tls_info::TlsInfoFactory,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/// A connector that uses `compio` for TCP connections.
|
|
16
|
+
#[derive(Clone, Copy, Debug, Default)]
|
|
17
|
+
pub struct TcpConnector {
|
|
18
|
+
_priv: (),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ===== impl TcpConnector =====
|
|
22
|
+
|
|
23
|
+
impl TcpConnector {
|
|
24
|
+
/// Creates a new [`TcpConnector`].
|
|
25
|
+
#[inline]
|
|
26
|
+
pub fn new() -> Self {
|
|
27
|
+
Self { _priv: () }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl super::TcpConnector for TcpConnector {
|
|
32
|
+
type TcpStream = std::net::TcpStream;
|
|
33
|
+
type Connection = CompioConnection<TcpStream>;
|
|
34
|
+
type Error = io::Error;
|
|
35
|
+
type Future = BoxConnecting<Self::Connection, Self::Error>;
|
|
36
|
+
type Sleep = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
|
|
37
|
+
|
|
38
|
+
#[inline]
|
|
39
|
+
fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future {
|
|
40
|
+
Box::pin(SendFuture::new(async move {
|
|
41
|
+
let socket = TcpSocket::from_std_stream(socket)?;
|
|
42
|
+
let tcp_stream = socket.connect(addr).await?;
|
|
43
|
+
Ok(CompioConnection {
|
|
44
|
+
peer_addr: tcp_stream.peer_addr().ok(),
|
|
45
|
+
local_addr: tcp_stream.local_addr().ok(),
|
|
46
|
+
inner: CompioIO::new(tcp_stream),
|
|
47
|
+
})
|
|
48
|
+
}))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[inline]
|
|
52
|
+
fn sleep(&self, duration: Duration) -> Self::Sleep {
|
|
53
|
+
Box::pin(SendFuture::new(compio::time::sleep(duration)))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl Connection for CompioConnection<TcpStream> {
|
|
58
|
+
fn connected(&self) -> Connected {
|
|
59
|
+
let connected = Connected::new();
|
|
60
|
+
if let (Some(remote_addr), Some(local_addr)) = (self.peer_addr, self.local_addr) {
|
|
61
|
+
connected.extra(HttpInfo {
|
|
62
|
+
remote_addr,
|
|
63
|
+
local_addr,
|
|
64
|
+
})
|
|
65
|
+
} else {
|
|
66
|
+
connected
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl TlsInfoFactory for CompioConnection<TcpStream> {}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//! Tokio-based TCP connector.
|
|
2
|
+
|
|
3
|
+
use std::{io, net::SocketAddr, time::Duration};
|
|
4
|
+
|
|
5
|
+
use tokio::net::{TcpSocket, TcpStream};
|
|
6
|
+
|
|
7
|
+
use super::BoxConnecting;
|
|
8
|
+
use crate::conn::{Connected, Connection, http::HttpInfo, tls_info::TlsInfoFactory};
|
|
9
|
+
|
|
10
|
+
/// A connector that uses `tokio` for TCP connections.
|
|
11
|
+
#[derive(Clone, Copy, Debug, Default)]
|
|
12
|
+
pub struct TcpConnector {
|
|
13
|
+
_priv: (),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl TcpConnector {
|
|
17
|
+
/// Creates a new [`TcpConnector`].
|
|
18
|
+
#[inline]
|
|
19
|
+
pub fn new() -> Self {
|
|
20
|
+
Self { _priv: () }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl super::TcpConnector for TcpConnector {
|
|
25
|
+
type TcpStream = std::net::TcpStream;
|
|
26
|
+
type Connection = TcpStream;
|
|
27
|
+
type Error = io::Error;
|
|
28
|
+
type Future = BoxConnecting<Self::Connection, Self::Error>;
|
|
29
|
+
type Sleep = tokio::time::Sleep;
|
|
30
|
+
|
|
31
|
+
#[inline]
|
|
32
|
+
fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future {
|
|
33
|
+
let socket = TcpSocket::from_std_stream(socket);
|
|
34
|
+
Box::pin(socket.connect(addr))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#[inline]
|
|
38
|
+
fn sleep(&self, duration: Duration) -> Self::Sleep {
|
|
39
|
+
tokio::time::sleep(duration)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl Connection for TcpStream {
|
|
44
|
+
fn connected(&self) -> Connected {
|
|
45
|
+
let connected = Connected::new();
|
|
46
|
+
if let (Ok(remote_addr), Ok(local_addr)) = (self.peer_addr(), self.local_addr()) {
|
|
47
|
+
connected.extra(HttpInfo {
|
|
48
|
+
remote_addr,
|
|
49
|
+
local_addr,
|
|
50
|
+
})
|
|
51
|
+
} else {
|
|
52
|
+
connected
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl TlsInfoFactory for TcpStream {}
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
//! TCP connection types and utilities.
|
|
2
|
+
|
|
3
|
+
#[cfg(feature = "tokio-rt")]
|
|
4
|
+
pub mod tokio;
|
|
5
|
+
|
|
6
|
+
#[cfg(feature = "compio-rt")]
|
|
7
|
+
pub mod compio;
|
|
8
|
+
|
|
9
|
+
use std::{
|
|
10
|
+
error::Error as StdError,
|
|
11
|
+
fmt,
|
|
12
|
+
future::Future,
|
|
13
|
+
io,
|
|
14
|
+
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
|
|
15
|
+
pin::{Pin, pin},
|
|
16
|
+
time::Duration,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
use futures_util::future::Either;
|
|
20
|
+
use socket2::TcpKeepalive;
|
|
21
|
+
|
|
22
|
+
use crate::{
|
|
23
|
+
conn::{Connection, net::SocketBindOptions},
|
|
24
|
+
dns,
|
|
25
|
+
error::BoxError,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type BoxConnecting<T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send>>;
|
|
29
|
+
|
|
30
|
+
/// A builder for tcp connections.
|
|
31
|
+
pub trait TcpConnector: Clone + Send + Sync + 'static {
|
|
32
|
+
/// The underlying stream type.
|
|
33
|
+
type TcpStream: From<socket2::Socket> + Send + Sync + 'static;
|
|
34
|
+
|
|
35
|
+
/// The type of connection returned by this builder.
|
|
36
|
+
type Connection: ::tokio::io::AsyncRead
|
|
37
|
+
+ ::tokio::io::AsyncWrite
|
|
38
|
+
+ Connection
|
|
39
|
+
+ Send
|
|
40
|
+
+ Unpin
|
|
41
|
+
+ 'static;
|
|
42
|
+
|
|
43
|
+
/// The type of error returned by this builder.
|
|
44
|
+
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
|
45
|
+
|
|
46
|
+
/// The future type returned by this builder.
|
|
47
|
+
type Future: Future<Output = Result<Self::Connection, Self::Error>> + Send + 'static;
|
|
48
|
+
|
|
49
|
+
/// The future type returned by this builder's sleep.
|
|
50
|
+
type Sleep: Future<Output = ()> + Send + 'static;
|
|
51
|
+
|
|
52
|
+
/// Build a connection from the given socket and connect to the address.
|
|
53
|
+
fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future;
|
|
54
|
+
|
|
55
|
+
/// Return a future that sleeps for the given duration.
|
|
56
|
+
fn sleep(&self, duration: Duration) -> Self::Sleep;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub(crate) struct ConnectingTcp<S: TcpConnector> {
|
|
60
|
+
preferred: ConnectingTcpRemote<S>,
|
|
61
|
+
fallback: Option<ConnectingTcpFallback<S>>,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
struct ConnectingTcpFallback<S: TcpConnector> {
|
|
65
|
+
delay: S::Sleep,
|
|
66
|
+
remote: ConnectingTcpRemote<S>,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
struct ConnectingTcpRemote<S: TcpConnector> {
|
|
70
|
+
addrs: dns::SocketAddrs,
|
|
71
|
+
connect_timeout: Option<Duration>,
|
|
72
|
+
connector: S,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
impl<S: TcpConnector> ConnectingTcp<S>
|
|
76
|
+
where
|
|
77
|
+
S::TcpStream: From<socket2::Socket>,
|
|
78
|
+
{
|
|
79
|
+
pub(crate) fn new(remote_addrs: dns::SocketAddrs, config: &TcpOptions, connector: S) -> Self {
|
|
80
|
+
if let Some(fallback_timeout) = config.happy_eyeballs_timeout {
|
|
81
|
+
let (preferred_addrs, fallback_addrs) = remote_addrs.split_by_preference(
|
|
82
|
+
config.socket_bind.ipv4_address,
|
|
83
|
+
config.socket_bind.ipv6_address,
|
|
84
|
+
);
|
|
85
|
+
if fallback_addrs.is_empty() {
|
|
86
|
+
return ConnectingTcp {
|
|
87
|
+
preferred: ConnectingTcpRemote::new(
|
|
88
|
+
preferred_addrs,
|
|
89
|
+
config.connect_timeout,
|
|
90
|
+
connector,
|
|
91
|
+
),
|
|
92
|
+
fallback: None,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ConnectingTcp {
|
|
97
|
+
preferred: ConnectingTcpRemote::new(
|
|
98
|
+
preferred_addrs,
|
|
99
|
+
config.connect_timeout,
|
|
100
|
+
connector.clone(),
|
|
101
|
+
),
|
|
102
|
+
fallback: Some(ConnectingTcpFallback {
|
|
103
|
+
delay: connector.sleep(fallback_timeout),
|
|
104
|
+
remote: ConnectingTcpRemote::new(
|
|
105
|
+
fallback_addrs,
|
|
106
|
+
config.connect_timeout,
|
|
107
|
+
connector,
|
|
108
|
+
),
|
|
109
|
+
}),
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
ConnectingTcp {
|
|
113
|
+
preferred: ConnectingTcpRemote::new(
|
|
114
|
+
remote_addrs,
|
|
115
|
+
config.connect_timeout,
|
|
116
|
+
connector,
|
|
117
|
+
),
|
|
118
|
+
fallback: None,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
impl<S: TcpConnector> ConnectingTcpRemote<S>
|
|
125
|
+
where
|
|
126
|
+
S::TcpStream: From<socket2::Socket>,
|
|
127
|
+
{
|
|
128
|
+
fn new(addrs: dns::SocketAddrs, connect_timeout: Option<Duration>, connector: S) -> Self {
|
|
129
|
+
let connect_timeout = connect_timeout.and_then(|t| t.checked_div(addrs.len() as u32));
|
|
130
|
+
Self {
|
|
131
|
+
addrs,
|
|
132
|
+
connect_timeout,
|
|
133
|
+
connector,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async fn connect(&mut self, config: &TcpOptions) -> Result<S::Connection, ConnectError> {
|
|
138
|
+
let mut err = None;
|
|
139
|
+
for addr in &mut self.addrs {
|
|
140
|
+
debug!("connecting to {}", addr);
|
|
141
|
+
match connect(&addr, config, self.connect_timeout, &self.connector) {
|
|
142
|
+
Ok(fut) => match fut.await {
|
|
143
|
+
Ok(tcp) => {
|
|
144
|
+
debug!("connected to {}", addr);
|
|
145
|
+
return Ok(tcp);
|
|
146
|
+
}
|
|
147
|
+
Err(mut e) => {
|
|
148
|
+
trace!("connect error for {}: {:?}", addr, e);
|
|
149
|
+
e.addr = Some(addr);
|
|
150
|
+
if err.is_none() {
|
|
151
|
+
err = Some(e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
Err(mut e) => {
|
|
156
|
+
trace!("connect error for {}: {:?}", addr, e);
|
|
157
|
+
e.addr = Some(addr);
|
|
158
|
+
if err.is_none() {
|
|
159
|
+
err = Some(e);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
match err {
|
|
166
|
+
Some(e) => Err(e),
|
|
167
|
+
None => Err(ConnectError::new(
|
|
168
|
+
"tcp connect error",
|
|
169
|
+
std::io::Error::new(std::io::ErrorKind::NotConnected, "Network unreachable"),
|
|
170
|
+
)),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
fn bind_local_address(
|
|
176
|
+
socket: &socket2::Socket,
|
|
177
|
+
dst_addr: &SocketAddr,
|
|
178
|
+
local_addr_ipv4: &Option<Ipv4Addr>,
|
|
179
|
+
local_addr_ipv6: &Option<Ipv6Addr>,
|
|
180
|
+
) -> io::Result<()> {
|
|
181
|
+
match (*dst_addr, local_addr_ipv4, local_addr_ipv6) {
|
|
182
|
+
(SocketAddr::V4(_), Some(addr), _) => {
|
|
183
|
+
socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
|
|
184
|
+
}
|
|
185
|
+
(SocketAddr::V6(_), _, Some(addr)) => {
|
|
186
|
+
socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
|
|
187
|
+
}
|
|
188
|
+
_ => {
|
|
189
|
+
if cfg!(windows) {
|
|
190
|
+
// Windows requires a socket be bound before calling connect
|
|
191
|
+
let any: SocketAddr = match *dst_addr {
|
|
192
|
+
SocketAddr::V4(_) => ([0, 0, 0, 0], 0).into(),
|
|
193
|
+
SocketAddr::V6(_) => ([0, 0, 0, 0, 0, 0, 0, 0], 0).into(),
|
|
194
|
+
};
|
|
195
|
+
socket.bind(&any.into())?;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
Ok(())
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn connect<S: TcpConnector>(
|
|
204
|
+
addr: &SocketAddr,
|
|
205
|
+
config: &TcpOptions,
|
|
206
|
+
connect_timeout: Option<Duration>,
|
|
207
|
+
connector: &S,
|
|
208
|
+
) -> Result<impl Future<Output = Result<S::Connection, ConnectError>>, ConnectError>
|
|
209
|
+
where
|
|
210
|
+
S::TcpStream: From<socket2::Socket>,
|
|
211
|
+
{
|
|
212
|
+
use socket2::{Domain, Protocol, Socket, Type};
|
|
213
|
+
|
|
214
|
+
let domain = Domain::for_address(*addr);
|
|
215
|
+
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
|
|
216
|
+
.map_err(ConnectError::m("tcp open error"))?;
|
|
217
|
+
|
|
218
|
+
// When constructing a Tokio `TcpSocket` from a raw fd/socket, the user is
|
|
219
|
+
// responsible for ensuring O_NONBLOCK is set.
|
|
220
|
+
socket
|
|
221
|
+
.set_nonblocking(true)
|
|
222
|
+
.map_err(ConnectError::m("tcp set_nonblocking error"))?;
|
|
223
|
+
|
|
224
|
+
if let Some(tcp_keepalive) = &config.tcp_keepalive.into_tcpkeepalive() {
|
|
225
|
+
if let Err(_e) = socket.set_tcp_keepalive(tcp_keepalive) {
|
|
226
|
+
warn!("tcp set_keepalive error: {_e}");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// That this only works for some socket types, particularly AF_INET sockets.
|
|
231
|
+
#[cfg(any(
|
|
232
|
+
target_os = "android",
|
|
233
|
+
target_os = "fuchsia",
|
|
234
|
+
target_os = "illumos",
|
|
235
|
+
target_os = "ios",
|
|
236
|
+
target_os = "linux",
|
|
237
|
+
target_os = "macos",
|
|
238
|
+
target_os = "solaris",
|
|
239
|
+
target_os = "tvos",
|
|
240
|
+
target_os = "visionos",
|
|
241
|
+
target_os = "watchos",
|
|
242
|
+
))]
|
|
243
|
+
if let Some(interface) = &config.socket_bind.interface {
|
|
244
|
+
// On Linux-like systems, set the interface to bind using
|
|
245
|
+
// `SO_BINDTODEVICE`.
|
|
246
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
247
|
+
socket
|
|
248
|
+
.bind_device(Some(interface.as_bytes()))
|
|
249
|
+
.map_err(ConnectError::m("tcp bind interface error"))?;
|
|
250
|
+
|
|
251
|
+
// On macOS-like and Solaris-like systems, we instead use `IP_BOUND_IF`.
|
|
252
|
+
// This socket option desires an integer index for the interface, so we
|
|
253
|
+
// must first determine the index of the requested interface name using
|
|
254
|
+
// `if_nametoindex`.
|
|
255
|
+
#[cfg(any(
|
|
256
|
+
target_os = "illumos",
|
|
257
|
+
target_os = "ios",
|
|
258
|
+
target_os = "macos",
|
|
259
|
+
target_os = "solaris",
|
|
260
|
+
target_os = "tvos",
|
|
261
|
+
target_os = "visionos",
|
|
262
|
+
target_os = "watchos",
|
|
263
|
+
))]
|
|
264
|
+
if let Ok(interface) = std::ffi::CString::new(interface.as_bytes()) {
|
|
265
|
+
#[allow(unsafe_code)]
|
|
266
|
+
let idx = unsafe { libc::if_nametoindex(interface.as_ptr()) };
|
|
267
|
+
let idx = std::num::NonZeroU32::new(idx).ok_or_else(|| {
|
|
268
|
+
// If the index is 0, check errno and return an I/O error.
|
|
269
|
+
ConnectError::new(
|
|
270
|
+
"error converting interface name to index",
|
|
271
|
+
io::Error::last_os_error(),
|
|
272
|
+
)
|
|
273
|
+
})?;
|
|
274
|
+
|
|
275
|
+
// Different setsockopt calls are necessary depending on whether the
|
|
276
|
+
// address is IPv4 or IPv6.
|
|
277
|
+
match addr {
|
|
278
|
+
SocketAddr::V4(_) => socket.bind_device_by_index_v4(Some(idx)),
|
|
279
|
+
SocketAddr::V6(_) => socket.bind_device_by_index_v6(Some(idx)),
|
|
280
|
+
}
|
|
281
|
+
.map_err(ConnectError::m("tcp bind interface error"))?;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
286
|
+
if let Some(tcp_user_timeout) = &config.tcp_user_timeout {
|
|
287
|
+
if let Err(_e) = socket.set_tcp_user_timeout(Some(*tcp_user_timeout)) {
|
|
288
|
+
warn!("tcp set_tcp_user_timeout error: {_e}");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
bind_local_address(
|
|
293
|
+
&socket,
|
|
294
|
+
addr,
|
|
295
|
+
&config.socket_bind.ipv4_address,
|
|
296
|
+
&config.socket_bind.ipv6_address,
|
|
297
|
+
)
|
|
298
|
+
.map_err(ConnectError::m("tcp bind local error"))?;
|
|
299
|
+
|
|
300
|
+
if config.reuse_address {
|
|
301
|
+
if let Err(_e) = socket.set_reuse_address(true) {
|
|
302
|
+
warn!("tcp set_reuse_address error: {_e}");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if let Some(size) = config.send_buffer_size {
|
|
307
|
+
if let Err(_e) = socket.set_send_buffer_size(size) {
|
|
308
|
+
warn!("tcp set_buffer_size error: {_e}");
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if let Some(size) = config.recv_buffer_size {
|
|
313
|
+
if let Err(_e) = socket.set_recv_buffer_size(size) {
|
|
314
|
+
warn!("tcp set_recv_buffer_size error: {_e}");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if let Err(_e) = socket.set_tcp_nodelay(config.nodelay) {
|
|
319
|
+
warn!("tcp set_tcp_nodelay error: {_e}");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let connect = connector.connect(socket.into(), *addr);
|
|
323
|
+
let sleep = connect_timeout.map(|dur| connector.sleep(dur));
|
|
324
|
+
|
|
325
|
+
Ok(async move {
|
|
326
|
+
match sleep {
|
|
327
|
+
Some(sleep) => match futures_util::future::select(pin!(sleep), pin!(connect)).await {
|
|
328
|
+
Either::Left(((), _)) => {
|
|
329
|
+
Err(io::Error::new(io::ErrorKind::TimedOut, "connect timeout").into())
|
|
330
|
+
}
|
|
331
|
+
Either::Right((Ok(s), _)) => Ok(s),
|
|
332
|
+
Either::Right((Err(e), _)) => Err(e.into()),
|
|
333
|
+
},
|
|
334
|
+
None => connect.await.map_err(Into::into),
|
|
335
|
+
}
|
|
336
|
+
.map_err(ConnectError::m("tcp connect error"))
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
impl<S: TcpConnector> ConnectingTcp<S>
|
|
341
|
+
where
|
|
342
|
+
S::TcpStream: From<socket2::Socket>,
|
|
343
|
+
{
|
|
344
|
+
pub(crate) async fn connect(
|
|
345
|
+
mut self,
|
|
346
|
+
config: &TcpOptions,
|
|
347
|
+
) -> Result<S::Connection, ConnectError> {
|
|
348
|
+
match self.fallback {
|
|
349
|
+
None => self.preferred.connect(config).await,
|
|
350
|
+
Some(mut fallback) => {
|
|
351
|
+
let preferred_fut = pin!(self.preferred.connect(config));
|
|
352
|
+
let fallback_fut = pin!(fallback.remote.connect(config));
|
|
353
|
+
let fallback_delay = pin!(fallback.delay);
|
|
354
|
+
|
|
355
|
+
let (result, future) =
|
|
356
|
+
match futures_util::future::select(preferred_fut, fallback_delay).await {
|
|
357
|
+
Either::Left((result, _fallback_delay)) => {
|
|
358
|
+
(result, Either::Right(fallback_fut))
|
|
359
|
+
}
|
|
360
|
+
Either::Right(((), preferred_fut)) => {
|
|
361
|
+
// Delay is done, start polling both the preferred and the fallback
|
|
362
|
+
futures_util::future::select(preferred_fut, fallback_fut)
|
|
363
|
+
.await
|
|
364
|
+
.factor_first()
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
if result.is_err() {
|
|
369
|
+
// Fallback to the remaining future (could be preferred or fallback)
|
|
370
|
+
// if we get an error
|
|
371
|
+
future.await
|
|
372
|
+
} else {
|
|
373
|
+
result
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Not publicly exported (so missing_docs doesn't trigger).
|
|
381
|
+
pub struct ConnectError {
|
|
382
|
+
pub(crate) msg: &'static str,
|
|
383
|
+
pub(crate) addr: Option<SocketAddr>,
|
|
384
|
+
pub(crate) cause: Option<BoxError>,
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
impl ConnectError {
|
|
388
|
+
pub(crate) fn new<E>(msg: &'static str, cause: E) -> ConnectError
|
|
389
|
+
where
|
|
390
|
+
E: Into<BoxError>,
|
|
391
|
+
{
|
|
392
|
+
ConnectError {
|
|
393
|
+
msg,
|
|
394
|
+
addr: None,
|
|
395
|
+
cause: Some(cause.into()),
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
pub(crate) fn dns<E>(cause: E) -> ConnectError
|
|
400
|
+
where
|
|
401
|
+
E: Into<BoxError>,
|
|
402
|
+
{
|
|
403
|
+
ConnectError::new("dns error", cause)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
pub(crate) fn m<E>(msg: &'static str) -> impl FnOnce(E) -> ConnectError
|
|
407
|
+
where
|
|
408
|
+
E: Into<BoxError>,
|
|
409
|
+
{
|
|
410
|
+
move |cause| ConnectError::new(msg, cause)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
impl fmt::Debug for ConnectError {
|
|
415
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
416
|
+
let mut b = f.debug_tuple("ConnectError");
|
|
417
|
+
b.field(&self.msg);
|
|
418
|
+
if let Some(ref addr) = self.addr {
|
|
419
|
+
b.field(addr);
|
|
420
|
+
}
|
|
421
|
+
if let Some(ref cause) = self.cause {
|
|
422
|
+
b.field(cause);
|
|
423
|
+
}
|
|
424
|
+
b.finish()
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
impl fmt::Display for ConnectError {
|
|
429
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
430
|
+
f.write_str(self.msg)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
impl StdError for ConnectError {
|
|
435
|
+
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
|
436
|
+
self.cause.as_ref().map(|e| &**e as _)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
#[derive(Clone)]
|
|
441
|
+
pub(crate) struct TcpOptions {
|
|
442
|
+
pub enforce_http: bool,
|
|
443
|
+
pub connect_timeout: Option<Duration>,
|
|
444
|
+
pub happy_eyeballs_timeout: Option<Duration>,
|
|
445
|
+
pub nodelay: bool,
|
|
446
|
+
pub reuse_address: bool,
|
|
447
|
+
pub send_buffer_size: Option<usize>,
|
|
448
|
+
pub recv_buffer_size: Option<usize>,
|
|
449
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
450
|
+
pub tcp_user_timeout: Option<Duration>,
|
|
451
|
+
pub tcp_keepalive: TcpKeepaliveOptions,
|
|
452
|
+
pub socket_bind: SocketBindOptions,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#[derive(Default, Debug, Clone, Copy)]
|
|
456
|
+
pub(crate) struct TcpKeepaliveOptions {
|
|
457
|
+
pub time: Option<Duration>,
|
|
458
|
+
#[cfg(any(
|
|
459
|
+
target_os = "android",
|
|
460
|
+
target_os = "dragonfly",
|
|
461
|
+
target_os = "freebsd",
|
|
462
|
+
target_os = "fuchsia",
|
|
463
|
+
target_os = "illumos",
|
|
464
|
+
target_os = "ios",
|
|
465
|
+
target_os = "visionos",
|
|
466
|
+
target_os = "linux",
|
|
467
|
+
target_os = "macos",
|
|
468
|
+
target_os = "netbsd",
|
|
469
|
+
target_os = "tvos",
|
|
470
|
+
target_os = "watchos",
|
|
471
|
+
target_os = "windows",
|
|
472
|
+
target_os = "cygwin",
|
|
473
|
+
))]
|
|
474
|
+
pub interval: Option<Duration>,
|
|
475
|
+
#[cfg(any(
|
|
476
|
+
target_os = "android",
|
|
477
|
+
target_os = "dragonfly",
|
|
478
|
+
target_os = "freebsd",
|
|
479
|
+
target_os = "fuchsia",
|
|
480
|
+
target_os = "illumos",
|
|
481
|
+
target_os = "ios",
|
|
482
|
+
target_os = "visionos",
|
|
483
|
+
target_os = "linux",
|
|
484
|
+
target_os = "macos",
|
|
485
|
+
target_os = "netbsd",
|
|
486
|
+
target_os = "tvos",
|
|
487
|
+
target_os = "watchos",
|
|
488
|
+
target_os = "cygwin",
|
|
489
|
+
target_os = "windows",
|
|
490
|
+
))]
|
|
491
|
+
pub retries: Option<u32>,
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
impl TcpKeepaliveOptions {
|
|
495
|
+
/// Converts into a `socket2::TcpKeealive` if there is any keep alive configuration.
|
|
496
|
+
pub(crate) fn into_tcpkeepalive(self) -> Option<TcpKeepalive> {
|
|
497
|
+
let mut dirty = false;
|
|
498
|
+
let mut ka = TcpKeepalive::new();
|
|
499
|
+
if let Some(time) = self.time {
|
|
500
|
+
ka = ka.with_time(time);
|
|
501
|
+
dirty = true
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
|
|
505
|
+
// value of the `tcp_keepalive` struct's `keepaliveinterval` field.
|
|
506
|
+
//
|
|
507
|
+
// Sets the time interval between TCP keepalive probes.
|
|
508
|
+
//
|
|
509
|
+
// Some platforms specify this value in seconds, so sub-second
|
|
510
|
+
// specifications may be omitted.
|
|
511
|
+
#[cfg(any(
|
|
512
|
+
target_os = "android",
|
|
513
|
+
target_os = "dragonfly",
|
|
514
|
+
target_os = "freebsd",
|
|
515
|
+
target_os = "fuchsia",
|
|
516
|
+
target_os = "illumos",
|
|
517
|
+
target_os = "ios",
|
|
518
|
+
target_os = "visionos",
|
|
519
|
+
target_os = "linux",
|
|
520
|
+
target_os = "macos",
|
|
521
|
+
target_os = "netbsd",
|
|
522
|
+
target_os = "tvos",
|
|
523
|
+
target_os = "watchos",
|
|
524
|
+
target_os = "windows",
|
|
525
|
+
target_os = "cygwin",
|
|
526
|
+
))]
|
|
527
|
+
{
|
|
528
|
+
if let Some(interval) = self.interval {
|
|
529
|
+
dirty = true;
|
|
530
|
+
ka = ka.with_interval(interval)
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Set the value of the `TCP_KEEPCNT` option.
|
|
535
|
+
//
|
|
536
|
+
// Set the maximum number of TCP keepalive probes that will be sent before
|
|
537
|
+
// dropping a connection, if TCP keepalive is enabled on this socket.
|
|
538
|
+
#[cfg(any(
|
|
539
|
+
target_os = "android",
|
|
540
|
+
target_os = "dragonfly",
|
|
541
|
+
target_os = "freebsd",
|
|
542
|
+
target_os = "fuchsia",
|
|
543
|
+
target_os = "illumos",
|
|
544
|
+
target_os = "ios",
|
|
545
|
+
target_os = "visionos",
|
|
546
|
+
target_os = "linux",
|
|
547
|
+
target_os = "macos",
|
|
548
|
+
target_os = "netbsd",
|
|
549
|
+
target_os = "tvos",
|
|
550
|
+
target_os = "watchos",
|
|
551
|
+
target_os = "cygwin",
|
|
552
|
+
target_os = "windows",
|
|
553
|
+
))]
|
|
554
|
+
if let Some(retries) = self.retries {
|
|
555
|
+
dirty = true;
|
|
556
|
+
ka = ka.with_retries(retries)
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
if dirty { Some(ka) } else { None }
|
|
560
|
+
}
|
|
561
|
+
}
|