use crate::core::socket_addr::SocketAddr as CoreSocketAddr;
use crate::route::{Route, RouteTree};
#[cfg(feature = "scheduler")]
use crate::scheduler::middleware::SchedulerMiddleware;
use crate::server::config::{ConnectionLimits, global_server_config};
use crate::server::connection::BoxedConnection;
use crate::server::connection_service::{ConnectionFuture, ConnectionService};
use crate::server::protocol::hyper_http::HyperServiceHandler;
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto::Builder;
use std::sync::Arc;
#[derive(Clone)]
pub struct RouteConnectionService {
#[allow(dead_code)] route: Route,
frozen_tree: Arc<RouteTree>,
limits: ConnectionLimits,
#[cfg(feature = "quic")]
webtransport_handler: Arc<dyn crate::server::quic::WebTransportHandler>,
}
impl RouteConnectionService {
#[inline]
pub fn new(route: Route) -> Self {
let limits = global_server_config().connection_limits.clone();
let frozen_tree = Arc::new(Self::build_route_tree(&route));
#[cfg(feature = "quic")]
let webtransport_handler: Arc<dyn crate::server::quic::WebTransportHandler> =
Arc::new(crate::server::quic::EchoHandler);
Self {
route,
frozen_tree,
limits,
#[cfg(feature = "quic")]
webtransport_handler,
}
}
fn build_route_tree(route: &Route) -> RouteTree {
#[allow(unused_mut)]
let mut route = route.clone();
#[cfg(feature = "session")]
route.check_session();
#[cfg(feature = "cookie")]
route.check_cookie();
#[cfg(feature = "scheduler")]
route.hook_first(SchedulerMiddleware::new());
route.convert_to_route_tree()
}
#[cfg(feature = "quic")]
pub fn with_webtransport_handler(
mut self,
handler: Arc<dyn crate::server::quic::WebTransportHandler>,
) -> Self {
self.webtransport_handler = handler;
self
}
fn handle_http_connection(
frozen_tree: Arc<RouteTree>,
stream: BoxedConnection,
peer: CoreSocketAddr,
limits: ConnectionLimits,
) -> ConnectionFuture {
let max_body_size = limits.max_body_size;
Box::pin(async move {
let io = TokioIo::new(stream);
let mut builder = Builder::new(TokioExecutor::new());
builder.http1().pipeline_flush(true);
builder
.http2()
.initial_stream_window_size(1024 * 1024) .initial_connection_window_size(2 * 1024 * 1024) .adaptive_window(true)
.max_concurrent_streams(256);
builder
.serve_connection_with_upgrades(
io,
HyperServiceHandler::with_limits(peer.into(), frozen_tree, max_body_size),
)
.await
})
}
}
impl ConnectionService for RouteConnectionService {
fn call(&self, stream: BoxedConnection, peer: CoreSocketAddr) -> ConnectionFuture {
#[cfg(feature = "quic")]
{
use crate::quic::connection::QuicConnection;
match stream.downcast::<QuicConnection>() {
Ok(quic) => {
let routes = Arc::clone(&self.frozen_tree);
let read_timeout = self.limits.h3_read_timeout;
let max_body_size = self.limits.max_body_size;
let max_wt_frame = self.limits.max_webtransport_frame_size;
let wt_read_timeout = self.limits.webtransport_read_timeout;
let max_wt_sessions = self.limits.max_webtransport_sessions;
let enable_datagram = global_server_config()
.quic_transport
.as_ref()
.map(|c| c.enable_datagram)
.unwrap_or(true);
let max_datagram_size = self.limits.webtransport_datagram_max_size;
let datagram_rate = self.limits.webtransport_datagram_rate;
let datagram_drop_metric = self.limits.webtransport_datagram_drop_metric;
let webtransport_handler = self.webtransport_handler.clone();
Box::pin(async move {
let incoming = quic.into_incoming();
crate::quic::service::handle_quic_connection(
incoming,
routes,
max_body_size,
read_timeout,
max_wt_frame,
wt_read_timeout,
max_wt_sessions,
enable_datagram,
max_datagram_size,
datagram_rate,
datagram_drop_metric,
webtransport_handler,
)
.await
.map_err(Into::into)
})
}
Err(stream) => {
Self::handle_http_connection(
Arc::clone(&self.frozen_tree),
stream,
peer,
self.limits.clone(),
)
}
}
}
#[cfg(not(feature = "quic"))]
Self::handle_http_connection(
Arc::clone(&self.frozen_tree),
stream,
peer,
self.limits.clone(),
)
}
}
impl From<Route> for RouteConnectionService {
#[inline]
fn from(route: Route) -> Self {
Self::new(route)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn test_route_connection_service_creation() {
let route = Route::new("");
let service = RouteConnectionService::new(route.clone());
assert_eq!(service.route.path, route.path);
}
#[test]
fn test_from_trait() {
let route = Route::new("test");
let service = RouteConnectionService::from(route.clone());
assert_eq!(service.route.path, route.path);
}
#[test]
fn test_route_connection_service_clone() {
let route = Route::new("/test");
let service1 = RouteConnectionService::new(route.clone());
let service2 = service1.clone();
assert_eq!(service1.route.path, service2.route.path);
assert_eq!(service2.route.path, route.path);
}
#[test]
fn test_new_with_nested_route() {
use crate::Request;
let route = Route::new("api").get(|_req: Request| async move { Ok("hello") });
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "api");
}
#[test]
fn test_new_with_empty_route() {
let route = Route::new("");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "");
}
#[test]
fn test_new_with_root_path() {
let route = Route::new("");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "");
}
#[test]
fn test_multiple_from_calls() {
let route1 = Route::new("path1");
let route2 = Route::new("path2");
let service1 = RouteConnectionService::from(route1);
let service2 = RouteConnectionService::from(route2);
assert_eq!(service1.route.path, "path1");
assert_eq!(service2.route.path, "path2");
}
#[test]
fn test_service_limits_field() {
let route = Route::new("test");
let service = RouteConnectionService::new(route);
let _ = service.limits.max_body_size;
}
#[test]
fn test_new_with_complex_route() {
use crate::Request;
let route = Route::new("api")
.get(|_req: Request| async move { Ok("GET") })
.post(|_req: Request| async move { Ok("POST") })
.put(|_req: Request| async move { Ok("PUT") });
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "api");
}
#[test]
fn test_service_with_nested_routes() {
use crate::Request;
let api = Route::new("api");
let users = Route::new("users").get(|_req: Request| async move { Ok("users") });
let posts = Route::new("posts").get(|_req: Request| async move { Ok("posts") });
let service = RouteConnectionService::new(api);
assert_eq!(service.route.path, "api");
let service2 = RouteConnectionService::new(users);
assert_eq!(service2.route.path, "users");
let service3 = RouteConnectionService::new(posts);
assert_eq!(service3.route.path, "posts");
}
#[test]
fn test_route_preservation() {
let original_route = Route::new("original");
let service = RouteConnectionService::new(original_route.clone());
assert_eq!(original_route.path, "original");
assert_eq!(service.route.path, "original");
}
#[cfg(feature = "quic")]
#[test]
fn test_with_webtransport_handler() {
use crate::server::quic::EchoHandler;
let route = Route::new("/test");
let handler: Arc<dyn crate::server::quic::WebTransportHandler> = Arc::new(EchoHandler);
let service = RouteConnectionService::new(route).with_webtransport_handler(handler.clone());
let _ = &service.webtransport_handler;
}
#[cfg(feature = "quic")]
#[test]
fn test_default_webtransport_handler() {
let route = Route::new("/test");
let service = RouteConnectionService::new(route);
let _ = &service.webtransport_handler;
}
#[cfg(feature = "quic")]
#[test]
fn test_webtransport_handler_override() {
use crate::server::quic::EchoHandler;
let route = Route::new("/test");
let custom_handler: Arc<dyn crate::server::quic::WebTransportHandler> =
Arc::new(EchoHandler);
let service1 = RouteConnectionService::new(route.clone());
let service2 = service1.clone().with_webtransport_handler(custom_handler);
assert_eq!(service1.route.path, service2.route.path);
}
#[test]
fn test_service_with_special_characters() {
let route = Route::new("api/v1/test-endpoint");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "api"); assert!(!service.route.children.is_empty()); }
#[test]
fn test_service_with_unicode_path() {
let route = Route::new("api/用户/资料");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "api"); assert!(!service.route.children.is_empty()); }
#[test]
fn test_service_with_long_path() {
let route = Route::new("api/v1/very/long/path/with/many/segments");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "api"); assert!(!service.route.children.is_empty()); }
#[test]
fn test_clone_independence() {
let route = Route::new("test");
let service1 = RouteConnectionService::new(route);
let service2 = service1.clone();
assert_eq!(service1.route.path, service2.route.path);
}
#[test]
fn test_from_trait_multiple_conversions() {
let routes = vec![
Route::new("path1"),
Route::new("path2"),
Route::new("path3"),
];
let services: Vec<RouteConnectionService> = routes
.into_iter()
.map(RouteConnectionService::from)
.collect();
assert_eq!(services.len(), 3);
assert_eq!(services[0].route.path, "path1");
assert_eq!(services[1].route.path, "path2");
assert_eq!(services[2].route.path, "path3");
}
#[test]
fn test_service_with_wildcard_route() {
let route = Route::new("*");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "*");
}
#[test]
fn test_service_with_param_route() {
let route = Route::new("users/:id");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "users"); assert!(!service.route.children.is_empty()); }
#[test]
fn test_service_with_glob_route() {
let route = Route::new("files/**");
let service = RouteConnectionService::new(route);
assert_eq!(service.route.path, "files"); assert!(!service.route.children.is_empty()); }
#[test]
fn test_connection_limits_initialization() {
let route = Route::new("test");
let service = RouteConnectionService::new(route);
let _ = service.limits.max_body_size;
let _ = service.limits.h3_read_timeout;
}
#[test]
fn test_clone_preserves_limits() {
let route = Route::new("test");
let service1 = RouteConnectionService::new(route);
let service2 = service1.clone();
assert_eq!(service1.limits.max_body_size, service2.limits.max_body_size);
assert_eq!(
service1.limits.h3_read_timeout,
service2.limits.h3_read_timeout
);
}
}