about summary refs log tree commit diff
path: root/src/main.rs
blob: d288cba8f96c1daf04c92321c7e928854cd71857 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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[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;
		}
	}
}