• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::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_str().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 => Ok(MixStream::Http(TcpStream::connect(addr).map_err(|e| {
116                     HttpClientError::new_with_cause(ErrorKind::Connect, Some(e))
117                 })?)),
118                 Scheme::HTTPS => {
119                     let tcp_stream = TcpStream::connect(addr).map_err(|e| {
120                         HttpClientError::new_with_cause(ErrorKind::Connect, Some(e))
121                     })?;
122 
123                     let tcp_stream = if is_proxy {
124                         tunnel(tcp_stream, host, port, auth)?
125                     } else {
126                         tcp_stream
127                     };
128 
129                     let tls_ssl = self.config.tls.ssl_new(&host_name).map_err(|e| {
130                         HttpClientError::new_with_cause(ErrorKind::Connect, Some(e))
131                     })?;
132 
133                     let stream = tls_ssl.into_inner().connect(tcp_stream).map_err(|e| {
134                         HttpClientError::new_with_cause(ErrorKind::Connect, Some(e))
135                     })?;
136                     Ok(MixStream::Https(stream))
137                 }
138             }
139         }
140     }
141 
tunnel( mut conn: TcpStream, host: String, port: u16, auth: Option<String>, ) -> Result<TcpStream, HttpClientError>142     fn tunnel(
143         mut conn: TcpStream,
144         host: String,
145         port: u16,
146         auth: Option<String>,
147     ) -> Result<TcpStream, HttpClientError> {
148         let mut req = Vec::new();
149 
150         // `unwrap()` never failed here.
151         write!(
152             &mut req,
153             "CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n"
154         )
155         .unwrap();
156 
157         if let Some(value) = auth {
158             write!(&mut req, "Proxy-Authorization: Basic {value}\r\n").unwrap();
159         }
160 
161         write!(&mut req, "\r\n").unwrap();
162 
163         conn.write_all(&req)
164             .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)))?;
165 
166         let mut buf = [0; 8192];
167         let mut pos = 0;
168 
169         loop {
170             let n = conn
171                 .read(&mut buf[pos..])
172                 .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)))?;
173 
174             if n == 0 {
175                 return Err(HttpClientError::new_with_message(
176                     ErrorKind::Connect,
177                     "Error receiving from proxy",
178                 ));
179             }
180 
181             pos += n;
182             let resp = &buf[..pos];
183             if resp.starts_with(b"HTTP/1.1 200") {
184                 if resp.ends_with(b"\r\n\r\n") {
185                     return Ok(conn);
186                 }
187                 if pos == buf.len() {
188                     return Err(HttpClientError::new_with_message(
189                         ErrorKind::Connect,
190                         "proxy headers too long for tunnel",
191                     ));
192                 }
193             } else if resp.starts_with(b"HTTP/1.1 407") {
194                 return Err(HttpClientError::new_with_message(
195                     ErrorKind::Connect,
196                     "proxy authentication required",
197                 ));
198             } else {
199                 return Err(HttpClientError::new_with_message(
200                     ErrorKind::Connect,
201                     "unsuccessful tunnel",
202                 ));
203             }
204         }
205     }
206 }
207