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
相关产品推荐
相关产品推荐

