首页 > 解决方案 > Rust:如何使用 async-std + TLS + HTTP 代理(http 隧道)?

问题描述

我购买了一个http代理(支持http隧道),但没有rust示例代码,我尝试使用crate surf,但没有找到代理方式,我必须自己实现。以下是我的代码:

use async_std::task::block_on;
use std::error::Error;
use std::result::Result;
use futures::{AsyncWriteExt, AsyncReadExt};

use async_tls::TlsConnector;
use async_std::net::TcpStream;

const PROXY: &str = "200.200.200.200:8000"; // this is proxy / http tunnel / example IP

async fn http_get_with_proxy(url: &str, proxy: &str) -> Result<String, Box<dyn Error>> {

    // 1. make proxy to build connection to target host
    let mut stream = TcpStream::connect(proxy).await?;
    let r = stream.write_all(format!("CONNECT www.domain.com:443 HTTP/1.1\r\n\r\n").as_bytes()).await?;

    // 2. start SSL handshake process, resuse TCP stream.
    let connector = TlsConnector::default();
    let mut tls_stream = connector.connect("www.example.com", stream).await?;

    // 3. send data with SSL protocal to proxy, proxy will forward data to target.
    let r = tls_stream.write_all(format!("GET https://www.example.com/ HTTP/1.1\r\n\r\n").as_bytes()).await?;
    let mut buf = String::new();

    // 4. recv data from proxy.
    let r = tls_stream.read_to_string(&mut buf).await;
    Ok(buf)
}

fn main() {
    let r = block_on( http_get_with_proxy("https://www.example.com/", PROXY));
    dbg!(r);
}

得到错误:

[src/main.rs:35] r = Err(
    Custom {
        kind: InvalidData,
        error: CorruptMessage,
    },
)

我不知道出了什么问题。

标签: rustproxyrust-async-std

解决方案


我不是 rust 专家,但我看到协议级别存在错误:

... CONNECT www.domain.com:443 HTTP/1.1\n\n ...

\r\n\r\n首先,不应该\n\n。更重要的是,必须等待代理的 HTTP 响应,只有在获得完整响应后才建立 TLS 连接。否则,代理的纯 HTTP 响应将被解释为 TLS 握手中的回复,因此握手将失败。有关此握手的简短示例,另请参见Wikipedia

... GET https://www.example.com/ HTTP/1.1\r\n\r\n ...

这不是一个有效的 HTTP/1.1 请求。它至少缺少Host标题,并且请求行应该只包含路径/而不是完整的 URL。

如果您真的想自己实现 HTTP 而不是使用库,请研究实际标准,而不是事后猜测协议的工作方式。


推荐阅读