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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
use core::fmt;
use std::{borrow::Cow, fmt::format, 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}");
}
}
// Fall through the match. We should guarantee that, if we end up here,
// we got a Status::Complete from the parser
let response = form_response(req, caddr).await;
if let Err(err) = stream.write_all(&response).await {
eprintln!("Error in handle_request sending response back. Bailing.\nError: {err}");
return;
} else {
return;
}
}
}
fn error(code: u16, msg: &'static str) -> Vec<u8> {
format!(
"HTTP/1.1 {code} {msg}\n\
server: piper/0.1\n\
content-length: 0\n\
cache-control: no-store\n\n"
)
.as_bytes()
.to_vec()
}
fn get_header<'h>(
headers: &'h [httparse::Header<'h>],
key: &'static str,
) -> Result<Option<&'h str>, Vec<u8>> {
headers
.iter()
.find(|h| h.name.to_lowercase() == key)
.map(|v| str::from_utf8(v.value).map_err(|_| error(400, "malformed header")))
.transpose()
}
async fn form_response<'req>(request: Request<'req, 'req>, caddr: SocketAddr) -> Vec<u8> {
let minor = request.version.unwrap();
let status = 200;
let message = "Gotcha";
let mut response = format!("HTTP/1.{minor} {status} {message}\n");
let requested_type = match get_header(request.headers, "content-type") {
Err(e) => return e,
Ok(None) | Ok(Some("text/plain")) => ContentType::Plain,
Ok(Some("application/json")) => ContentType::Json,
Ok(Some(_)) => return error(400, "unknown content-type"),
};
let body = match requested_type {
ContentType::Plain => format!("{}", caddr.ip()),
ContentType::Json => format!(r#"{{"ip": "{}"}}"#, 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: {requested_type}");
header!("Cache-Control: no-store");
response.push('\n');
response.push_str(&body);
response.into_bytes()
}
enum ContentType {
Plain,
Json,
}
impl fmt::Display for ContentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ContentType::Plain => write!(f, "text/plain"),
ContentType::Json => write!(f, "application/json"),
}
}
}
|