grove
at @13
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 303 => "See Other",
64 400 => "Bad Request",
65 401 => "Unauthorized",
66 403 => "Forbidden",
67 404 => "Not Found",
68 405 => "Method Not Allowed",
69 409 => "Conflict",
70 411 => "Length Required",
71 413 => "Payload Too Large",
72 500 => "Internal Server Error",
73 503 => "Service Unavailable",
74 _ => "",
75 }
76}
77
78fn bad(msg: &str) -> io::Error {
79 io::Error::new(io::ErrorKind::InvalidData, msg.to_string())
80}
81
82/// Read one request. Errors carry the status to answer with.
83fn read_request(stream: &TcpStream) -> Result<Request, (u16, String)> {
84 let mut reader = BufReader::new(stream);
85 let mut head = Vec::new();
86 loop {
87 let mut line = Vec::new();
88 let n = reader.read_until(b'\n', &mut line).map_err(|e| (400, e.to_string()))?;
89 if n == 0 {
90 return Err((400, "connection closed".into()));
91 }
92 head.extend_from_slice(&line);
93 if head.len() > MAX_HEAD {
94 return Err((413, "headers too large".into()));
95 }
96 if line == b"\r\n" || line == b"\n" {
97 break;
98 }
99 }
100 let head = String::from_utf8(head).map_err(|_| (400, "headers aren't UTF-8".to_string()))?;
101 let mut lines = head.lines();
102 let first = lines.next().unwrap_or("");
103 let mut parts = first.split(' ');
104 let (method, target, version) = (parts.next().unwrap_or(""), parts.next().unwrap_or(""), parts.next().unwrap_or(""));
105 if method.is_empty() || !target.starts_with('/') || !version.starts_with("HTTP/1.") {
106 return Err((400, "bad request line".into()));
107 }
108 let mut headers = Vec::new();
109 for line in lines {
110 if line.is_empty() {
111 break;
112 }
113 let (k, v) = line.split_once(':').ok_or((400, "bad header".to_string()))?;
114 headers.push((k.trim().to_string(), v.trim().to_string()));
115 }
116 let find = |name: &str| headers.iter().find(|(k, _)| k.eq_ignore_ascii_case(name)).map(|(_, v)| v.clone());
117 if find("transfer-encoding").is_some() {
118 return Err((411, "send a Content-Length".into()));
119 }
120 let len = match find("content-length") {
121 Some(v) => v.parse::<usize>().map_err(|_| (400, "bad Content-Length".to_string()))?,
122 None => 0,
123 };
124 if len > MAX_BODY {
125 return Err((413, "body too large".into()));
126 }
127 let mut body = vec![0u8; len];
128 reader.read_exact(&mut body).map_err(|e| (400, e.to_string()))?;
129 let (path, query) = match target.split_once('?') {
130 Some((p, q)) => (p.to_string(), q.to_string()),
131 None => (target.to_string(), String::new()),
132 };
133 Ok(Request { method: method.to_string(), path, query, headers, body })
134}
135
136fn write_response(stream: &mut TcpStream, r: &Response) -> io::Result<()> {
137 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());
138 for (k, v) in &r.headers {
139 // Header values come from the server itself, but never let one end the head early.
140 if !v.contains(['\r', '\n']) {
141 head += &format!("{k}: {v}\r\n");
142 }
143 }
144 head += "Connection: close\r\n\r\n";
145 stream.write_all(head.as_bytes())?;
146 stream.write_all(&r.body)?;
147 stream.flush()
148}
149
150pub type Handler = Arc<dyn Fn(Request) -> Response + Send + Sync>;
151
152/// Accept connections forever, each on its own thread, at most MAX_CONNECTIONS at once.
153pub fn serve(listener: TcpListener, handler: Handler) -> io::Result<()> {
154 let active = Arc::new(AtomicUsize::new(0));
155 for stream in listener.incoming() {
156 let mut stream = match stream {
157 Ok(s) => s,
158 Err(_) => continue,
159 };
160 if active.load(Ordering::SeqCst) >= MAX_CONNECTIONS {
161 let _ = write_response(&mut stream, &Response::error(503, "busy"));
162 continue;
163 }
164 active.fetch_add(1, Ordering::SeqCst);
165 let (handler, active) = (handler.clone(), active.clone());
166 std::thread::spawn(move || {
167 let _ = stream.set_read_timeout(Some(Duration::from_secs(60)));
168 let _ = stream.set_write_timeout(Some(Duration::from_secs(60)));
169 let response = match read_request(&stream) {
170 // A bug in one request must not take the server down: answer 500 and carry on.
171 Ok(req) => std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| handler(req)))
172 .unwrap_or_else(|_| Response::error(500, "internal error")),
173 Err((status, msg)) => Response::error(status, &msg),
174 };
175 let _ = write_response(&mut stream, &response);
176 active.fetch_sub(1, Ordering::SeqCst);
177 });
178 }
179 Err(bad("listener closed"))
180}