Skip to main content

proxifier_rs/
https.rs

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    /// The only way to construct a [`HttpsProxy`] as a TLS configuration is required for HTTP CONNECT proxies.
19    ///
20    /// This implementation is based on `rustls`
21    pub fn with_client_config(config: Arc<ClientConfig>) -> Self {
22        Self { config }
23    }
24
25    /// Opens a tunnel via proxy server to target.
26    ///
27    /// `auth` should be [`Auth::HTTPAuthorizationHeader`] or [`Auth::NoAuth`] when no authentication is required, when given it will be set on Authorization header in initial handshake request to proxy server
28    ///
29    /// Implementation is based on: [HTTP CONNECT](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect) and has TLS support via `rustls`
30    ///
31    /// Subtly different, [crate::http::tunnel] is an implementation based on [IP Proxy](https://www.rfc-editor.org/rfc/rfc9484.html)
32    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                // EOF
66                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}