1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! Asynchronous `Connector` trait and `HttpConnector` implementation.
15 
16 mod stream;
17 
18 use core::future::Future;
19 use std::io::{Error, ErrorKind};
20 use std::sync::Arc;
21 
22 /// Information of an IO.
23 pub use stream::ConnInfo;
24 use ylong_http::request::uri::Uri;
25 
26 use crate::async_impl::dns::{DefaultDnsResolver, EyeBallConfig, HappyEyeballs, Resolver};
27 use crate::runtime::{AsyncRead, AsyncWrite, TcpStream};
28 use crate::util::config::ConnectorConfig;
29 use crate::{HttpClientError, Timeout};
30 
31 /// `Connector` trait used by `async_impl::Client`. `Connector` provides
32 /// asynchronous connection establishment interfaces.
33 pub trait Connector {
34     /// Streams that this connector produces.
35     type Stream: AsyncRead + AsyncWrite + ConnInfo + Unpin + Sync + Send + 'static;
36 
37     /// Futures generated by this connector when attempting to create a stream.
38     type Future: Future<Output = Result<Self::Stream, HttpClientError>>
39         + Unpin
40         + Sync
41         + Send
42         + 'static;
43 
44     /// Attempts to establish a connection.
connect(&self, uri: &Uri) -> Self::Future45     fn connect(&self, uri: &Uri) -> Self::Future;
46 }
47 
48 /// Connector for creating HTTP or HTTPS connections asynchronously.
49 ///
50 /// `HttpConnector` implements `async_impl::Connector` trait.
51 pub struct HttpConnector {
52     config: ConnectorConfig,
53     resolver: Arc<dyn Resolver>,
54 }
55 
56 impl HttpConnector {
57     /// Creates a new `HttpConnector` with a `ConnectorConfig`.
new(config: ConnectorConfig, resolver: Arc<dyn Resolver>) -> Self58     pub(crate) fn new(config: ConnectorConfig, resolver: Arc<dyn Resolver>) -> Self {
59         Self { config, resolver }
60     }
61 
62     /// Creates a new `HttpConnector` with a given dns `Resolver`.
with_dns_resolver<R>(resolver: R) -> Self where R: Resolver,63     pub(crate) fn with_dns_resolver<R>(resolver: R) -> Self
64     where
65         R: Resolver,
66     {
67         let resolver = Arc::new(resolver) as Arc<dyn Resolver>;
68         Self {
69             config: Default::default(),
70             resolver,
71         }
72     }
73 }
74 
75 impl Default for HttpConnector {
default() -> Self76     fn default() -> Self {
77         Self {
78             config: Default::default(),
79             resolver: Arc::new(DefaultDnsResolver::default()),
80         }
81     }
82 }
83 
tcp_stream(eyeballs: HappyEyeballs) -> Result<TcpStream, HttpClientError>84 async fn tcp_stream(eyeballs: HappyEyeballs) -> Result<TcpStream, HttpClientError> {
85     eyeballs
86         .connect()
87         .await
88         .map_err(|e| {
89             #[cfg(target_os = "linux")]
90             if format!("{}", e).contains("failed to lookup address information") {
91                 return HttpClientError::from_dns_error(crate::ErrorKind::Connect, e);
92             }
93             #[cfg(target_os = "windows")]
94             if let Some(code) = e.raw_os_error() {
95                 if (0x2329..=0x26B2).contains(&code) || code == 0x2AF9 {
96                     return HttpClientError::from_dns_error(crate::ErrorKind::Connect, e);
97                 }
98             }
99             HttpClientError::from_io_error(crate::ErrorKind::Connect, e)
100         })
101         .and_then(|stream| match stream.set_nodelay(true) {
102             Ok(()) => Ok(stream),
103             Err(e) => err_from_io!(Connect, e),
104         })
105 }
106 
eyeballs_connect( resolver: Arc<dyn Resolver>, addr: &str, timeout: Timeout, ) -> Result<TcpStream, HttpClientError>107 async fn eyeballs_connect(
108     resolver: Arc<dyn Resolver>,
109     addr: &str,
110     timeout: Timeout,
111 ) -> Result<TcpStream, HttpClientError> {
112     let addr_fut = resolver.resolve(addr);
113     let socket_addr = addr_fut.await.map_err(|e| {
114         HttpClientError::from_dns_error(
115             crate::ErrorKind::Connect,
116             Error::new(ErrorKind::Interrupted, e),
117         )
118     })?;
119 
120     let addrs = socket_addr.collect::<Vec<_>>();
121     let eyeball_config = EyeBallConfig::new(timeout.inner(), None);
122     let happy_eyeballs = HappyEyeballs::new(addrs, eyeball_config);
123     tcp_stream(happy_eyeballs).await
124 }
125 
126 #[cfg(not(feature = "__tls"))]
127 mod no_tls {
128     use core::future::Future;
129     use core::pin::Pin;
130 
131     use ylong_http::request::uri::Uri;
132 
133     use super::{eyeballs_connect, Connector, HttpConnector};
134     use crate::async_impl::connector::stream::HttpStream;
135     use crate::async_impl::interceptor::{ConnDetail, ConnProtocol};
136     use crate::runtime::TcpStream;
137     use crate::HttpClientError;
138 
139     impl Connector for HttpConnector {
140         type Stream = HttpStream<TcpStream>;
141         type Future =
142             Pin<Box<dyn Future<Output = Result<Self::Stream, HttpClientError>> + Sync + Send>>;
143 
connect(&self, uri: &Uri) -> Self::Future144         fn connect(&self, uri: &Uri) -> Self::Future {
145             // Checks if this uri need be proxied.
146             let mut is_proxy = false;
147             let mut addr = uri.authority().unwrap().to_string();
148             if let Some(proxy) = self.config.proxies.match_proxy(uri) {
149                 addr = proxy.via_proxy(uri).authority().unwrap().to_string();
150                 is_proxy = true;
151             }
152             let resolver = self.resolver.clone();
153             let timeout = self.config.timeout.clone();
154             Box::pin(async move {
155                 let stream = eyeballs_connect(resolver, addr.as_str(), timeout).await?;
156                 let local = stream
157                     .local_addr()
158                     .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?;
159                 let peer = stream
160                     .peer_addr()
161                     .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?;
162                 let detail = ConnDetail {
163                     protocol: ConnProtocol::Tcp,
164                     alpn: None,
165                     local,
166                     peer,
167                     addr,
168                     proxy: is_proxy,
169                 };
170                 Ok(HttpStream::new(stream, detail))
171             })
172         }
173     }
174 }
175 
176 #[cfg(feature = "__tls")]
177 mod tls {
178     use core::future::Future;
179     use core::pin::Pin;
180     use std::error;
181     use std::fmt::{Debug, Display, Formatter};
182     use std::io::{Error, ErrorKind, Write};
183 
184     use ylong_http::request::uri::{Scheme, Uri};
185 
186     use super::{eyeballs_connect, Connector, HttpConnector};
187     use crate::async_impl::connector::stream::HttpStream;
188     use crate::async_impl::interceptor::{ConnDetail, ConnProtocol};
189     use crate::async_impl::ssl_stream::{AsyncSslStream, MixStream};
190     #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))]
191     use crate::config::FchownConfig;
192     use crate::runtime::{AsyncReadExt, AsyncWriteExt, TcpStream};
193     use crate::{HttpClientError, TlsConfig};
194 
195     impl Connector for HttpConnector {
196         type Stream = HttpStream<MixStream<TcpStream>>;
197         type Future =
198             Pin<Box<dyn Future<Output = Result<Self::Stream, HttpClientError>> + Sync + Send>>;
199 
connect(&self, uri: &Uri) -> Self::Future200         fn connect(&self, uri: &Uri) -> Self::Future {
201             // Make sure all parts of uri is accurate.
202             let mut addr = uri.authority().unwrap().to_string();
203             let mut auth = None;
204             let mut is_proxy = false;
205 
206             if let Some(proxy) = self.config.proxies.match_proxy(uri) {
207                 addr = proxy.via_proxy(uri).authority().unwrap().to_string();
208                 auth = proxy
209                     .intercept
210                     .proxy_info()
211                     .basic_auth
212                     .as_ref()
213                     .and_then(|v| v.to_string().ok());
214                 is_proxy = true;
215             }
216             #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__c_openssl"))]
217             let fchown = self.config.fchown.clone();
218             let resolver = self.resolver.clone();
219             let timeout = self.config.timeout.clone();
220             match *uri.scheme().unwrap() {
221                 Scheme::HTTP => Box::pin(async move {
222                     let stream = eyeballs_connect(resolver, addr.as_str(), timeout).await?;
223 
224                     #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__tls"))]
225                     if let Some(fchown) = fchown {
226                         let _ = stream.fchown(fchown.uid, fchown.gid);
227                     }
228 
229                     let local = stream.local_addr().map_err(|e| {
230                         HttpClientError::from_io_error(crate::ErrorKind::Connect, e)
231                     })?;
232                     let peer = stream.peer_addr().map_err(|e| {
233                         HttpClientError::from_io_error(crate::ErrorKind::Connect, e)
234                     })?;
235                     let detail = ConnDetail {
236                         protocol: ConnProtocol::Tcp,
237                         alpn: None,
238                         local,
239                         peer,
240                         addr,
241                         proxy: is_proxy,
242                     };
243 
244                     Ok(HttpStream::new(MixStream::Http(stream), detail))
245                 }),
246                 Scheme::HTTPS => {
247                     let host = uri.host().unwrap().to_string();
248                     let port = uri.port().unwrap().as_u16().unwrap();
249                     let config = self.config.tls.clone();
250                     Box::pin(async move {
251                         let stream = eyeballs_connect(resolver, addr.as_str(), timeout).await?;
252                         #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__tls"))]
253                         {
254                             https_connect(
255                                 config,
256                                 addr,
257                                 stream,
258                                 is_proxy,
259                                 auth,
260                                 (host, port),
261                                 fchown,
262                             )
263                             .await
264                         }
265                         #[cfg(not(all(
266                             target_os = "linux",
267                             feature = "ylong_base",
268                             feature = "__c_openssl"
269                         )))]
270                         {
271                             https_connect(config, addr, stream, is_proxy, auth, (host, port)).await
272                         }
273                     })
274                 }
275             }
276         }
277     }
278 
https_connect( config: TlsConfig, addr: String, tcp_stream: TcpStream, is_proxy: bool, auth: Option<String>, (host, port): (String, u16), #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__tls"))] fchown: Option< FchownConfig, >, ) -> Result<HttpStream<MixStream<TcpStream>>, HttpClientError>279     async fn https_connect(
280         config: TlsConfig,
281         addr: String,
282         tcp_stream: TcpStream,
283         is_proxy: bool,
284         auth: Option<String>,
285         (host, port): (String, u16),
286         #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__tls"))] fchown: Option<
287             FchownConfig,
288         >,
289     ) -> Result<HttpStream<MixStream<TcpStream>>, HttpClientError> {
290         let mut tcp = tcp_stream;
291         #[cfg(all(target_os = "linux", feature = "ylong_base", feature = "__tls"))]
292         if let Some(fchown) = fchown {
293             let _ = tcp.fchown(fchown.uid, fchown.gid);
294         }
295         let local = tcp
296             .local_addr()
297             .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?;
298         let peer = tcp
299             .peer_addr()
300             .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?;
301         if is_proxy {
302             tcp = tunnel(tcp, &host, port, auth)
303                 .await
304                 .map_err(|e| HttpClientError::from_io_error(crate::ErrorKind::Connect, e))?;
305         };
306 
307         let pinned_key = config.pinning_host_match(addr.as_str());
308         let mut stream = config
309             .ssl_new(&host)
310             .and_then(|ssl| AsyncSslStream::new(ssl.into_inner(), tcp, pinned_key))
311             .map_err(|e| {
312                 HttpClientError::from_tls_error(
313                     crate::ErrorKind::Connect,
314                     Error::new(ErrorKind::Other, e),
315                 )
316             })?;
317 
318         Pin::new(&mut stream).connect().await.map_err(|e| {
319             HttpClientError::from_tls_error(
320                 crate::ErrorKind::Connect,
321                 Error::new(ErrorKind::Other, e),
322             )
323         })?;
324 
325         let alpn = stream.negotiated_alpn_protocol().map(Vec::from);
326         let detail = ConnDetail {
327             protocol: ConnProtocol::Tcp,
328             alpn,
329             local,
330             peer,
331             addr,
332             proxy: is_proxy,
333         };
334 
335         Ok(HttpStream::new(MixStream::Https(stream), detail))
336     }
337 
tunnel( mut conn: TcpStream, host: &str, port: u16, auth: Option<String>, ) -> Result<TcpStream, Error>338     async fn tunnel(
339         mut conn: TcpStream,
340         host: &str,
341         port: u16,
342         auth: Option<String>,
343     ) -> Result<TcpStream, Error> {
344         let mut req = Vec::new();
345 
346         write!(
347             &mut req,
348             "CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n"
349         )?;
350 
351         if let Some(value) = auth {
352             write!(&mut req, "Proxy-Authorization: Basic {value}\r\n")?;
353         }
354 
355         write!(&mut req, "\r\n")?;
356 
357         conn.write_all(&req).await?;
358 
359         let mut buf = [0; 8192];
360         let mut pos = 0;
361 
362         loop {
363             let n = conn.read(&mut buf[pos..]).await?;
364 
365             if n == 0 {
366                 return Err(other_io_error(CreateTunnelErr::Unsuccessful));
367             }
368 
369             pos += n;
370             let resp = &buf[..pos];
371             if resp.starts_with(b"HTTP/1.1 200") || resp.starts_with(b"HTTP/1.0 200") {
372                 if resp.ends_with(b"\r\n\r\n") {
373                     return Ok(conn);
374                 }
375                 if pos == buf.len() {
376                     return Err(other_io_error(CreateTunnelErr::ProxyHeadersTooLong));
377                 }
378             } else if resp.starts_with(b"HTTP/1.1 407") {
379                 return Err(other_io_error(CreateTunnelErr::ProxyAuthenticationRequired));
380             } else {
381                 return Err(other_io_error(CreateTunnelErr::Unsuccessful));
382             }
383         }
384     }
385 
other_io_error(err: CreateTunnelErr) -> Error386     fn other_io_error(err: CreateTunnelErr) -> Error {
387         Error::new(ErrorKind::Other, err)
388     }
389 
390     enum CreateTunnelErr {
391         ProxyHeadersTooLong,
392         ProxyAuthenticationRequired,
393         Unsuccessful,
394     }
395 
396     impl Debug for CreateTunnelErr {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result397         fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
398             match self {
399                 Self::ProxyHeadersTooLong => f.write_str("Proxy headers too long for tunnel"),
400                 Self::ProxyAuthenticationRequired => f.write_str("Proxy authentication required"),
401                 Self::Unsuccessful => f.write_str("Unsuccessful tunnel"),
402             }
403         }
404     }
405 
406     impl Display for CreateTunnelErr {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result407         fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
408             Debug::fmt(self, f)
409         }
410     }
411 
412     impl error::Error for CreateTunnelErr {}
413 
414     #[cfg(all(test, feature = "__tls"))]
415     mod ut_tunnel_error_debug {
416         use crate::async_impl::connector::tls::CreateTunnelErr;
417 
418         /// UT test cases for debug of`CreateTunnelErr`.
419         ///
420         /// # Brief
421         /// 1. Checks `CreateTunnelErr` debug by calling `CreateTunnelErr::fmt`.
422         /// 2. Checks if the result is as expected.
423         #[test]
ut_tunnel_error_debug_assert()424         fn ut_tunnel_error_debug_assert() {
425             assert_eq!(
426                 format!("{:?}", CreateTunnelErr::ProxyHeadersTooLong),
427                 "Proxy headers too long for tunnel"
428             );
429             assert_eq!(
430                 format!("{:?}", CreateTunnelErr::ProxyAuthenticationRequired),
431                 "Proxy authentication required"
432             );
433             assert_eq!(
434                 format!("{:?}", CreateTunnelErr::Unsuccessful),
435                 "Unsuccessful tunnel"
436             );
437             assert_eq!(
438                 format!("{}", CreateTunnelErr::ProxyHeadersTooLong),
439                 "Proxy headers too long for tunnel"
440             );
441             assert_eq!(
442                 format!("{}", CreateTunnelErr::ProxyAuthenticationRequired),
443                 "Proxy authentication required"
444             );
445             assert_eq!(
446                 format!("{}", CreateTunnelErr::Unsuccessful),
447                 "Unsuccessful tunnel"
448             );
449         }
450     }
451 
452     #[cfg(all(test, feature = "__tls", feature = "ylong_base"))]
453     mod ut_create_tunnel_err_debug {
454         use std::net::SocketAddr;
455         use std::str::FromStr;
456 
457         use ylong_runtime::io::AsyncWriteExt;
458 
459         use crate::async_impl::connector::tcp_stream;
460         use crate::async_impl::connector::tls::{other_io_error, tunnel, CreateTunnelErr};
461         use crate::async_impl::dns::{EyeBallConfig, HappyEyeballs};
462         use crate::start_tcp_server;
463         use crate::util::test_utils::{format_header_str, TcpHandle};
464 
465         /// UT test cases for `tunnel`.
466         ///
467         /// # Brief
468         /// 1. Creates a `tcp stream` by calling `tcp_stream`.
469         /// 2. Sends a `Request` by `tunnel`.
470         /// 3. Checks if the result is as expected.
471         #[test]
ut_ssl_tunnel_error()472         fn ut_ssl_tunnel_error() {
473             let mut handles = vec![];
474             start_tcp_server!(
475                Handles: handles,
476                EndWith: "\r\n\r\n",
477                Shutdown: std::net::Shutdown::Both,
478             );
479             let handle = handles.pop().expect("No more handles !");
480 
481             let eyeballs = HappyEyeballs::new(
482                 vec![SocketAddr::from_str(handle.addr.as_str()).unwrap()],
483                 EyeBallConfig::new(None, None),
484             );
485 
486             let handle = ylong_runtime::spawn(async move {
487                 let tcp = tcp_stream(eyeballs).await.unwrap();
488                 let res = tunnel(
489                     tcp,
490                     "ylong_http.com",
491                     443,
492                     Some(String::from("base64 bytes")),
493                 )
494                 .await;
495                 assert_eq!(
496                     format!("{:?}", res.err()),
497                     format!("{:?}", Some(other_io_error(CreateTunnelErr::Unsuccessful)))
498                 );
499                 handle
500                     .server_shutdown
501                     .recv()
502                     .expect("server send order failed !");
503             });
504             ylong_runtime::block_on(handle).unwrap();
505 
506             start_tcp_server!(
507                Handles: handles,
508                EndWith: "\r\n\r\n",
509                Response: {
510                    Status: 407,
511                    Version: "HTTP/1.1",
512                    Header: "Content-Length", "11",
513                    Body: "METHOD GET!",
514                },
515                Shutdown: std::net::Shutdown::Both,
516             );
517             let handle = handles.pop().expect("No more handles !");
518 
519             let eyeballs = HappyEyeballs::new(
520                 vec![SocketAddr::from_str(handle.addr.as_str()).unwrap()],
521                 EyeBallConfig::new(None, None),
522             );
523             let handle = ylong_runtime::spawn(async move {
524                 let tcp = tcp_stream(eyeballs).await.unwrap();
525                 let res = tunnel(
526                     tcp,
527                     "ylong_http.com",
528                     443,
529                     Some(String::from("base64 bytes")),
530                 )
531                 .await;
532                 assert_eq!(
533                     format!("{:?}", res.err()),
534                     format!(
535                         "{:?}",
536                         Some(other_io_error(CreateTunnelErr::ProxyAuthenticationRequired))
537                     )
538                 );
539                 handle
540                     .server_shutdown
541                     .recv()
542                     .expect("server send order failed !");
543             });
544             ylong_runtime::block_on(handle).unwrap();
545 
546             start_tcp_server!(
547                Handles: handles,
548                EndWith: "\r\n\r\n",
549                Response: {
550                    Status: 402,
551                    Version: "HTTP/1.1",
552                    Header: "Content-Length", "11",
553                    Body: "METHOD GET!",
554                },
555                Shutdown: std::net::Shutdown::Both,
556             );
557             let handle = handles.pop().expect("No more handles !");
558 
559             let eyeballs = HappyEyeballs::new(
560                 vec![SocketAddr::from_str(handle.addr.as_str()).unwrap()],
561                 EyeBallConfig::new(None, None),
562             );
563             let handle = ylong_runtime::spawn(async move {
564                 let tcp = tcp_stream(eyeballs).await.unwrap();
565                 let res = tunnel(
566                     tcp,
567                     "ylong_http.com",
568                     443,
569                     Some(String::from("base64 bytes")),
570                 )
571                 .await;
572                 assert_eq!(
573                     format!("{:?}", res.err()),
574                     format!("{:?}", Some(other_io_error(CreateTunnelErr::Unsuccessful)))
575                 );
576                 handle
577                     .server_shutdown
578                     .recv()
579                     .expect("server send order failed !");
580             });
581             ylong_runtime::block_on(handle).unwrap();
582         }
583 
584         /// UT test cases for `tunnel`.
585         ///
586         /// # Brief
587         /// 1. Creates a `tcp stream` by calling `tcp_stream`.
588         /// 2. Sends a `Request` by `tunnel`.
589         /// 3. Checks if the result is as expected.
590         #[test]
ut_ssl_tunnel_connect()591         fn ut_ssl_tunnel_connect() {
592             let mut handles = vec![];
593 
594             start_tcp_server!(
595                Handles: handles,
596                EndWith: "\r\n\r\n",
597                 Response: {
598                    Status: 200,
599                    Version: "HTTP/1.1",
600                    Body: "",
601                },
602                Shutdown: std::net::Shutdown::Both,
603             );
604             let handle = handles.pop().expect("No more handles !");
605 
606             let eyeballs = HappyEyeballs::new(
607                 vec![SocketAddr::from_str(handle.addr.as_str()).unwrap()],
608                 EyeBallConfig::new(None, None),
609             );
610             let handle = ylong_runtime::spawn(async move {
611                 let tcp = tcp_stream(eyeballs).await.unwrap();
612                 let res = tunnel(
613                     tcp,
614                     "ylong_http.com",
615                     443,
616                     Some(String::from("base64 bytes")),
617                 )
618                 .await;
619                 assert!(res.is_ok());
620                 handle
621                     .server_shutdown
622                     .recv()
623                     .expect("server send order failed !");
624             });
625             ylong_runtime::block_on(handle).unwrap();
626         }
627 
628         /// UT test cases for response beyond size of `tunnel`.
629         ///
630         /// # Brief
631         /// 1. Creates a `tcp stream` by calling `tcp_stream`.
632         /// 2. Sends a `Request` by `tunnel`.
633         /// 3. Checks if the result is as expected.
634         #[test]
ut_ssl_tunnel_resp_beyond_size()635         fn ut_ssl_tunnel_resp_beyond_size() {
636             let mut handles = vec![];
637 
638             let buf = vec![b'b'; 8192];
639             let body = String::from_utf8(buf).unwrap();
640 
641             start_tcp_server!(
642                Handles: handles,
643                EndWith: "\r\n\r\n",
644                 Response: {
645                    Status: 200,
646                    Version: "HTTP/1.1",
647                    Header: "Content-Length", "11",
648                    Body: body.as_str(),
649                },
650             );
651             let handle = handles.pop().expect("No more handles !");
652 
653             let eyeballs = HappyEyeballs::new(
654                 vec![SocketAddr::from_str(handle.addr.as_str()).unwrap()],
655                 EyeBallConfig::new(None, None),
656             );
657             let handle = ylong_runtime::spawn(async move {
658                 let tcp = tcp_stream(eyeballs).await.unwrap();
659                 let res = tunnel(
660                     tcp,
661                     "ylong_http.com",
662                     443,
663                     Some(String::from("base64 bytes")),
664                 )
665                 .await;
666                 assert_eq!(
667                     format!("{:?}", res.err()),
668                     format!(
669                         "{:?}",
670                         Some(other_io_error(CreateTunnelErr::ProxyHeadersTooLong))
671                     )
672                 );
673                 handle
674                     .server_shutdown
675                     .recv()
676                     .expect("server send order failed !");
677             });
678             ylong_runtime::block_on(handle).unwrap();
679         }
680     }
681 }
682