#http-proxy #proxy-server #proxy

http-mitm-proxy

A HTTP proxy server library intended to be a backend of application like Burp proxy

17 breaking releases

Uses new Rust 2024

0.18.0 Jan 24, 2026
0.16.0 Jul 27, 2025
0.14.0 Mar 3, 2025
0.12.0 Dec 6, 2024
0.3.0 Nov 27, 2023

#215 in HTTP server

Download history 71/week @ 2025-12-30 76/week @ 2026-01-06 17/week @ 2026-01-13 118/week @ 2026-01-20 14/week @ 2026-01-27 29/week @ 2026-02-03 59/week @ 2026-02-10 26/week @ 2026-02-17 186/week @ 2026-02-24 157/week @ 2026-03-03 139/week @ 2026-03-10 45/week @ 2026-03-17 216/week @ 2026-03-24 181/week @ 2026-03-31 182/week @ 2026-04-07 93/week @ 2026-04-14

684 downloads per month
Used in 2 crates

MIT license

43KB
788 lines

http-mitm-proxy

Crates.io

A HTTP proxy server library intended to be a backend of application like Burp proxy.

  • Sniff HTTP and HTTPS traffic by signing certificate on the fly.
  • Server Sent Event
  • WebSocket ("raw" traffic only. Parsers will not be implemented in this crate.)

Usage

use std::path::PathBuf;

use clap::{Args, Parser};
use http_mitm_proxy::{
    DefaultClient, MitmProxy, RemoteAddr, hyper::service::service_fn, moka::sync::Cache,
};
use tracing_subscriber::EnvFilter;

#[derive(Parser)]
struct Opt {
    #[clap(flatten)]
    external_issuer: Option<ExternalIssuer>,
}

#[derive(Args, Debug)]
struct ExternalIssuer {
    #[arg(required = false)]
    cert: PathBuf,
    #[arg(required = false)]
    private_key: PathBuf,
}

fn make_root_issuer() -> rcgen::Issuer<'static, rcgen::KeyPair> {
    let mut params = rcgen::CertificateParams::default();

    params.distinguished_name = rcgen::DistinguishedName::new();
    params.distinguished_name.push(
        rcgen::DnType::CommonName,
        rcgen::DnValue::Utf8String("<HTTP-MITM-PROXY CA>".to_string()),
    );
    params.key_usages = vec![
        rcgen::KeyUsagePurpose::KeyCertSign,
        rcgen::KeyUsagePurpose::CrlSign,
    ];
    params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);

    let signing_key = rcgen::KeyPair::generate().unwrap();

    let cert = params.self_signed(&signing_key).unwrap();

    println!();
    println!("Trust this cert if you want to use HTTPS");
    println!();
    println!("{}", cert.pem());
    println!();

    /*
        Save this cert to ca.crt and use it with curl like this:
        curl https://www.google.com -x http://127.0.0.1:3003 --cacert ca.crt
    */

    println!("Private key");
    println!("{}", signing_key.serialize_pem());

    rcgen::Issuer::new(params, signing_key)
}

#[tokio::main]
async fn main() {
    let opt = Opt::parse();

    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::from_default_env())
        .init();

    let root_issuer = if let Some(external_issuer) = opt.external_issuer {
        // Use existing key
        let signing_key = rcgen::KeyPair::from_pem(
            &std::fs::read_to_string(&external_issuer.private_key).unwrap(),
        )
        .unwrap();

        rcgen::Issuer::from_ca_cert_pem(
            &std::fs::read_to_string(&external_issuer.cert).unwrap(),
            signing_key,
        )
        .unwrap()
    } else {
        make_root_issuer()
    };

    let proxy = MitmProxy::new(
        // This is the root cert that will be used to sign the fake certificates
        Some(root_issuer),
        Some(Cache::new(128)),
    );

    let client = DefaultClient::new();
    let server = proxy
        .bind(
            ("127.0.0.1", 3003),
            service_fn(move |req| {
                let client = client.clone();
                async move {
                    let uri = req.uri().clone();
                    let remote_addr = req.extensions().get::<RemoteAddr>().unwrap().0;
                    println!(
                        "Received request from {}: {} {}",
                        remote_addr,
                        req.method(),
                        uri
                    );

                    // You can modify request here
                    // or You can just return response anywhere

                    let (res, _upgrade) = client.send_request(req).await?;

                    println!("{} -> {}", uri, res.status());

                    // You can modify response here

                    Ok::<_, http_mitm_proxy::default_client::Error>(res)
                }
            }),
        )
        .await
        .unwrap();

    println!("HTTP Proxy is listening on http://127.0.0.1:3003");

    server.await;
}

Dependencies

~22–41MB
~647K SLoC