1use std::fmt::Display;
5use std::fs::File;
6use std::io::Write as _;
7use std::net::TcpStream;
8use std::path::Path;
9use std::sync::Arc;
10
11use arrayvec::ArrayVec;
12use compact_str::CompactString;
13
14#[allow(non_camel_case_types)]
15#[derive(Debug)]
16pub enum StatusCode {
17 BAD_REQUEST, METHOD_NOT_ALLOWED, REQUEST_TIMEOUT, PAYLOAD_TOO_LARGE, URI_TOO_LONG, REQUEST_HEADER_FIELDS_TOO_LARGE, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED, HTTP_VERSION_NOT_SUPPORTED, }
27
28const STATUS_STRS: [&str; 9] = [
29 "400 Bad Request",
30 "405 Method Not Allowed",
31 "408 Request Timeout",
32 "413 Content Too Large",
33 "414 URI Too Long",
34 "431 Request Header Fields Too Large",
35 "500 Internal Server Error",
36 "501 Not Implemented",
37 "505 HTTP Version Not Supported",
38];
39
40impl Display for StatusCode {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.write_str(match self {
43 StatusCode::BAD_REQUEST => STATUS_STRS[0],
44 StatusCode::METHOD_NOT_ALLOWED => STATUS_STRS[1],
45 StatusCode::REQUEST_TIMEOUT => STATUS_STRS[2],
46 StatusCode::PAYLOAD_TOO_LARGE => STATUS_STRS[3],
47 StatusCode::URI_TOO_LONG => STATUS_STRS[4],
48 StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => STATUS_STRS[5],
49 StatusCode::INTERNAL_SERVER_ERROR => STATUS_STRS[6],
50 StatusCode::NOT_IMPLEMENTED => STATUS_STRS[7],
51 StatusCode::HTTP_VERSION_NOT_SUPPORTED => STATUS_STRS[8],
52 })
53 }
54}
55
56#[derive(Debug)]
57pub enum Response {
58 Found {
59 resolved_path: Option<Arc<Path>>,
60 len: u64,
61 mime_type: Option<&'static str>,
62 },
63 Redirect {
64 path: Arc<str>,
65 query: CompactString,
66 },
67 NotFound,
68 StatusStr(StatusCode),
69}
70
71impl Response {
72 pub fn write_to(self, conn: &mut TcpStream, keep_alive: bool) -> std::io::Result<()> {
73 let mut buf = ArrayVec::<u8, 256>::new();
74 let mut body_path = None;
75
76 match self {
77 Response::Found {
78 resolved_path,
79 len,
80 mime_type,
81 } => {
82 write!(buf, "HTTP/1.1 200 OK\r\n")?; write!(buf, "Content-Length: {len}\r\n")?; if let Some(mime_type) = mime_type {
86 write!(buf, "Content-Type: {mime_type}\r\n")?; }
88
89 if len > 0 {
90 body_path = resolved_path;
91 }
92 }
93 Response::Redirect { path, query } => {
94 write!(buf, "HTTP/1.1 302 Found\r\n")?; write!(buf, "Content-Length: 0\r\n")?; write!(buf, "Location: {path}{query}\r\n")?; }
98 Response::NotFound => {
99 write!(buf, "HTTP/1.1 404 Not Found\r\n")?; write!(buf, "Content-Length: 0\r\n")?; }
102 Response::StatusStr(status) => {
103 write!(buf, "HTTP/1.1 {status}\r\n")?; write!(buf, "Content-Length: 0\r\n")?; }
106 }
107
108 if keep_alive {
109 write!(buf, "Connection: keep-alive\r\n\r\n")?; } else {
111 write!(buf, "Connection: close\r\n\r\n")?;
112 };
113
114 conn.write_all(buf.as_mut_slice())?;
115
116 if let Some(path) = body_path {
117 let mut f = File::options().read(true).open(path)?;
118 std::io::copy(&mut f, conn)?;
119 }
120
121 Ok(())
122 }
123}
124
125pub struct Responses(ResponsesInner);
126
127impl Default for Responses {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl Responses {
134 pub fn new() -> Self {
135 Self(ResponsesInner::Single(None))
136 }
137
138 pub fn push(&mut self, response: Response) {
139 match &mut self.0 {
140 ResponsesInner::Single(existing) => match existing.take() {
141 None => {
142 *existing = Some(response);
143 }
144 Some(existing) => {
145 self.0 = ResponsesInner::Multiple(vec![existing, response]);
146 }
147 },
148 ResponsesInner::Multiple(v) => {
149 v.push(response);
150 }
151 }
152 }
153
154 pub fn pop(&mut self) -> Option<Response> {
155 match &mut self.0 {
156 ResponsesInner::Single(existing) => existing.take(),
157 ResponsesInner::Multiple(v) => v.pop(),
158 }
159 }
160
161 pub fn drain<'this>(&'this mut self) -> ResponsesDrain<'this> {
162 match &mut self.0 {
163 ResponsesInner::Single(response) => {
164 ResponsesDrain(ResponsesDrainInner::Single(response.take()))
165 }
166 ResponsesInner::Multiple(v) => {
167 ResponsesDrain(ResponsesDrainInner::Multiple(v.drain(..)))
168 }
169 }
170 }
171}
172
173enum ResponsesInner {
174 Single(Option<Response>),
175 Multiple(Vec<Response>),
176}
177
178pub struct ResponsesDrain<'resp>(ResponsesDrainInner<'resp>);
179
180enum ResponsesDrainInner<'resp> {
181 Single(Option<Response>),
182 Multiple(std::vec::Drain<'resp, Response>),
183}
184
185impl Iterator for ResponsesDrain<'_> {
186 type Item = Response;
187
188 fn next(&mut self) -> Option<Self::Item> {
189 match &mut self.0 {
190 ResponsesDrainInner::Single(response) => response.take(),
191 ResponsesDrainInner::Multiple(iter) => iter.next(),
192 }
193 }
194}