#[cfg(feature = "hyper-backend")]
pub(crate) mod hyper;
pub(crate) mod native;
use crate::conn::ConnConfig;
use crate::service::{H1Service, Upgraded};
use crate::write::DateCache;
use bytes::Bytes;
use std::cell::RefCell;
use std::future::Future;
use std::io;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::io::{AsyncRead, AsyncWrite};
pub(crate) trait Backend {
fn serve<IO, S>(
io: IO,
service: Rc<S>,
cfg: Rc<ConnConfig>,
date: Rc<RefCell<DateCache>>,
buffered: Bytes,
peer: Option<SocketAddr>,
) -> impl Future<Output = io::Result<Option<Upgraded>>>
where
IO: AsyncRead + AsyncWrite + Unpin + 'static,
S: H1Service + 'static;
}
#[cfg(not(feature = "hyper-backend"))]
pub(crate) type ActiveBackend = native::NativeBackend;
#[cfg(feature = "hyper-backend")]
pub(crate) type ActiveBackend = hyper::HyperBackend;
pub async fn serve_connection<IO, S>(
io: IO,
service: Rc<S>,
cfg: Rc<ConnConfig>,
date: Rc<RefCell<DateCache>>,
buffered: Bytes,
peer: Option<SocketAddr>,
) -> io::Result<Option<Upgraded>>
where
IO: AsyncRead + AsyncWrite + Unpin + 'static,
S: H1Service + 'static,
{
ActiveBackend::serve(io, service, cfg, date, buffered, peer).await
}
#[cfg(test)]
mod tests {
use super::*;
use crate::write::DateCache;
use crate::{ConnConfig, Request, Response};
use std::cell::RefCell;
use std::rc::Rc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn hello(_req: Request) -> Response {
Response::text("hi")
}
#[tokio::test]
async fn serve_connection_serves_through_the_active_backend() {
let (mut client, server) = tokio::io::duplex(4096);
let local = tokio::task::LocalSet::new();
let task = local.spawn_local(serve_connection(
server,
Rc::new(hello),
Rc::new(ConnConfig::default()),
Rc::new(RefCell::new(DateCache::new())),
bytes::Bytes::new(),
None,
));
let out = local
.run_until(async move {
client
.write_all(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: close\r\n\r\n")
.await
.unwrap();
let mut out = Vec::new();
let _ = tokio::time::timeout(
std::time::Duration::from_secs(2),
client.read_to_end(&mut out),
)
.await;
let _ = task.await;
String::from_utf8_lossy(&out).into_owned()
})
.await;
assert!(out.starts_with("HTTP/1.1 200 OK"), "{out}");
assert!(out.ends_with("hi"), "{out}");
}
#[tokio::test]
async fn serve_connection_honors_pre_buffered_bytes() {
let (mut client, server) = tokio::io::duplex(4096);
let local = tokio::task::LocalSet::new();
let task = local.spawn_local(serve_connection(
server,
Rc::new(hello),
Rc::new(ConnConfig::default()),
Rc::new(RefCell::new(DateCache::new())),
bytes::Bytes::from_static(b"GET "),
None,
));
let out = local
.run_until(async move {
client
.write_all(b"/ HTTP/1.1\r\nHost: a\r\nConnection: close\r\n\r\n")
.await
.unwrap();
let mut out = Vec::new();
let _ = tokio::time::timeout(
std::time::Duration::from_secs(2),
client.read_to_end(&mut out),
)
.await;
let _ = task.await;
String::from_utf8_lossy(&out).into_owned()
})
.await;
assert!(out.starts_with("HTTP/1.1 200 OK"), "{out}");
}
}