use crate::mock_actor::MockActor;
use async_std::net::TcpListener;
use async_std::prelude::*;
use bastion::prelude::*;
use http_types::{Response, StatusCode};
use log::{debug, info, warn};
use std::net::SocketAddr;
#[derive(Clone)]
pub(crate) struct ServerActor {
pub(crate) actor_ref: ChildRef,
pub(crate) address: SocketAddr,
}
impl ServerActor {
pub(crate) async fn start(mock_actor: MockActor) -> ServerActor {
let listener = get_available_port()
.await
.expect("No free port - cannot start an HTTP mock server!");
Self::start_with(listener, mock_actor)
}
pub(crate) fn start_with(listener: TcpListener, mock_actor: MockActor) -> ServerActor {
let address = listener.local_addr().unwrap();
let server_actors = Bastion::children(|children: Children| {
children
.with_exec(move |ctx: BastionContext| {
async move {
loop {
msg! { ctx.recv().await?,
msg: (ChildRef, TcpListener) => {
let (mock_actor, listener) = msg;
debug!("Mock server started listening on {}!", listener.local_addr().unwrap());
listen(mock_actor, listener).await;
debug!("Shutting down!");
};
_: _ => {
warn!("Received a message I was not listening for.");
};
}
}
}
})
})
.expect("Couldn't create the server actor.");
let server_actor = server_actors.elems()[0].clone();
server_actor
.tell_anonymously((mock_actor.actor_ref, listener))
.expect("Failed to post TcpListener and mock actor address.");
ServerActor {
actor_ref: server_actor,
address,
}
}
}
async fn listen(mock_actor: ChildRef, listener: async_std::net::TcpListener) {
let addr = format!("http://{}", listener.local_addr().unwrap());
while let Some(stream) = listener.incoming().next().await {
let stream = stream.unwrap();
let addr = addr.clone();
let actor = mock_actor.clone();
async_std::task::spawn(async {
if let Err(err) = accept(actor, addr, stream).await {
warn!("{}", err);
}
});
}
}
async fn accept(
mock_actor: ChildRef,
addr: String,
stream: async_std::net::TcpStream,
) -> http_types::Result<()> {
debug!("Starting new connection from {}", stream.peer_addr()?);
async_h1::accept(&addr, stream.clone(), move |req| {
let a = mock_actor.clone();
async move {
info!("Request: {:?}", req);
let answer = (&a).ask_anonymously(req).unwrap();
let response = msg! { answer.await.expect("Couldn't receive the answer."),
msg: Response => msg;
_: _ => Response::new(StatusCode::NotFound);
};
info!("Response: {:?}", response);
Ok(response)
}
})
.await?;
Ok(())
}
async fn get_available_port() -> Option<TcpListener> {
for port in 8000..9000 {
match TcpListener::bind(("127.0.0.1", port)).await {
Ok(l) => return Some(l),
Err(_) => continue,
}
}
None
}