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 use std::io::{Read, Write};
15 
16 use ylong_http::request::uri::Uri;
17 
18 use crate::util::config::ConnectorConfig;
19 
20 /// `Connector` trait used by `Client`. `Connector` provides synchronous
21 /// connection establishment interfaces.
22 pub trait Connector {
23     /// The connection object established by `Connector::connect`.
24     type Stream: Read + Write + 'static;
25     /// Possible errors during connection establishment.
26     type Error: Into<Box<dyn std::error::Error + Send + Sync>>;
27 
28     /// Attempts to establish a synchronous connection.
connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error>29     fn connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error>;
30 }
31 
32 /// Connector for creating HTTP connections synchronously.
33 ///
34 /// `HttpConnector` implements `sync_impl::Connector` trait.
35 pub struct HttpConnector {
36     config: ConnectorConfig,
37 }
38 
39 impl HttpConnector {
40     /// Creates a new `HttpConnector`.
new(config: ConnectorConfig) -> HttpConnector41     pub(crate) fn new(config: ConnectorConfig) -> HttpConnector {
42         HttpConnector { config }
43     }
44 }
45 
46 impl Default for HttpConnector {
default() -> Self47     fn default() -> Self {
48         Self::new(ConnectorConfig::default())
49     }
50 }
51 
52 #[cfg(not(feature = "__tls"))]
53 pub mod no_tls {
54     use std::io::Error;
55     use std::net::TcpStream;
56 
57     use ylong_http::request::uri::Uri;
58 
59     use crate::sync_impl::Connector;
60 
61     impl Connector for super::HttpConnector {
62         type Stream = TcpStream;
63         type Error = Error;
64 
connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error>65         fn connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error> {
66             let addr = if let Some(proxy) = self.config.proxies.match_proxy(uri) {
67                 proxy.via_proxy(uri).authority().unwrap().to_string()
68             } else {
69                 uri.authority().unwrap().to_string()
70             };
71             TcpStream::connect(addr)
72         }
73     }
74 }
75 
76 #[cfg(feature = "__tls")]
77 pub mod tls_conn {
78     use std::io::{Read, Write};
79     use std::net::TcpStream;
80 
81     use ylong_http::request::uri::{Scheme, Uri};
82 
83     use crate::sync_impl::{Connector, MixStream};
84     use crate::{ErrorKind, HttpClientError};
85 
86     impl Connector for super::HttpConnector {
87         type Stream = MixStream<TcpStream>;
88         type Error = HttpClientError;
89 
connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error>90         fn connect(&self, uri: &Uri) -> Result<Self::Stream, Self::Error> {
91             // Make sure all parts of uri is accurate.
92             let mut addr = uri.authority().unwrap().to_string();
93             let host = uri.host().unwrap().as_str().to_string();
94             let port = uri.port().unwrap().as_u16().unwrap();
95             let mut auth = None;
96             let mut is_proxy = false;
97 
98             if let Some(proxy) = self.config.proxies.match_proxy(uri) {
99                 addr = proxy.via_proxy(uri).authority().unwrap().to_string();
100                 auth = proxy
101                     .intercept
102                     .proxy_info()
103                     .basic_auth
104                     .as_ref()
105                     .and_then(|v| v.to_string().ok());
106                 is_proxy = true;
107             }
108 
109             let host_name = match uri.host() {
110                 Some(host) => host.to_string(),
111                 None => "no host in uri".to_string(),
112             };
113 
114             match *uri.scheme().unwrap() {
115                 Scheme::HTTP => {
116                     Ok(MixStream::Http(TcpStream::connect(addr).map_err(|e| {
117                         HttpClientError::from_error(ErrorKind::Connect, e)
118                     })?))
119                 }
120                 Scheme::HTTPS => {
121                     let tcp_stream = TcpStream::connect(addr)
122                         .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?;
123 
124                     let tcp_stream = if is_proxy {
125                         tunnel(tcp_stream, host, port, auth)?
126                     } else {
127                         tcp_stream
128                     };
129 
130                     let tls_ssl = self
131                         .config
132                         .tls
133                         .ssl_new(&host_name)
134                         .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?;
135 
136                     let stream = tls_ssl
137                         .into_inner()
138                         .connect(tcp_stream)
139                         .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?;
140                     Ok(MixStream::Https(stream))
141                 }
142             }
143         }
144     }
145 
tunnel( mut conn: TcpStream, host: String, port: u16, auth: Option<String>, ) -> Result<TcpStream, HttpClientError>146     fn tunnel(
147         mut conn: TcpStream,
148         host: String,
149         port: u16,
150         auth: Option<String>,
151     ) -> Result<TcpStream, HttpClientError> {
152         let mut req = Vec::new();
153 
154         // `unwrap()` never failed here.
155         write!(
156             &mut req,
157             "CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n"
158         )
159         .unwrap();
160 
161         if let Some(value) = auth {
162             write!(&mut req, "Proxy-Authorization: Basic {value}\r\n").unwrap();
163         }
164 
165         write!(&mut req, "\r\n").unwrap();
166 
167         conn.write_all(&req)
168             .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?;
169 
170         let mut buf = [0; 8192];
171         let mut pos = 0;
172 
173         loop {
174             let n = conn
175                 .read(&mut buf[pos..])
176                 .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?;
177 
178             if n == 0 {
179                 return Err(HttpClientError::from_str(
180                     ErrorKind::Connect,
181                     "Error receiving from proxy",
182                 ));
183             }
184 
185             pos += n;
186             let resp = &buf[..pos];
187             if resp.starts_with(b"HTTP/1.1 200") {
188                 if resp.ends_with(b"\r\n\r\n") {
189                     return Ok(conn);
190                 }
191                 if pos == buf.len() {
192                     return Err(HttpClientError::from_str(
193                         ErrorKind::Connect,
194                         "proxy headers too long for tunnel",
195                     ));
196                 }
197             } else if resp.starts_with(b"HTTP/1.1 407") {
198                 return Err(HttpClientError::from_str(
199                     ErrorKind::Connect,
200                     "proxy authentication required",
201                 ));
202             } else {
203                 return Err(HttpClientError::from_str(
204                     ErrorKind::Connect,
205                     "unsuccessful tunnel",
206                 ));
207             }
208         }
209     }
210 }
211