//! HTTP/2 Server Connections use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::marker::Unpin; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; use pin_project_lite::pin_project; use tokio::io::{AsyncRead, AsyncWrite}; use crate::body::{Body as IncomingBody, HttpBody as Body}; use crate::common::exec::ConnStreamExec; use crate::proto; use crate::service::HttpService; pin_project! { /// A future binding an HTTP/2 connection with a Service. /// /// Polling this future will drive HTTP forward. #[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. #[derive(Clone, Debug)] pub struct Builder { exec: E, 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: AsyncRead + AsyncWrite + Unpin, B: Body + 'static, B::Error: Into>, E: ConnStreamExec, { /// 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: AsyncRead + AsyncWrite + Unpin + 'static, B: Body + 'static, B::Error: Into>, E: ConnStreamExec, { 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. pub fn new(exec: E) -> Self { Self { exec: exec, h2_builder: Default::default(), } } /// 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://http2.github.io/http2-spec/#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 no limit (`std::u32::MAX`). Passing `None` will do nothing. /// /// [spec]: https://http2.github.io/http2-spec/#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. /// /// # Cargo Feature /// 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. /// /// # Cargo Feature /// 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 <= std::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 ~16MB, but may 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 // } /// 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: AsyncRead + AsyncWrite + Unpin, E: ConnStreamExec, { let proto = proto::h2::Server::new(io, service, &self.h2_builder, self.exec.clone()); Connection { conn: proto } } }