You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust实现HTTP/HTTPS隧道代理:HTTPS流量转发异常求助

隧道代理无法处理HTTPS流量问题

我需要实现一个将流量转发到上游代理服务器的代理服务器,拓扑结构如下:

client <-> proxy server <-> upstream proxy server <-> ..... <-> target server

经过搜索和协助,我编写了如下HTTP/HTTPS隧道代理的Rust代码:

use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};

async fn handle_connection(mut inbound: TcpStream, target_addr: &str) -> io::Result<()> {
    let mut buffer = [0; 1024];
    let n = inbound.read(&mut buffer).await?;
    let request = String::from_utf8_lossy(&buffer[..n]);
    println!("request {:?}", request);

    let mut outbound = TcpStream::connect(target_addr).await?;
    println!("outbound {:?}", outbound);

    if request.starts_with("CONNECT") {
        inbound
            .write_all(b"HTTP/1.1 200 Connection Established\n\n")
            .await?;
    }
    let (mut ri, mut wi) = inbound.split();
    let (mut ro, mut wo) = outbound.split();
    let initial_data = &buffer[..n];
    let write_initial = async {
        if !initial_data.is_empty() {
            wo.write_all(initial_data).await?;
        }
        io::copy(&mut ri, &mut wo).await
    };
    tokio::try_join!(write_initial, io::copy(&mut ro, &mut wi))?;

    // tokio::try_join!(io::copy(&mut ri, &mut wo), io::copy(&mut ro, &mut wi))?;
    Ok(())
}

#[tokio::main]
async fn main() -> io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8181").await?;
    loop {
        let (inbound, _) = listener.accept().await?;

        tokio::spawn(async move {
            let target_addr = "127.0.0.1:12345";
            if let Err(e) = handle_connection(inbound, target_addr).await {
                eprintln!("Failed to forward connection: {}", e);
            }
        });
    }
}

该代码可正常处理HTTP流量,测试命令及结果如下:

$ curl -v 'http://httpbin.org/anything' -x http://localhost:8181
*   Trying 127.0.0.1:8181...
* Connected to (nil) (127.0.0.1) port 8181 (#0)
> GET http://httpbin.org/anything HTTP/1.1
> Host: httpbin.org
> User-Agent: curl/7.81.0
> Accept: */*
> Proxy-Connection: Keep-Alive
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Length: 373
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: *
< Connection: keep-alive
< Content-Type: application/json
< Date: Mon, 02 Sep 2024 12:39:53 GMT
< Keep-Alive: timeout=4
< Proxy-Connection: keep-alive
< Server: gunicorn/19.9.0
< 
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.81.0", 
    "X-Amzn-Trace-Id": "Root=1-66d5b219-3fc519364e7b7fc100db86cc"
  }, 
  "json": null, 
  "method": "GET", 
  "origin": "x.y.z.k", 
  "url": "http://httpbin.org/anything"
}
* Connection #0 to host (nil) left intact

但无法处理HTTPS流量,测试命令及错误如下:

$ curl -v 'https://httpbin.org/anything' -x http://localhost:8181
*   Trying 127.0.0.1:8181...
* Connected to (nil) (127.0.0.1) port 8181 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to httpbin.org:443
> CONNECT httpbin.org:443 HTTP/1.1
> Host: httpbin.org:443
> User-Agent: curl/7.81.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 Connection Established
< 
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* (5454) (IN), , Unknown (72):
* error:0A00010B:SSL routines::wrong version number
* Closing connection 0
curl: (35) error:0A00010B:SSL routines::wrong version number

上游代理127.0.0.1:12345本身可正常处理HTTPS流量,测试结果如下:

$ curl -v 'https://httpbin.org/anything' -x http://localhost:12345
*   Trying 127.0.0.1:12345...
* Connected to (nil) (127.0.0.1) port 12345 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to httpbin.org:443
> CONNECT httpbin.org:443 HTTP/1.1
> Host: httpbin.org:443
> User-Agent: curl/7.81.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 Connection established
< 
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=httpbin.org
*  start date: Aug 20 00:00:00 2024 GMT
*  expire date: Sep 17 23:59:59 2025 GMT
*  subjectAltName: host "httpbin.org" matched cert's "httpbin.org"
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x562af2195eb0)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET /anything HTTP/2
> Host: httpbin.org
> user-agent: curl/7.81.0
> accept: */*
> 
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 200 
< date: Tue, 03 Sep 2024 00:21:55 GMT
< content-type: application/json
< content-length: 342
< server: gunicorn/19.9.0
< access-control-allow-origin: *
< access-control-allow-credentials: true
< 
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.81.0", 
    "X-Amzn-Trace-Id": "Root=1-66d656a3-3b632abc5c2a7cff6f66e16d"
  }, 
  "json": null, 
  "method": "GET", 
  "origin": "a.b.e.f", 
  "url": "https://httpbin.org/anything"
}
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection #0 to host (nil) left intact

内容的提问来源于stack exchange,提问作者GooT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:04:51