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