librespot_core/
proxytunnel.rs

1use std::io;
2
3use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
4
5pub async fn proxy_connect<T: AsyncRead + AsyncWrite + Unpin>(
6    mut proxy_connection: T,
7    connect_host: &str,
8    connect_port: &str,
9) -> io::Result<T> {
10    let mut buffer = Vec::new();
11    buffer.extend_from_slice(b"CONNECT ");
12    buffer.extend_from_slice(connect_host.as_bytes());
13    buffer.push(b':');
14    buffer.extend_from_slice(connect_port.as_bytes());
15    buffer.extend_from_slice(b" HTTP/1.1\r\n\r\n");
16
17    proxy_connection.write_all(buffer.as_ref()).await?;
18
19    buffer.resize(buffer.capacity(), 0);
20
21    let mut offset = 0;
22    loop {
23        let bytes_read = proxy_connection.read(&mut buffer[offset..]).await?;
24        if bytes_read == 0 {
25            return Err(io::Error::other("Early EOF from proxy"));
26        }
27        offset += bytes_read;
28
29        let mut headers = [httparse::EMPTY_HEADER; 16];
30        let mut response = httparse::Response::new(&mut headers);
31
32        let status = response
33            .parse(&buffer[..offset])
34            .map_err(io::Error::other)?;
35
36        if status.is_complete() {
37            return match response.code {
38                Some(200) => Ok(proxy_connection), // Proxy says all is well
39                Some(code) => {
40                    let reason = response.reason.unwrap_or("no reason");
41                    let msg = format!("Proxy responded with {code}: {reason}");
42                    Err(io::Error::other(msg))
43                }
44                None => Err(io::Error::other("Malformed response from proxy")),
45            };
46        }
47
48        if offset >= buffer.len() {
49            buffer.resize(buffer.len() + 100, 0);
50        }
51    }
52}