1use crate::units::BYTES_PER_KIB;
4use anyhow::{Context, Result, ensure};
5use serde_json::{Value, json};
6use std::io::{BufRead, BufReader, Read, Write};
7use std::net::TcpStream;
8
9fn line(reader: &mut impl BufRead, remaining: &mut usize) -> Result<String> {
10 let mut bytes = Vec::new();
11 let n = reader
12 .take((*remaining + 1) as u64)
13 .read_until(b'\n', &mut bytes)?;
14
15 ensure!(
16 n > 0 && n <= *remaining && bytes.ends_with(b"\n"),
17 "invalid or oversized HTTP headers"
18 );
19
20 *remaining -= n;
21
22 Ok(String::from_utf8(bytes)?
23 .trim_end_matches(['\r', '\n'])
24 .to_owned())
25}
26
27pub(super) fn read_http(
28 stream: &mut TcpStream,
29 max_body: usize,
30) -> Result<(String, String, Vec<u8>)> {
31 let mut reader = BufReader::new(stream.try_clone()?);
32 let mut remaining = 16 * BYTES_PER_KIB;
33 let first = line(&mut reader, &mut remaining)?;
34 let parts: Vec<_> = first.split_whitespace().collect();
35
36 ensure!(
37 parts.len() == 3 && parts[2].starts_with("HTTP/1."),
38 "invalid request line"
39 );
40
41 let mut length = None;
42 let mut expect = false;
43
44 loop {
45 let h = line(&mut reader, &mut remaining)?;
46
47 if h.is_empty() {
48 break;
49 }
50
51 let (key, value) = h.split_once(':').context("invalid header")?;
52
53 match key.to_ascii_lowercase().as_str() {
54 "content-length" => {
55 ensure!(length.is_none(), "duplicate Content-Length");
56
57 length = Some(
58 value
59 .trim()
60 .parse::<usize>()
61 .context("invalid Content-Length")?,
62 );
63 }
64 "transfer-encoding" => {
65 anyhow::bail!("Transfer-Encoding is unsupported; send Content-Length")
66 }
67 "expect" => {
68 ensure!(
69 value.trim().eq_ignore_ascii_case("100-continue"),
70 "unsupported Expect"
71 );
72
73 expect = true;
74 }
75 _ => {}
76 }
77 }
78
79 let length = length.unwrap_or(0);
80
81 ensure!(
82 length <= max_body,
83 "request body exceeds server limit of {max_body} bytes"
84 );
85
86 if expect {
87 stream.write_all(b"HTTP/1.1 100 Continue\r\n\r\n")?;
88 stream.flush()?;
89 }
90
91 let mut body = vec![0; length];
92
93 reader.read_exact(&mut body)?;
94
95 Ok((parts[0].to_owned(), parts[1].to_owned(), body))
96}
97
98pub(super) fn respond(out: &mut impl Write, status: u16, body: &Value) -> Result<()> {
99 let bytes = serde_json::to_vec(body)?;
100 let reason = match status {
101 200 => "OK",
102 400 => "Bad Request",
103 404 => "Not Found",
104 409 => "Conflict",
105 499 => "Client Closed Request",
106 503 => "Service Unavailable",
107 _ => "Internal Server Error",
108 };
109
110 write!(
111 out,
112 "HTTP/1.1 {status} {reason}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
113 bytes.len()
114 )?;
115 out.write_all(&bytes)?;
116 out.flush()?;
117
118 Ok(())
119}
120
121pub(super) fn error(out: &mut TcpStream, status: u16, message: &str) -> Result<()> {
122 respond(
123 out,
124 status,
125 &json!({"error":{"message":message,"type":if status >= 500 {"server_error"} else {"invalid_request_error"},"param":null,"code":null}}),
126 )
127}
128
129pub(super) fn sse(out: &mut impl Write, body: &Value) -> Result<()> {
130 writeln!(out, "data: {}\n", serde_json::to_string(body)?)?;
131 out.flush()?;
132
133 Ok(())
134}
135
136#[cfg(test)]
137#[path = "../../tests/unit/server/http.rs"]
138mod tests;