85 lines
3.1 KiB
Rust
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())
|
|
}
|