NiceLeeのBlog 用爱发电 bilibili~

Rust HTTP代理的实现(同步)

2022-11-10
nIceLee

阅读:


实现HTTP + HTTPS代理(同步)

前言

代理HTTPS请求

  • 正常情况
    浏览器与(host, port)建立TCP连接,套上加密TLS,发送HTTP请求

  • 代理HTTPS

    • 浏览器与代理Server建立TCP连接
    • 浏览器发送HTTP CONNECT消息
    • 代理Server从HTTP CONNECT消息中提取出host、port,并与(host, port)建立TCP连接
    • 代理Server给浏览器返回Connection Established消息
    • 代理Server充当浏览器与(host, port)之间的管道,来什么转发什么
    • 浏览器套上加密TLS,发送HTTP请求

代理HTTP请求

  • 正常情况
    浏览器与(host, port)建立TCP连接,发送HTTP请求

  • 代理HTTP

    • 浏览器与代理Server建立TCP连接,发送HTTP请求
    • 代理Server从HTTP请求头部提取出host、port,与(host, port)建立TCP连接,并发送HTTP请求头部
    • 代理Server充当浏览器与(host, port)之间的管道,来什么转发什么

实现

理清原理,并没有实现难度。
需要注意的地方是从收到的消息中判断该请求是HTTPS代理还是HTTP代理。

依赖

[dependencies]
regex = "1.5.4"

实现

use std::{net::TcpListener, thread};
use s02_sync_http_proxy::http_proxy::handle_connection;

fn main() {
    let listener = TcpListener::bind("127.0.0.1:1081").unwrap();
    for stream in listener.incoming() {
        let stream = stream.unwrap();
        thread::spawn(move || {
            if let Err(x) = handle_connection(stream) {
                println!("{:?}", x);
            }
        });
    }
}

// http_proxy.rs
use std::{
    io::{prelude::*, Error, ErrorKind},
    net::TcpStream,
    thread,
};

use regex::Regex;

fn pip(mut from: TcpStream, mut to: TcpStream) -> Result<(), Error> {
    let mut buffer = [0u8; 1024];
    let mut n = from.read(&mut buffer[..])?;
    while n > 0 {
        to.write_all(&mut buffer[..n])?;
        n = from.read(&mut buffer[..])?;
    }
    Ok(())
}
pub fn handle_connection(mut stream: TcpStream) -> Result<(), Error> {
    let local_stream_read = stream.try_clone()?;
    let mut local_stream_write = stream.try_clone()?;

    let mut head = [0u8; 2048];
    let n = stream.read(&mut head[..])?;

    let head_str =
        std::str::from_utf8(&head[..n]).map_err(|x| Error::new(ErrorKind::Interrupted, x))?;
    let reg = Regex::new(r"(CONNECT|Host:) ([^ :\r\n]+)(?::(\d+))?").unwrap();
    if let Some(caps) = reg.captures(head_str) {
        let host = &caps[2];
        let port = caps.get(3).map_or("80", |m| m.as_str());
        println!("{} {}", host, port);
        let dst_addr = format!("{}:{}", host, port);
        let remote_stream_read = TcpStream::connect(dst_addr).unwrap();
        let mut remote_stream_write = remote_stream_read.try_clone().unwrap();

        if head_str.starts_with("CONNECT") {
            local_stream_write
                .write_all("HTTP/1.1 200 Connection Established\r\n\r\n".as_bytes())?;
        } else {
            remote_stream_write.write_all(&head[..n])?;
        }

        thread::spawn(move || {
            if let Err(_) = pip(remote_stream_read, local_stream_write) {
                ()
            }
        });
        thread::spawn(move || {
            if let Err(_) = pip(local_stream_read, remote_stream_write) {
                ()
            }
        });
        // Ok(())
    } else {
        // let err_msg = format!("TCP 头部不是HTTPS CONNECT请求 或者 HTTP消息:\r\n {}", head_str);
        // Err(Error::new(
        //     ErrorKind::Other,
        //     err_msg,
        // ))
    }
    Ok(())
    // println!("Request: {:#?}", http_request);
}

代码

rust-http-proxy-demo


内容
隐藏