grove
at @11
1//! A small HTTP/1.1 server: one request per connection, bodies by Content-Length.
2//!
3//! grove sits behind Caddy, which handles TLS, HTTP/2 and keep-alive towards clients; this
4//! side only has to be correct and bounded.
5
6use std::io::{self, BufRead, BufReader, Read, Write};
7use std::net::{TcpListener, TcpStream};
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::sync::Arc;
10use std::time::Duration;
11
12const MAX_HEAD: usize = 64 << 10;
13pub const MAX_BODY: usize = 512 << 20;
14const MAX_CONNECTIONS: usize = 128;
15
16pub struct Request {
17 pub method: String,
18 pub path: String,
19 pub query: String,
20 pub headers: Vec<(String, String)>,
21 pub body: Vec<u8>,
22}
23
24impl Request {
25 pub fn header(&self, name: &str) -> Option<&str> {
26 self.headers.iter().find(|(k, _)| k.eq_ignore_ascii_case(name)).map(|(_, v)| v.as_str())
27 }
28}
29
30pub struct Response {
31 pub status: u16,
32 pub content_type: String,
33 pub headers: Vec<(&'static str, String)>,
34 pub body: Vec<u8>,
35}
36
37impl Response {
38 pub fn json(status: u16, v: &crate::json::Value) -> Response {
39 Response::with(status, "application/json", v.encode().into_bytes())
40 }
41 pub fn bytes(body: Vec<u8>) -> Response {
42 Response::with(200, "application/octet-stream", body)
43 }
44 pub fn with(status: u16, content_type: &str, body: Vec<u8>) -> Response {
45 Response { status, content_type: content_type.to_string(), headers: Vec::new(), body }
46 }
47 pub fn html(status: u16, page: String) -> Response {
48 Response::with(status, "text/html; charset=utf-8", page.into_bytes())
49 }
50 pub fn header(mut self, name: &'static str, value: impl Into<String>) -> Response {
51 self.headers.push((name, value.into()));
52 self
53 }
54 pub fn error(status: u16, msg: &str) -> Response {
55 Response::json(status, &crate::json::obj([("error", msg.into())]))
56 }
57}
58
59fn reason(status: u16) -> &'static str {
60 match status {
61 200 => "OK",
62 302 => "Found",
63 400 => "Bad Request",
64 401 => "Unauthorized",
65 403 => "Forbidden",
66 404 => "Not Found",
67 405 => "Method Not Allowed",
68 409 => "Conflict",
69 411 => "Length Required",
70 413 => "Payload Too Large",
71 500 => "Internal Server Error",
72 503 => "Service Unavailable",
73 _ => "",
74 }
75}
76
77fn bad(msg: &str) -> io::Error {
78 io::Error::new(io::ErrorKind::InvalidData, msg.to_string())
79}
80
81/// Read one request. Errors carry the status to answer with.
82fn read_request(stream: &TcpStream) -> Result<Request, (u16, String)> {
83 let mut reader = BufReader::new(stream);
84 let mut head = Vec::new();
85 loop {
86 let mut line = Vec::new();
87 let n = reader.read_until(b'\n', &mut line).map_err(|e| (400, e.to_string()))?;
88 if n == 0 {
89 return Err((400, "connection closed".into()));
90 }
91 head.extend_from_slice(&line);
92 if head.len() > MAX_HEAD {
93 return Err((413, "headers too large".into()));
94 }
95 if line == b"\r\n" || line == b"\n" {
96 break;
97 }
98 }
99 let head = String::from_utf8(head).map_err(|_| (400, "headers aren't UTF-8".to_string()))?;
100 let mut lines = head.lines();
101 let first = lines.next().unwrap_or("");
102 let mut parts = first.split(' ');
103 let (method, target, version) = (parts.next().unwrap_or(""), parts.next().unwrap_or(""), parts.next().unwrap_or(""));
104 if method.is_empty() || !target.starts_with('/') || !version.starts_with("HTTP/1.") {
105 return Err((400, "bad request line".into()));
106 }
107 let mut headers = Vec::new();
108 for line in lines {
109 if line.is_empty() {
110 break;
111 }
112 let (k, v) = line.split_once(':').ok_or((400, "bad header".to_string()))?;
113 headers.push((k.trim().to_string(), v.trim().to_string()));
114 }
115 let find = |name: &str| headers.iter().find(|(k, _)| k.eq_ignore_ascii_case(name)).map(|(_, v)| v.clone());
116 if find("transfer-encoding").is_some() {
117 return Err((411, "send a Content-Length".into()));
118 }
119 let len = match find("content-length") {
120 Some(v) => v.parse::<usize>().map_err(|_| (400, "bad Content-Length".to_string()))?,
121 None => 0,
122 };
123 if len > MAX_BODY {
124 return Err((413, "body too large".into()));
125 }
126 let mut body = vec![0u8; len];
127 reader.read_exact(&mut body).map_err(|e| (400, e.to_string()))?;
128 let (path, query) = match target.split_once('?') {
129 Some((p, q)) => (p.to_string(), q.to_string()),
130 None => (target.to_string(), String::new()),
131 };
132 Ok(Request { method: method.to_string(), path, query, headers, body })
133}
134
135fn write_response(stream: &mut TcpStream, r: &Response) -> io::Result<()> {
136 let mut head = format!("HTTP/1.1 {} {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n", r.status, reason(r.status), r.content_type, r.body.len());
137 for (k, v) in &r.headers {
138 // Header values come from the server itself, but never let one end the head early.
139 if !v.contains(['\r', '\n']) {
140 head += &format!("{k}: {v}\r\n");
141 }
142 }
143 head += "Connection: close\r\n\r\n";
144 stream.write_all(head.as_bytes())?;
145 stream.write_all(&r.body)?;
146 stream.flush()
147}
148
149pub type Handler = Arc<dyn Fn(Request) -> Response + Send + Sync>;
150
151/// Accept connections forever, each on its own thread, at most MAX_CONNECTIONS at once.
152pub fn serve(listener: TcpListener, handler: Handler) -> io::Result<()> {
153 let active = Arc::new(AtomicUsize::new(0));
154 for stream in listener.incoming() {
155 let mut stream = match stream {
156 Ok(s) => s,
157 Err(_) => continue,
158 };
159 if active.load(Ordering::SeqCst) >= MAX_CONNECTIONS {
160 let _ = write_response(&mut stream, &Response::error(503, "busy"));
161 continue;
162 }
163 active.fetch_add(1, Ordering::SeqCst);
164 let (handler, active) = (handler.clone(), active.clone());
165 std::thread::spawn(move || {
166 let _ = stream.set_read_timeout(Some(Duration::from_secs(60)));
167 let _ = stream.set_write_timeout(Some(Duration::from_secs(60)));
168 let response = match read_request(&stream) {
169 // A bug in one request must not take the server down: answer 500 and carry on.
170 Ok(req) => std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| handler(req)))
171 .unwrap_or_else(|_| Response::error(500, "internal error")),
172 Err((status, msg)) => Response::error(status, &msg),
173 };
174 let _ = write_response(&mut stream, &response);
175 active.fetch_sub(1, Ordering::SeqCst);
176 });
177 }
178 Err(bad("listener closed"))
179}