use std::sync::Arc;
use async_trait::async_trait;
use product_os_http::{Response, StatusCode};
use product_os_http_body::BodyBytes as Body;
use product_os_server::{BeforeResult, ProductOSMiddleware, ProductOSMiddlewareAsync, RequestData};
pub const CA_PEM_PATH: &str = "/_product-os/ca.pem";
pub const CA_SETUP_PATH: &str = "/_product-os/setup";
pub struct CaServeMiddleware {
inner: Arc<dyn ProductOSMiddlewareAsync>,
cert_pem: Vec<u8>,
proxy_host: String,
proxy_port: u16,
}
impl CaServeMiddleware {
pub fn new(
inner: Arc<dyn ProductOSMiddlewareAsync>,
cert_pem: Vec<u8>,
proxy_host: String,
proxy_port: u16,
) -> Self {
Self {
inner,
cert_pem,
proxy_host,
proxy_port,
}
}
fn setup_html(&self) -> String {
format!(
r#"<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Product OS Proxy CA Setup</title></head>
<body>
<h1>Product OS Proxy — CA Setup</h1>
<p>Download the MITM root CA and install it in your system trust store.</p>
<p><a href="{CA_PEM_PATH}">Download ca.pem</a></p>
<p>Configure your browser or system to use HTTP proxy <code>{host}:{port}</code>.</p>
<p>Then run <code>pos-proxy cert guide</code> for platform-specific trust steps.</p>
</body></html>"#,
host = self.proxy_host,
port = self.proxy_port,
)
}
}
impl ProductOSMiddleware for CaServeMiddleware {}
#[async_trait]
impl ProductOSMiddlewareAsync for CaServeMiddleware {
async fn before(&self, request: product_os_http::Request<Body>) -> BeforeResult {
let path = request.uri().path();
if path == CA_PEM_PATH {
return BeforeResult::Bypass(
Response::builder()
.status(StatusCode::OK)
.header("content-type", "application/x-pem-file")
.body(Body::from(self.cert_pem.clone()))
.unwrap_or_else(|_| Response::new(Body::empty())),
);
}
if path == CA_SETUP_PATH {
let html = self.setup_html();
return BeforeResult::Bypass(
Response::builder()
.status(StatusCode::OK)
.header("content-type", "text/html; charset=utf-8")
.body(Body::from(html))
.unwrap_or_else(|_| Response::new(Body::empty())),
);
}
self.inner.before(request).await
}
async fn after(
&self,
response: product_os_http::Response<Body>,
request_data: RequestData,
) -> product_os_http::Response<Body> {
self.inner.after(response, request_data).await
}
async fn success(
&self,
response: product_os_http::Response<Body>,
request_data: RequestData,
) -> product_os_http::Response<Body> {
self.inner.success(response, request_data).await
}
async fn failure(
&self,
response: product_os_http::Response<Body>,
request_data: RequestData,
) -> product_os_http::Response<Body> {
self.inner.failure(response, request_data).await
}
async fn error(&self) -> product_os_http::Response<Body> {
self.inner.error().await
}
async fn before_product_os_response(
&self,
request: product_os_http::Request<Body>,
) -> BeforeResult {
self.inner.before_product_os_response(request).await
}
async fn after_product_os_response(
&self,
response: product_os_request::ProductOSResponse<Body>,
request_data: RequestData,
) -> product_os_http::Response<Body> {
self.inner
.after_product_os_response(response, request_data)
.await
}
}