use std::{cell::Cell, net::SocketAddr, rc::Rc};
use async_net::TcpListener;
use futures_lite::pin;
use hyper::server::conn::http1::Builder as Http1Builder;
use mlua::prelude::*;
use mlua_luau_scheduler::LuaSpawnExt;
use crate::{
server::{config::ServeConfig, handle::ServeHandle, service::Service},
shared::{
futures::{Either, either},
hyper::{HyperIo, HyperTimer},
},
};
pub mod config;
pub mod handle;
pub mod service;
pub mod upgrade;
pub async fn serve(lua: Lua, port: u16, config: ServeConfig) -> LuaResult<ServeHandle> {
let address = SocketAddr::from((config.address, port));
let service = Service {
lua: lua.clone(),
address,
config,
};
let listener = TcpListener::bind(address).await?;
let (handle, shutdown_rx) = ServeHandle::new(address);
lua.spawn_local({
let lua = lua.clone();
async move {
let handle_dropped = Rc::new(Cell::new(false));
loop {
let (conn, addr) = if handle_dropped.get() {
match listener.accept().await {
Ok(acc) => acc,
Err(_err) => {
continue;
}
}
} else {
match either(shutdown_rx.recv(), listener.accept()).await {
Either::Left(Ok(())) => break,
Either::Left(Err(_)) => {
handle_dropped.set(true);
continue;
}
Either::Right(Ok(acc)) => acc,
Either::Right(Err(_err)) => {
continue;
}
}
};
lua.spawn_local({
let rx = shutdown_rx.clone();
let io = HyperIo::from(conn);
let mut svc = service.clone();
svc.address = addr;
let handle_dropped = Rc::clone(&handle_dropped);
async move {
let conn = Http1Builder::new()
.writev(false)
.timer(HyperTimer)
.keep_alive(true)
.serve_connection(io, svc)
.with_upgrades();
if handle_dropped.get() {
if let Err(_err) = conn.await {
}
} else {
pin!(conn);
match either(rx.recv(), conn.as_mut()).await {
Either::Left(Ok(())) => conn.as_mut().graceful_shutdown(),
Either::Left(Err(_)) => {
handle_dropped.set(true);
if let Err(_err) = conn.await {
}
}
Either::Right(Ok(())) => {}
Either::Right(Err(_err)) => {
}
}
}
}
});
}
}
});
Ok(handle)
}