monolake_services/http/
detect.rs

1//! HTTP version detection and handling module.
2//!
3//! This module provides functionality to detect the HTTP version (HTTP/1.x or HTTP/2)
4//! of incoming connections and route them accordingly. It is designed to work seamlessly
5//! with monoio's asynchronous runtime and the service_async framework.
6//!
7//! # Key Components
8//!
9//! - [`H2Detect`]: The main service component responsible for HTTP version detection.
10//! - [`H2DetectError`]: Error type for version detection operations.
11//!
12//! # Features
13//!
14//! - Automatic detection of HTTP/2 connections based on the client preface
15//! - Seamless handling of both HTTP/1.x and HTTP/2 connections
16//! - Integration with `service_async` for easy composition in service stacks
17//! - Efficient I/O handling using monoio's asynchronous primitives
18//!
19//! # Usage
20//!
21//! This service is typically used as part of a larger service stack, placed before
22//! the main HTTP handling logic. Here's a basic example:
23//!
24//! ```ignore
25//! use service_async::{layer::FactoryLayer, stack::FactoryStack};
26//!
27//! let config = Config { /* ... */ };
28//! let stack = FactoryStack::new(config)
29//!     .push(HttpCoreService::layer())
30//!     .push(H2Detect::layer())
31//!     // ... other layers ...
32//!     ;
33//!
34//! let service = stack.make_async().await.unwrap();
35//! // Use the service to handle incoming connections
36//! ```
37//!
38//! # Performance Considerations
39//!
40//! - Uses efficient buffering to minimize I/O operations during version detection
41//! - Implements zero-copy techniques where possible to reduce memory overhead
42
43use service_async::{
44    layer::{layer_fn, FactoryLayer},
45    AsyncMakeService, MakeService,
46};
47
48use crate::common::{DetectService, PrefixDetector};
49
50const PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
51
52/// Service for detecting HTTP version and routing connections accordingly.
53///
54/// `H2Detect` examines the initial bytes of an incoming connection to
55/// determine whether it's an HTTP/2 connection (by checking for the HTTP/2 preface)
56/// or an HTTP/1.x connection. It then forwards the connection to the inner service
57/// with appropriate version information.
58/// For implementation details and example usage, see the
59/// [module level documentation](crate::http::detect).
60#[derive(Clone)]
61pub struct H2Detect<T> {
62    inner: T,
63}
64
65#[derive(thiserror::Error, Debug)]
66pub enum H2DetectError<E> {
67    #[error("inner error: {0:?}")]
68    Inner(E),
69    #[error("io error: {0:?}")]
70    Io(std::io::Error),
71}
72
73impl<F: MakeService> MakeService for H2Detect<F> {
74    type Service = DetectService<PrefixDetector, F::Service>;
75    type Error = F::Error;
76
77    fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
78        Ok(DetectService {
79            inner: self.inner.make_via_ref(old.map(|o| &o.inner))?,
80            detector: PrefixDetector(PREFACE),
81        })
82    }
83}
84
85impl<F: AsyncMakeService> AsyncMakeService for H2Detect<F> {
86    type Service = DetectService<PrefixDetector, F::Service>;
87    type Error = F::Error;
88
89    async fn make_via_ref(
90        &self,
91        old: Option<&Self::Service>,
92    ) -> Result<Self::Service, Self::Error> {
93        Ok(DetectService {
94            inner: self.inner.make_via_ref(old.map(|o| &o.inner)).await?,
95            detector: PrefixDetector(PREFACE),
96        })
97    }
98}
99
100impl<F> H2Detect<F> {
101    pub fn layer<C>() -> impl FactoryLayer<C, F, Factory = Self> {
102        layer_fn(|_: &C, inner| H2Detect { inner })
103    }
104}