use base_http::{
InterimHandler, InterimResponses, Request, Response, Status,
body::{BufferedBody, ReadableBody},
};
use base_http_server::{Handler, Options};
use base_scheduler::{
BufferPool, BufferPoolOptions, BufferPooling, Cancellation, Clock, IoBuf, OperationResult,
SocketAccept, SocketAddress, SocketClose, SocketGetOption, SocketOperation, SocketSetOption,
SocketStreamReader, SocketStreamReading, SocketStreamWriter, SocketStreamWriting, TcpNoDelay,
};
use base_scheduler_native::{NativeScheduler, NativeSocket};
use futures_lite::future::block_on;
use std::{
future::Future,
io::{self, Read, Write},
sync::Mutex,
time::Instant,
};
mod support;
use support::*;
struct Recording<'a> {
inner: &'a NativeScheduler,
nodelay: Mutex<Vec<bool>>,
}
impl SocketOperation for Recording<'_> {
type Socket = NativeSocket;
}
impl BufferPooling for Recording<'_> {
type Pool = <NativeScheduler as BufferPooling>::Pool;
fn buffer_pool(&self, options: BufferPoolOptions) -> io::Result<Self::Pool> {
self.inner.buffer_pool(options)
}
}
impl SocketAccept for Recording<'_> {
fn socket_accept(
&self,
socket: &NativeSocket,
cancellation: &Cancellation,
) -> impl Future<Output = OperationResult<(NativeSocket, SocketAddress)>> + Send {
self.inner.socket_accept(socket, cancellation)
}
}
impl Clock for Recording<'_> {
fn now(&self) -> Instant {
self.inner.now()
}
fn sleep_until(
&self,
deadline: Instant,
cancellation: &Cancellation,
) -> impl Future<Output = OperationResult<()>> + Send {
self.inner.sleep_until(deadline, cancellation)
}
}
impl SocketSetOption<TcpNoDelay> for Recording<'_> {
fn socket_set_option(
&self,
socket: &NativeSocket,
option: TcpNoDelay,
value: bool,
cancellation: &Cancellation,
) -> impl Future<Output = OperationResult<()>> + Send {
self.inner
.socket_set_option(socket, option, value, cancellation)
}
}
impl SocketClose for Recording<'_> {
async fn socket_close(&self, socket: NativeSocket) -> io::Result<()> {
let nodelay = self
.inner
.socket_get_option(&socket, TcpNoDelay, &Cancellation::never())
.await
.unwrap();
self.nodelay.lock().unwrap().push(nodelay);
self.inner.socket_close(socket).await
}
fn socket_close_submit(
&self,
socket: NativeSocket,
) -> impl Future<Output = io::Result<()>> + Send {
self.inner.socket_close_submit(socket)
}
}
impl<'a> SocketStreamReading for Recording<'a> {
fn socket_stream_reader<'scheduler>(
&'scheduler self,
socket: &NativeSocket,
) -> io::Result<
impl SocketStreamReader<Buffer = <Self::Pool as BufferPool>::Buffer>
+ 'scheduler
+ use<'scheduler, 'a>,
> {
self.inner.socket_stream_reader(socket)
}
fn socket_stream_reader_with_pool<'scheduler>(
&'scheduler self,
socket: &NativeSocket,
pool: Self::Pool,
) -> io::Result<
impl SocketStreamReader<Buffer = <Self::Pool as BufferPool>::Buffer>
+ 'scheduler
+ use<'scheduler, 'a>,
> {
self.inner.socket_stream_reader_with_pool(socket, pool)
}
}
impl<'a> SocketStreamWriting for Recording<'a> {
fn socket_stream_writer<'scheduler>(
&'scheduler self,
socket: &NativeSocket,
) -> io::Result<
impl SocketStreamWriter<Buffer = <Self::Pool as BufferPool>::Buffer>
+ 'scheduler
+ use<'scheduler, 'a>,
> {
self.inner.socket_stream_writer(socket)
}
fn socket_stream_writer_with_buffer<'scheduler, B: IoBuf>(
&'scheduler self,
socket: &NativeSocket,
) -> io::Result<impl SocketStreamWriter<Buffer = B> + 'scheduler + use<'scheduler, 'a, B>> {
self.inner.socket_stream_writer_with_buffer::<B>(socket)
}
}
struct Hello;
impl Handler for Hello {
type Error = io::Error;
type Body<B: ReadableBody> = BufferedBody;
async fn call<B: ReadableBody, I: InterimHandler>(
&self,
_: Request<B>,
_: &mut InterimResponses<I>,
_: &Cancellation,
) -> Result<Response<BufferedBody>, io::Error> {
Ok(Response::new(
Status::OK,
BufferedBody::from(b"hello".to_vec()),
))
}
}
fn served_nodelay(options: Options) -> Vec<bool> {
let scheduler = scheduler();
let recording = Recording {
inner: &scheduler,
nodelay: Mutex::default(),
};
block_on(async {
let (listener, address) = listener(&scheduler).await;
let cancellation = Cancellation::new();
let peer = {
let cancellation = cancellation.clone();
std::thread::spawn(move || {
let mut peer = connect(address);
peer.get_mut()
.write_all(b"GET / HTTP/1.1\r\nHost: example\r\nConnection: close\r\n\r\n")
.unwrap();
let mut response = Vec::new();
peer.read_to_end(&mut response).unwrap();
cancellation.cancel();
response
})
};
let statistics =
base_http_server::serve(&recording, listener, &Hello, &options, &cancellation)
.await
.unwrap();
assert_eq!((statistics.connections, statistics.requests), (1, 1));
assert!(peer.join().unwrap().ends_with(b"\r\n\r\nhello"));
});
block_on(scheduler.shutdown());
recording.nodelay.into_inner().unwrap()
}
#[test]
fn accepted_connections_disable_nagle_unless_configured_otherwise() {
assert_eq!(served_nodelay(Options::default()), [true, false]);
let options = Options {
tcp_nodelay: false,
..Default::default()
};
assert_eq!(served_nodelay(options), [false, false]);
}