wreq-rb 0.3.0

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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +2688 -0
  3. data/Cargo.toml +6 -0
  4. data/README.md +179 -0
  5. data/ext/wreq_rb/Cargo.toml +39 -0
  6. data/ext/wreq_rb/extconf.rb +22 -0
  7. data/ext/wreq_rb/src/client.rs +565 -0
  8. data/ext/wreq_rb/src/error.rs +25 -0
  9. data/ext/wreq_rb/src/lib.rs +20 -0
  10. data/ext/wreq_rb/src/response.rs +132 -0
  11. data/lib/wreq-rb/version.rb +5 -0
  12. data/lib/wreq-rb.rb +17 -0
  13. data/patches/0001-add-transfer-size-tracking.patch +292 -0
  14. data/vendor/wreq/Cargo.toml +306 -0
  15. data/vendor/wreq/LICENSE +202 -0
  16. data/vendor/wreq/README.md +122 -0
  17. data/vendor/wreq/examples/cert_store.rs +77 -0
  18. data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
  19. data/vendor/wreq/examples/emulation.rs +118 -0
  20. data/vendor/wreq/examples/form.rs +14 -0
  21. data/vendor/wreq/examples/http1_websocket.rs +37 -0
  22. data/vendor/wreq/examples/http2_websocket.rs +45 -0
  23. data/vendor/wreq/examples/json_dynamic.rs +41 -0
  24. data/vendor/wreq/examples/json_typed.rs +47 -0
  25. data/vendor/wreq/examples/keylog.rs +16 -0
  26. data/vendor/wreq/examples/request_with_emulation.rs +115 -0
  27. data/vendor/wreq/examples/request_with_interface.rs +37 -0
  28. data/vendor/wreq/examples/request_with_local_address.rs +16 -0
  29. data/vendor/wreq/examples/request_with_proxy.rs +13 -0
  30. data/vendor/wreq/examples/request_with_redirect.rs +22 -0
  31. data/vendor/wreq/examples/request_with_version.rs +15 -0
  32. data/vendor/wreq/examples/tor_socks.rs +24 -0
  33. data/vendor/wreq/examples/unix_socket.rs +33 -0
  34. data/vendor/wreq/src/client/body.rs +304 -0
  35. data/vendor/wreq/src/client/conn/conn.rs +231 -0
  36. data/vendor/wreq/src/client/conn/connector.rs +549 -0
  37. data/vendor/wreq/src/client/conn/http.rs +1023 -0
  38. data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
  39. data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
  40. data/vendor/wreq/src/client/conn/proxy.rs +39 -0
  41. data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
  42. data/vendor/wreq/src/client/conn/uds.rs +44 -0
  43. data/vendor/wreq/src/client/conn/verbose.rs +149 -0
  44. data/vendor/wreq/src/client/conn.rs +323 -0
  45. data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
  46. data/vendor/wreq/src/client/core/body/length.rs +118 -0
  47. data/vendor/wreq/src/client/core/body.rs +34 -0
  48. data/vendor/wreq/src/client/core/common/buf.rs +149 -0
  49. data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
  50. data/vendor/wreq/src/client/core/common/watch.rs +76 -0
  51. data/vendor/wreq/src/client/core/common.rs +3 -0
  52. data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
  53. data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
  54. data/vendor/wreq/src/client/core/conn.rs +11 -0
  55. data/vendor/wreq/src/client/core/dispatch.rs +299 -0
  56. data/vendor/wreq/src/client/core/error.rs +435 -0
  57. data/vendor/wreq/src/client/core/ext.rs +201 -0
  58. data/vendor/wreq/src/client/core/http1.rs +178 -0
  59. data/vendor/wreq/src/client/core/http2.rs +483 -0
  60. data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
  61. data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
  62. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
  63. data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
  64. data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
  65. data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
  66. data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
  67. data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
  68. data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
  69. data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
  70. data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
  71. data/vendor/wreq/src/client/core/proto.rs +58 -0
  72. data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
  73. data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
  74. data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
  75. data/vendor/wreq/src/client/core/rt.rs +25 -0
  76. data/vendor/wreq/src/client/core/upgrade.rs +267 -0
  77. data/vendor/wreq/src/client/core.rs +16 -0
  78. data/vendor/wreq/src/client/emulation.rs +161 -0
  79. data/vendor/wreq/src/client/http/client/error.rs +142 -0
  80. data/vendor/wreq/src/client/http/client/exec.rs +29 -0
  81. data/vendor/wreq/src/client/http/client/extra.rs +77 -0
  82. data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
  83. data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
  84. data/vendor/wreq/src/client/http/client/util.rs +104 -0
  85. data/vendor/wreq/src/client/http/client.rs +1003 -0
  86. data/vendor/wreq/src/client/http/future.rs +99 -0
  87. data/vendor/wreq/src/client/http.rs +1629 -0
  88. data/vendor/wreq/src/client/layer/config/options.rs +156 -0
  89. data/vendor/wreq/src/client/layer/config.rs +116 -0
  90. data/vendor/wreq/src/client/layer/cookie.rs +161 -0
  91. data/vendor/wreq/src/client/layer/decoder.rs +139 -0
  92. data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
  93. data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
  94. data/vendor/wreq/src/client/layer/redirect.rs +145 -0
  95. data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
  96. data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
  97. data/vendor/wreq/src/client/layer/retry.rs +151 -0
  98. data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
  99. data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
  100. data/vendor/wreq/src/client/layer/timeout.rs +177 -0
  101. data/vendor/wreq/src/client/layer.rs +15 -0
  102. data/vendor/wreq/src/client/multipart.rs +717 -0
  103. data/vendor/wreq/src/client/request.rs +818 -0
  104. data/vendor/wreq/src/client/response.rs +534 -0
  105. data/vendor/wreq/src/client/ws/json.rs +99 -0
  106. data/vendor/wreq/src/client/ws/message.rs +453 -0
  107. data/vendor/wreq/src/client/ws.rs +714 -0
  108. data/vendor/wreq/src/client.rs +27 -0
  109. data/vendor/wreq/src/config.rs +140 -0
  110. data/vendor/wreq/src/cookie.rs +579 -0
  111. data/vendor/wreq/src/dns/gai.rs +249 -0
  112. data/vendor/wreq/src/dns/hickory.rs +78 -0
  113. data/vendor/wreq/src/dns/resolve.rs +180 -0
  114. data/vendor/wreq/src/dns.rs +69 -0
  115. data/vendor/wreq/src/error.rs +502 -0
  116. data/vendor/wreq/src/ext.rs +398 -0
  117. data/vendor/wreq/src/hash.rs +143 -0
  118. data/vendor/wreq/src/header.rs +506 -0
  119. data/vendor/wreq/src/into_uri.rs +187 -0
  120. data/vendor/wreq/src/lib.rs +586 -0
  121. data/vendor/wreq/src/proxy/mac.rs +82 -0
  122. data/vendor/wreq/src/proxy/matcher.rs +806 -0
  123. data/vendor/wreq/src/proxy/uds.rs +66 -0
  124. data/vendor/wreq/src/proxy/win.rs +31 -0
  125. data/vendor/wreq/src/proxy.rs +569 -0
  126. data/vendor/wreq/src/redirect.rs +575 -0
  127. data/vendor/wreq/src/retry.rs +198 -0
  128. data/vendor/wreq/src/sync.rs +129 -0
  129. data/vendor/wreq/src/tls/conn/cache.rs +123 -0
  130. data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
  131. data/vendor/wreq/src/tls/conn/ext.rs +82 -0
  132. data/vendor/wreq/src/tls/conn/macros.rs +34 -0
  133. data/vendor/wreq/src/tls/conn/service.rs +138 -0
  134. data/vendor/wreq/src/tls/conn.rs +681 -0
  135. data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
  136. data/vendor/wreq/src/tls/keylog.rs +99 -0
  137. data/vendor/wreq/src/tls/options.rs +464 -0
  138. data/vendor/wreq/src/tls/x509/identity.rs +122 -0
  139. data/vendor/wreq/src/tls/x509/parser.rs +71 -0
  140. data/vendor/wreq/src/tls/x509/store.rs +228 -0
  141. data/vendor/wreq/src/tls/x509.rs +68 -0
  142. data/vendor/wreq/src/tls.rs +154 -0
  143. data/vendor/wreq/src/trace.rs +55 -0
  144. data/vendor/wreq/src/util.rs +122 -0
  145. data/vendor/wreq/tests/badssl.rs +228 -0
  146. data/vendor/wreq/tests/brotli.rs +350 -0
  147. data/vendor/wreq/tests/client.rs +1098 -0
  148. data/vendor/wreq/tests/connector_layers.rs +227 -0
  149. data/vendor/wreq/tests/cookie.rs +306 -0
  150. data/vendor/wreq/tests/deflate.rs +347 -0
  151. data/vendor/wreq/tests/emulation.rs +260 -0
  152. data/vendor/wreq/tests/gzip.rs +347 -0
  153. data/vendor/wreq/tests/layers.rs +261 -0
  154. data/vendor/wreq/tests/multipart.rs +165 -0
  155. data/vendor/wreq/tests/proxy.rs +438 -0
  156. data/vendor/wreq/tests/redirect.rs +629 -0
  157. data/vendor/wreq/tests/retry.rs +135 -0
  158. data/vendor/wreq/tests/support/delay_server.rs +117 -0
  159. data/vendor/wreq/tests/support/error.rs +16 -0
  160. data/vendor/wreq/tests/support/layer.rs +183 -0
  161. data/vendor/wreq/tests/support/mod.rs +9 -0
  162. data/vendor/wreq/tests/support/server.rs +232 -0
  163. data/vendor/wreq/tests/timeouts.rs +281 -0
  164. data/vendor/wreq/tests/unix_socket.rs +135 -0
  165. data/vendor/wreq/tests/upgrade.rs +98 -0
  166. data/vendor/wreq/tests/zstd.rs +559 -0
  167. metadata +225 -0
@@ -0,0 +1,233 @@
1
+ use std::{
2
+ borrow::Cow,
3
+ task::{Context, Poll},
4
+ };
5
+
6
+ use bytes::Bytes;
7
+ use http::Uri;
8
+ use tokio::io::{AsyncRead, AsyncWrite};
9
+ use tokio_socks::{
10
+ TargetAddr,
11
+ tcp::{Socks4Stream, Socks5Stream},
12
+ };
13
+ use tower::Service;
14
+
15
+ use super::Tunneling;
16
+ use crate::{
17
+ dns::{GaiResolver, InternalResolve, Name},
18
+ error::BoxError,
19
+ ext::UriExt,
20
+ };
21
+
22
+ #[derive(Debug)]
23
+ pub enum SocksError {
24
+ ConnectFailed(BoxError),
25
+ DnsResolveFailure(BoxError),
26
+ Socks(tokio_socks::Error),
27
+ Io(std::io::Error),
28
+ Utf8(std::str::Utf8Error),
29
+ DnsFailure,
30
+ MissingHost,
31
+ }
32
+
33
+ impl std::fmt::Display for SocksError {
34
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35
+ f.write_str("SOCKS error: ")?;
36
+
37
+ match self {
38
+ Self::ConnectFailed(e) => {
39
+ f.write_fmt(format_args!("failed to create underlying connection: {e}"))
40
+ }
41
+ Self::Socks(e) => f.write_fmt(format_args!("error during SOCKS handshake: {e}")),
42
+ Self::Io(e) => f.write_fmt(format_args!("io error during SOCKS handshake: {e}")),
43
+ Self::Utf8(e) => f.write_fmt(format_args!(
44
+ "invalid UTF-8 during SOCKS authentication: {e}"
45
+ )),
46
+ Self::DnsResolveFailure(e) => {
47
+ f.write_fmt(format_args!("failed to resolve DNS for SOCKS target: {e}"))
48
+ }
49
+ Self::DnsFailure => f.write_str("could not resolve to acceptable address type"),
50
+ Self::MissingHost => f.write_str("missing destination host"),
51
+ }
52
+ }
53
+ }
54
+
55
+ impl std::error::Error for SocksError {}
56
+
57
+ impl From<std::io::Error> for SocksError {
58
+ fn from(err: std::io::Error) -> Self {
59
+ Self::Io(err)
60
+ }
61
+ }
62
+
63
+ impl From<std::str::Utf8Error> for SocksError {
64
+ fn from(err: std::str::Utf8Error) -> Self {
65
+ Self::Utf8(err)
66
+ }
67
+ }
68
+
69
+ impl From<tokio_socks::Error> for SocksError {
70
+ fn from(err: tokio_socks::Error) -> Self {
71
+ Self::Socks(err)
72
+ }
73
+ }
74
+
75
+ /// Represents the SOCKS protocol version.
76
+ #[derive(Clone, Copy)]
77
+ #[repr(u8)]
78
+ pub enum Version {
79
+ V4,
80
+ V5,
81
+ }
82
+
83
+ /// Represents the DNS resolution strategy for SOCKS connections.
84
+ #[derive(Clone, Copy)]
85
+ #[repr(u8)]
86
+ pub enum DnsResolve {
87
+ Local,
88
+ Remote,
89
+ }
90
+
91
+ /// A connector that establishes connections through a SOCKS proxy.
92
+ pub struct SocksConnector<C, R = GaiResolver> {
93
+ inner: C,
94
+ resolver: R,
95
+ proxy_dst: Uri,
96
+ auth: Option<(Bytes, Bytes)>,
97
+ version: Version,
98
+ dns_resolve: DnsResolve,
99
+ }
100
+
101
+ impl<C, R> SocksConnector<C, R>
102
+ where
103
+ R: InternalResolve + Clone,
104
+ {
105
+ /// Create a new SOCKS connector with the given inner service.
106
+ ///
107
+ /// This wraps an underlying connector, and stores the address of a
108
+ /// SOCKS proxy server.
109
+ ///
110
+ /// A `SocksConnector` can then be called with any destination. The `proxy_dst` passed to
111
+ /// `call` will not be used to create the underlying connection, but will
112
+ /// be used in a SOCKS handshake sent to the proxy destination.
113
+ pub fn new_with_resolver(proxy_dst: Uri, inner: C, resolver: R) -> Self {
114
+ SocksConnector {
115
+ inner,
116
+ resolver,
117
+ proxy_dst,
118
+ version: Version::V5,
119
+ dns_resolve: DnsResolve::Local,
120
+ auth: None,
121
+ }
122
+ }
123
+
124
+ /// Sets the authentication credentials for the SOCKS proxy connection.
125
+ #[inline]
126
+ pub fn set_auth(&mut self, auth: Option<(Bytes, Bytes)>) {
127
+ self.auth = auth;
128
+ }
129
+
130
+ /// Sets whether to use the SOCKS5 protocol for the proxy connection.
131
+ #[inline]
132
+ pub fn set_version(&mut self, version: Version) {
133
+ self.version = version;
134
+ }
135
+
136
+ /// Sets whether to resolve DNS locally or let the proxy handle DNS resolution.
137
+ #[inline]
138
+ pub fn set_dns_mode(&mut self, dns_resolve: DnsResolve) {
139
+ self.dns_resolve = dns_resolve;
140
+ }
141
+ }
142
+
143
+ impl<C, R> Service<Uri> for SocksConnector<C, R>
144
+ where
145
+ C: Service<Uri>,
146
+ C::Future: Send + 'static,
147
+ C::Response: AsyncRead + AsyncWrite + Unpin + Send + 'static,
148
+ C::Error: Into<BoxError>,
149
+ R: InternalResolve + Clone + Send + 'static,
150
+ <R as InternalResolve>::Future: Send + 'static,
151
+ {
152
+ type Response = C::Response;
153
+ type Error = SocksError;
154
+ type Future = Tunneling<C::Future, C::Response, Self::Error>;
155
+
156
+ #[inline]
157
+ fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
158
+ self.inner
159
+ .poll_ready(cx)
160
+ .map_err(Into::into)
161
+ .map_err(SocksError::ConnectFailed)
162
+ }
163
+
164
+ fn call(&mut self, dst: Uri) -> Self::Future {
165
+ let connecting = self.inner.call(self.proxy_dst.clone());
166
+
167
+ let version = self.version;
168
+ let dns_resolve = self.dns_resolve;
169
+ let auth = self.auth.clone();
170
+ let mut resolver = self.resolver.clone();
171
+
172
+ let fut = async move {
173
+ let host = dst.host().ok_or(SocksError::MissingHost)?;
174
+ let port = dst.port_or_default();
175
+
176
+ // Attempt to tcp connect to the proxy server.
177
+ // This will return a `tokio::net::TcpStream` if successful.
178
+ let socket = connecting
179
+ .await
180
+ .map_err(Into::into)
181
+ .map_err(SocksError::ConnectFailed)?;
182
+
183
+ // Resolve the target address using the provided resolver.
184
+ let target_addr = match dns_resolve {
185
+ DnsResolve::Local => {
186
+ let mut socket_addr = resolver
187
+ .resolve(Name::new(host.into()))
188
+ .await
189
+ .map(|mut s| s.next())
190
+ .transpose()
191
+ .ok_or(SocksError::DnsFailure)?
192
+ .map_err(Into::into)
193
+ .map_err(SocksError::DnsResolveFailure)?;
194
+ socket_addr.set_port(port);
195
+ TargetAddr::Ip(socket_addr)
196
+ }
197
+ DnsResolve::Remote => TargetAddr::Domain(Cow::Borrowed(host), port),
198
+ };
199
+
200
+ match version {
201
+ Version::V4 => {
202
+ // For SOCKS4, we connect directly to the target address.
203
+ let stream = Socks4Stream::connect_with_socket(socket, target_addr).await?;
204
+ Ok(stream.into_inner())
205
+ }
206
+ Version::V5 => {
207
+ // For SOCKS5, we need to handle authentication if provided.
208
+ // The `auth` is an optional tuple of (username, password).
209
+ let stream = match auth {
210
+ Some((username, password)) => {
211
+ let username = std::str::from_utf8(&username)?;
212
+ let password = std::str::from_utf8(&password)?;
213
+ Socks5Stream::connect_with_password_and_socket(
214
+ socket,
215
+ target_addr,
216
+ username,
217
+ password,
218
+ )
219
+ .await?
220
+ }
221
+ None => Socks5Stream::connect_with_socket(socket, target_addr).await?,
222
+ };
223
+ Ok(stream.into_inner())
224
+ }
225
+ }
226
+ };
227
+
228
+ Tunneling {
229
+ fut: Box::pin(fut),
230
+ _marker: Default::default(),
231
+ }
232
+ }
233
+ }
@@ -0,0 +1,260 @@
1
+ use std::{
2
+ io,
3
+ marker::{PhantomData, Unpin},
4
+ pin::Pin,
5
+ task::{self, Poll, ready},
6
+ };
7
+
8
+ use http::{HeaderMap, HeaderValue, Uri};
9
+ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
10
+ use tower::Service;
11
+
12
+ use super::Tunneling;
13
+ use crate::{error::BoxError, ext::UriExt};
14
+
15
+ /// Tunnel Proxy via HTTP CONNECT
16
+ ///
17
+ /// This is a connector that can be used by the `Client`. It wraps
18
+ /// another connector, and after getting an underlying connection, it creates
19
+ /// an HTTP CONNECT tunnel over it.
20
+ #[derive(Debug)]
21
+ pub struct TunnelConnector<C> {
22
+ headers: Headers,
23
+ inner: C,
24
+ proxy_dst: Uri,
25
+ }
26
+
27
+ #[derive(Clone, Debug)]
28
+ enum Headers {
29
+ Empty,
30
+ Auth(HeaderValue),
31
+ Extra(HeaderMap),
32
+ }
33
+
34
+ #[derive(Debug)]
35
+ pub enum TunnelError {
36
+ ConnectFailed(BoxError),
37
+ Io(std::io::Error),
38
+ MissingHost,
39
+ ProxyAuthRequired,
40
+ ProxyHeadersTooLong,
41
+ TunnelUnexpectedEof,
42
+ TunnelUnsuccessful,
43
+ }
44
+
45
+ impl<C> TunnelConnector<C> {
46
+ /// Create a new tunnel connector.
47
+ ///
48
+ /// This wraps an underlying connector, and stores the address of a
49
+ /// tunneling proxy server.
50
+ ///
51
+ /// A `TunnelConnector` can then be called with any destination. The `proxy_dst` passed to
52
+ /// `call` will not be used to create the underlying connection, but will
53
+ /// be used in an HTTP CONNECT request sent to the proxy destination.
54
+ pub fn new(proxy_dst: Uri, connector: C) -> Self {
55
+ Self {
56
+ headers: Headers::Empty,
57
+ inner: connector,
58
+ proxy_dst,
59
+ }
60
+ }
61
+
62
+ /// Add `proxy-authorization` header value to the CONNECT request.
63
+ pub fn with_auth(mut self, mut auth: HeaderValue) -> Self {
64
+ // just in case the user forgot
65
+ auth.set_sensitive(true);
66
+ match self.headers {
67
+ Headers::Empty => {
68
+ self.headers = Headers::Auth(auth);
69
+ }
70
+ Headers::Auth(ref mut existing) => {
71
+ *existing = auth;
72
+ }
73
+ Headers::Extra(ref mut extra) => {
74
+ extra.insert(http::header::PROXY_AUTHORIZATION, auth);
75
+ }
76
+ }
77
+
78
+ self
79
+ }
80
+
81
+ /// Add extra headers to be sent with the CONNECT request.
82
+ ///
83
+ /// If existing headers have been set, these will be merged.
84
+ pub fn with_headers(mut self, mut headers: HeaderMap) -> Self {
85
+ match self.headers {
86
+ Headers::Empty => {
87
+ self.headers = Headers::Extra(headers);
88
+ }
89
+ Headers::Auth(auth) => {
90
+ headers
91
+ .entry(http::header::PROXY_AUTHORIZATION)
92
+ .or_insert(auth);
93
+ self.headers = Headers::Extra(headers);
94
+ }
95
+ Headers::Extra(ref mut extra) => {
96
+ extra.extend(headers);
97
+ }
98
+ }
99
+
100
+ self
101
+ }
102
+ }
103
+
104
+ impl<C> Service<Uri> for TunnelConnector<C>
105
+ where
106
+ C: Service<Uri>,
107
+ C::Future: Send + 'static,
108
+ C::Response: AsyncRead + AsyncWrite + Unpin + Send + 'static,
109
+ C::Error: Into<BoxError>,
110
+ {
111
+ type Response = C::Response;
112
+ type Error = TunnelError;
113
+ type Future = Tunneling<C::Future, C::Response, Self::Error>;
114
+
115
+ #[inline]
116
+ fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
117
+ self.inner
118
+ .poll_ready(cx)
119
+ .map_err(Into::into)
120
+ .map_err(TunnelError::ConnectFailed)
121
+ }
122
+
123
+ fn call(&mut self, dst: Uri) -> Self::Future {
124
+ let connecting = self.inner.call(self.proxy_dst.clone());
125
+ let headers = self.headers.clone();
126
+
127
+ Tunneling {
128
+ fut: Box::pin(async move {
129
+ let conn = connecting
130
+ .await
131
+ .map_err(Into::into)
132
+ .map_err(TunnelError::ConnectFailed)?;
133
+ tunnel(
134
+ conn,
135
+ dst.host().ok_or(TunnelError::MissingHost)?,
136
+ dst.port_or_default(),
137
+ &headers,
138
+ )
139
+ .await
140
+ }),
141
+ _marker: PhantomData,
142
+ }
143
+ }
144
+ }
145
+
146
+ async fn tunnel<T>(mut conn: T, host: &str, port: u16, headers: &Headers) -> Result<T, TunnelError>
147
+ where
148
+ T: AsyncRead + AsyncWrite + Unpin,
149
+ {
150
+ let mut buf = format!(
151
+ "\
152
+ CONNECT {host}:{port} HTTP/1.1\r\n\
153
+ Host: {host}:{port}\r\n\
154
+ "
155
+ )
156
+ .into_bytes();
157
+
158
+ match headers {
159
+ Headers::Auth(auth) => {
160
+ buf.extend_from_slice(b"Proxy-Authorization: ");
161
+ buf.extend_from_slice(auth.as_bytes());
162
+ buf.extend_from_slice(b"\r\n");
163
+ }
164
+ Headers::Extra(extra) => {
165
+ for (name, value) in extra {
166
+ buf.extend_from_slice(name.as_str().as_bytes());
167
+ buf.extend_from_slice(b": ");
168
+ buf.extend_from_slice(value.as_bytes());
169
+ buf.extend_from_slice(b"\r\n");
170
+ }
171
+ }
172
+ Headers::Empty => (),
173
+ }
174
+
175
+ // headers end
176
+ buf.extend_from_slice(b"\r\n");
177
+
178
+ write_all(&mut conn, &buf).await.map_err(TunnelError::Io)?;
179
+
180
+ let mut buf = [0; 8192];
181
+ let mut pos = 0;
182
+
183
+ loop {
184
+ let n = read(&mut conn, &mut buf[pos..])
185
+ .await
186
+ .map_err(TunnelError::Io)?;
187
+
188
+ if n == 0 {
189
+ return Err(TunnelError::TunnelUnexpectedEof);
190
+ }
191
+ pos += n;
192
+
193
+ let recvd = &buf[..pos];
194
+ if recvd.starts_with(b"HTTP/1.1 200") || recvd.starts_with(b"HTTP/1.0 200") {
195
+ if recvd.ends_with(b"\r\n\r\n") {
196
+ return Ok(conn);
197
+ }
198
+ if pos == buf.len() {
199
+ return Err(TunnelError::ProxyHeadersTooLong);
200
+ }
201
+ // else read more
202
+ } else if recvd.starts_with(b"HTTP/1.1 407") {
203
+ return Err(TunnelError::ProxyAuthRequired);
204
+ } else {
205
+ return Err(TunnelError::TunnelUnsuccessful);
206
+ }
207
+ }
208
+ }
209
+
210
+ async fn read<T>(io: &mut T, buf: &mut [u8]) -> io::Result<usize>
211
+ where
212
+ T: AsyncRead + Unpin,
213
+ {
214
+ std::future::poll_fn(move |cx| {
215
+ let mut buf = ReadBuf::new(buf);
216
+ ready!(Pin::new(&mut *io).poll_read(cx, &mut buf))?;
217
+ Poll::Ready(Ok(buf.filled().len()))
218
+ })
219
+ .await
220
+ }
221
+
222
+ async fn write_all<T>(io: &mut T, buf: &[u8]) -> io::Result<()>
223
+ where
224
+ T: AsyncWrite + Unpin,
225
+ {
226
+ let mut n = 0;
227
+ std::future::poll_fn(move |cx| {
228
+ while n < buf.len() {
229
+ n += ready!(Pin::new(&mut *io).poll_write(cx, &buf[n..])?);
230
+ }
231
+ Poll::Ready(Ok(()))
232
+ })
233
+ .await
234
+ }
235
+
236
+ impl std::fmt::Display for TunnelError {
237
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238
+ f.write_str("tunnel error: ")?;
239
+
240
+ f.write_str(match self {
241
+ TunnelError::MissingHost => "missing destination host",
242
+ TunnelError::ProxyAuthRequired => "proxy authorization required",
243
+ TunnelError::ProxyHeadersTooLong => "proxy response headers too long",
244
+ TunnelError::TunnelUnexpectedEof => "unexpected end of file",
245
+ TunnelError::TunnelUnsuccessful => "unsuccessful",
246
+ TunnelError::ConnectFailed(_) => "failed to create underlying connection",
247
+ TunnelError::Io(_) => "io error establishing tunnel",
248
+ })
249
+ }
250
+ }
251
+
252
+ impl std::error::Error for TunnelError {
253
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
254
+ match self {
255
+ TunnelError::Io(e) => Some(e),
256
+ TunnelError::ConnectFailed(e) => Some(&**e),
257
+ _ => None,
258
+ }
259
+ }
260
+ }
@@ -0,0 +1,39 @@
1
+ //! Proxy helpers
2
+
3
+ #[cfg(feature = "socks")]
4
+ pub mod socks;
5
+ pub mod tunnel;
6
+
7
+ use std::{
8
+ marker::PhantomData,
9
+ pin::Pin,
10
+ task::{Context, Poll},
11
+ };
12
+
13
+ use pin_project_lite::pin_project;
14
+
15
+ pin_project! {
16
+ // Not publicly exported (so missing_docs doesn't trigger).
17
+ //
18
+ // We return this `Future` instead of the `Pin<Box<dyn Future>>` directly
19
+ // so that users don't rely on it fitting in a `Pin<Box<dyn Future>>` slot
20
+ // (and thus we can change the type in the future).
21
+ #[must_use = "futures do nothing unless polled"]
22
+ pub struct Tunneling<Fut, T, E> {
23
+ #[pin]
24
+ fut: Pin<Box<dyn Future<Output = Result<T, E>> + Send>>,
25
+ _marker: PhantomData<Fut>,
26
+ }
27
+ }
28
+
29
+ impl<F, T, E1, E2> Future for Tunneling<F, T, E2>
30
+ where
31
+ F: Future<Output = Result<T, E1>>,
32
+ {
33
+ type Output = Result<T, E2>;
34
+
35
+ #[inline]
36
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
37
+ self.project().fut.poll(cx)
38
+ }
39
+ }
@@ -0,0 +1,98 @@
1
+ use bytes::Bytes;
2
+ use tokio::net::TcpStream;
3
+ #[cfg(unix)]
4
+ use tokio::net::UnixStream;
5
+ use tokio_boring2::SslStream;
6
+
7
+ use crate::tls::{TlsInfo, conn::MaybeHttpsStream};
8
+
9
+ /// A trait for extracting TLS information from a connection.
10
+ ///
11
+ /// Implementors can provide access to peer certificate data or other TLS-related metadata.
12
+ /// For non-TLS connections, this typically returns `None`.
13
+ pub trait TlsInfoFactory {
14
+ fn tls_info(&self) -> Option<TlsInfo>;
15
+ }
16
+
17
+ fn extract_tls_info<S>(ssl_stream: &SslStream<S>) -> TlsInfo {
18
+ let ssl = ssl_stream.ssl();
19
+ TlsInfo {
20
+ peer_certificate: ssl
21
+ .peer_certificate()
22
+ .and_then(|cert| cert.to_der().ok())
23
+ .map(Bytes::from),
24
+ peer_certificate_chain: ssl.peer_cert_chain().map(|chain| {
25
+ chain
26
+ .iter()
27
+ .filter_map(|cert| cert.to_der().ok())
28
+ .map(Bytes::from)
29
+ .collect()
30
+ }),
31
+ }
32
+ }
33
+
34
+ // ===== impl TcpStream =====
35
+
36
+ impl TlsInfoFactory for TcpStream {
37
+ fn tls_info(&self) -> Option<TlsInfo> {
38
+ None
39
+ }
40
+ }
41
+
42
+ impl TlsInfoFactory for SslStream<TcpStream> {
43
+ #[inline]
44
+ fn tls_info(&self) -> Option<TlsInfo> {
45
+ Some(extract_tls_info(self))
46
+ }
47
+ }
48
+
49
+ impl TlsInfoFactory for MaybeHttpsStream<TcpStream> {
50
+ fn tls_info(&self) -> Option<TlsInfo> {
51
+ match self {
52
+ MaybeHttpsStream::Https(tls) => tls.tls_info(),
53
+ MaybeHttpsStream::Http(_) => None,
54
+ }
55
+ }
56
+ }
57
+
58
+ impl TlsInfoFactory for SslStream<MaybeHttpsStream<TcpStream>> {
59
+ #[inline]
60
+ fn tls_info(&self) -> Option<TlsInfo> {
61
+ Some(extract_tls_info(self))
62
+ }
63
+ }
64
+
65
+ // ===== impl UnixStream =====
66
+
67
+ #[cfg(unix)]
68
+ impl TlsInfoFactory for UnixStream {
69
+ fn tls_info(&self) -> Option<TlsInfo> {
70
+ None
71
+ }
72
+ }
73
+
74
+ #[cfg(unix)]
75
+ impl TlsInfoFactory for SslStream<UnixStream> {
76
+ #[inline]
77
+ fn tls_info(&self) -> Option<TlsInfo> {
78
+ Some(extract_tls_info(self))
79
+ }
80
+ }
81
+
82
+ #[cfg(unix)]
83
+ impl TlsInfoFactory for MaybeHttpsStream<UnixStream> {
84
+ fn tls_info(&self) -> Option<TlsInfo> {
85
+ match self {
86
+ MaybeHttpsStream::Https(tls) => tls.tls_info(),
87
+ MaybeHttpsStream::Http(_) => None,
88
+ }
89
+ }
90
+ }
91
+
92
+ #[cfg(unix)]
93
+ impl TlsInfoFactory for SslStream<MaybeHttpsStream<UnixStream>> {
94
+ #[inline]
95
+ fn tls_info(&self) -> Option<TlsInfo> {
96
+ Some(extract_tls_info(self))
97
+ }
98
+ }
@@ -0,0 +1,44 @@
1
+ use std::{
2
+ io,
3
+ path::Path,
4
+ pin::Pin,
5
+ sync::Arc,
6
+ task::{Context, Poll},
7
+ };
8
+
9
+ use http::Uri;
10
+ use tokio::net::UnixStream;
11
+
12
+ use super::{Connected, Connection};
13
+
14
+ type ConnectResult = io::Result<UnixStream>;
15
+ type BoxConnecting = Pin<Box<dyn Future<Output = ConnectResult> + Send>>;
16
+
17
+ #[derive(Clone)]
18
+ pub struct UnixConnector(pub(crate) Arc<Path>);
19
+
20
+ impl tower::Service<Uri> for UnixConnector {
21
+ type Response = UnixStream;
22
+ type Error = io::Error;
23
+ type Future = BoxConnecting;
24
+
25
+ #[inline]
26
+ fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
27
+ Poll::Ready(Ok(()))
28
+ }
29
+
30
+ fn call(&mut self, _: Uri) -> Self::Future {
31
+ let fut = UnixStream::connect(self.0.clone());
32
+ Box::pin(async move {
33
+ let io = fut.await?;
34
+ Ok::<_, io::Error>(io)
35
+ })
36
+ }
37
+ }
38
+
39
+ impl Connection for UnixStream {
40
+ #[inline]
41
+ fn connected(&self) -> Connected {
42
+ Connected::new()
43
+ }
44
+ }