Skip to content

Commit f3bae58

Browse files
authored
Merge pull request #108 from Tuntii/core/openapi/ws
feat(core/openapi/ws): dual-stack (H1+H3) runtime, async validation context, OpenAPI ref integrity, WS permessage-deflate negotiation, aligned docs
2 parents a943c4a + 93af870 commit f3bae58

File tree

12 files changed

+377
-230
lines changed

12 files changed

+377
-230
lines changed

Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ members = [
1616
]
1717

1818
[workspace.package]
19-
version = "0.1.300"
19+
version = "0.1.333"
2020
edition = "2021"
2121
authors = ["RustAPI Contributors"]
2222
license = "MIT OR Apache-2.0"
@@ -137,3 +137,4 @@ strip = false
137137

138138

139139

140+

RELEASES.md

Lines changed: 0 additions & 156 deletions
This file was deleted.

crates/rustapi-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,4 @@ replay = ["dep:async-trait"]
110110

111111

112112

113+

crates/rustapi-core/src/app.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,20 +1072,7 @@ impl RustApi {
10721072
server.run().await
10731073
}
10741074

1075-
/// Run both HTTP/1.1 and HTTP/3 servers simultaneously
1076-
///
1077-
/// This allows clients to use either protocol. The HTTP/1.1 server
1078-
/// will advertise HTTP/3 availability via Alt-Svc header.
1079-
///
1080-
/// # Example
1081-
///
1082-
/// ```rust,ignore
1083-
/// RustApi::new()
1084-
/// .route("/", get(hello))
1085-
/// .run_dual_stack("0.0.0.0:8080", Http3Config::new("cert.pem", "key.pem"))
1086-
/// .await
1087-
/// ```
1088-
/// Configure HTTP/3 support
1075+
/// Configure HTTP/3 support for `run_http3` and `run_dual_stack`.
10891076
///
10901077
/// # Example
10911078
///
@@ -1101,10 +1088,10 @@ impl RustApi {
11011088
self
11021089
}
11031090

1104-
/// Run both HTTP/1.1 and HTTP/3 servers simultaneously
1091+
/// Run both HTTP/1.1 (TCP) and HTTP/3 (QUIC/UDP) simultaneously.
11051092
///
1106-
/// This allows clients to use either protocol. The HTTP/1.1 server
1107-
/// will advertise HTTP/3 availability via Alt-Svc header.
1093+
/// The HTTP/3 listener is bound to the same host and port as `http_addr`
1094+
/// so clients can upgrade to either protocol on one endpoint.
11081095
///
11091096
/// # Example
11101097
///
@@ -1118,22 +1105,53 @@ impl RustApi {
11181105
#[cfg(feature = "http3")]
11191106
pub async fn run_dual_stack(
11201107
mut self,
1121-
_http_addr: &str,
1108+
http_addr: &str,
11221109
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1123-
// TODO: Dual-stack requires Router, LayerStack, InterceptorChain to implement Clone.
1124-
// For now, we only run HTTP/3.
1125-
// In the future, we can either:
1126-
// 1. Make Router/LayerStack/InterceptorChain Clone
1127-
// 2. Use Arc<RwLock<...>> pattern
1128-
// 3. Create shared state mechanism
1129-
1130-
let config = self
1110+
use std::sync::Arc;
1111+
1112+
let mut config = self
11311113
.http3_config
11321114
.take()
11331115
.ok_or("HTTP/3 config not set. Use .with_http3(...)")?;
11341116

1135-
tracing::warn!("run_dual_stack currently only runs HTTP/3. HTTP/1.1 support coming soon.");
1136-
self.run_http3(config).await
1117+
let http_socket: std::net::SocketAddr = http_addr.parse()?;
1118+
config.bind_addr = if http_socket.ip().is_ipv6() {
1119+
format!("[{}]", http_socket.ip())
1120+
} else {
1121+
http_socket.ip().to_string()
1122+
};
1123+
config.port = http_socket.port();
1124+
let http_addr = http_socket.to_string();
1125+
1126+
// Apply status page if configured
1127+
self.apply_status_page();
1128+
1129+
// Apply body limit layer if configured
1130+
if let Some(limit) = self.body_limit {
1131+
self.layers.prepend(Box::new(BodyLimitLayer::new(limit)));
1132+
}
1133+
1134+
let router = Arc::new(self.router);
1135+
let layers = Arc::new(self.layers);
1136+
let interceptors = Arc::new(self.interceptors);
1137+
1138+
let http1_server =
1139+
Server::from_shared(router.clone(), layers.clone(), interceptors.clone());
1140+
let http3_server =
1141+
crate::http3::Http3Server::new(&config, router, layers, interceptors).await?;
1142+
1143+
tracing::info!(
1144+
http1_addr = %http_addr,
1145+
http3_addr = %config.socket_addr(),
1146+
"Starting dual-stack HTTP/1.1 + HTTP/3 servers"
1147+
);
1148+
1149+
tokio::try_join!(
1150+
http1_server.run_with_shutdown(&http_addr, std::future::pending::<()>()),
1151+
http3_server.run_with_shutdown(std::future::pending::<()>()),
1152+
)?;
1153+
1154+
Ok(())
11371155
}
11381156
}
11391157

0 commit comments

Comments
 (0)