Skip to content

Commit

Permalink
support auto http configuration (http1 + h2)
Browse files Browse the repository at this point in the history
  • Loading branch information
glendc committed Dec 2, 2023
1 parent 9c08d48 commit 7f3642f
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 2 deletions.
7 changes: 6 additions & 1 deletion examples/tokio_tcp_http_hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ async fn main() {
// - find good way to pass state from stream to http
// - provide HttpServer so that we can use it to serve http requests

let web_server = http::HttpServer::auto()
let mut http_server = http::HttpServer::auto();

http_server.http1().preserve_header_case(true);
http_server.h2().adaptive_window(true);

let web_server = http_server
.compression()
.trace()
.timeout(Duration::from_secs(10))
Expand Down
256 changes: 255 additions & 1 deletion src/http/server/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::time::Duration;
use hyper::server::conn::http2::Builder as Http2Builder;
use hyper::{rt::Timer, server::conn::http1::Builder as Http1Builder};
use hyper_util::server::conn::auto::Builder as AutoBuilder;
use hyper_util::server::conn::auto::Http1Builder as InnerAutoHttp1Builder;
use hyper_util::server::conn::auto::Http2Builder as InnerAutoHttp2Builder;

use crate::service::{http::ServiceBuilderExt, util::Identity, Layer, Service, ServiceBuilder};
use crate::{tcp::TcpStream, BoxError};
Expand Down Expand Up @@ -280,8 +282,260 @@ impl HttpServer<AutoBuilder<GlobalExecutor>, Identity> {
service_builder: ServiceBuilder::new(),
}
}
}

impl<E, L> HttpServer<AutoBuilder<E>, L> {
/// Http1 configuration.
pub fn http1(&mut self) -> AutoHttp1Builder<'_, E> {
AutoHttp1Builder {
inner: self.builder.http1(),
}
}

/// H2 configuration.
pub fn h2(&mut self) -> AutoH2Builder<'_, E> {
AutoH2Builder {
inner: self.builder.http2(),
}
}
}

pub struct AutoHttp1Builder<'a, E> {
inner: InnerAutoHttp1Builder<'a, E>,
}

impl<'a, E> AutoHttp1Builder<'a, E> {
/// Set whether HTTP/1 connections should support half-closures.
///
/// Clients can chose to shutdown their write-side while waiting
/// for the server to respond. Setting this to `true` will
/// prevent closing the connection immediately if `read`
/// detects an EOF in the middle of a request.
///
/// Default is `false`.
pub fn half_close(&mut self, val: bool) -> &mut Self {
self.inner.half_close(val);
self
}

/// Enables or disables HTTP/1 keep-alive.
///
/// Default is true.
pub fn keep_alive(&mut self, val: bool) -> &mut Self {
self.inner.keep_alive(val);
self
}

/// Set whether HTTP/1 connections will write header names as title case at
/// the socket level.
///
/// Note that this setting does not affect HTTP/2.
///
/// Default is false.
pub fn title_case_headers(&mut self, enabled: bool) -> &mut Self {
self.inner.title_case_headers(enabled);
self
}

/// Set whether to support preserving original header cases.
///
/// Currently, this will record the original cases received, and store them
/// in a private extension on the `Request`. It will also look for and use
/// such an extension in any provided `Response`.
///
/// Since the relevant extension is still private, there is no way to
/// interact with the original cases. The only effect this can have now is
/// to forward the cases in a proxy-like fashion.
///
/// Note that this setting does not affect HTTP/2.
///
/// Default is false.
pub fn preserve_header_case(&mut self, enabled: bool) -> &mut Self {
self.inner.preserve_header_case(enabled);
self
}

/// Set a timeout for reading client request headers. If a client does not
/// transmit the entire header within this time, the connection is closed.
///
/// Default is None.
pub fn header_read_timeout(&mut self, read_timeout: Duration) -> &mut Self {
self.inner.header_read_timeout(read_timeout);
self
}

/// Set whether HTTP/1 connections should try to use vectored writes,
/// or always flatten into a single buffer.
///
/// Note that setting this to false may mean more copies of body data,
/// but may also improve performance when an IO transport doesn't
/// support vectored writes well, such as most TLS implementations.
///
/// Setting this to true will force hyper to use queued strategy
/// which may eliminate unnecessary cloning on some TLS backends
///
/// Default is `auto`. In this mode hyper will try to guess which
/// mode to use
pub fn writev(&mut self, val: bool) -> &mut Self {
self.inner.writev(val);
self
}

/// Set the maximum buffer size for the connection.
///
/// Default is ~400kb.
///
/// # Panics
///
/// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum.
pub fn max_buf_size(&mut self, max: usize) -> &mut Self {
self.inner.max_buf_size(max);
self
}

/// Aggregates flushes to better support pipelined responses.
///
/// Experimental, may have bugs.
///
/// Default is false.
pub fn pipeline_flush(&mut self, enabled: bool) -> &mut Self {
self.inner.pipeline_flush(enabled);
self
}

/// Set the timer used in background tasks.
pub fn timer<M>(&mut self, timer: M) -> &mut Self
where
M: Timer + Send + Sync + 'static,
{
self.inner.timer(timer);
self
}
}

pub struct AutoH2Builder<'a, E> {
inner: InnerAutoHttp2Builder<'a, E>,
}

impl<'a, E> AutoH2Builder<'a, E> {
/// 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<Option<u32>>) -> &mut Self {
self.inner.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<Option<u32>>) -> &mut Self {
self.inner.initial_connection_window_size(sz);
self
}

/// Sets whether to use an adaptive flow control.
///
/// Enabling this will override the limits set in
/// `http2_initial_stream_window_size` and
/// `http2_initial_connection_window_size`.
pub fn adaptive_window(&mut self, enabled: bool) -> &mut Self {
self.inner.adaptive_window(enabled);
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<Option<u32>>) -> &mut Self {
self.inner.max_frame_size(sz);
self
}

// TODO: add methods to configure the http/1.1 and h2 settings
/// 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<Option<u32>>) -> &mut Self {
self.inner.max_concurrent_streams(max);
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<Option<Duration>>) -> &mut Self {
self.inner.keep_alive_interval(interval);
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 `http2_keep_alive_interval` is disabled.
///
/// Default is 20 seconds.
///
/// # Cargo Feature
///
pub fn keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self {
self.inner.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 {
self.inner.max_send_buf_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.inner.enable_connect_protocol();
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.inner.max_header_list_size(max);
self
}

/// Set the timer used in background tasks.
pub fn timer<M>(&mut self, timer: M) -> &mut Self
where
M: Timer + Send + Sync + 'static,
{
self.inner.timer(timer);
self
}
}

impl<B, L> HttpServer<B, L> {
Expand Down

0 comments on commit 7f3642f

Please sign in to comment.