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