use std::{net::SocketAddr, sync::Arc}; use httparse::{Request, Status}; use smol::{ LocalExecutor, future, io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, }; fn main() { let exec = LocalExecutor::new(); let arxc = Arc::new(exec); let moving_arxc = Arc::clone(&arxc); future::block_on(arxc.run(async move { async_main(moving_arxc).await })); } async fn async_main(ex: Arc>) { let addr = SocketAddr::from(([0, 0, 0, 0], 12345)); let listen = TcpListener::bind(addr).await.unwrap(); loop { let (stream, caddr) = listen.accept().await.unwrap(); println!("accepted!"); ex.spawn(async move { handle_request(stream, caddr).await }) .detach(); } } async fn handle_request(mut stream: TcpStream, caddr: SocketAddr) { let mut buffer = vec![0; 1024]; let mut data_end = 0; loop { println!("Waiting on read"); match stream.read(&mut buffer[data_end..]).await { Err(_err) => { eprintln!("Error in handle_request during read. Bailing."); return; } Ok(bytes_read) => { data_end += bytes_read; println!("read {bytes_read}"); } } let mut headers = [httparse::EMPTY_HEADER; 64]; let mut req = Request::new(&mut headers); match req.parse(&buffer[..data_end]) { Err(err) => { eprintln!("Error in handle_request during parsing. Bailing.\nerror: {err}"); return; } Ok(Status::Partial) => { // Going around again. // Make sure we have room in the buffer. 512 seems good :) if buffer.len() == data_end { buffer.resize(buffer.len() + 512, 0); } println!("partial!"); continue; } Ok(Status::Complete(body_offset)) => { println!("Got entire request!"); println!("method: {}", req.method.unwrap()); println!("path: {}", req.path.unwrap()); println!("body_offset: {body_offset}"); } } let minor = req.version.unwrap(); let status = 200; let message = "Gotcha"; // Fall through the match. We should guarantee that, if we end up here, // we got a Status::Complete from the parser let mut response = format!("HTTP/1.{minor} {status} {message}\n"); let body = format!("{}", caddr.ip()); let length = body.len(); macro_rules! header { ($content:expr) => { response.push_str(&format!($content)); response.push('\n'); }; } header!("Content-Length: {length}"); header!("Content-Type: text/plain"); header!("Cache-Control: no-store"); response.push('\n'); response.push_str(&body); if let Err(err) = stream.write_all(response.as_bytes()).await { eprintln!("Error in handle_request sending response back. Bailing.\nError: {err}"); return; } else { return; } } }