use crate::mock_server::bare_server::MockServerState;
use hyper::http;
use hyper::service::{make_service_fn, service_fn};
use std::net::TcpListener;
use std::sync::Arc;
use tokio::sync::RwLock;
type DynError = Box<dyn std::error::Error + Send + Sync>;
pub(super) async fn run_server(
listener: TcpListener,
server_state: Arc<RwLock<MockServerState>>,
shutdown_signal: tokio::sync::oneshot::Receiver<()>,
) {
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 wiremock_request = crate::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)
}
}))
}
});
let server = hyper::Server::from_tcp(listener)
.unwrap()
.executor(LocalExec)
.serve(request_handler)
.with_graceful_shutdown(async {
let _ = shutdown_signal.await;
});
if let Err(e) = server.await {
panic!("Mock server failed: {}", e);
}
}
#[derive(Clone, Copy, Debug)]
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);
}
}
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);
}
}
}