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