about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2025-05-31 10:31:17 -0500
committergennyble <gen@nyble.dev>2025-05-31 10:31:17 -0500
commitcfebf65b706be63e0531b62bc03034ec870a0fad (patch)
treeebb3e3a803b679a728d9d122705a3e5e6b9a3860 /src
downloadpiper-cfebf65b706be63e0531b62bc03034ec870a0fad.tar.gz
piper-cfebf65b706be63e0531b62bc03034ec870a0fad.zip
initial commit; support for text/plain
Diffstat (limited to 'src')
-rw-r--r--src/main.rs104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..61203f6
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,104 @@
+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<LocalExecutor<'_>>) {
+	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).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;
+		}
+	}
+}