1use std::{
8 sync::Arc,
9 time::{Duration, SystemTime},
10};
11
12use bytes::{Buf, Bytes, BytesMut};
13use thiserror::Error;
14use tokio::{io::AsyncReadExt, net::TcpStream, time};
15
16use crate::{
17 body::Body,
18 common::{HttpVersion, Method, StatusCode},
19 date,
20 encode::{ConnectionDirective, EncodeError, ResponseWriter},
21 headers::header_keys,
22 log,
23 parse::{self, BodyError, BodyMode, ParseError},
24 request::Request,
25 response::{Response, ResponseBody},
26 routing::Handler,
27};
28
29pub struct Connection {
31 id: u64,
32 stream: TcpStream,
33 handler: Arc<dyn Handler>,
34 buffer: BytesMut,
35 state: ConnectionState,
36 config: ConnectionConfig,
37}
38
39#[derive(Debug, Clone)]
41pub struct ConnectionConfig {
42 pub header_read_timeout: Duration,
43 pub body_read_timeout: Duration,
44 pub idle_timeout: Duration,
45}
46
47impl Default for ConnectionConfig {
48 fn default() -> Self {
49 Self {
50 header_read_timeout: Duration::from_secs(5),
51 body_read_timeout: Duration::from_secs(30),
52 idle_timeout: Duration::from_secs(60),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum TimeoutKind {
60 Header,
61 Body,
62 Idle,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum ConnectionState {
68 Idle,
70 Streaming,
72 Closing,
74}
75
76impl Connection {
77 pub fn new(
79 id: u64,
80 stream: TcpStream,
81 handler: Arc<dyn Handler>,
82 config: ConnectionConfig,
83 ) -> Self {
84 Self {
85 id,
86 stream,
87 handler,
88 buffer: BytesMut::with_capacity(16 * 1024),
89 state: ConnectionState::Idle,
90 config,
91 }
92 }
93
94 pub fn id(&self) -> u64 {
96 self.id
97 }
98
99 pub async fn run(mut self) -> Result<(), ConnectionError> {
101 let mut processed_requests = false;
102
103 loop {
104 self.state = ConnectionState::Idle;
105
106 if parse::needs_more_head(&self.buffer[..]) {
107 let buffer_empty = self.buffer.is_empty();
108 let (timeout_duration, timeout_kind) = if buffer_empty && processed_requests {
109 (self.config.idle_timeout, TimeoutKind::Idle)
110 } else {
111 (self.config.header_read_timeout, TimeoutKind::Header)
112 };
113
114 match time::timeout(timeout_duration, self.stream.read_buf(&mut self.buffer)).await
115 {
116 Ok(Ok(0)) => return Ok(()),
117 Ok(Ok(_)) => {}
118 Ok(Err(error)) => return Err(ConnectionError::Io(error)),
119 Err(_) => {
120 log::warn(&format!(
121 "connection {} timed out during {:?} read",
122 self.id, timeout_kind
123 ));
124 self.respond_with_error(StatusCode::REQUEST_TIMEOUT, "Request timeout")
125 .await?;
126 return Err(ConnectionError::Timeout(timeout_kind));
127 }
128 }
129
130 continue;
131 }
132
133 self.state = ConnectionState::Streaming;
134
135 let (mut request, body_mode, consumed) =
136 match parse::parse_request_head(&self.buffer[..]) {
137 Ok(result) => result,
138 Err(err) => {
139 self.respond_with_error(StatusCode::BAD_REQUEST, "Bad Request")
140 .await?;
141 self.state = ConnectionState::Closing;
142 return Err(ConnectionError::Parse(err));
143 }
144 };
145 self.buffer.advance(consumed);
146
147 let request_method = request.method().clone();
148 let request_version = request.version();
149 let request_close = should_close_after_request(request_version, request.headers());
150
151 if request.expect_100_continue() {
152 let mut writer = ResponseWriter::new(&mut self.stream);
153 writer.write_continue().await?;
154 }
155
156 if body_mode != BodyMode::None {
157 let mut reader = parse::body_reader(body_mode, &mut self.stream, &mut self.buffer);
158 let read_result = time::timeout(self.config.body_read_timeout, async move {
159 let mut buf = BytesMut::new();
160 while let Some(chunk) = reader.read_next().await? {
161 buf.extend_from_slice(&chunk);
162 }
163 Ok::<BytesMut, BodyError>(buf)
164 })
165 .await;
166
167 match read_result {
168 Ok(Ok(bytes)) => {
169 request.set_body_bytes(bytes.freeze());
170 }
171 Ok(Err(err)) => {
172 self.respond_with_error(StatusCode::BAD_REQUEST, "Malformed request body")
173 .await?;
174 self.state = ConnectionState::Closing;
175 return Err(ConnectionError::Body(err));
176 }
177 Err(_) => {
178 log::warn(&format!(
179 "connection {} timed out while reading request body",
180 self.id
181 ));
182 self.respond_with_error(
183 StatusCode::REQUEST_TIMEOUT,
184 "Request body timeout",
185 )
186 .await?;
187 self.state = ConnectionState::Closing;
188 return Err(ConnectionError::Timeout(TimeoutKind::Body));
189 }
190 }
191 }
192 let request_snapshot = request.clone();
193 request.set_body(Body::Empty);
194
195 let mut response = self.handler.handle(request);
196 if response.status().as_u16() < 400 {
197 apply_request_preconditions(&request_snapshot, &mut response);
198 }
199 let response_close = response_requests_close(&response);
200
201 let mut directive = if request_close || response_close {
202 ConnectionDirective::Close
203 } else {
204 ConnectionDirective::KeepAlive
205 };
206
207 if request_version != HttpVersion::Http11 {
208 directive = ConnectionDirective::Close;
209 }
210
211 let mut writer = ResponseWriter::new(&mut self.stream);
212 writer
213 .write_response(&mut response, &request_method, directive)
214 .await?;
215 writer.flush().await?;
216
217 processed_requests = true;
218
219 if matches!(directive, ConnectionDirective::Close) {
220 self.state = ConnectionState::Closing;
221 return Ok(());
222 }
223 }
224 }
225
226 async fn respond_with_error(
227 &mut self,
228 status: StatusCode,
229 message: &str,
230 ) -> Result<(), EncodeError> {
231 log::warn(&format!(
232 "connection {} sending {}: {}",
233 self.id, status, message
234 ));
235 let mut response = Response::new(status);
236 response
237 .headers_mut()
238 .insert(header_keys::CONTENT_TYPE, "text/plain; charset=utf-8");
239 response.set_body_bytes(Bytes::copy_from_slice(message.as_bytes()));
240
241 let mut writer = ResponseWriter::new(&mut self.stream);
242 let method = Method::Get;
243 writer
244 .write_response(&mut response, &method, ConnectionDirective::Close)
245 .await?;
246 writer.flush().await
247 }
248}
249
250#[derive(Debug, Error)]
252pub enum ConnectionError {
253 #[error(transparent)]
254 Io(#[from] std::io::Error),
255 #[error(transparent)]
256 Encode(#[from] EncodeError),
257 #[error(transparent)]
258 Body(#[from] BodyError),
259 #[error(transparent)]
260 Parse(#[from] ParseError),
261 #[error("connection timeout while handling {0:?}")]
262 Timeout(TimeoutKind),
263}
264
265fn should_close_after_request(version: HttpVersion, headers: &crate::headers::Headers) -> bool {
266 match version {
267 HttpVersion::Http11 => headers
268 .get(header_keys::CONNECTION)
269 .map_or(false, |value| contains_token(value, "close")),
270 HttpVersion::Http10 => !headers
271 .get(header_keys::CONNECTION)
272 .map_or(false, |value| contains_token(value, "keep-alive")),
273 _ => true,
274 }
275}
276
277fn response_requests_close(response: &Response) -> bool {
278 response
279 .headers()
280 .get(header_keys::CONNECTION)
281 .map_or(false, |value| contains_token(value, "close"))
282}
283
284fn contains_token(value: &str, token: &str) -> bool {
285 value
286 .split(',')
287 .any(|part| part.trim().eq_ignore_ascii_case(token))
288}
289
290fn apply_request_preconditions(request: &Request, response: &mut Response) {
291 let mut decision: Option<StatusCode> = None;
292 let response_etag = response.headers().get(header_keys::ETAG);
293
294 if let Some(if_match) = request.if_match() {
295 if !etag_list_matches(if_match, response_etag, true) {
296 decision = Some(StatusCode::PRECONDITION_FAILED);
297 }
298 }
299
300 if decision.is_none() {
301 if let Some(unmodified_since) = request.if_unmodified_since() {
302 if let Some(last_modified) = parse_last_modified(response) {
303 if last_modified > unmodified_since {
304 decision = Some(StatusCode::PRECONDITION_FAILED);
305 }
306 }
307 }
308 }
309
310 if decision.is_none() {
311 if let Some(if_none_match) = request.if_none_match() {
312 if etag_list_matches(if_none_match, response_etag, false) {
313 if matches!(request.method(), Method::Get | Method::Head) {
314 decision = Some(StatusCode::NOT_MODIFIED);
315 } else {
316 decision = Some(StatusCode::PRECONDITION_FAILED);
317 }
318 }
319 } else if let Some(if_modified_since) = request.if_modified_since() {
320 if matches!(request.method(), Method::Get | Method::Head) {
321 if let Some(last_modified) = parse_last_modified(response) {
322 if last_modified <= if_modified_since {
323 decision = Some(StatusCode::NOT_MODIFIED);
324 }
325 }
326 }
327 }
328 }
329
330 if let Some(status) = decision {
331 log::info(&format!(
332 "precondition evaluation changed response to {} for {}",
333 status,
334 request.method().as_str()
335 ));
336 response.set_status(status);
337 response.set_body(ResponseBody::Empty);
338 response.take_trailers();
339 let headers = response.headers_mut();
340 headers.remove(header_keys::CONTENT_LENGTH);
341 headers.remove(header_keys::TRANSFER_ENCODING);
342 if status == StatusCode::NOT_MODIFIED {
343 headers.remove(header_keys::CONTENT_TYPE);
344 }
345 }
346}
347
348fn parse_last_modified(response: &Response) -> Option<SystemTime> {
349 response
350 .headers()
351 .get(header_keys::LAST_MODIFIED)
352 .and_then(|value| date::parse_http_date(value.trim()))
353}
354
355fn etag_list_matches(value: &str, entity_tag: Option<&str>, strong: bool) -> bool {
356 let trimmed = value.trim();
357 if trimmed == "*" {
358 return entity_tag.is_some();
359 }
360
361 let entity = entity_tag.map(|v| v.trim()).filter(|v| !v.is_empty());
362 let Some(entity) = entity else {
363 return false;
364 };
365
366 trimmed
367 .split(',')
368 .map(|candidate| candidate.trim())
369 .filter(|candidate| !candidate.is_empty())
370 .any(|candidate| {
371 if strong {
372 candidate == entity
373 } else {
374 weak_etag_equal(candidate, entity)
375 }
376 })
377}
378
379fn weak_etag_equal(a: &str, b: &str) -> bool {
380 if a == b {
381 return true;
382 }
383
384 let a = a.trim_start_matches("W/");
385 let b = b.trim_start_matches("W/");
386 a == b
387}