1mod body;
2mod options;
3mod tests;
4mod write;
5mod writebuf;
6mod zerocopy;
7
8pub(crate) use body::Http1Body;
9pub use options::*;
10pub use zerocopy::*;
11
12#[cfg(unix)]
13pub(crate) type RawHandle = std::os::fd::RawFd;
14#[cfg(windows)]
15pub(crate) type RawHandle = std::os::windows::io::RawHandle;
16
17use std::{mem::MaybeUninit, time::UNIX_EPOCH};
18
19use bytes::{Buf, Bytes, BytesMut};
20use http::{header, HeaderMap, HeaderValue, Request, Response, Version};
21use http_body::Body;
22use http_body_util::Empty;
23use memchr::memchr3_iter;
24use tokio::io::AsyncReadExt;
25use tokio_util::sync::CancellationToken;
26
27use crate::{h1::writebuf::WriteBuf, EarlyHints, HttpProtocol, Incoming, Upgrade, Upgraded};
28
29const HEX_DIGITS: &[u8; 16] = b"0123456789ABCDEF";
30const WRITE_BUF_BATCH_THRESHOLD: usize = 16384;
31
32pub struct Http1<Io> {
64 io: Io,
65 options: options::Http1Options,
66 cancel_token: Option<CancellationToken>,
67 parsed_headers: Box<[MaybeUninit<httparse::Header<'static>>]>,
68 date_header_value_cached: Option<(String, std::time::SystemTime)>,
69 cached_headers: Option<HeaderMap>,
70 read_buf: BytesMut,
71 response_head_buf: Vec<u8>,
72 write_buf: WriteBuf,
73 connection_idle: bool,
74}
75
76#[cfg(all(
77 any(target_os = "linux", target_os = "freebsd"),
78 feature = "h1-zerocopy"
79))]
80impl<Io> Http1<Io>
81where
82 for<'a> Io: tokio::io::AsyncRead
83 + tokio::io::AsyncWrite
84 + vibeio::io::AsInnerRawHandle<'a>
85 + Unpin
86 + 'static,
87{
88 #[inline]
99 pub fn zerocopy(self) -> Http1Zerocopy<Io> {
100 Http1Zerocopy { inner: self }
101 }
102}
103
104impl<Io> Http1<Io>
105where
106 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + 'static,
107{
108 #[inline]
119 pub fn new(io: Io, options: options::Http1Options) -> Self {
120 let read_buf = BytesMut::with_capacity(options.max_header_size);
122 let parsed_headers: Box<[MaybeUninit<httparse::Header<'static>>]> =
123 Box::new_uninit_slice(options.max_header_count);
124 Self {
125 io,
126 options,
127 cancel_token: None,
128 parsed_headers,
129 date_header_value_cached: None,
130 cached_headers: None,
131 read_buf,
132 response_head_buf: Vec::with_capacity(1024),
133 write_buf: WriteBuf::new(),
134 connection_idle: false,
135 }
136 }
137
138 #[inline]
139 fn get_date_header_value(&mut self) -> &str {
140 let now = std::time::SystemTime::now();
141 if self.date_header_value_cached.as_ref().is_none_or(|v| {
142 v.1.duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs())
143 != now.duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs())
144 }) {
145 let value = httpdate::fmt_http_date(now).to_string();
146 self.date_header_value_cached = Some((value, now));
147 }
148 self.date_header_value_cached
149 .as_ref()
150 .map(|v| v.0.as_str())
151 .unwrap_or("")
152 }
153
154 #[inline]
164 pub fn graceful_shutdown_token(mut self, token: CancellationToken) -> Self {
165 self.cancel_token = Some(token);
166 self
167 }
168
169 #[inline]
170 async fn fill_buf(&mut self) -> Result<usize, std::io::Error> {
171 if self.read_buf.remaining() < 1024 {
172 self.read_buf.reserve(1024);
173 }
174 let spare_capacity = self.read_buf.spare_capacity_mut();
175 let n = self
177 .io
178 .read(unsafe {
179 &mut *std::ptr::slice_from_raw_parts_mut(
180 spare_capacity.as_mut_ptr() as *mut u8,
181 spare_capacity.len(),
182 )
183 })
184 .await?;
185 if n == 0 {
186 return Ok(0);
187 }
188 unsafe { self.read_buf.set_len(self.read_buf.len() + n) };
189 Ok(n)
190 }
191
192 #[inline]
193 async fn get_head(
194 &mut self,
195 ) -> Result<Option<(Bytes, &mut [MaybeUninit<httparse::Header<'static>>])>, std::io::Error>
196 {
197 let mut request_line_read = false;
198 let mut bytes_read: usize = 0;
199 let mut whitespace_trimmed = None;
200 let mut just_started = true;
201 while bytes_read < self.options.max_header_size {
202 let old_bytes_read = bytes_read;
203 let begin_search = old_bytes_read.saturating_sub(3);
204
205 let have_to_read_buf = !just_started || self.read_buf.is_empty();
206 just_started = false;
207 if have_to_read_buf {
208 let n = self.fill_buf().await?;
209 if n == 0 {
210 if whitespace_trimmed.is_none() {
211 return Ok(None);
212 }
213 return Err(std::io::Error::new(
214 std::io::ErrorKind::UnexpectedEof,
215 "unexpected EOF",
216 ));
217 } else {
218 self.connection_idle = false;
219 }
220 bytes_read = (old_bytes_read + n).min(self.options.max_header_size);
221 } else {
222 bytes_read =
223 (old_bytes_read + self.read_buf.len()).min(self.options.max_header_size)
224 }
225
226 if whitespace_trimmed.is_none() {
227 whitespace_trimmed = self.read_buf[old_bytes_read..bytes_read]
228 .iter()
229 .position(|b| !b.is_ascii_whitespace());
230 }
231
232 if let Some(whitespace_trimmed) = whitespace_trimmed {
233 if !request_line_read {
235 let memchr = memchr3_iter(
236 b' ',
237 b'\r',
238 b'\n',
239 &self.read_buf[whitespace_trimmed..bytes_read],
240 );
241 let mut spaces = 0;
242 for separator_index in memchr {
243 if self.read_buf[whitespace_trimmed + separator_index] == b' ' {
244 if spaces >= 2 {
245 return Err(std::io::Error::new(
246 std::io::ErrorKind::InvalidInput,
247 "bad request first line",
248 ));
249 }
250 spaces += 1;
251 } else if spaces == 2 {
252 request_line_read = true;
253 break;
254 } else {
255 return Err(std::io::Error::new(
256 std::io::ErrorKind::InvalidInput,
257 "bad request first line",
258 ));
259 }
260 }
261 }
262
263 if request_line_read {
264 let begin_search = begin_search.max(whitespace_trimmed);
265 if let Some((separator_index, separator_len)) =
266 search_header_body_separator(&self.read_buf[begin_search..bytes_read])
267 {
268 let to_parse_length =
269 begin_search + separator_index + separator_len - whitespace_trimmed;
270 self.read_buf.advance(whitespace_trimmed);
271 let head = self.read_buf.split_to(to_parse_length);
272 return Ok(Some((head.freeze(), &mut self.parsed_headers)));
273 }
274 }
275 }
276 }
277 Err(std::io::Error::new(
278 std::io::ErrorKind::InvalidData,
279 "request too large",
280 ))
281 }
282
283 #[inline]
284 pub(crate) async fn handle_with_error_fn_and_zerocopy<
285 F,
286 Fut,
287 ResB,
288 ResBE,
289 ResE,
290 EF,
291 EFut,
292 EResB,
293 EResBE,
294 EResE,
295 ZF,
296 ZFut,
297 >(
298 mut self,
299 request_fn: F,
300 error_fn: EF,
301 mut zerocopy_fn: Option<ZF>,
302 ) -> Result<(), std::io::Error>
303 where
304 F: Fn(Request<Incoming>) -> Fut + 'static,
305 Fut: std::future::Future<Output = Result<Response<ResB>, ResE>> + 'static,
306 ResB: Body<Data = bytes::Bytes, Error = ResBE> + Unpin + 'static,
307 ResE: std::error::Error,
308 ResBE: std::error::Error,
309 EF: FnOnce(bool) -> EFut,
310 EFut: std::future::Future<Output = Result<Response<EResB>, EResE>>,
311 EResB: Body<Data = bytes::Bytes, Error = EResBE> + Unpin + 'static,
312 EResE: std::error::Error,
313 EResBE: std::error::Error,
314 ZF: FnMut(RawHandle, &'static Io, u64) -> ZFut,
315 ZFut: std::future::Future<Output = Result<(), std::io::Error>>,
316 {
317 let mut keep_alive = true;
318
319 while keep_alive {
320 let (mut request, body_tx, send_continue_body) = match if let Some(timeout) =
321 self.options.header_read_timeout
322 {
323 vibeio::time::timeout(timeout, async {
324 if let Some(token) = self.cancel_token.clone() {
325 token.run_until_cancelled(self.read_request()).await
326 } else {
327 Some(self.read_request().await)
328 }
329 })
330 .await
331 } else {
332 Ok(Some(self.read_request().await))
333 } {
334 Ok(Some(Ok(Some(d)))) => d,
335 Ok(Some(Ok(None))) => {
336 return Ok(());
337 }
338 Ok(Some(Err(e)))
339 if self.connection_idle
340 && matches!(
341 e.kind(),
342 std::io::ErrorKind::BrokenPipe
343 | std::io::ErrorKind::ConnectionReset
344 | std::io::ErrorKind::ConnectionAborted
345 | std::io::ErrorKind::UnexpectedEof
346 ) =>
347 {
348 return Ok(());
350 }
351 Ok(Some(Err(e))) => {
352 if let Ok(mut response) = error_fn(false).await {
353 response
354 .headers_mut()
355 .insert(header::CONNECTION, HeaderValue::from_static("close"));
356
357 let _ = self
358 .write_response(response, Version::HTTP_11, false, zerocopy_fn.as_mut())
359 .await;
360 }
361 return Err(e);
362 }
363 Ok(None) => {
364 return Ok(());
366 }
367 Err(_) if self.connection_idle => {
368 return Ok(());
370 }
371 Err(_) => {
372 if let Ok(mut response) = error_fn(true).await {
374 response
375 .headers_mut()
376 .insert(header::CONNECTION, HeaderValue::from_static("close"));
377
378 let _ = self
379 .write_response(response, Version::HTTP_11, false, zerocopy_fn.as_mut())
380 .await;
381 }
382 return Err(std::io::Error::new(
383 std::io::ErrorKind::TimedOut,
384 "header read timeout",
385 ));
386 }
387 };
388
389 let connection_header_split = request
391 .headers()
392 .get(header::CONNECTION)
393 .and_then(|v| v.to_str().ok())
394 .map(|v| v.split(",").map(|v| v.trim()));
395 let is_connection_close = connection_header_split
396 .clone()
397 .is_some_and(|mut split| split.any(|v| v.eq_ignore_ascii_case("close")));
398 let is_connection_keep_alive = connection_header_split
399 .is_some_and(|mut split| split.any(|v| v.eq_ignore_ascii_case("keep-alive")));
400 keep_alive = !is_connection_close
401 && (is_connection_keep_alive || request.version() == http::Version::HTTP_11);
402
403 let version = request.version();
404 let is_100_continue = send_continue_body.is_some();
405
406 let early_hints_fut = if self.options.enable_early_hints {
408 let (early_hints, mut early_hints_rx) = EarlyHints::new_lazy();
409 request.extensions_mut().insert(early_hints);
410 let mut_self = unsafe { std::mem::transmute::<&mut Self, &mut Self>(&mut self) };
414 futures_util::future::Either::Left(async move {
415 while let Some((headers, sender)) =
416 std::future::poll_fn(|cx| early_hints_rx.poll_recv(cx)).await
417 {
418 sender
419 .into_inner()
420 .send(mut_self.write_early_hints(version, headers).await)
421 .ok();
422 }
423 futures_util::future::pending::<Result<(), std::io::Error>>().await
424 })
425 } else {
426 futures_util::future::Either::Right(futures_util::future::pending::<
427 Result<(), std::io::Error>,
428 >())
429 };
430
431 let content_length = request
433 .headers()
434 .get(header::CONTENT_LENGTH)
435 .and_then(|v| v.to_str().ok())
436 .and_then(|v| v.parse::<u64>().ok())
437 .unwrap_or(0);
438 let chunked = request
439 .headers()
440 .get(header::TRANSFER_ENCODING)
441 .and_then(|v| v.to_str().ok())
442 .is_some_and(|v| {
443 v.split(',')
444 .any(|v| v.trim().eq_ignore_ascii_case("chunked"))
445 });
446 let has_trailers = request
447 .headers()
448 .get(header::TRAILER)
449 .map(|v| v.to_str().ok().is_some_and(|s| !s.is_empty()))
450 .unwrap_or(false);
451 let write_trailers = request
452 .headers()
453 .get(header::TE)
454 .and_then(|v| v.to_str().ok())
455 .map(|v| {
456 v.split(',')
457 .any(|v| v.trim().eq_ignore_ascii_case("trailers"))
458 })
459 .unwrap_or(false);
460
461 let (upgrade_tx, upgrade_rx) = oneshot::async_channel();
463 let upgrade = Upgrade::new(upgrade_rx);
464 let upgraded = upgrade.upgraded.clone();
465 request.extensions_mut().insert(upgrade);
466
467 let mut continue_sent = false;
468 let mut response = {
469 let read_body_fut = async {
470 if chunked {
471 self.read_chunked_body_fn(
472 body_tx,
473 has_trailers,
474 &send_continue_body,
475 &mut continue_sent,
476 version,
477 )
478 .await
479 } else {
480 self.read_body_fn(
481 body_tx,
482 content_length,
483 &send_continue_body,
484 &mut continue_sent,
485 version,
486 )
487 .await
488 }
489 };
490 let read_body_fut_pin = std::pin::pin!(read_body_fut);
491 let request_fut = request_fn(request);
492 let request_fut_pin = std::pin::pin!(request_fut);
493 let early_hints_fut_pin = std::pin::pin!(early_hints_fut);
494
495 let select_read_body_either =
496 futures_util::future::select(request_fut_pin, early_hints_fut_pin);
497 let select_either =
498 futures_util::future::select(read_body_fut_pin, select_read_body_either).await;
499
500 let (response, body_fut) = match select_either {
501 futures_util::future::Either::Left((result, request_fut)) => {
502 result?;
503 (
504 match request_fut.await {
505 futures_util::future::Either::Left((response, _)) => response,
506 futures_util::future::Either::Right((_, _)) => unreachable!(),
507 },
508 None,
509 )
510 }
511 futures_util::future::Either::Right((response, read_body_fut)) => (
512 match response {
513 futures_util::future::Either::Left((response, _)) => response,
514 futures_util::future::Either::Right((_, _)) => unreachable!(),
515 },
516 Some(read_body_fut),
517 ),
518 };
519
520 if let Some(body_fut) = body_fut {
522 body_fut.await?;
523 }
524
525 response.map_err(|e| std::io::Error::other(e.to_string()))?
526 };
527
528 if !continue_sent
530 && is_100_continue
531 && !response.status().is_client_error()
532 && !response.status().is_server_error()
533 {
534 self.write_100_continue(version).await?;
535 }
536
537 let mut was_upgraded = false;
538 if upgraded.load(std::sync::atomic::Ordering::Relaxed) {
539 was_upgraded = true;
540 response
541 .headers_mut()
542 .insert(header::CONNECTION, HeaderValue::from_static("upgrade"));
543 } else if keep_alive {
544 if version == Version::HTTP_10
545 || response.headers().contains_key(header::CONNECTION)
546 {
547 response
548 .headers_mut()
549 .insert(header::CONNECTION, HeaderValue::from_static("keep-alive"));
550 }
551 } else if version == Version::HTTP_11
552 || response.headers().contains_key(header::CONNECTION)
553 {
554 response
555 .headers_mut()
556 .insert(header::CONNECTION, HeaderValue::from_static("close"));
557 }
558
559 self.write_response(response, version, write_trailers, zerocopy_fn.as_mut())
560 .await?;
561
562 if was_upgraded {
563 let frozen_buf = self.read_buf.freeze();
565 let _ = upgrade_tx.send(Upgraded::new(
566 self.io,
567 if frozen_buf.is_empty() {
568 None
569 } else {
570 Some(frozen_buf)
571 },
572 ));
573 return Ok(());
574 }
575
576 if self.cancel_token.as_ref().is_some_and(|t| t.is_cancelled()) {
577 break;
579 }
580
581 self.connection_idle = true;
582 }
583 Ok(())
584 }
585}
586
587impl<Io> HttpProtocol for Http1<Io>
588where
589 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + 'static,
590{
591 #[inline]
592 fn handle_with_error_fn<F, Fut, ResB, ResBE, ResE, EF, EFut, EResB, EResBE, EResE>(
593 self,
594 request_fn: F,
595 error_fn: EF,
596 ) -> impl std::future::Future<Output = Result<(), std::io::Error>>
597 where
598 F: Fn(Request<Incoming>) -> Fut + 'static,
599 Fut: std::future::Future<Output = Result<Response<ResB>, ResE>> + 'static,
600 ResB: Body<Data = bytes::Bytes, Error = ResBE> + Unpin + 'static,
601 ResE: std::error::Error,
602 ResBE: std::error::Error,
603 EF: FnOnce(bool) -> EFut,
604 EFut: std::future::Future<Output = Result<Response<EResB>, EResE>>,
605 EResB: Body<Data = bytes::Bytes, Error = EResBE> + Unpin + 'static,
606 EResE: std::error::Error,
607 EResBE: std::error::Error,
608 {
609 #[allow(clippy::type_complexity)]
610 let no_zerocopy: Option<
611 Box<
612 dyn FnMut(
613 RawHandle,
614 &Io,
615 u64,
616 ) -> Box<
617 dyn std::future::Future<Output = Result<(), std::io::Error>>
618 + Unpin
619 + Send
620 + Sync,
621 >,
622 >,
623 > = None;
624 self.handle_with_error_fn_and_zerocopy(request_fn, error_fn, no_zerocopy)
625 }
626
627 #[inline]
628 fn handle<F, Fut, ResB, ResBE, ResE>(
629 self,
630 request_fn: F,
631 ) -> impl std::future::Future<Output = Result<(), std::io::Error>>
632 where
633 F: Fn(Request<Incoming>) -> Fut + 'static,
634 Fut: std::future::Future<Output = Result<Response<ResB>, ResE>> + 'static,
635 ResB: Body<Data = bytes::Bytes, Error = ResBE> + Unpin + 'static,
636 ResE: std::error::Error,
637 ResBE: std::error::Error,
638 {
639 self.handle_with_error_fn(request_fn, |is_timeout| async move {
640 let mut response = Response::builder();
641 if is_timeout {
642 response = response.status(http::StatusCode::REQUEST_TIMEOUT);
643 } else {
644 response = response.status(http::StatusCode::BAD_REQUEST);
645 }
646 response.body(Empty::new())
647 })
648 }
649}
650
651#[inline]
654fn search_header_body_separator(slice: &[u8]) -> Option<(usize, usize)> {
655 if slice.len() < 2 {
656 return None;
658 }
659 for (i, b) in slice.iter().copied().enumerate() {
660 if b == b'\r' {
661 if slice[i + 1..].chunks(3).next() == Some(&b"\n\r\n"[..]) {
662 return Some((i, 4));
663 }
664 } else if b == b'\n' && slice.get(i + 1) == Some(&b'\n') {
665 return Some((i, 2));
666 }
667 }
668 None
669}
670
671#[inline]
673fn write_chunk_size(dst: &mut [u8; 18], len: usize) -> &[u8] {
674 let mut n = len;
675 let mut pos = dst.len() - 2;
676 loop {
677 pos -= 1;
678 dst[pos] = HEX_DIGITS[n & 0xF];
679 n >>= 4;
680 if n == 0 {
681 break;
682 }
683 }
684 dst[dst.len() - 2] = b'\r';
685 dst[dst.len() - 1] = b'\n';
686 &dst[pos..]
687}