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}