#![cfg_attr(docsrs, feature(doc_cfg))]
mod body;
#[cfg(feature = "decoder")]
mod decoder;
mod error;
mod noop;
mod proxy;
mod rewind;
pub mod certificate_authority;
use futures::{Sink, SinkExt, Stream, StreamExt};
use hyper::{Request, Response, StatusCode, Uri};
use std::net::SocketAddr;
use tokio_tungstenite::tungstenite::{self, Message};
use tracing::error;
pub use futures;
pub use hyper;
pub use hyper_util;
#[cfg(feature = "openssl-ca")]
pub use openssl;
#[cfg(feature = "rcgen-ca")]
pub use rcgen;
pub use tokio_rustls::rustls;
pub use tokio_tungstenite;
pub use body::Body;
#[cfg(feature = "decoder")]
pub use decoder::{decode_request, decode_response};
pub use error::Error;
pub use noop::*;
pub use proxy::*;
#[derive(Debug)]
pub enum RequestOrResponse {
Request(Request<Body>),
Response(Response<Body>),
}
impl From<Request<Body>> for RequestOrResponse {
fn from(req: Request<Body>) -> Self {
Self::Request(req)
}
}
impl From<Response<Body>> for RequestOrResponse {
fn from(res: Response<Body>) -> Self {
Self::Response(res)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub struct HttpContext {
pub client_addr: SocketAddr,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum WebSocketContext {
#[non_exhaustive]
ClientToServer {
src: SocketAddr,
dst: Uri,
},
#[non_exhaustive]
ServerToClient {
src: Uri,
dst: SocketAddr,
},
}
pub trait HttpHandler: Clone + Send + Sync + 'static {
fn handle_request(
&mut self,
_ctx: &HttpContext,
req: Request<Body>,
) -> impl Future<Output = RequestOrResponse> + Send {
async { req.into() }
}
fn handle_response(
&mut self,
_ctx: &HttpContext,
res: Response<Body>,
) -> impl Future<Output = Response<Body>> + Send {
async { res }
}
fn handle_error(
&mut self,
_ctx: &HttpContext,
err: hyper_util::client::legacy::Error,
) -> impl Future<Output = Response<Body>> + Send {
async move {
error!("Failed to forward request: {}", err);
Response::builder()
.status(StatusCode::BAD_GATEWAY)
.body(Body::empty())
.expect("Failed to build response")
}
}
fn should_intercept(
&mut self,
_ctx: &HttpContext,
_req: &Request<Body>,
) -> impl Future<Output = bool> + Send {
async { true }
}
}
pub trait WebSocketHandler: Clone + Send + Sync + 'static {
fn handle_websocket(
mut self,
ctx: WebSocketContext,
mut stream: impl Stream<Item = Result<Message, tungstenite::Error>> + Unpin + Send + 'static,
mut sink: impl Sink<Message, Error = tungstenite::Error> + Unpin + Send + 'static,
) -> impl Future<Output = ()> + Send {
async move {
while let Some(message) = stream.next().await {
match message {
Ok(message) => {
let Some(message) = self.handle_message(&ctx, message).await else {
continue;
};
match sink.send(message).await {
Err(tungstenite::Error::ConnectionClosed) => (),
Err(e) => error!("WebSocket send error: {}", e),
_ => (),
}
}
Err(e) => {
error!("WebSocket message error: {}", e);
match sink.send(Message::Close(None)).await {
Err(tungstenite::Error::ConnectionClosed) => (),
Err(e) => error!("WebSocket close error: {}", e),
_ => (),
};
break;
}
}
}
}
}
fn handle_message(
&mut self,
_ctx: &WebSocketContext,
message: Message,
) -> impl Future<Output = Option<Message>> + Send {
async { Some(message) }
}
}