Archived
3
0
This repository has been archived on 2025-09-02. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zmp24-docs/lib/inferium/examples/simple_server.rs
2025-03-06 22:23:23 +01:00

85 lines
3.1 KiB
Rust

use std::{collections::HashMap, net::{SocketAddr, TcpListener}};
use inferium::{
h1::{ProtocolVariant, Request, ResponseHead, ServerSendError, SyncServer},
HeaderKey, Method, Status, StdInet
};
fn main() {
let listener = TcpListener::bind("localhost:8080").unwrap();
loop {
let (conn, addr) = listener.accept().unwrap();
println!("connection from {addr:?}");
let mut server_handler = SyncServer::<StdInet>::new(StdInet::new(conn));
// We'll serve the client as long as it sends valid requests.
// Note that this will effectively block other clients.
while let Ok(request) = server_handler.receive_request() {
// This matching is here to provide a way of controlling the while loop.
match handle_request(request, addr) {
Ok((h, b)) => if let Err(_) = send_response(h, b, &mut server_handler) { 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 {
// We will not handle POST requests with bodies - so let's tell the client to f*ck off.
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>";
// The URI can contain both path and parameters - so we're just getting the path here.
Ok(match (headers.method(), headers.uri().path()) {
// The ok response with our index page
(&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),
// The not found response with an example not found page
_ => (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),
})
}
fn send_response(
response: ResponseHead, body: &[u8], conn: &mut SyncServer<StdInet>
)-> Result<(), ServerSendError> {
conn.send_response(&response)?;
// The send body can fail on an I/O error or if the content-length header does not match the
// actual sent length in this scenario. But we know that we have the correct length so with
// `.try_into().unwrap()` we tell inferium to convert the error and panic on (not so much)
// possible body length discrepancy.
conn.send_body_bytes(body).map_err(|e| e.try_into().unwrap())
}