1pub use crate::auth::Auth;
2use crate::errors;
3use crate::is_ok_status;
4use http::Uri;
5use rustls_pki_types::ServerName;
6use std::{net::SocketAddrV4, sync::Arc};
7use tokio::{
8 io::{AsyncReadExt, AsyncWriteExt},
9 net::TcpStream,
10};
11use tokio_rustls::{TlsConnector, client::TlsStream, rustls::ClientConfig};
12
13pub struct HttpsProxy {
14 config: Arc<ClientConfig>,
15}
16
17impl HttpsProxy {
18 pub fn with_client_config(config: Arc<ClientConfig>) -> Self {
22 Self { config }
23 }
24
25 pub async fn tunnel(
33 &self,
34 dest: &Uri,
35 proxy_server: SocketAddrV4,
36 auth: Auth,
37 ) -> crate::Result<TlsStream<TcpStream>> {
38
39 let mut conn = TcpStream::connect(proxy_server).await?;
40 let connector = TlsConnector::from(self.config.clone());
41 let dnsname =
42 ServerName::try_from(format!("{}", dest.host().ok_or(errors::Error::InvalidURI)?))?;
43
44 let target = format!(
45 "{}:{}",
46 dest.host().ok_or(errors::Error::InvalidURI)?,
47 dest.port().ok_or(errors::Error::InvalidURI)?
48 );
49 let mut packet = format!("CONNECT {target} HTTP/1.1\r\nHost: {target}\r\n");
50 if let Auth::HTTPAuthorizationHeader(header) = auth {
51 packet.push_str(&format!("{}\r\n", header));
52 }
53
54 packet.push_str("\r\n");
55
56 conn.write_all(packet.as_bytes()).await?;
57 conn.flush().await?;
58
59 let mut response = Vec::new();
60 loop {
61 let mut buf = [0u8; 1024];
62 let n = conn.read(&mut buf).await?;
63
64 if n == 0 {
65 break;
67 }
68
69 response.extend_from_slice(&buf[..n]);
70 if response.ends_with(b"\r\n\r\n") {
71 break;
72 }
73 }
74
75 let response = String::from_utf8_lossy(&response);
76 if !is_ok_status(response.clone()) {
77 return Err(errors::Error::ProxyResponseNotOk(response.into()));
78 }
79
80 let tunnel = connector.connect(dnsname, conn).await?;
81 Ok(tunnel)
82 }
83}