use std::future::Future;
use std::pin::Pin;
use bytes::Bytes;
use hitbox_core::Upstream;
use hitbox_http::{BufferedBody, CacheableHttpRequest, CacheableHttpResponse};
use http::Extensions;
use reqwest_middleware::{Next, Result};
pub struct ReqwestUpstream<'a> {
next: Next<'a>,
extensions: Extensions,
}
impl<'a> ReqwestUpstream<'a> {
pub fn new(next: Next<'a>, extensions: Extensions) -> Self {
Self { next, extensions }
}
}
impl<'a> Upstream<CacheableHttpRequest<reqwest::Body>> for ReqwestUpstream<'a> {
type Response = Result<CacheableHttpResponse<reqwest::Body>>;
type Future = Pin<Box<dyn Future<Output = Self::Response> + Send + 'a>>;
fn call(&mut self, req: CacheableHttpRequest<reqwest::Body>) -> Self::Future {
let next = self.next.clone();
let mut extensions = std::mem::take(&mut self.extensions);
Box::pin(async move {
let http_request = req.into_request();
let (parts, buffered_body) = http_request.into_parts();
let body = buffered_body_to_reqwest(buffered_body);
let http_request = http::Request::from_parts(parts, body);
let reqwest_request: reqwest::Request = http_request
.try_into()
.map_err(|e: reqwest::Error| reqwest_middleware::Error::Reqwest(e))?;
let response = next.run(reqwest_request, &mut extensions).await?;
let http_response: http::Response<reqwest::Body> = response.into();
let (parts, body) = http_response.into_parts();
let buffered_body = BufferedBody::Passthrough(body);
let http_response = http::Response::from_parts(parts, buffered_body);
Ok(CacheableHttpResponse::from_response(http_response))
})
}
}
pub fn buffered_body_to_reqwest(buffered: BufferedBody<reqwest::Body>) -> reqwest::Body {
match buffered {
BufferedBody::Passthrough(body) => body,
BufferedBody::Complete(Some(bytes)) => reqwest::Body::from(bytes),
BufferedBody::Complete(None) => reqwest::Body::from(Bytes::new()),
BufferedBody::Partial(partial) => {
reqwest::Body::wrap(partial)
}
}
}