95 lines
3.4 KiB
Rust
95 lines
3.4 KiB
Rust
// This is an async port of `examples/simple_server.rs`. Please see that example first.
|
|
//
|
|
// Features `async` and `tokio-net` must be enabled for this example to compile.
|
|
// It is also possible to enable feature `full` (which will enable all the features).
|
|
|
|
use std::{collections::HashMap, net::SocketAddr};
|
|
use tokio::net::{TcpListener, TcpStream};
|
|
use inferium::{
|
|
h1::{ProtocolVariant, Request, ResponseHead, ServerSendError, AsyncServer},
|
|
HeaderKey, Method, Status, TokioInet
|
|
};
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let listener = TcpListener::bind("localhost:8080").await.unwrap();
|
|
loop {
|
|
let (conn, addr) = listener.accept().await.unwrap();
|
|
// Here we are creating a new asynchronous task for every client.
|
|
// This will fork off in an asynchronous manner and won't block our accept loop.
|
|
tokio::task::spawn(async move {
|
|
// We created this new async block, so we need to `.await` on this function to propagate
|
|
// the future from the function to the top of the spawned task (this async block).
|
|
handle_client(conn, addr).await;
|
|
});
|
|
// You can now handle multiple clients at once... congratulations.
|
|
}
|
|
}
|
|
|
|
async fn handle_client(conn: TcpStream, addr: SocketAddr) {
|
|
println!("connection from {addr:?}");
|
|
let mut server_handler = AsyncServer::<TokioInet>::new(TokioInet::new(conn));
|
|
// When receiving or sending - we call the same functions with `.await` appended (in an async
|
|
// context). This will automatically poll the returned future from the top of the context.
|
|
// The polling is handled by tokio here - so we don't need to worry about it.
|
|
while let Ok(request) = server_handler.receive_request().await {
|
|
match handle_request(request, addr) {
|
|
Ok((h, b)) => if let Err(_) = send_response(h, b, &mut server_handler).await { break; },
|
|
Err(()) => break,
|
|
}
|
|
};
|
|
println!("ended connection for {addr:?}");
|
|
}
|
|
|
|
fn handle_request<T>(
|
|
req: Request<T>, addr: SocketAddr
|
|
) -> Result<(ResponseHead, &'static [u8]), ()> {
|
|
let Request::HeadersOnly(headers) = req else {
|
|
return Err(());
|
|
};
|
|
|
|
println!("req from {addr:?}: {headers}");
|
|
|
|
const OK_RESPONSE: &[u8] = b"<!DOCTYPE html>
|
|
<html>
|
|
<body>
|
|
<h1>Hello, world!</h1>
|
|
<p>Hello from inferium.</p>
|
|
</body>
|
|
</html>";
|
|
const NOT_FOUND_RESPONSE: &[u8] = b"<!DOCTYPE html>
|
|
<html>
|
|
<body>
|
|
<h1>Not found</h1>
|
|
<p>This page was not found</p>
|
|
</body>
|
|
</html>";
|
|
|
|
Ok(match (headers.method(), headers.uri().path()) {
|
|
(&Method::GET, "/") => (ResponseHead::new(
|
|
Status::Ok,
|
|
ProtocolVariant::HTTP1_0,
|
|
HashMap::from([
|
|
(HeaderKey::SERVER, "inferium".parse().unwrap()),
|
|
(HeaderKey::CONTENT_LENGTH, OK_RESPONSE.len().into()),
|
|
])
|
|
), OK_RESPONSE),
|
|
|
|
_ => (ResponseHead::new(
|
|
Status::NotFound,
|
|
ProtocolVariant::HTTP1_0,
|
|
HashMap::from([
|
|
(HeaderKey::SERVER, "inferium".parse().unwrap()),
|
|
(HeaderKey::CONTENT_LENGTH, NOT_FOUND_RESPONSE.len().into()),
|
|
])
|
|
), NOT_FOUND_RESPONSE),
|
|
})
|
|
}
|
|
|
|
async fn send_response(
|
|
response: ResponseHead, body: &[u8], conn: &mut AsyncServer<TokioInet>
|
|
)-> Result<(), ServerSendError> {
|
|
conn.send_response(&response).await?;
|
|
conn.send_body_bytes(body).await.map_err(|e| e.try_into().unwrap())
|
|
}
|