use parking_lot::Mutex;
use rama_core::bytes::Bytes;
use rama_core::error::{BoxError, BoxErrorExt as _};
use rama_core::extensions::{Extensions, ExtensionsRef};
use rama_core::{Layer, Service};
use rama_http::StreamingBody;
use rama_http_types::body::GuardedBody;
use rama_http_types::{Body, Request, Response};
use rama_net::client::{ConnectorService, EstablishedClientConnection};
pub struct BindBodyToConn<C> {
conn: Mutex<Option<C>>,
extensions: Extensions,
}
impl<C: ExtensionsRef> BindBodyToConn<C> {
pub fn new(conn: C) -> Self {
Self {
extensions: conn.extensions().clone(),
conn: Mutex::new(Some(conn)),
}
}
}
impl<C> std::fmt::Debug for BindBodyToConn<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BindBodyToConn").finish_non_exhaustive()
}
}
impl<C> ExtensionsRef for BindBodyToConn<C> {
fn extensions(&self) -> &Extensions {
&self.extensions
}
}
impl<C, ReqBody, RespBody> Service<Request<ReqBody>> for BindBodyToConn<C>
where
C: Service<Request<ReqBody>, Output = Response<RespBody>, Error = BoxError>,
ReqBody: Send + 'static,
RespBody: StreamingBody<Data = Bytes, Error: Into<BoxError>> + Send + Sync + 'static,
{
type Output = Response;
type Error = BoxError;
async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
let Some(conn) = self.conn.lock().take() else {
return Err(BoxError::from_static_str(
"BindBodyToConn: connection already served (must be served exactly once)",
));
};
let resp = conn.serve(req).await?;
Ok(resp.map(|body| Body::new(GuardedBody::new(body, conn))))
}
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct BindBodyToConnLayer;
impl BindBodyToConnLayer {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl<S> Layer<S> for BindBodyToConnLayer {
type Service = BindBodyToConnector<S>;
fn layer(&self, inner: S) -> Self::Service {
BindBodyToConnector { inner }
}
}
#[derive(Debug, Clone)]
pub struct BindBodyToConnector<S> {
inner: S,
}
impl<S, Input> Service<Input> for BindBodyToConnector<S>
where
S: ConnectorService<Input>,
Input: Send + 'static,
{
type Output = EstablishedClientConnection<BindBodyToConn<S::Connection>, Input>;
type Error = S::Error;
async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
let EstablishedClientConnection { conn, input } = self.inner.connect(input).await?;
Ok(EstablishedClientConnection {
conn: BindBodyToConn::new(conn),
input,
})
}
}