Skip to main content

proxifier_rs/
https.rs

1pub use crate::auth;
2pub use 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::Auth::HTTPAuthorizationHeader`] or [`None`] 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: Option<auth::Auth>,
37    ) -> crate::Result<TlsStream<TcpStream>> {
38        let mut conn = TcpStream::connect(proxy_server).await?;
39        let connector = TlsConnector::from(self.config.clone());
40        let dnsname =
41            ServerName::try_from(format!("{}", dest.host().ok_or(errors::Error::InvalidURI)?))?;
42
43        let target = format!(
44            "{}:{}",
45            dest.host().ok_or(errors::Error::InvalidURI)?,
46            dest.port().ok_or(errors::Error::InvalidURI)?
47        );
48        let mut packet = format!("CONNECT {target} HTTP/1.1\r\nHost: {target}\r\n");
49        if let Some(auth::Auth::HTTPAuthorizationHeader(header)) = auth {
50            packet.push_str(&format!("{}\r\n", header));
51        }
52
53        packet.push_str("\r\n");
54
55        conn.write_all(packet.as_bytes()).await?;
56        conn.flush().await?;
57
58        let mut response = Vec::new();
59        loop {
60            let mut buf = [0u8; 1024];
61            let n = conn.read(&mut buf).await?;
62
63            if n == 0 {
64                // EOF
65                break;
66            }
67
68            response.extend_from_slice(&buf[..n]);
69            if response.ends_with(b"\r\n\r\n") {
70                break;
71            }
72        }
73
74        let response = String::from_utf8_lossy(&response);
75        if !is_ok_status(response.clone()) {
76            return Err(errors::Error::ProxyResponseNotOk(response.into()));
77        }
78
79        let tunnel = connector.connect(dnsname, conn).await?;
80        Ok(tunnel)
81    }
82}