use std::{
fmt,
future::Future,
net::SocketAddr,
pin::Pin,
rc::Rc,
time::{Duration, Instant},
};
use bytes::BytesMut;
use crate::{date::DateService, KeepAlive};
pub(crate) type GracefulShutdownFuture = Pin<Box<dyn Future<Output = ()>>>;
#[derive(Clone)]
pub(crate) struct GracefulShutdownSignal(Rc<dyn Fn() -> GracefulShutdownFuture>);
impl GracefulShutdownSignal {
pub(crate) fn new<F, Fut>(signal: F) -> Self
where
F: Fn() -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
{
Self(Rc::new(move || Box::pin(signal())))
}
fn notified(&self) -> GracefulShutdownFuture {
(self.0)()
}
}
impl fmt::Debug for GracefulShutdownSignal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GracefulShutdownSignal")
.finish_non_exhaustive()
}
}
pub(crate) const DEFAULT_H2_CONN_WINDOW_SIZE: u32 = 1024 * 1024 * 2;
pub(crate) const DEFAULT_H2_STREAM_WINDOW_SIZE: u32 = 1024 * 1024;
pub(crate) const DEFAULT_H1_WRITE_BUFFER_SIZE: usize = 32_768;
#[derive(Default, Debug)]
pub struct ServiceConfigBuilder {
inner: Inner,
}
impl ServiceConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn secure(mut self, secure: bool) -> Self {
self.inner.secure = secure;
self
}
pub fn local_addr(mut self, local_addr: Option<SocketAddr>) -> Self {
self.inner.local_addr = local_addr;
self
}
pub fn keep_alive(mut self, keep_alive: KeepAlive) -> Self {
self.inner.keep_alive = keep_alive;
self
}
pub fn client_request_timeout(mut self, timeout: Duration) -> Self {
self.inner.client_request_timeout = timeout;
self
}
pub fn client_disconnect_timeout(mut self, timeout: Duration) -> Self {
self.inner.client_disconnect_timeout = timeout;
self
}
pub fn tcp_nodelay(mut self, nodelay: Option<bool>) -> Self {
self.inner.tcp_nodelay = nodelay;
self
}
pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
self.inner.h1_allow_half_closed = allow;
self
}
pub fn h1_write_buffer_size(mut self, size: usize) -> Self {
assert!(
size > 0,
"HTTP/1 write buffer size must be greater than zero"
);
self.inner.h1_write_buffer_size = size;
self
}
pub(crate) fn graceful_shutdown_signal(
mut self,
signal: Option<GracefulShutdownSignal>,
) -> Self {
self.inner.graceful_shutdown_signal = signal;
self
}
pub fn h2_initial_window_size(mut self, size: u32) -> Self {
self.inner.h2_stream_window_size = size;
self
}
pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self {
self.inner.h2_conn_window_size = size;
self
}
pub fn build(self) -> ServiceConfig {
ServiceConfig(Rc::new(self.inner))
}
}
#[derive(Debug, Clone, Default)]
pub struct ServiceConfig(Rc<Inner>);
#[derive(Debug)]
struct Inner {
keep_alive: KeepAlive,
client_request_timeout: Duration,
client_disconnect_timeout: Duration,
secure: bool,
local_addr: Option<SocketAddr>,
tcp_nodelay: Option<bool>,
date_service: DateService,
h1_allow_half_closed: bool,
h1_write_buffer_size: usize,
h2_conn_window_size: u32,
h2_stream_window_size: u32,
graceful_shutdown_signal: Option<GracefulShutdownSignal>,
}
impl Default for Inner {
fn default() -> Self {
Self {
keep_alive: KeepAlive::default(),
client_request_timeout: Duration::from_secs(5),
client_disconnect_timeout: Duration::ZERO,
secure: false,
local_addr: None,
tcp_nodelay: None,
date_service: DateService::new(),
h1_allow_half_closed: true,
h1_write_buffer_size: DEFAULT_H1_WRITE_BUFFER_SIZE,
h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,
h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,
graceful_shutdown_signal: None,
}
}
}
impl ServiceConfig {
pub fn new(
keep_alive: KeepAlive,
client_request_timeout: Duration,
client_disconnect_timeout: Duration,
secure: bool,
local_addr: Option<SocketAddr>,
) -> ServiceConfig {
ServiceConfig(Rc::new(Inner {
keep_alive: keep_alive.normalize(),
client_request_timeout,
client_disconnect_timeout,
secure,
local_addr,
tcp_nodelay: None,
date_service: DateService::new(),
h1_allow_half_closed: true,
h1_write_buffer_size: DEFAULT_H1_WRITE_BUFFER_SIZE,
h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE,
h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE,
graceful_shutdown_signal: None,
}))
}
#[inline]
pub fn secure(&self) -> bool {
self.0.secure
}
#[inline]
pub fn local_addr(&self) -> Option<SocketAddr> {
self.0.local_addr
}
#[inline]
pub fn keep_alive(&self) -> KeepAlive {
self.0.keep_alive
}
pub fn keep_alive_deadline(&self) -> Option<Instant> {
match self.keep_alive() {
KeepAlive::Timeout(dur) => Some(self.now() + dur),
KeepAlive::Os => None,
KeepAlive::Disabled => None,
}
}
pub fn client_request_deadline(&self) -> Option<Instant> {
let timeout = self.0.client_request_timeout;
(timeout != Duration::ZERO).then(|| self.now() + timeout)
}
pub fn client_disconnect_deadline(&self) -> Option<Instant> {
let timeout = self.0.client_disconnect_timeout;
(timeout != Duration::ZERO).then(|| self.now() + timeout)
}
pub fn h1_allow_half_closed(&self) -> bool {
self.0.h1_allow_half_closed
}
pub fn h1_write_buffer_size(&self) -> usize {
self.0.h1_write_buffer_size
}
pub(crate) fn graceful_shutdown(&self) -> Option<GracefulShutdownFuture> {
self.0
.graceful_shutdown_signal
.as_ref()
.map(GracefulShutdownSignal::notified)
}
pub fn tcp_nodelay(&self) -> Option<bool> {
self.0.tcp_nodelay
}
pub fn h2_initial_window_size(&self) -> u32 {
self.0.h2_stream_window_size
}
pub fn h2_initial_connection_window_size(&self) -> u32 {
self.0.h2_conn_window_size
}
pub(crate) fn now(&self) -> Instant {
self.0.date_service.now()
}
#[doc(hidden)]
pub fn write_date_header(&self, dst: &mut BytesMut, camel_case: bool) {
let mut buf: [u8; 37] = [0; 37];
buf[..6].copy_from_slice(if camel_case { b"Date: " } else { b"date: " });
self.0
.date_service
.with_date(|date| buf[6..35].copy_from_slice(&date.bytes));
buf[35..].copy_from_slice(b"\r\n");
dst.extend_from_slice(&buf);
}
#[allow(unused)] pub(crate) fn write_date_header_value(&self, dst: &mut BytesMut) {
self.0
.date_service
.with_date(|date| dst.extend_from_slice(&date.bytes));
}
}
#[cfg(test)]
mod tests {
use actix_rt::{
task::yield_now,
time::{sleep, sleep_until},
};
use memchr::memmem;
use super::*;
use crate::{date::DATE_VALUE_LENGTH, notify_on_drop};
#[actix_rt::test]
async fn test_date_service_update() {
let settings =
ServiceConfig::new(KeepAlive::Os, Duration::ZERO, Duration::ZERO, false, None);
yield_now().await;
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf1, false);
let now1 = settings.now();
sleep_until((Instant::now() + Duration::from_secs(2)).into()).await;
yield_now().await;
let now2 = settings.now();
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf2, false);
assert_ne!(now1, now2);
assert_ne!(buf1, buf2);
drop(settings);
let mut times = 0;
while !notify_on_drop::is_dropped() {
sleep(Duration::from_millis(100)).await;
times += 1;
assert!(times < 10, "Timeout waiting for task drop");
}
}
#[actix_rt::test]
async fn test_date_service_drop() {
let service = Rc::new(DateService::new());
yield_now().await;
let clone1 = service.clone();
let clone2 = service.clone();
let clone3 = service.clone();
drop(clone1);
assert!(!notify_on_drop::is_dropped());
drop(clone2);
assert!(!notify_on_drop::is_dropped());
drop(clone3);
assert!(!notify_on_drop::is_dropped());
drop(service);
let mut times = 0;
while !notify_on_drop::is_dropped() {
sleep(Duration::from_millis(100)).await;
times += 1;
assert!(times < 10, "Timeout waiting for task drop");
}
}
#[test]
fn test_date_len() {
assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
}
#[actix_rt::test]
async fn test_date() {
let settings = ServiceConfig::default();
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf1, false);
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf2, false);
assert_eq!(buf1, buf2);
}
#[actix_rt::test]
async fn test_date_camel_case() {
let settings = ServiceConfig::default();
let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf, false);
assert!(memmem::find(&buf, b"date:").is_some());
let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.write_date_header(&mut buf, true);
assert!(memmem::find(&buf, b"Date:").is_some());
}
}