diff --git a/Cargo.toml b/Cargo.toml index a31b8813fd..f97e591a39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ futures-channel = { version = "0.3", optional = true } futures-core = { version = "0.3.31", optional = true } futures-util = { version = "0.3", default-features = false, features = ["alloc"], optional = true } h2 = { version = "0.4.2", optional = true } +h3 = { version = "0.0.8", optional = true } http-body-util = { version = "0.1", optional = true } httparse = { version = "1.9", optional = true } httpdate = { version = "1.0", optional = true } @@ -69,7 +70,7 @@ tokio-util = "0.7.10" [features] # Nothing by default -default = [] +default = ["http3", "server"] # Easily turn it all on full = [ @@ -82,6 +83,7 @@ full = [ # HTTP versions http1 = ["dep:atomic-waker", "dep:futures-channel", "dep:futures-core", "dep:httparse", "dep:itoa", "dep:pin-utils"] http2 = ["dep:futures-channel", "dep:futures-core", "dep:h2"] +http3 = ["dep:h3"] # Client/Server client = ["dep:want", "dep:pin-project-lite", "dep:smallvec"] diff --git a/src/cfg.rs b/src/cfg.rs index 71a5351d21..dbd52e1381 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -15,7 +15,7 @@ macro_rules! cfg_proto { ($($item:item)*) => { cfg_feature! { #![all( - any(feature = "http1", feature = "http2"), + any(feature = "http1", feature = "http2", feature = "http3"), any(feature = "client", feature = "server"), )] $($item)* diff --git a/src/common/mod.rs b/src/common/mod.rs index 4b73437203..4cc84cae75 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -7,6 +7,7 @@ pub(crate) mod either; #[cfg(any( all(feature = "client", any(feature = "http1", feature = "http2")), all(feature = "server", feature = "http1"), + all(feature = "server", feature = "http3"), ))] pub(crate) mod future; pub(crate) mod io; diff --git a/src/proto/h3/glue.rs b/src/proto/h3/glue.rs new file mode 100644 index 0000000000..a2335d5aed --- /dev/null +++ b/src/proto/h3/glue.rs @@ -0,0 +1,165 @@ +use std::task::{Context, Poll}; + +use bytes::Buf; + +pub(super) struct Conn(Q); + +pub(super) struct BidiStream(S); +pub(super) struct SendStream(S); +pub(super) struct RecvStream(S); + +impl h3::quic::Connection for Conn +where + Q: crate::rt::quic::Connection, + B: Buf, +{ + type RecvStream = RecvStream; + type OpenStreams = Self; + + fn poll_accept_recv(&mut self, _cx: &mut Context<'_>) + -> Poll> + { + todo!(); + } + + fn poll_accept_bidi(&mut self, _cx: &mut Context<'_>) + -> Poll> + { + todo!(); + } + + fn opener(&self) -> Self::OpenStreams { + todo!(); + } +} + +impl h3::quic::OpenStreams for Conn +where + Q: crate::rt::quic::Connection, + B: Buf, +{ + type BidiStream = BidiStream; + type SendStream = SendStream; + + fn poll_open_send(&mut self, _cx: &mut Context<'_>) + -> Poll> + { + todo!(); + } + + fn poll_open_bidi(&mut self, _cx: &mut Context<'_>) + -> Poll> + { + todo!(); + } + + fn close(&mut self, _: h3::error::Code, _: &[u8]) { + + } +} + +impl h3::quic::SendStream for BidiStream +where + S: crate::rt::quic::SendStream, + B: Buf, +{ + // Required methods + fn poll_ready( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + todo!(); + } + fn send_data>>( + &mut self, + data: T, + ) -> Result<(), h3::quic::StreamErrorIncoming> { + todo!(); + } + fn poll_finish( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + todo!(); + } + fn reset(&mut self, reset_code: u64) { + todo!(); + } + fn send_id(&self) -> h3::quic::StreamId { + todo!() + } +} + +impl h3::quic::SendStream for SendStream +where + S: crate::rt::quic::SendStream, + B: Buf, +{ + // Required methods + fn poll_ready( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + todo!(); + } + fn send_data>>( + &mut self, + data: T, + ) -> Result<(), h3::quic::StreamErrorIncoming> { + todo!(); + } + fn poll_finish( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + todo!(); + } + fn reset(&mut self, reset_code: u64) { + todo!(); + } + fn send_id(&self) -> h3::quic::StreamId { + todo!() + } +} + +impl h3::quic::RecvStream for BidiStream +where + S: crate::rt::quic::RecvStream, +{ + type Buf = S::Buf; + + // Required methods + fn poll_data( + &mut self, + cx: &mut Context<'_>, + ) -> Poll, h3::quic::StreamErrorIncoming>> { + todo!(); + } + fn stop_sending(&mut self, error_code: u64) { + todo!(); + } + fn recv_id(&self) -> h3::quic::StreamId { + todo!(); + } +} + +impl h3::quic::RecvStream for RecvStream +where + S: crate::rt::quic::RecvStream, +{ + type Buf = S::Buf; + + // Required methods + fn poll_data( + &mut self, + cx: &mut Context<'_>, + ) -> Poll, h3::quic::StreamErrorIncoming>> { + todo!(); + } + fn stop_sending(&mut self, error_code: u64) { + todo!(); + } + fn recv_id(&self) -> h3::quic::StreamId { + todo!(); + } +} diff --git a/src/proto/h3/mod.rs b/src/proto/h3/mod.rs new file mode 100644 index 0000000000..2138ed50b2 --- /dev/null +++ b/src/proto/h3/mod.rs @@ -0,0 +1,34 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use bytes::Buf; +use h3::server::Connection; + +use pin_project_lite::pin_project; + +mod glue; + +pin_project! { + pub(crate) struct Server + where + Q: crate::rt::quic::Connection, + B: Buf, + { + exec: E, + q: Connection, B>, + s: S, + } +} + +impl Future for Server +where + Q: crate::rt::quic::Connection, + B: Buf, +{ + type Output = crate::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + todo!() + } +} diff --git a/src/proto/mod.rs b/src/proto/mod.rs index fcdf2b97c0..40acde179b 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -16,6 +16,9 @@ cfg_feature! { #[cfg(feature = "http2")] pub(crate) mod h2; +#[cfg(feature = "http3")] +pub(crate) mod h3; + /// An Incoming Message head. Includes request/status line, and headers. #[cfg(feature = "http1")] #[derive(Debug, Default)] diff --git a/src/rt/mod.rs b/src/rt/mod.rs index d1b2f81792..08dceb5813 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -17,7 +17,7 @@ pub mod bounds; mod io; mod timer; -#[cfg(hyper_unstable_quic)] +//#[cfg(hyper_unstable_quic)] #[cfg_attr(docsrs, doc(cfg(hyper_unstable_quic)))] pub mod quic; diff --git a/src/server/conn/http3.rs b/src/server/conn/http3.rs new file mode 100644 index 0000000000..20b149aea3 --- /dev/null +++ b/src/server/conn/http3.rs @@ -0,0 +1,58 @@ +//! HTTP/3 Server Connections + +use std::error::Error as StdError; + +use pin_project_lite::pin_project; + +use crate::body::{Body, Incoming as IncomingBody}; +use crate::rt::quic; +use crate::service::HttpService; + +pin_project! { + /// A Future representing an HTTP/3 connection. + #[must_use = "futures do nothing unless polled"] + pub struct Connection { + _i: (Q, S, E), + } +} + +/// A configuration builder for HTTP/3 server connections. +/// +/// **Note**: The default values of options are *not considered stable*. They +/// are subject to change at any time. +#[derive(Clone, Debug)] +pub struct Builder { + exec: E, +} + +// ===== impl Connection ===== + +// ===== impl Builder ===== + +impl Builder { + /// Create a new connection builder. + pub fn new(exec: E) -> Self { + Self { + exec, + } + } + + /// Bind a connection together with a [`Service`](crate::service::Service). + /// + /// This returns a Future that must be polled in order for HTTP to be + /// driven on the connection. + pub fn serve_connection(&self, quic: Q, service: S) -> Connection + where + S: HttpService, + S::Error: Into>, + Bd: Body + 'static, + Bd::Error: Into>, + Q: quic::Connection, + //E: Http2ServerConnExec, + E: Clone, + { + Connection { + _i: (quic, service, self.exec.clone()), + } + } +} diff --git a/src/server/conn/mod.rs b/src/server/conn/mod.rs index 54b309e88e..63f3089a9f 100644 --- a/src/server/conn/mod.rs +++ b/src/server/conn/mod.rs @@ -18,3 +18,5 @@ pub mod http1; #[cfg(feature = "http2")] pub mod http2; +//#[cfg(feature = "http3")] +pub mod http3;