use std::{net::TcpListener, sync::Arc};
use hyper::{
http,
service::{make_service_fn, service_fn},
};
use crate::{wiremock::mock_server::bare_server::MockServerState, StubrResult};
type DynError = Box<dyn std::error::Error + Send + Sync>;
pub(super) async fn try_run_server(
listener: TcpListener, server_state: Arc<tokio::sync::RwLock<MockServerState>>, shutdown_signal: tokio::sync::oneshot::Receiver<()>,
) -> StubrResult<()> {
let request_handler = make_service_fn(move |_| {
let server_state = server_state.clone();
async move {
Ok::<_, DynError>(service_fn(move |request: hyper::Request<hyper::Body>| {
let server_state = server_state.clone();
async move {
let content_type = request.headers().get("content-type").map(|v| v.as_bytes());
match content_type {
Some(b"application/grpc") => {
#[cfg(feature = "grpc")]
{
crate::wiremock::grpc::handle_grpc(request, server_state).await
}
#[cfg(not(feature = "grpc"))]
{
panic!("Received a gRPC request but 'grpc' feature is not turned on")
}
},
_ => handle_http(request, server_state).await,
}
}
}))
}
});
let server = hyper::Server::from_tcp(listener)
.unwrap()
.executor(LocalExec)
.serve(request_handler)
.with_graceful_shutdown(async {
let _ = shutdown_signal.await;
});
Ok(server.await?)
}
async fn handle_http(
request: hyper::Request<hyper::Body>, server_state: Arc<tokio::sync::RwLock<MockServerState>>,
) -> Result<hyper::Response<hyper::Body>, Box<dyn std::error::Error + Send + Sync>> {
let wiremock_request = crate::wiremock::Request::from_hyper(request).await;
let (response, delay) = server_state.write().await.handle_request(wiremock_request).await;
if let Some(delay) = delay {
delay.await;
}
Ok::<_, DynError>(http_types_response_to_hyper_response(response).await)
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct LocalExec;
impl<F> hyper::rt::Executor<F> for LocalExec
where
F: std::future::Future + 'static, {
fn execute(&self, fut: F) {
tokio::task::spawn_local(fut);
}
}
pub(crate) async fn http_types_response_to_hyper_response(mut response: http_types::Response) -> hyper::Response<hyper::Body> {
let version = response.version().map(|v| v.into()).unwrap_or_default();
let mut builder = http::response::Builder::new().status(response.status() as u16).version(version);
headers_to_hyperium_headers(response.as_mut(), builder.headers_mut().unwrap());
let body_bytes = response.take_body().into_bytes().await.unwrap();
let body = hyper::Body::from(body_bytes);
builder.body(body).unwrap()
}
fn headers_to_hyperium_headers(headers: &mut http_types::Headers, hyperium_headers: &mut http::HeaderMap) {
for (name, values) in headers {
let name = format!("{}", name).into_bytes();
let name = http::header::HeaderName::from_bytes(&name).unwrap();
for value in values.iter() {
let value = format!("{}", value).into_bytes();
let value = http::header::HeaderValue::from_bytes(&value).unwrap();
hyperium_headers.append(&name, value);
}
}
}