-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathreqwest_proxy.rs
More file actions
148 lines (118 loc) · 3.98 KB
/
reqwest_proxy.rs
File metadata and controls
148 lines (118 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/// This example demonstrates how to use `http-mitm-proxy` with `reqwest`.
use std::path::PathBuf;
use bytes::Bytes;
use clap::{Args, Parser};
use http_mitm_proxy::{
MitmProxy,
hyper::{body::Body, 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 = reqwest::Client::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();
// You can modify request here
// or You can just return response anywhere
let req = to_reqwest(req);
let res = client.execute(req).await?;
println!("{} -> {}", uri, res.status());
// You can modify response here
Ok::<_, reqwest::Error>(from_reqwest(res))
}
}),
)
.await
.unwrap();
println!("HTTP Proxy is listening on http://127.0.0.1:3003");
server.await;
}
fn to_reqwest<T>(req: hyper::Request<T>) -> reqwest::Request
where
T: Body + Send + Sync + 'static,
T::Data: Into<Bytes>,
T::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let (parts, body) = req.into_parts();
let url = reqwest::Url::parse(&parts.uri.to_string()).unwrap();
let mut req = reqwest::Request::new(parts.method, url);
*req.headers_mut() = parts.headers;
req.body_mut().replace(reqwest::Body::wrap(body));
req
}
fn from_reqwest(res: reqwest::Response) -> hyper::Response<reqwest::Body> {
let mut hres = hyper::Response::builder()
.status(res.status())
.version(res.version());
*hres.headers_mut().unwrap() = res.headers().clone();
let body = reqwest::Body::from(res);
hres.body(body).unwrap()
}