From 40ca5d786f93f26dc133c07115161223af4d17c2 Mon Sep 17 00:00:00 2001 From: ihciah Date: Thu, 24 Oct 2024 09:14:12 +0000 Subject: [PATCH] feat: generic protocol detector --- docs/assets/http_conn_reuse.png | Bin 0 -> 48316 bytes docs/assets/https_conn_reuse.png | Bin 0 -> 47306 bytes docs/en/index.md | 0 docs/zh/index.md | 36 ++++++ monolake-services/src/common/detect.rs | 118 ++++++++++++++++++ monolake-services/src/common/mod.rs | 2 + monolake-services/src/http/core.rs | 10 +- monolake-services/src/http/detect.rs | 92 +++----------- .../http/handlers/connection_persistence.rs | 4 +- .../src/http/handlers/content_handler.rs | 4 +- monolake-services/src/http/handlers/mod.rs | 4 +- monolake-services/src/http/handlers/route.rs | 4 +- monolake-services/src/lib.rs | 10 +- monolake/src/factory.rs | 4 +- 14 files changed, 194 insertions(+), 94 deletions(-) create mode 100644 docs/assets/http_conn_reuse.png create mode 100644 docs/assets/https_conn_reuse.png create mode 100644 docs/en/index.md create mode 100644 docs/zh/index.md create mode 100644 monolake-services/src/common/detect.rs diff --git a/docs/assets/http_conn_reuse.png b/docs/assets/http_conn_reuse.png new file mode 100644 index 0000000000000000000000000000000000000000..c85f9cb34b9fe40fb6b98ff3d3dfb8ba38d2f0e7 GIT binary patch literal 48316 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 literal 0 HcmV?d00001 diff --git a/docs/assets/https_conn_reuse.png b/docs/assets/https_conn_reuse.png new file mode 100644 index 0000000000000000000000000000000000000000..f266d3b9b47e92cf5a1167e38305f464fe7478cd GIT binary patch literal 47306 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 pI5iyfJRK*z4}lq=ZWWLaCXxmQ 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 literal 0 HcmV?d00001 diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 0000000..210cb43 --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,36 @@ +--- +title: "MonoLake 概述" +keywords: ["MonoLake", "Monoio", "Proxy", "Rust", "io-uring"] +description: "MonoLake 网关框架概述" +params: + author: ChiHai, RainJiang +--- + +MonoLake 是一款使用 Rust 语言的高性能网关框架。长久以来,对于一款网关产品,性能、易扩展性和安全总是难以兼得,我们设计了 MonoLake 来打破这一现状。 + +MonoLake 底层基于高效的 Monoio 运行时(网关是该运行时的首要目标场景);采用 thread-pre-core 架构减少共享;提供了适用于网关的洋葱模型的 Service Chain 抽象,以及组装这些 Service 的工具支持;对于常用的协议,如 tls、http、thrift 等,我们也提供了丰富的实现。 + +MonoLake 的设计目标是高性能、易扩展、安全可靠。希望 MonoLake 能够成为用户构建高性能网关的首选。 + +### 性能 +> MonoLake 的性能源自 Rust 高效的异步系统、Monoio Runtime、io_uring 和 thread-per-core 架构。 + +借助 Rust 的无栈协程,我们可以高效地执行异步逻辑;Monoio Runtime 则提供了高效的异步 IO 支持,它在提供 io_uring 能力的同时也支持了 epoll/kqueue 等传统的异步 IO 方式;采用 thread-per-core 架构尽可能地减少跨线程共享,对于仅存在在单线程上的数据,可以避免带任务窃取的 Runtime 下的锁和原子操作的开销。 + +在我们的性能测试中,MonoLake 在 HTTP 代理场景的性能与 Nginx 相当。 + +### 易扩展性 +> 不同于开箱即用的网关,MonoLake 虽然提供了一个可执行网关,但该网关仅做 PoC 之用,我们期望的是用户借助我们提供的框架去构建自己的网关系统。 + +通常,用户在基于 Envoy、Nginx 等产品做二次开发时,必须了解整套产品的数据流和架构设计,并在已有的扩展点插入自己的逻辑。如果自定义逻辑中含有异步逻辑,则基于 callback 的整个过程会冗长复杂,且容易在生命周期管理上出现问题并导致内存访问异常。 + +借助 MonoLake 框架开发自己的网关则简单的多。TODO + +### 安全性 + +## MonoLake 架构 +MonoLake 主要由 MonoLake-core 和 MonoLake-services 以及一些抽象 Trait 构成。MonoLake-core 提供了启动器、线程管理、Listener 实现等;MonoLake-services 则提供了常用的 Service 实现,如 tcp、tls、http(我们同时支持了原生的 monoio-http 和 hyper)、thrift 等。 + +Service 是 MonoLake 的核心抽象,Service 描述了一个异步的 Request 处理逻辑: `async fn call(&self, req: Request) -> Result`。Request 作为泛型,在 `TcpService` 处它是一个 TCP 连接 `TcpStream`;而 `HttpHandler` 对应的 Request 就是一个 http 请求 `http::Request`。 + +Service 嵌套定义,由外层 Service 负责调用内层,例如 `HttpService` diff --git a/monolake-services/src/common/detect.rs b/monolake-services/src/common/detect.rs new file mode 100644 index 0000000..e4763bc --- /dev/null +++ b/monolake-services/src/common/detect.rs @@ -0,0 +1,118 @@ +use std::{future::Future, io, io::Cursor}; + +use monoio::{ + buf::IoBufMut, + io::{AsyncReadRent, AsyncReadRentExt, PrefixedReadIo}, +}; +use service_async::Service; + +/// Detect is a trait for detecting a certain pattern in the input stream. +/// +/// It accepts an input stream and returns a tuple of the detected pattern and the wrapped input +/// stream which is usually a `PrefixedReadIo`. The implementation can choose to whether add the +/// prefix data. +/// If it fails to detect the pattern, it should represent the error inside the `DetOut`. +pub trait Detect { + type DetOut; + type IOOut; + + fn detect(&self, io: IO) -> impl Future>; +} + +/// DetectService is a service that detects a certain pattern in the input stream and forwards the +/// detected pattern and the wrapped input stream to the inner service. +pub struct DetectService { + pub detector: D, + pub inner: S, +} + +#[derive(thiserror::Error, Debug)] +pub enum DetectError { + #[error("service error: {0:?}")] + Svc(E), + #[error("io error: {0:?}")] + Io(std::io::Error), +} + +impl Service<(R, CX)> for DetectService +where + D: Detect, + S: Service<(D::DetOut, D::IOOut, CX)>, +{ + type Response = S::Response; + type Error = DetectError; + + async fn call(&self, (io, cx): (R, CX)) -> Result { + let (det, io) = self.detector.detect(io).await.map_err(DetectError::Io)?; + self.inner + .call((det, io, cx)) + .await + .map_err(DetectError::Svc) + } +} + +/// FixedLengthDetector detects a fixed length of bytes from the input stream. +pub struct FixedLengthDetector(pub F); + +impl Detect for FixedLengthDetector +where + F: Fn(&mut [u8]) -> DetOut, + IO: AsyncReadRent, +{ + type DetOut = DetOut; + type IOOut = PrefixedReadIo>>; + + async fn detect(&self, mut io: IO) -> io::Result<(Self::DetOut, Self::IOOut)> { + let buf = Vec::with_capacity(N).slice_mut(..N); + let (r, buf) = io.read_exact(buf).await; + r?; + + let mut buf = buf.into_inner(); + let r = (self.0)(&mut buf); + Ok((r, PrefixedReadIo::new(io, Cursor::new(buf)))) + } +} + +/// PrefixDetector detects a certain prefix from the input stream. +/// +/// If the prefix matches, it returns true and the wrapped input stream with the prefix data. +/// Otherwise, it returns false and the input stream with the prefix data(the prefix maybe less than +/// the static str's length). +pub struct PrefixDetector(pub &'static [u8]); + +impl Detect for PrefixDetector +where + IO: AsyncReadRent, +{ + type DetOut = bool; + type IOOut = PrefixedReadIo>>; + + async fn detect(&self, mut io: IO) -> io::Result<(Self::DetOut, Self::IOOut)> { + let l = self.0.len(); + let mut written = 0; + let mut buf: Vec = Vec::with_capacity(l); + let mut eq = true; + loop { + // # Safety + // The buf must have enough capacity to write the data. + let buf_slice = unsafe { buf.slice_mut_unchecked(written..l) }; + let (result, buf_slice) = io.read(buf_slice).await; + buf = buf_slice.into_inner(); + match result? { + 0 => { + break; + } + n => { + let curr = written; + written += n; + if self.0[curr..written] != buf[curr..written] { + eq = false; + break; + } + } + } + } + let io = PrefixedReadIo::new(io, Cursor::new(buf)); + Ok((eq && written == l, io)) + } +} diff --git a/monolake-services/src/common/mod.rs b/monolake-services/src/common/mod.rs index 0bc6fc7..516773a 100644 --- a/monolake-services/src/common/mod.rs +++ b/monolake-services/src/common/mod.rs @@ -2,6 +2,7 @@ mod cancel; mod context; mod delay; +mod detect; mod erase; mod map; mod panic; @@ -10,6 +11,7 @@ mod timeout; pub use cancel::{linked_list, Canceller, CancellerDropper, Waiter}; pub use context::ContextService; pub use delay::{Delay, DelayService}; +pub use detect::{Detect, DetectService, FixedLengthDetector, PrefixDetector}; pub use erase::EraseResp; pub use map::{FnSvc, Map, MapErr}; pub use panic::{CatchPanicError, CatchPanicService}; diff --git a/monolake-services/src/http/core.rs b/monolake-services/src/http/core.rs index e32d3d0..ad8236b 100644 --- a/monolake-services/src/http/core.rs +++ b/monolake-services/src/http/core.rs @@ -16,7 +16,7 @@ //! //! - Support for HTTP/1, HTTP/1.1, and HTTP/2 protocols //! - Composable design allowing a stack of `HttpHandler` implementations -//! - Automatic protocol detection when combined with `HttpVersionDetect` +//! - Automatic protocol detection when combined with `H2Detect` //! - Efficient handling of concurrent requests using asynchronous I/O //! - Configurable timeout settings for different stages of request processing //! - Integration with `service_async` for easy composition in service stacks @@ -25,17 +25,17 @@ //! # Usage //! //! `HttpCoreService` is typically used as part of a larger service stack, often in combination -//! with `HttpVersionDetect` for automatic protocol detection. Here's a basic example: +//! with `H2Detect` for automatic protocol detection. Here's a basic example: //! //! ```ignore //! use service_async::{layer::FactoryLayer, stack::FactoryStack}; //! -//! use crate::http::{HttpCoreService, HttpVersionDetect}; +//! use crate::http::{HttpCoreService, H2Detect}; //! //! let config = Config { /* ... */ }; //! let stack = FactoryStack::new(config) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()) +//! .push(H2Detect::layer()) //! // ... other handlers implementing HttpHandler ... //! ; //! @@ -52,7 +52,7 @@ //! //! # Automatic Protocol Detection //! -//! When used in conjunction with `HttpVersionDetect`, `HttpCoreService` can automatically +//! When used in conjunction with `H2Detect`, `HttpCoreService` can automatically //! detect whether an incoming connection is using HTTP/1, HTTP/1.1, or HTTP/2, and handle //! it appropriately. This allows for seamless support of multiple HTTP versions without //! the need for separate server configurations. diff --git a/monolake-services/src/http/detect.rs b/monolake-services/src/http/detect.rs index 14e14a0..8af5e20 100644 --- a/monolake-services/src/http/detect.rs +++ b/monolake-services/src/http/detect.rs @@ -6,8 +6,8 @@ //! //! # Key Components //! -//! - [`HttpVersionDetect`]: The main service component responsible for HTTP version detection. -//! - [`HttpVersionDetectError`]: Error type for version detection operations. +//! - [`H2Detect`]: The main service component responsible for HTTP version detection. +//! - [`H2DetectError`]: Error type for version detection operations. //! //! # Features //! @@ -27,7 +27,7 @@ //! let config = Config { /* ... */ }; //! let stack = FactoryStack::new(config) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()) +//! .push(H2Detect::layer()) //! // ... other layers ... //! ; //! @@ -39,122 +39,66 @@ //! //! - Uses efficient buffering to minimize I/O operations during version detection //! - Implements zero-copy techniques where possible to reduce memory overhead -use std::io::Cursor; -use monoio::{ - buf::IoBufMut, - io::{AsyncReadRent, AsyncWriteRent, PrefixedReadIo}, -}; -use monolake_core::http::HttpAccept; use service_async::{ layer::{layer_fn, FactoryLayer}, - AsyncMakeService, MakeService, Service, + AsyncMakeService, MakeService, }; -use crate::tcp::Accept; +use crate::common::{DetectService, PrefixDetector}; const PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; /// Service for detecting HTTP version and routing connections accordingly. /// -/// `HttpVersionDetect` examines the initial bytes of an incoming connection to +/// `H2Detect` examines the initial bytes of an incoming connection to /// determine whether it's an HTTP/2 connection (by checking for the HTTP/2 preface) /// or an HTTP/1.x connection. It then forwards the connection to the inner service /// with appropriate version information. /// For implementation details and example usage, see the /// [module level documentation](crate::http::detect). #[derive(Clone)] -pub struct HttpVersionDetect { +pub struct H2Detect { inner: T, } #[derive(thiserror::Error, Debug)] -pub enum HttpVersionDetectError { +pub enum H2DetectError { #[error("inner error: {0:?}")] Inner(E), #[error("io error: {0:?}")] Io(std::io::Error), } -impl MakeService for HttpVersionDetect { - type Service = HttpVersionDetect; +impl MakeService for H2Detect { + type Service = DetectService; type Error = F::Error; fn make_via_ref(&self, old: Option<&Self::Service>) -> Result { - Ok(HttpVersionDetect { + Ok(DetectService { inner: self.inner.make_via_ref(old.map(|o| &o.inner))?, + detector: PrefixDetector(PREFACE), }) } } -impl AsyncMakeService for HttpVersionDetect { - type Service = HttpVersionDetect; +impl AsyncMakeService for H2Detect { + type Service = DetectService; type Error = F::Error; async fn make_via_ref( &self, old: Option<&Self::Service>, ) -> Result { - Ok(HttpVersionDetect { + Ok(DetectService { inner: self.inner.make_via_ref(old.map(|o| &o.inner)).await?, + detector: PrefixDetector(PREFACE), }) } } -impl HttpVersionDetect { +impl H2Detect { pub fn layer() -> impl FactoryLayer { - layer_fn(|_: &C, inner| HttpVersionDetect { inner }) - } -} - -impl Service> for HttpVersionDetect -where - Stream: AsyncReadRent + AsyncWriteRent, - T: Service>>, CX>>, -{ - type Response = T::Response; - type Error = HttpVersionDetectError; - - async fn call( - &self, - incoming_stream: Accept, - ) -> Result { - let (mut stream, addr) = incoming_stream; - let mut buf = vec![0; PREFACE.len()]; - let mut pos = 0; - let mut h2_detect = false; - - loop { - let buf_slice = unsafe { buf.slice_mut_unchecked(pos..PREFACE.len()) }; - let (result, buf_slice) = stream.read(buf_slice).await; - buf = buf_slice.into_inner(); - match result { - Ok(0) => { - break; - } - Ok(n) => { - if PREFACE[pos..pos + n] != buf[pos..pos + n] { - break; - } - pos += n; - } - Err(e) => { - return Err(HttpVersionDetectError::Io(e)); - } - } - - if pos == PREFACE.len() { - h2_detect = true; - break; - } - } - - let preface_buf = std::io::Cursor::new(buf); - let rewind_io = monoio::io::PrefixedReadIo::new(stream, preface_buf); - - self.inner - .call((h2_detect, rewind_io, addr)) - .await - .map_err(HttpVersionDetectError::Inner) + layer_fn(|_: &C, inner| H2Detect { inner }) } } diff --git a/monolake-services/src/http/handlers/connection_persistence.rs b/monolake-services/src/http/handlers/connection_persistence.rs index dd4c075..a89c730 100644 --- a/monolake-services/src/http/handlers/connection_persistence.rs +++ b/monolake-services/src/http/handlers/connection_persistence.rs @@ -25,7 +25,7 @@ //! common::ContextService, //! http::{ //! core::HttpCoreService, -//! detect::HttpVersionDetect, +//! detect::H2Detect, //! handlers::{ //! route::RouteConfig, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler, //! UpstreamHandler, @@ -60,7 +60,7 @@ //! .push(RewriteAndRouteHandler::layer()) //! .push(ConnectionReuseHandler::layer()) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()); +//! .push(H2Detect::layer()); //! //! // Use the service to handle HTTP requests //! ``` diff --git a/monolake-services/src/http/handlers/content_handler.rs b/monolake-services/src/http/handlers/content_handler.rs index 96f6f1e..919afa6 100644 --- a/monolake-services/src/http/handlers/content_handler.rs +++ b/monolake-services/src/http/handlers/content_handler.rs @@ -25,7 +25,7 @@ //! common::ContextService, //! http::{ //! core::HttpCoreService, -//! detect::HttpVersionDetect, +//! detect::H2Detect, //! handlers::{ //! route::RouteConfig, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler, //! UpstreamHandler, @@ -60,7 +60,7 @@ //! .push(RewriteAndRouteHandler::layer()) //! .push(ConnectionReuseHandler::layer()) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()); +//! .push(H2Detect::layer()); //! //! // Use the service to handle HTTP requests //! ``` diff --git a/monolake-services/src/http/handlers/mod.rs b/monolake-services/src/http/handlers/mod.rs index 6a47b3c..9e6a6fd 100644 --- a/monolake-services/src/http/handlers/mod.rs +++ b/monolake-services/src/http/handlers/mod.rs @@ -61,7 +61,7 @@ //! common::ContextService, //! http::{ //! core::HttpCoreService, -//! detect::HttpVersionDetect, +//! detect::H2Detect, //! handlers::{ //! route::RouteConfig, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler, //! UpstreamHandler, @@ -96,7 +96,7 @@ //! .push(RewriteAndRouteHandler::layer()) //! .push(ConnectionReuseHandler::layer()) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()); +//! .push(H2Detect::layer()); //! //! // Use the service to handle HTTP requests //! ``` diff --git a/monolake-services/src/http/handlers/route.rs b/monolake-services/src/http/handlers/route.rs index b945c40..bb38ac4 100644 --- a/monolake-services/src/http/handlers/route.rs +++ b/monolake-services/src/http/handlers/route.rs @@ -32,7 +32,7 @@ //! common::ContextService, //! http::{ //! core::HttpCoreService, -//! detect::HttpVersionDetect, +//! detect::H2Detect, //! handlers::{ //! route::RouteConfig, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler, //! UpstreamHandler, @@ -67,7 +67,7 @@ //! .push(RewriteAndRouteHandler::layer()) //! .push(ConnectionReuseHandler::layer()) //! .push(HttpCoreService::layer()) -//! .push(HttpVersionDetect::layer()); +//! .push(H2Detect::layer()); //! //! // Use the service to handle HTTP requests //! ``` diff --git a/monolake-services/src/lib.rs b/monolake-services/src/lib.rs index b2a5031..2388ae6 100644 --- a/monolake-services/src/lib.rs +++ b/monolake-services/src/lib.rs @@ -15,9 +15,9 @@ //! //! - [`HttpCoreService`](http::core): The main service for handling HTTP/1.1 and HTTP/2 //! connections. -//! - [`HttpVersionDetect`](http::detect): Automatic detection of HTTP protocol versions. -//! #[cfg_attr(feature = "hyper", doc = "- [`HyperCoreService`](hyper::HyperCoreService): A -//! high-performance HTTP service built on top of the Hyper library.")] +//! - [`H2Detect`](http::detect): Automatic detection of HTTP protocol versions. #[cfg_attr(feature +//! = "hyper", doc = "- [`HyperCoreService`](hyper::HyperCoreService): A high-performance HTTP +//! service built on top of the Hyper library.")] //! //! #### Request Handlers //! @@ -108,7 +108,7 @@ //! //! ```ignore //! use monolake_services::{ -//! HttpCoreService, HttpVersionDetect, ConnectionReuseHandler, +//! HttpCoreService, H2Detect, ConnectionReuseHandler, //! ContentHandler, RewriteAndRouteHandler, UpstreamHandler, UnifiedTlsService, //! ProxyProtocolService, HyperCoreService //! }; @@ -124,7 +124,7 @@ //! .push(ContentHandler::layer()) //! .push(ConnectionReuseHandler::layer()) //! .push(HyperCoreService::layer()); -//! .push(HttpVersionDetect::layer()) +//! .push(H2Detect::layer()) //! .push(UnifiedTlsService::layer()) //! .push(ContextService::layer()); //! diff --git a/monolake/src/factory.rs b/monolake/src/factory.rs index 125bbd6..e29e556 100644 --- a/monolake/src/factory.rs +++ b/monolake/src/factory.rs @@ -12,7 +12,7 @@ use monolake_services::{ common::ContextService, http::{ core::HttpCoreService, - detect::HttpVersionDetect, + detect::H2Detect, handlers::{ upstream::HttpUpstreamTimeout, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler, UpstreamHandler, @@ -55,7 +55,7 @@ pub fn l7_factory( let stacks = stacks .push(ConnectionReuseHandler::layer()) .push(HttpCoreService::layer()) - .push(HttpVersionDetect::layer()); + .push(H2Detect::layer()); #[cfg(feature = "tls")] let stacks = stacks.push(monolake_services::tls::UnifiedTlsFactory::layer());