use std::io::{Read, Write};
use std::net::TcpListener;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
pub fn start_capture_server() -> Result<
(u16, thread::JoinHandle<()>, Arc<Mutex<String>>),
Box<dyn std::error::Error + Send + Sync>,
> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let port = listener.local_addr()?.port();
let captured = Arc::new(Mutex::new(String::new()));
let captured_clone = captured.clone();
let handle = thread::spawn(move || {
let (mut stream, _) = match listener.accept() {
Ok(x) => x,
Err(_) => return,
};
let mut buf = vec![0u8; 65535];
let n = match stream.read(&mut buf) {
Ok(n) => n,
Err(_) => return,
};
let req = String::from_utf8_lossy(&buf[..n]).to_string();
*captured_clone.lock().unwrap() = req;
let response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
});
Ok((port, handle, captured))
}
pub fn start_https_capture_server() -> Result<
(
u16,
thread::JoinHandle<()>,
Arc<Mutex<String>>,
std::path::PathBuf,
),
Box<dyn std::error::Error + Send + Sync>,
> {
let captured = Arc::new(Mutex::new(String::new()));
let captured_clone = captured.clone();
let (port_tx, port_rx) = mpsc::sync_channel(0);
let handle = thread::spawn(move || {
let _ = rustls::crypto::ring::default_provider().install_default();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("runtime");
rt.block_on(async {
let CertifiedKey { cert, signing_key } =
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_string()]).expect("cert");
let cert_der = cert.der().to_vec();
let cert_pem = cert.pem();
let key_der = signing_key.serialize_der();
let key_der = rustls::pki_types::PrivateKeyDer::Pkcs8(
rustls::pki_types::PrivatePkcs8KeyDer::from(key_der),
);
let config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(
vec![rustls::pki_types::CertificateDer::from(cert_der)],
key_der,
)
.expect("server config");
let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(config));
let cert_path = std::env::temp_dir().join(format!(
"blinders-test-server-cert-{}.pem",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::write(&cert_path, &cert_pem).expect("write server cert");
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let port = listener.local_addr().expect("addr").port();
let _ = port_tx.send((port, cert_path));
let (tcp, _) = listener.accept().await.expect("accept");
let mut tls = acceptor.accept(tcp).await.expect("tls accept");
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut buf = vec![0u8; 65535];
let n = tls.read(&mut buf).await.expect("read");
let req = String::from_utf8_lossy(&buf[..n]).to_string();
*captured_clone.lock().unwrap() = req;
let response = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
tls.write_all(response).await.expect("write");
tls.flush().await.expect("flush");
});
});
let (port, server_cert_path) = port_rx
.recv()
.map_err(|_| "server thread did not send port")?;
Ok((port, handle, captured, server_cert_path))
}
use rcgen::CertifiedKey;
pub fn curl_available() -> bool {
std::process::Command::new("curl")
.arg("--version")
.output()
.is_ok()
}