use super::BufferConfig;
use super::conn::accept_loop;
use super::handle::ConnCtx;
use super::router::{Router, ServerDispatch};
use super::server_lifecycle::{
ServerContextSnapshot, ServerControl, ServerSupervisor, SupervisorJoin, poll_supervisor_join,
};
use crate::task::spawn_async;
use crate::{RuntimeError, net, runtime};
use std::future::{Future, IntoFuture};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
pub struct ServerHandle {
control: Option<tokio::sync::watch::Sender<ServerControl>>,
join: Option<SupervisorJoin>,
}
impl ServerHandle {
pub fn shutdown(&self) {
if let Some(control) = self.control.as_ref() {
ServerControl::send_graceful(control);
}
}
pub fn join(mut self) -> ServerHandleFuture {
ServerHandleFuture::new(self.control.take(), self.join.take())
}
pub fn shutdown_and_join(self) -> ServerHandleFuture {
self.shutdown();
self.join()
}
pub fn cancel(&self) {
if let Some(control) = self.control.as_ref() {
ServerControl::send_abort(control);
}
}
}
impl Drop for ServerHandle {
fn drop(&mut self) {
if let Some(control) = self.control.take() {
ServerControl::send_abort(&control);
}
}
}
impl IntoFuture for ServerHandle {
type Output = Result<(), RuntimeError>;
type IntoFuture = ServerHandleFuture;
fn into_future(self) -> Self::IntoFuture {
self.join()
}
}
pub struct ServerHandleFuture {
control: Option<tokio::sync::watch::Sender<ServerControl>>,
join: Option<SupervisorJoin>,
}
impl ServerHandleFuture {
fn new(
control: Option<tokio::sync::watch::Sender<ServerControl>>,
join: Option<SupervisorJoin>,
) -> Self {
Self { control, join }
}
pub(super) fn from_join(join: SupervisorJoin) -> Self {
Self::new(None, Some(join))
}
pub fn shutdown(&self) {
if let Some(control) = self.control.as_ref() {
ServerControl::send_graceful(control);
}
}
pub fn cancel(&self) {
if let Some(control) = self.control.as_ref() {
ServerControl::send_abort(control);
}
}
}
impl Future for ServerHandleFuture {
type Output = Result<(), RuntimeError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = match self.join.as_mut() {
Some(join) => poll_supervisor_join(join, cx),
None => Poll::Ready(Err(RuntimeError::TaskPanicked(
"server supervisor join authority unavailable".into(),
))),
};
match result {
Poll::Pending => Poll::Pending,
Poll::Ready(result) => {
self.control.take();
Poll::Ready(result)
}
}
}
}
impl Drop for ServerHandleFuture {
fn drop(&mut self) {
if let Some(control) = self.control.take() {
ServerControl::send_abort(&control);
}
}
}
fn make_conn_limit(limit: Option<usize>) -> Option<Arc<tokio::sync::Semaphore>> {
limit.map(|n| Arc::new(tokio::sync::Semaphore::new(n)))
}
pub async fn serve_async(
listener: tokio::net::TcpListener,
router: Router,
) -> Result<(), RuntimeError> {
let buffers = router.buffer_config();
let dispatch = ServerDispatch::Single(router.freeze());
serve_async_dispatch(listener, dispatch, None, buffers).await
}
pub async fn serve_async_tls(
listener: tokio::net::TcpListener,
router: Router,
tls_config: Arc<rustls::ServerConfig>,
) -> Result<(), RuntimeError> {
let buffers = router.buffer_config();
let dispatch = ServerDispatch::Single(router.freeze());
let acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
serve_async_dispatch(listener, dispatch, Some(acceptor), buffers).await
}
pub async fn serve_async_hosts(
listener: tokio::net::TcpListener,
host_router: super::host_router::HostRouter,
) -> Result<(), RuntimeError> {
let buffers = host_router.buffer_config();
let dispatch = ServerDispatch::Host(host_router.freeze());
serve_async_dispatch(listener, dispatch, None, buffers).await
}
pub async fn serve_async_hosts_tls(
listener: tokio::net::TcpListener,
host_router: super::host_router::HostRouter,
tls_config: Arc<rustls::ServerConfig>,
) -> Result<(), RuntimeError> {
let buffers = host_router.buffer_config();
let dispatch = ServerDispatch::Host(host_router.freeze());
let acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
serve_async_dispatch(listener, dispatch, Some(acceptor), buffers).await
}
pub fn serve_background(listener: tokio::net::TcpListener, router: Router) -> ServerHandle {
let buffers = router.buffer_config();
let dispatch = ServerDispatch::Single(router.freeze());
serve_background_dispatch(listener, dispatch, None, buffers)
}
pub fn serve_background_tls(
listener: tokio::net::TcpListener,
router: Router,
tls_config: Arc<rustls::ServerConfig>,
) -> ServerHandle {
let buffers = router.buffer_config();
let dispatch = ServerDispatch::Single(router.freeze());
let acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
serve_background_dispatch(listener, dispatch, Some(acceptor), buffers)
}
pub fn serve_background_hosts(
listener: tokio::net::TcpListener,
host_router: super::host_router::HostRouter,
) -> ServerHandle {
let buffers = host_router.buffer_config();
let dispatch = ServerDispatch::Host(host_router.freeze());
serve_background_dispatch(listener, dispatch, None, buffers)
}
pub fn serve_background_hosts_tls(
listener: tokio::net::TcpListener,
host_router: super::host_router::HostRouter,
tls_config: Arc<rustls::ServerConfig>,
) -> ServerHandle {
let buffers = host_router.buffer_config();
let dispatch = ServerDispatch::Host(host_router.freeze());
let acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
serve_background_dispatch(listener, dispatch, Some(acceptor), buffers)
}
fn serve_background_dispatch(
listener: tokio::net::TcpListener,
dispatch: ServerDispatch,
tls_acceptor: Option<tokio_rustls::TlsAcceptor>,
buffers: BufferConfig,
) -> ServerHandle {
if tokio::runtime::Handle::try_current().is_err() {
let (control, receiver) = tokio::sync::watch::channel(ServerControl::Running);
drop(receiver);
return ServerHandle {
control: Some(control),
join: Some(SupervisorJoin::Ready(std::future::ready(Err(
RuntimeError::InvalidArgument(
"serve_background requires an active Tokio runtime".into(),
),
)))),
};
}
let snapshot = ServerContextSnapshot::capture(buffers, tls_acceptor.is_some());
let (supervisor, control) = ServerSupervisor::new(listener, dispatch, tls_acceptor, snapshot);
let join = match runtime::has_runtime() {
true => SupervisorJoin::Camber(spawn_async(supervisor.run()).into_future()),
false => SupervisorJoin::Tokio(tokio::spawn(supervisor.run())),
};
ServerHandle {
control: Some(control),
join: Some(join),
}
}
async fn serve_async_dispatch(
listener: tokio::net::TcpListener,
dispatch: ServerDispatch,
tls_acceptor: Option<tokio_rustls::TlsAcceptor>,
buffers: BufferConfig,
) -> Result<(), RuntimeError> {
let is_tls = tls_acceptor.is_some();
let snapshot = ServerContextSnapshot::capture(buffers, is_tls);
let (supervisor, control) = ServerSupervisor::new(listener, dispatch, tls_acceptor, snapshot);
drop(control);
supervisor.run().await
}
pub fn serve(addr: &str, router: Router) -> Result<(), RuntimeError> {
match runtime::has_runtime() {
true => {
let listener = net::listen(addr)?;
serve_listener(listener, router)
}
false => runtime::run(|| {
let listener = net::listen(addr)?;
serve_listener(listener, router)
})?,
}
}
pub fn serve_listener(listener: net::Listener, router: Router) -> Result<(), RuntimeError> {
let buffers = router.buffer_config();
let dispatch = ServerDispatch::Single(router.freeze());
serve_dispatch(listener, dispatch, buffers)
}
pub fn serve_hosts(
listener: net::Listener,
host_router: super::host_router::HostRouter,
) -> Result<(), RuntimeError> {
let buffers = host_router.buffer_config();
let dispatch = ServerDispatch::Host(host_router.freeze());
serve_dispatch(listener, dispatch, buffers)
}
fn serve_dispatch(
listener: net::Listener,
dispatch: ServerDispatch,
buffers: BufferConfig,
) -> Result<(), RuntimeError> {
let router = Arc::new(dispatch);
let rt = runtime::current_runtime();
let shutdown_notify = Arc::clone(&rt.shutdown_notify);
let keepalive_timeout = rt.config.keepalive_timeout;
let conn_limit = make_conn_limit(rt.config.connection_limit);
let tls_acceptor = rt
.config
.tls_config
.as_ref()
.map(|cfg| tokio_rustls::TlsAcceptor::from(Arc::clone(cfg)));
let is_tls = tls_acceptor.is_some();
let ctx = Arc::new(ConnCtx::from_runtime(&rt, buffers, is_tls));
drop(rt);
tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
accept_loop(
&listener,
router,
ctx,
shutdown_notify,
keepalive_timeout,
tls_acceptor,
conn_limit,
)
.await
})
})?;
listener.cleanup()
}