//! HTTP/2 Server Connections use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use crate::rt::{Read, Write}; use futures_util::ready; use pin_project_lite::pin_project; use crate::body::{Body, Incoming as IncomingBody}; use crate::proto; use crate::rt::bounds::Http2ServerConnExec; use crate::service::HttpService; use crate::{common::time::Time, rt::Timer}; pin_project! { /// A [`Future`](core::future::Future) representing an HTTP/2 connection, bound to a /// [`Service`](crate::service::Service), returned from /// [`Builder::serve_connection`](struct.Builder.html#method.serve_connection). /// /// To drive HTTP on this connection this future **must be polled**, typically with /// `.await`. If it isn't polled, no progress will be made on this connection. #[must_use = "futures do nothing unless polled"] pub struct Connection where S: HttpService, { conn: proto::h2::Server, } } /// A configuration builder for HTTP/2 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, timer: Time, h2_builder: proto::h2::server::Config, } // ===== impl Connection ===== impl fmt::Debug for Connection where S: HttpService, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection").finish() } } impl Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, E: Http2ServerConnExec, { /// Start a graceful shutdown process for this connection. /// /// This `Connection` should continue to be polled until shutdown /// can finish. /// /// # Note /// /// This should only be called while the `Connection` future is still /// pending. If called after `Connection::poll` has resolved, this does /// nothing. pub fn graceful_shutdown(mut self: Pin<&mut Self>) { self.conn.graceful_shutdown(); } } impl Future for Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, E: Http2ServerConnExec, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.conn).poll(cx)) { Ok(_done) => { //TODO: the proto::h2::Server no longer needs to return //the Dispatched enum Poll::Ready(Ok(())) } Err(e) => Poll::Ready(Err(e)), } } } // ===== impl Builder ===== impl Builder { /// Create a new connection builder. /// /// This starts with the default options, and an executor which is a type /// that implements [`Http2ServerConnExec`] trait. /// /// [`Http2ServerConnExec`]: crate::rt::bounds::Http2ServerConnExec pub fn new(exec: E) -> Self { Self { exec, timer: Time::Empty, h2_builder: Default::default(), } } /// Configures the maximum number of pending reset streams allowed before a GOAWAY will be sent. /// /// This will default to the default value set by the [`h2` crate](https://crates.io/crates/h2). /// As of v0.4.0, it is 20. /// /// See for more information. pub fn max_pending_accept_reset_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_pending_accept_reset_streams = max.into(); self } /// Configures the maximum number of local reset streams allowed before a GOAWAY will be sent. /// /// If not set, hyper will use a default, currently of 1024. /// /// If `None` is supplied, hyper will not apply any limit. /// This is not advised, as it can potentially expose servers to DOS vulnerabilities. /// /// See for more information. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn max_local_error_reset_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_local_error_reset_streams = max.into(); self } /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. /// /// [spec]: https://httpwg.org/specs/rfc9113.html#SETTINGS_INITIAL_WINDOW_SIZE pub fn initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_stream_window_size = sz; } self } /// Sets the max connection-level flow control for HTTP2. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. pub fn initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_conn_window_size = sz; } self } /// Sets whether to use an adaptive flow control. /// /// Enabling this will override the limits set in /// `initial_stream_window_size` and /// `initial_connection_window_size`. pub fn adaptive_window(&mut self, enabled: bool) -> &mut Self { use proto::h2::SPEC_WINDOW_SIZE; self.h2_builder.adaptive_window = enabled; if enabled { self.h2_builder.initial_conn_window_size = SPEC_WINDOW_SIZE; self.h2_builder.initial_stream_window_size = SPEC_WINDOW_SIZE; } self } /// Sets the maximum frame size to use for HTTP2. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. pub fn max_frame_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.max_frame_size = sz; } self } /// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2 /// connections. /// /// Default is 200, but not part of the stability of hyper. It could change /// in a future release. You are encouraged to set your own limit. /// /// Passing `None` will remove any limit. /// /// [spec]: https://httpwg.org/specs/rfc9113.html#SETTINGS_MAX_CONCURRENT_STREAMS pub fn max_concurrent_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_concurrent_streams = max.into(); self } /// Sets an interval for HTTP2 Ping frames should be sent to keep a /// connection alive. /// /// Pass `None` to disable HTTP2 keep-alive. /// /// Default is currently disabled. pub fn keep_alive_interval(&mut self, interval: impl Into>) -> &mut Self { self.h2_builder.keep_alive_interval = interval.into(); self } /// Sets a timeout for receiving an acknowledgement of the keep-alive ping. /// /// If the ping is not acknowledged within the timeout, the connection will /// be closed. Does nothing if `keep_alive_interval` is disabled. /// /// Default is 20 seconds. pub fn keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self { self.h2_builder.keep_alive_timeout = timeout; self } /// Set the maximum write buffer size for each HTTP/2 stream. /// /// Default is currently ~400KB, but may change. /// /// # Panics /// /// The value must be no larger than `u32::MAX`. pub fn max_send_buf_size(&mut self, max: usize) -> &mut Self { assert!(max <= u32::MAX as usize); self.h2_builder.max_send_buffer_size = max; self } /// Enables the [extended CONNECT protocol]. /// /// [extended CONNECT protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4 pub fn enable_connect_protocol(&mut self) -> &mut Self { self.h2_builder.enable_connect_protocol = true; self } /// Sets the max size of received header frames. /// /// Default is currently 16KB, but can change. pub fn max_header_list_size(&mut self, max: u32) -> &mut Self { self.h2_builder.max_header_list_size = max; self } /// Set the timer used in background tasks. pub fn timer(&mut self, timer: M) -> &mut Self where M: Timer + Send + Sync + 'static, { self.timer = Time::Timer(Arc::new(timer)); self } /// Set whether the `date` header should be included in HTTP responses. /// /// Note that including the `date` header is recommended by RFC 7231. /// /// Default is true. pub fn auto_date_header(&mut self, enabled: bool) -> &mut Self { self.h2_builder.date_header = enabled; self } /// 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, io: I, service: S) -> Connection where S: HttpService, S::Error: Into>, Bd: Body + 'static, Bd::Error: Into>, I: Read + Write + Unpin, E: Http2ServerConnExec, { let proto = proto::h2::Server::new( io, service, &self.h2_builder, self.exec.clone(), self.timer.clone(), ); Connection { conn: proto } } }