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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1922 -397
  3. data/LICENSE +203 -0
  4. data/README.md +19 -15
  5. data/ext/wreq_rb/Cargo.toml +4 -6
  6. data/ext/wreq_rb/src/client.rs +41 -48
  7. data/lib/wreq-rb/version.rb +1 -1
  8. data/patches/0001-add-transfer-size-tracking.patch +76 -67
  9. data/vendor/wreq/Cargo.toml +119 -71
  10. data/vendor/wreq/README.md +25 -20
  11. data/vendor/wreq/bench/http1.rs +25 -0
  12. data/vendor/wreq/bench/http1_over_tls.rs +25 -0
  13. data/vendor/wreq/bench/http2.rs +25 -0
  14. data/vendor/wreq/bench/http2_over_tls.rs +25 -0
  15. data/vendor/wreq/bench/support/bench.rs +91 -0
  16. data/vendor/wreq/bench/support/client.rs +217 -0
  17. data/vendor/wreq/bench/support/server.rs +188 -0
  18. data/vendor/wreq/bench/support.rs +56 -0
  19. data/vendor/wreq/examples/cert_store.rs +4 -4
  20. data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
  21. data/vendor/wreq/examples/http2_websocket.rs +2 -2
  22. data/vendor/wreq/examples/keylog.rs +3 -3
  23. data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
  24. data/vendor/wreq/examples/rt.rs +23 -0
  25. data/vendor/wreq/src/client/body.rs +23 -61
  26. data/vendor/wreq/src/client/emulate.rs +119 -0
  27. data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
  28. data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
  29. data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
  30. data/vendor/wreq/src/client/layer/config.rs +27 -6
  31. data/vendor/wreq/src/client/layer/decoder.rs +9 -4
  32. data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
  33. data/vendor/wreq/src/client/layer/redirect.rs +4 -5
  34. data/vendor/wreq/src/client/layer/retry.rs +8 -5
  35. data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
  36. data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
  37. data/vendor/wreq/src/client/layer/timeout.rs +24 -74
  38. data/vendor/wreq/src/client/layer.rs +1 -2
  39. data/vendor/wreq/src/client/multipart.rs +137 -154
  40. data/vendor/wreq/src/client/request.rs +202 -118
  41. data/vendor/wreq/src/client/response.rs +46 -45
  42. data/vendor/wreq/src/client/upgrade.rs +15 -0
  43. data/vendor/wreq/src/client/ws.rs +73 -25
  44. data/vendor/wreq/src/client.rs +1655 -17
  45. data/vendor/wreq/src/config.rs +11 -11
  46. data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
  47. data/vendor/wreq/src/conn/descriptor.rs +143 -0
  48. data/vendor/wreq/src/conn/http.rs +484 -0
  49. data/vendor/wreq/src/conn/net/io.rs +75 -0
  50. data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
  51. data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
  52. data/vendor/wreq/src/conn/net/tcp.rs +561 -0
  53. data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
  54. data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
  55. data/vendor/wreq/src/conn/net/uds.rs +11 -0
  56. data/vendor/wreq/src/conn/net.rs +130 -0
  57. data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
  58. data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
  59. data/vendor/wreq/src/conn/tls_info.rs +47 -0
  60. data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
  61. data/vendor/wreq/src/cookie.rs +302 -142
  62. data/vendor/wreq/src/dns/gai/compio.rs +77 -0
  63. data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
  64. data/vendor/wreq/src/dns/gai.rs +14 -164
  65. data/vendor/wreq/src/dns/hickory.rs +16 -23
  66. data/vendor/wreq/src/dns/resolve.rs +7 -41
  67. data/vendor/wreq/src/dns.rs +90 -7
  68. data/vendor/wreq/src/error.rs +57 -31
  69. data/vendor/wreq/src/ext.rs +25 -0
  70. data/vendor/wreq/src/group.rs +211 -0
  71. data/vendor/wreq/src/header.rs +100 -112
  72. data/vendor/wreq/src/lib.rs +124 -73
  73. data/vendor/wreq/src/proxy.rs +6 -20
  74. data/vendor/wreq/src/redirect.rs +1 -1
  75. data/vendor/wreq/src/rt.rs +208 -0
  76. data/vendor/wreq/src/sync.rs +97 -98
  77. data/vendor/wreq/src/tls/compress.rs +124 -0
  78. data/vendor/wreq/src/tls/conn/ext.rs +54 -45
  79. data/vendor/wreq/src/tls/conn/service.rs +14 -18
  80. data/vendor/wreq/src/tls/conn.rs +169 -241
  81. data/vendor/wreq/src/tls/keylog.rs +68 -5
  82. data/vendor/wreq/src/tls/session.rs +205 -0
  83. data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
  84. data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
  85. data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
  86. data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
  87. data/vendor/wreq/src/tls.rs +489 -25
  88. data/vendor/wreq/src/trace.rs +0 -12
  89. data/vendor/wreq/src/util.rs +1 -1
  90. data/vendor/wreq/tests/badssl.rs +10 -10
  91. data/vendor/wreq/tests/client.rs +3 -9
  92. data/vendor/wreq/tests/cookie.rs +6 -8
  93. data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
  94. data/vendor/wreq/tests/multipart.rs +43 -1
  95. data/vendor/wreq/tests/proxy.rs +1 -1
  96. data/vendor/wreq/tests/support/layer.rs +1 -0
  97. metadata +49 -71
  98. data/patches/0002-add-cancel-connections.patch +0 -181
  99. data/vendor/wreq/src/client/conn/conn.rs +0 -231
  100. data/vendor/wreq/src/client/conn/http.rs +0 -1023
  101. data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
  102. data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
  103. data/vendor/wreq/src/client/core/body/length.rs +0 -118
  104. data/vendor/wreq/src/client/core/body.rs +0 -34
  105. data/vendor/wreq/src/client/core/common/buf.rs +0 -149
  106. data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
  107. data/vendor/wreq/src/client/core/common/watch.rs +0 -76
  108. data/vendor/wreq/src/client/core/common.rs +0 -3
  109. data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
  110. data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
  111. data/vendor/wreq/src/client/core/conn.rs +0 -11
  112. data/vendor/wreq/src/client/core/dispatch.rs +0 -299
  113. data/vendor/wreq/src/client/core/error.rs +0 -435
  114. data/vendor/wreq/src/client/core/ext.rs +0 -201
  115. data/vendor/wreq/src/client/core/http1.rs +0 -178
  116. data/vendor/wreq/src/client/core/http2.rs +0 -483
  117. data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
  118. data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
  119. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
  120. data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
  121. data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
  122. data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
  123. data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
  124. data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
  125. data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
  126. data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
  127. data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
  128. data/vendor/wreq/src/client/core/proto.rs +0 -58
  129. data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
  130. data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
  131. data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
  132. data/vendor/wreq/src/client/core/rt.rs +0 -25
  133. data/vendor/wreq/src/client/core/upgrade.rs +0 -267
  134. data/vendor/wreq/src/client/core.rs +0 -16
  135. data/vendor/wreq/src/client/emulation.rs +0 -161
  136. data/vendor/wreq/src/client/http/client/error.rs +0 -142
  137. data/vendor/wreq/src/client/http/client/exec.rs +0 -29
  138. data/vendor/wreq/src/client/http/client/extra.rs +0 -77
  139. data/vendor/wreq/src/client/http/client/util.rs +0 -104
  140. data/vendor/wreq/src/client/http.rs +0 -1629
  141. data/vendor/wreq/src/client/layer/config/options.rs +0 -156
  142. data/vendor/wreq/src/client/layer/cookie.rs +0 -161
  143. data/vendor/wreq/src/hash.rs +0 -143
  144. data/vendor/wreq/src/tls/conn/cache.rs +0 -123
  145. data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
  146. data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
  147. data/vendor/wreq/src/tls/options.rs +0 -464
  148. /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
  149. /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
  150. /data/vendor/wreq/src/{client/conn → conn}/verbose.rs +0 -0
@@ -0,0 +1,143 @@
1
+ use std::{
2
+ hash::{BuildHasher, Hash, Hasher},
3
+ num::NonZeroU64,
4
+ sync::{
5
+ Arc, LazyLock,
6
+ atomic::{AtomicU64, Ordering},
7
+ },
8
+ };
9
+
10
+ use http::{Uri, Version};
11
+ use lru::DefaultHasher;
12
+
13
+ use crate::{conn::net::SocketBindOptions, group::Group, proxy::Matcher, tls::TlsOptions};
14
+
15
+ /// A key that uniquely identifies a group of interchangeable connections for pooling.
16
+ ///
17
+ /// This ID is derived from all parameters that define a connection endpoint,
18
+ /// such as URI, proxy, and local socket bindings. Connections with the same
19
+ /// ID are considered equivalent and can be reused.
20
+ #[derive(Debug, Clone)]
21
+ pub(crate) struct ConnectionId(Arc<(Group, AtomicU64)>);
22
+
23
+ /// A blueprint for creating a new client connection, containing all necessary parameters.
24
+ ///
25
+ /// This descriptor bundles the target `Uri`, HTTP version, `TlsOptions`, proxy settings,
26
+ /// and other configurations needed to establish a connection.
27
+ #[must_use]
28
+ #[derive(Clone)]
29
+ pub(crate) struct ConnectionDescriptor {
30
+ uri: Uri,
31
+ version: Option<Version>,
32
+ proxy: Option<Matcher>,
33
+ tls_options: Option<TlsOptions>,
34
+ socket_bind: Option<SocketBindOptions>,
35
+ connection_id: ConnectionId,
36
+ }
37
+
38
+ // ===== impl ConnectionId =====
39
+
40
+ impl Hash for ConnectionId {
41
+ fn hash<H: Hasher>(&self, state: &mut H) {
42
+ let hash = self.0.1.load(Ordering::Relaxed);
43
+ if hash != 0 {
44
+ state.write_u64(hash);
45
+ return;
46
+ }
47
+
48
+ static HASHER: LazyLock<DefaultHasher> = LazyLock::new(DefaultHasher::default);
49
+ let computed_hash = NonZeroU64::new(HASHER.hash_one(&self.0.0))
50
+ .map(NonZeroU64::get)
51
+ .unwrap_or(1);
52
+
53
+ let _ = self.0.1.compare_exchange(
54
+ u64::MIN,
55
+ computed_hash,
56
+ Ordering::Relaxed,
57
+ Ordering::Relaxed,
58
+ );
59
+ state.write_u64(computed_hash);
60
+ }
61
+ }
62
+
63
+ impl PartialEq for ConnectionId {
64
+ #[inline]
65
+ fn eq(&self, other: &Self) -> bool {
66
+ self.0.0.eq(&other.0.0)
67
+ }
68
+ }
69
+
70
+ impl Eq for ConnectionId {}
71
+
72
+ // ===== impl ConnectionDescriptor =====
73
+
74
+ impl ConnectionDescriptor {
75
+ /// Create a new [`ConnectionDescriptor`].
76
+ pub(crate) fn new(
77
+ uri: Uri,
78
+ mut group: Group,
79
+ proxy: Option<Matcher>,
80
+ version: Option<Version>,
81
+ tls_options: Option<TlsOptions>,
82
+ socket_bind: Option<SocketBindOptions>,
83
+ ) -> ConnectionDescriptor {
84
+ let connection_id = {
85
+ group
86
+ .uri(uri.clone())
87
+ .version(version)
88
+ .proxy(proxy.clone())
89
+ .socket_bind(socket_bind.clone());
90
+ ConnectionId(Arc::new((group, AtomicU64::new(u64::MIN))))
91
+ };
92
+
93
+ ConnectionDescriptor {
94
+ uri,
95
+ proxy,
96
+ version,
97
+ tls_options,
98
+ socket_bind,
99
+ connection_id,
100
+ }
101
+ }
102
+
103
+ /// Returns a [`ConnectionId`] group ID for this descriptor.
104
+ #[inline]
105
+ pub(crate) fn id(&self) -> ConnectionId {
106
+ self.connection_id.clone()
107
+ }
108
+
109
+ /// Returns a reference to the [`Uri`].
110
+ #[inline]
111
+ pub(crate) fn uri(&self) -> &Uri {
112
+ &self.uri
113
+ }
114
+
115
+ /// Returns a mutable reference to the [`Uri`].
116
+ #[inline]
117
+ pub(crate) fn uri_mut(&mut self) -> &mut Uri {
118
+ &mut self.uri
119
+ }
120
+
121
+ /// Return the negotiated HTTP version, if any.
122
+ pub(crate) fn version(&self) -> Option<Version> {
123
+ self.version
124
+ }
125
+
126
+ /// Return a reference to the [`TlsOptions`].
127
+ #[inline]
128
+ pub(crate) fn tls_options(&self) -> Option<&TlsOptions> {
129
+ self.tls_options.as_ref()
130
+ }
131
+
132
+ /// Return a reference to the [`Matcher`].
133
+ #[inline]
134
+ pub(crate) fn proxy(&self) -> Option<&Matcher> {
135
+ self.proxy.as_ref()
136
+ }
137
+
138
+ /// Return a reference to the [`SocketBindOptions`].
139
+ #[inline]
140
+ pub(crate) fn socket_bind_options(&self) -> Option<&SocketBindOptions> {
141
+ self.socket_bind.as_ref()
142
+ }
143
+ }
@@ -0,0 +1,484 @@
1
+ use std::{
2
+ future::Future,
3
+ marker::PhantomData,
4
+ net::{Ipv4Addr, Ipv6Addr, SocketAddr},
5
+ pin::Pin,
6
+ sync::Arc,
7
+ task::{self, Poll},
8
+ time::Duration,
9
+ };
10
+
11
+ use http::uri::{Scheme, Uri};
12
+ use pin_project_lite::pin_project;
13
+ use tokio::io::{AsyncRead, AsyncWrite};
14
+ use tower::{BoxError, Service};
15
+
16
+ use super::{
17
+ Connection,
18
+ net::{
19
+ SocketBindOptions,
20
+ tcp::{ConnectError, ConnectingTcp, TcpConnector, TcpKeepaliveOptions, TcpOptions},
21
+ },
22
+ };
23
+ use crate::dns::{self, InternalResolve};
24
+
25
+ static INVALID_NOT_HTTP: &str = "invalid URI, scheme is not http";
26
+ static INVALID_MISSING_SCHEME: &str = "invalid URI, scheme is missing";
27
+ static INVALID_MISSING_HOST: &str = "invalid URI, host is missing";
28
+
29
+ type ConnectResult<S> = Result<<S as TcpConnector>::Connection, ConnectError>;
30
+ type BoxConnecting<S> = Pin<Box<dyn Future<Output = ConnectResult<S>> + Send>>;
31
+
32
+ /// A trait for configuring HTTP transport options on a [`Service<Uri>`] connector.
33
+ ///
34
+ /// Provides methods to adjust TCP/socket-level settings such as keepalive,
35
+ /// timeouts, buffer sizes, and local address binding. [`HttpConnector`]
36
+ /// is the default implementation.
37
+ pub trait HttpConnect: Service<Uri> + Clone + Send + Sized + 'static
38
+ where
39
+ Self::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
40
+ Self::Error: Into<BoxError>,
41
+ Self::Future: Unpin + Send + 'static,
42
+ {
43
+ /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration
44
+ /// to remain idle before sending TCP keepalive probes.
45
+ fn enforce_http(&mut self, enforced: bool);
46
+
47
+ /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`.
48
+ fn set_nodelay(&mut self, nodelay: bool);
49
+
50
+ /// Sets the value of the `SO_SNDBUF` option on the socket.
51
+ fn set_send_buffer_size(&mut self, size: Option<usize>);
52
+
53
+ /// Sets the value of the `SO_RCVBUF` option on the socket.
54
+ fn set_recv_buffer_size(&mut self, size: Option<usize>);
55
+
56
+ /// Set that all socket have `SO_REUSEADDR` set to the supplied value `reuse_address`.
57
+ fn set_reuse_address(&mut self, reuse: bool);
58
+
59
+ /// Sets the value of the `TCP_USER_TIMEOUT` option on the socket.
60
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
61
+ fn set_tcp_user_timeout(&mut self, time: Option<Duration>);
62
+
63
+ /// Set the connect timeout.
64
+ fn set_connect_timeout(&mut self, dur: Option<Duration>);
65
+
66
+ /// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
67
+ ///
68
+ /// [RFC 6555]: https://tools.ietf.org/html/rfc6555
69
+ fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>);
70
+
71
+ /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration
72
+ /// to remain idle before sending TCP keepalive probes.
73
+ fn set_keepalive(&mut self, time: Option<Duration>);
74
+
75
+ /// Set the duration between two successive TCP keepalive retransmissions,
76
+ /// if acknowledgement to the previous keepalive transmission is not received.
77
+ fn set_keepalive_interval(&mut self, interval: Option<Duration>);
78
+
79
+ /// Set the number of retransmissions to be carried out before declaring that remote end is not
80
+ /// available.
81
+ fn set_keepalive_retries(&mut self, retries: Option<u32>);
82
+
83
+ /// Sets the name of the interface to bind sockets produced.
84
+ #[cfg(any(
85
+ target_os = "android",
86
+ target_os = "fuchsia",
87
+ target_os = "illumos",
88
+ target_os = "ios",
89
+ target_os = "linux",
90
+ target_os = "macos",
91
+ target_os = "solaris",
92
+ target_os = "tvos",
93
+ target_os = "visionos",
94
+ target_os = "watchos",
95
+ ))]
96
+ fn set_interface<I: Into<std::borrow::Cow<'static, str>>>(&mut self, interface: I);
97
+
98
+ /// Set that all sockets are bound to the configured IPv4 or IPv6 address (depending on host's
99
+ /// preferences) before connection.
100
+ fn set_local_addresses<V4, V6>(&mut self, ipv4_address: V4, ipv6_address: V6)
101
+ where
102
+ V4: Into<Option<Ipv4Addr>>,
103
+ V6: Into<Option<Ipv6Addr>>;
104
+ }
105
+
106
+ /// A connector for the `http` scheme.
107
+ ///
108
+ /// Performs DNS resolution in a thread pool, and then connects over TCP.
109
+ ///
110
+ /// # Note
111
+ ///
112
+ /// Sets the [`HttpInfo`] value on responses, which includes
113
+ /// transport information such as the remote socket address used.
114
+ #[derive(Clone)]
115
+ pub struct HttpConnector<R, S> {
116
+ options: Arc<TcpOptions>,
117
+ resolver: R,
118
+ connector: S,
119
+ }
120
+
121
+ /// Extra information about the transport when an HttpConnector is used.
122
+ ///
123
+ /// # Example
124
+ ///
125
+ /// ```
126
+ /// # fn doc(res: http::Response<()>) {
127
+ /// use crate::util::client::connect::HttpInfo;
128
+ ///
129
+ /// // res = http::Response
130
+ /// res.extensions().get::<HttpInfo>().map(|info| {
131
+ /// println!("remote addr = {}", info.remote_addr());
132
+ /// });
133
+ /// # }
134
+ /// ```
135
+ ///
136
+ /// # Note
137
+ ///
138
+ /// If a different connector is used besides [`HttpConnector`],
139
+ /// this value will not exist in the extensions. Consult that specific
140
+ /// connector to see what "extra" information it might provide to responses.
141
+ #[derive(Clone, Debug)]
142
+ pub struct HttpInfo {
143
+ pub(crate) remote_addr: SocketAddr,
144
+ pub(crate) local_addr: SocketAddr,
145
+ }
146
+
147
+ // ===== impl HttpConnector =====
148
+
149
+ impl<R, S> HttpConnector<R, S> {
150
+ /// Construct a new [`HttpConnector`].
151
+ pub fn new(resolver: R, connector: S) -> HttpConnector<R, S> {
152
+ HttpConnector {
153
+ options: Arc::new(TcpOptions {
154
+ enforce_http: true,
155
+ connect_timeout: None,
156
+ happy_eyeballs_timeout: Some(Duration::from_millis(300)),
157
+ nodelay: false,
158
+ reuse_address: false,
159
+ send_buffer_size: None,
160
+ recv_buffer_size: None,
161
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
162
+ tcp_user_timeout: None,
163
+ tcp_keepalive: TcpKeepaliveOptions::default(),
164
+ socket_bind: SocketBindOptions::default(),
165
+ }),
166
+ resolver,
167
+ connector,
168
+ }
169
+ }
170
+
171
+ fn config_mut(&mut self) -> &mut TcpOptions {
172
+ // If the are HttpConnector clones, this will clone the inner
173
+ // config. So mutating the config won't ever affect previous
174
+ // clones.
175
+ Arc::make_mut(&mut self.options)
176
+ }
177
+ }
178
+
179
+ impl<R, S> HttpConnect for HttpConnector<R, S>
180
+ where
181
+ R: InternalResolve + Clone + Send + Sync + 'static,
182
+ R::Future: Send,
183
+ S: TcpConnector,
184
+ {
185
+ /// Option to enforce all `Uri`s have the `http` scheme.
186
+ ///
187
+ /// Enabled by default.
188
+ #[inline]
189
+ fn enforce_http(&mut self, is_enforced: bool) {
190
+ self.config_mut().enforce_http = is_enforced;
191
+ }
192
+
193
+ /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`.
194
+ ///
195
+ /// Default is `false`.
196
+ #[inline]
197
+ fn set_nodelay(&mut self, nodelay: bool) {
198
+ self.config_mut().nodelay = nodelay;
199
+ }
200
+
201
+ /// Sets the value of the SO_SNDBUF option on the socket.
202
+ #[inline]
203
+ fn set_send_buffer_size(&mut self, size: Option<usize>) {
204
+ self.config_mut().send_buffer_size = size;
205
+ }
206
+
207
+ /// Sets the value of the SO_RCVBUF option on the socket.
208
+ #[inline]
209
+ fn set_recv_buffer_size(&mut self, size: Option<usize>) {
210
+ self.config_mut().recv_buffer_size = size;
211
+ }
212
+
213
+ /// Set that all socket have `SO_REUSEADDR` set to the supplied value `reuse_address`.
214
+ ///
215
+ /// Default is `false`.
216
+ #[inline]
217
+ fn set_reuse_address(&mut self, reuse_address: bool) {
218
+ self.config_mut().reuse_address = reuse_address;
219
+ }
220
+
221
+ /// Sets the value of the TCP_USER_TIMEOUT option on the socket.
222
+ #[inline]
223
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
224
+ fn set_tcp_user_timeout(&mut self, time: Option<Duration>) {
225
+ self.config_mut().tcp_user_timeout = time;
226
+ }
227
+
228
+ /// Set the connect timeout.
229
+ ///
230
+ /// If a domain resolves to multiple IP addresses, the timeout will be
231
+ /// evenly divided across them.
232
+ ///
233
+ /// Default is `None`.
234
+ #[inline]
235
+ fn set_connect_timeout(&mut self, dur: Option<Duration>) {
236
+ self.config_mut().connect_timeout = dur;
237
+ }
238
+
239
+ /// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
240
+ ///
241
+ /// If hostname resolves to both IPv4 and IPv6 addresses and connection
242
+ /// cannot be established using preferred address family before timeout
243
+ /// elapses, then connector will in parallel attempt connection using other
244
+ /// address family.
245
+ ///
246
+ /// If `None`, parallel connection attempts are disabled.
247
+ ///
248
+ /// Default is 300 milliseconds.
249
+ ///
250
+ /// [RFC 6555]: https://tools.ietf.org/html/rfc6555
251
+ #[inline]
252
+ fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>) {
253
+ self.config_mut().happy_eyeballs_timeout = dur;
254
+ }
255
+
256
+ /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration
257
+ /// to remain idle before sending TCP keepalive probes.
258
+ ///
259
+ /// If `None`, keepalive is disabled.
260
+ ///
261
+ /// Default is `None`.
262
+ #[inline]
263
+ fn set_keepalive(&mut self, time: Option<Duration>) {
264
+ self.config_mut().tcp_keepalive.time = time;
265
+ }
266
+
267
+ /// Set the duration between two successive TCP keepalive retransmissions,
268
+ /// if acknowledgement to the previous keepalive transmission is not received.
269
+ #[inline]
270
+ fn set_keepalive_interval(&mut self, interval: Option<Duration>) {
271
+ self.config_mut().tcp_keepalive.interval = interval;
272
+ }
273
+
274
+ /// Set the number of retransmissions to be carried out before declaring that remote end is not
275
+ /// available.
276
+ #[inline]
277
+ fn set_keepalive_retries(&mut self, retries: Option<u32>) {
278
+ self.config_mut().tcp_keepalive.retries = retries;
279
+ }
280
+
281
+ /// Sets the name of the interface to bind sockets produced by this
282
+ /// connector.
283
+ ///
284
+ /// On Linux, this sets the `SO_BINDTODEVICE` option on this socket (see
285
+ /// [`man 7 socket`] for details). On macOS (and macOS-derived systems like
286
+ /// iOS), illumos, and Solaris, this will instead use the `IP_BOUND_IF`
287
+ /// socket option (see [`man 7p ip`]).
288
+ ///
289
+ /// If a socket is bound to an interface, only packets received from that particular
290
+ /// interface are processed by the socket. Note that this only works for some socket
291
+ /// types, particularly `AF_INET`` sockets.
292
+ ///
293
+ /// On Linux it can be used to specify a [VRF], but the binary needs
294
+ /// to either have `CAP_NET_RAW` or to be run as root.
295
+ ///
296
+ /// This function is only available on the following operating systems:
297
+ /// - Linux, including Android
298
+ /// - Fuchsia
299
+ /// - illumos and Solaris
300
+ /// - macOS, iOS, visionOS, watchOS, and tvOS
301
+ ///
302
+ /// [VRF]: https://www.kernel.org/doc/Documentation/networking/vrf.txt
303
+ /// [`man 7 socket`]: https://man7.org/linux/man-pages/man7/socket.7.html
304
+ /// [`man 7p ip`]: https://docs.oracle.com/cd/E86824_01/html/E54777/ip-7p.html
305
+ #[cfg(any(
306
+ target_os = "android",
307
+ target_os = "fuchsia",
308
+ target_os = "illumos",
309
+ target_os = "ios",
310
+ target_os = "linux",
311
+ target_os = "macos",
312
+ target_os = "solaris",
313
+ target_os = "tvos",
314
+ target_os = "visionos",
315
+ target_os = "watchos",
316
+ ))]
317
+ fn set_interface<I: Into<std::borrow::Cow<'static, str>>>(&mut self, interface: I) {
318
+ self.config_mut().socket_bind.set_interface(interface);
319
+ }
320
+
321
+ /// Set that all sockets are bound to the configured IPv4 or IPv6 address (depending on host's
322
+ /// preferences) before connection.
323
+ ///
324
+ /// If `None`, the sockets will not be bound.
325
+ ///
326
+ /// Default is `None`.
327
+ fn set_local_addresses<V4, V6>(&mut self, ipv4_address: V4, ipv6_address: V6)
328
+ where
329
+ V4: Into<Option<Ipv4Addr>>,
330
+ V6: Into<Option<Ipv6Addr>>,
331
+ {
332
+ self.config_mut()
333
+ .socket_bind
334
+ .set_local_addresses(ipv4_address, ipv6_address);
335
+ }
336
+ }
337
+
338
+ impl<R, S> Service<Uri> for HttpConnector<R, S>
339
+ where
340
+ R: InternalResolve + Clone + Send + Sync + 'static,
341
+ R::Future: Send,
342
+ S: TcpConnector,
343
+ S::TcpStream: From<socket2::Socket>,
344
+ {
345
+ type Response = S::Connection;
346
+ type Error = ConnectError;
347
+ type Future = HttpConnecting<R, S>;
348
+
349
+ #[inline]
350
+ fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
351
+ self.resolver.poll_ready(cx).map_err(ConnectError::dns)
352
+ }
353
+
354
+ fn call(&mut self, dst: Uri) -> Self::Future {
355
+ let mut this = self.clone();
356
+
357
+ let fut = async move {
358
+ let options = &this.options;
359
+
360
+ let (host, port) = get_host_port(options, &dst)?;
361
+ let host = host.trim_start_matches('[').trim_end_matches(']');
362
+
363
+ let addrs = if let Some(addrs) = dns::SocketAddrs::try_parse(host, port) {
364
+ addrs
365
+ } else {
366
+ let addrs = dns::resolve(&mut this.resolver, dns::Name::new(host.into()))
367
+ .await
368
+ .map_err(ConnectError::dns)?;
369
+ let addrs = addrs
370
+ .map(|mut addr| {
371
+ set_port(&mut addr, port, dst.port().is_some());
372
+ addr
373
+ })
374
+ .collect();
375
+ dns::SocketAddrs::new(addrs)
376
+ };
377
+
378
+ ConnectingTcp::new(addrs, options, this.connector)
379
+ .connect(options)
380
+ .await
381
+ };
382
+
383
+ HttpConnecting {
384
+ fut: Box::pin(fut),
385
+ _marker: PhantomData,
386
+ }
387
+ }
388
+ }
389
+
390
+ fn get_host_port<'u>(options: &TcpOptions, dst: &'u Uri) -> Result<(&'u str, u16), ConnectError> {
391
+ trace!(
392
+ "Http::connect; scheme={:?}, host={:?}, port={:?}",
393
+ dst.scheme(),
394
+ dst.host(),
395
+ dst.port(),
396
+ );
397
+
398
+ if options.enforce_http {
399
+ if dst.scheme() != Some(&Scheme::HTTP) {
400
+ return Err(ConnectError {
401
+ msg: INVALID_NOT_HTTP,
402
+ addr: None,
403
+ cause: None,
404
+ });
405
+ }
406
+ } else if dst.scheme().is_none() {
407
+ return Err(ConnectError {
408
+ msg: INVALID_MISSING_SCHEME,
409
+ addr: None,
410
+ cause: None,
411
+ });
412
+ }
413
+
414
+ let host = match dst.host() {
415
+ Some(s) => s,
416
+ None => {
417
+ return Err(ConnectError {
418
+ msg: INVALID_MISSING_HOST,
419
+ addr: None,
420
+ cause: None,
421
+ });
422
+ }
423
+ };
424
+ let port = match dst.port() {
425
+ Some(port) => port.as_u16(),
426
+ None => {
427
+ if dst.scheme() == Some(&Scheme::HTTPS) {
428
+ 443
429
+ } else {
430
+ 80
431
+ }
432
+ }
433
+ };
434
+
435
+ Ok((host, port))
436
+ }
437
+
438
+ /// Respect explicit ports in the URI, if none, either
439
+ /// keep non `0` ports resolved from a custom dns resolver,
440
+ /// or use the default port for the scheme.
441
+ fn set_port(addr: &mut SocketAddr, host_port: u16, explicit: bool) {
442
+ if explicit || addr.port() == 0 {
443
+ addr.set_port(host_port)
444
+ };
445
+ }
446
+
447
+ impl HttpInfo {
448
+ /// Get the remote address of the transport used.
449
+ pub fn remote_addr(&self) -> SocketAddr {
450
+ self.remote_addr
451
+ }
452
+
453
+ /// Get the local address of the transport used.
454
+ pub fn local_addr(&self) -> SocketAddr {
455
+ self.local_addr
456
+ }
457
+ }
458
+
459
+ pin_project! {
460
+ // Not publicly exported (so missing_docs doesn't trigger).
461
+ //
462
+ // We return this `Future` instead of the `Pin<Box<dyn Future>>` directly
463
+ // so that users don't rely on it fitting in a `Pin<Box<dyn Future>>` slot
464
+ // (and thus we can change the type in the future).
465
+ #[must_use = "futures do nothing unless polled"]
466
+ pub struct HttpConnecting<R, S: TcpConnector> {
467
+ #[pin]
468
+ fut: BoxConnecting<S>,
469
+ _marker: PhantomData<R>,
470
+ }
471
+ }
472
+
473
+ impl<R, S> Future for HttpConnecting<R, S>
474
+ where
475
+ R: InternalResolve,
476
+ S: TcpConnector,
477
+ {
478
+ type Output = ConnectResult<S>;
479
+
480
+ #[inline]
481
+ fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
482
+ self.project().fut.poll(cx)
483
+ }
484
+ }
@@ -0,0 +1,75 @@
1
+ //! I/O types and utilities for network connections.
2
+
3
+ #![cfg(feature = "compio-rt")]
4
+
5
+ use std::{
6
+ io,
7
+ net::SocketAddr,
8
+ pin::Pin,
9
+ task::{Context, Poll, ready},
10
+ };
11
+
12
+ use compio::io::{AsyncRead, AsyncWrite, util::Splittable};
13
+ use wreq_rt::rt::compio::io::CompioIO;
14
+
15
+ /// [`compio`] with peer and local socket addresses.
16
+ #[derive(Debug)]
17
+ pub struct CompioConnection<T: Splittable> {
18
+ pub(super) inner: CompioIO<T>,
19
+ pub(super) peer_addr: Option<SocketAddr>,
20
+ pub(super) local_addr: Option<SocketAddr>,
21
+ }
22
+
23
+ // ===== impl CompioConnection =====
24
+
25
+ impl<S> tokio::io::AsyncRead for CompioConnection<S>
26
+ where
27
+ S: Splittable + 'static,
28
+ S::ReadHalf: AsyncRead + Unpin,
29
+ S::WriteHalf: AsyncWrite + Unpin,
30
+ {
31
+ #[inline(always)]
32
+ fn poll_read(
33
+ mut self: Pin<&mut Self>,
34
+ cx: &mut Context<'_>,
35
+ buf: &mut tokio::io::ReadBuf<'_>,
36
+ ) -> Poll<io::Result<()>> {
37
+ // Flush any buffered writes before reading. This is necessary
38
+ // because code like hyper_util::rt::write_all (used by Tunnel
39
+ // and SOCKS handshakes) and hyper's own body encoder may call
40
+ // poll_write without poll_flush, leaving data buffered in
41
+ // compio's AsyncWriteStream. Since HTTP/1.1 is half-duplex
42
+ // (write then read), flushing here ensures the remote peer
43
+ // receives our data before we wait for its response.
44
+ // In HTTP/2 the stream is split, so this combined poll_read
45
+ // is not called and concurrent reads/writes are unaffected.
46
+ ready!(tokio::io::AsyncWrite::poll_flush(self.as_mut(), cx))?;
47
+ Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
48
+ }
49
+ }
50
+
51
+ impl<S> tokio::io::AsyncWrite for CompioConnection<S>
52
+ where
53
+ S: Splittable + 'static,
54
+ S::ReadHalf: AsyncRead + Unpin,
55
+ S::WriteHalf: AsyncWrite + Unpin,
56
+ {
57
+ #[inline(always)]
58
+ fn poll_write(
59
+ self: Pin<&mut Self>,
60
+ cx: &mut Context<'_>,
61
+ buf: &[u8],
62
+ ) -> Poll<io::Result<usize>> {
63
+ Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
64
+ }
65
+
66
+ #[inline(always)]
67
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
68
+ Pin::new(&mut self.get_mut().inner).poll_flush(cx)
69
+ }
70
+
71
+ #[inline(always)]
72
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
73
+ Pin::new(&mut self.get_mut().inner).poll_shutdown(cx)
74
+ }
75
+ }