http_handle/
async_server.rs1#[cfg(feature = "async")]
10#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
11use crate::async_runtime::run_blocking;
12#[cfg(feature = "async")]
13#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
14use crate::error::ServerError;
15#[cfg(feature = "async")]
16#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
17use crate::server::Server;
18
19#[cfg(feature = "async")]
24#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
25pub async fn start_async(server: Server) -> Result<(), ServerError> {
46 let listener = tokio::net::TcpListener::bind(server.address())
47 .await
48 .map_err(ServerError::from)?;
49 loop {
50 let (stream, _) =
51 listener.accept().await.map_err(ServerError::from)?;
52 let server_clone = server.clone();
53 let std_stream =
54 stream.into_std().map_err(ServerError::from)?;
55 drop(tokio::spawn(async move {
56 let _ = run_blocking(move || {
57 crate::server::handle_connection(
58 std_stream,
59 &server_clone,
60 )
61 })
62 .await;
63 }));
64 }
65}
66
67#[cfg(all(test, feature = "async"))]
68mod tests {
69 use super::*;
70 use std::io::Write;
71 use std::net::{TcpListener, TcpStream};
72 use tempfile::TempDir;
73 use tokio::time::{Duration, sleep};
74
75 fn free_addr() -> String {
76 let listener = TcpListener::bind("127.0.0.1:0").expect("bind");
77 let addr = listener.local_addr().expect("addr");
78 drop(listener);
79 addr.to_string()
80 }
81
82 #[tokio::test]
83 async fn async_server_accepts_connections() {
84 let root = TempDir::new().expect("tmp");
85 std::fs::write(root.path().join("index.html"), b"hello")
86 .expect("write");
87 std::fs::create_dir(root.path().join("404")).expect("404 dir");
88 std::fs::write(root.path().join("404/index.html"), b"404")
89 .expect("404 page");
90
91 let addr = free_addr();
92 let server = Server::builder()
93 .address(&addr)
94 .document_root(root.path().to_str().expect("path"))
95 .build()
96 .expect("server");
97
98 let task = tokio::spawn(start_async(server));
99 sleep(Duration::from_millis(40)).await;
100
101 let mut stream = TcpStream::connect(&addr).expect("connect");
102 stream
103 .write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
104 .expect("write");
105 sleep(Duration::from_millis(80)).await;
106
107 task.abort();
108 }
109}