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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
use core::fmt;
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}");
}
}
// 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()
}
/// Create a response, whatever it is. This function forms responses for
/// success, and errors.
async fn form_response<'req>(request: Request<'req, 'req>, caddr: SocketAddr) -> Vec<u8> {
let is_head = match request.method.unwrap() {
"GET" => false,
"HEAD" => true,
// This is not strictly correct, but it's correct enough? :)
// 405 is "The request method is known by the server but
// is not supported by the target resource"
_ => return error(405, "we do not support that request method"),
};
let minor = request.version.unwrap();
let status = 200;
let message = "Gotcha";
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(415, "unsupported content-type"),
};
// Make our response body!
let body = match requested_type {
ContentType::Plain => format!("{}", caddr.ip()),
ContentType::Json => format!(r#"{{"ip": "{}"}}"#, caddr.ip()),
};
let length = body.len();
// And now we form the request :D
let mut response = format!("HTTP/1.{minor} {status} {message}\n");
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");
if !is_head {
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"),
}
}
}
|