feat(server): add http1_only configuration

A new configuration http1_only to Builder and Connection are added, which indicates that the upgrading to h2 does not perform when a parsing error occurs.

Fixes #1512.
This commit is contained in:
Yusuke Sasaki
2018-06-05 01:57:19 +09:00
committed by Sean McArthur
parent 785914e77e
commit 14d9246de2
2 changed files with 58 additions and 18 deletions

View File

@@ -37,12 +37,23 @@ use error::{Kind, Parse};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Http { pub struct Http {
exec: Exec, exec: Exec,
http2: bool, mode: ConnectionMode,
keep_alive: bool, keep_alive: bool,
max_buf_size: Option<usize>, max_buf_size: Option<usize>,
pipeline_flush: bool, pipeline_flush: bool,
} }
/// The internal mode of HTTP protocol which indicates the behavior when an parse error occurs.
#[derive(Clone, Debug, PartialEq)]
enum ConnectionMode {
/// Always use HTTP/1 and do not upgrade when an parse error occurs.
H1Only,
/// Always use HTTP/2.
H2Only,
/// Use HTTP/1 and try to upgrade to h2 when an parse error occurs.
Fallback,
}
/// A stream mapping incoming IOs to new services. /// A stream mapping incoming IOs to new services.
/// ///
/// Yields `Connecting`s that are futures that should be put on a reactor. /// Yields `Connecting`s that are futures that should be put on a reactor.
@@ -94,6 +105,7 @@ where
S::ResBody, S::ResBody,
>, >,
>>, >>,
fallback: bool,
} }
/// Deconstructed parts of a `Connection`. /// Deconstructed parts of a `Connection`.
@@ -126,18 +138,34 @@ impl Http {
pub fn new() -> Http { pub fn new() -> Http {
Http { Http {
exec: Exec::Default, exec: Exec::Default,
http2: false, mode: ConnectionMode::Fallback,
keep_alive: true, keep_alive: true,
max_buf_size: None, max_buf_size: None,
pipeline_flush: false, pipeline_flush: false,
} }
} }
/// Sets whether HTTP1 is required.
///
/// Default is false
pub fn http1_only(&mut self, val: bool) -> &mut Self {
if val {
self.mode = ConnectionMode::H1Only;
} else {
self.mode = ConnectionMode::Fallback;
}
self
}
/// Sets whether HTTP2 is required. /// Sets whether HTTP2 is required.
/// ///
/// Default is false /// Default is false
pub fn http2_only(&mut self, val: bool) -> &mut Self { pub fn http2_only(&mut self, val: bool) -> &mut Self {
self.http2 = val; if val {
self.mode = ConnectionMode::H2Only;
} else {
self.mode = ConnectionMode::Fallback;
}
self self
} }
@@ -230,25 +258,29 @@ impl Http {
Bd: Payload, Bd: Payload,
I: AsyncRead + AsyncWrite, I: AsyncRead + AsyncWrite,
{ {
let either = if !self.http2 { let either = match self.mode {
let mut conn = proto::Conn::new(io); ConnectionMode::H1Only | ConnectionMode::Fallback => {
if !self.keep_alive { let mut conn = proto::Conn::new(io);
conn.disable_keep_alive(); if !self.keep_alive {
conn.disable_keep_alive();
}
conn.set_flush_pipeline(self.pipeline_flush);
if let Some(max) = self.max_buf_size {
conn.set_max_buf_size(max);
}
let sd = proto::h1::dispatch::Server::new(service);
Either::A(proto::h1::Dispatcher::new(sd, conn))
} }
conn.set_flush_pipeline(self.pipeline_flush); ConnectionMode::H2Only => {
if let Some(max) = self.max_buf_size { let rewind_io = Rewind::new(io);
conn.set_max_buf_size(max); let h2 = proto::h2::Server::new(rewind_io, service, self.exec.clone());
Either::B(h2)
} }
let sd = proto::h1::dispatch::Server::new(service);
Either::A(proto::h1::Dispatcher::new(sd, conn))
} else {
let rewind_io = Rewind::new(io);
let h2 = proto::h2::Server::new(rewind_io, service, self.exec.clone());
Either::B(h2)
}; };
Connection { Connection {
conn: Some(either), conn: Some(either),
fallback: self.mode == ConnectionMode::Fallback,
} }
} }
@@ -385,7 +417,7 @@ where
Err(e) => { Err(e) => {
debug!("error polling connection protocol without shutdown: {}", e); debug!("error polling connection protocol without shutdown: {}", e);
match *e.kind() { match *e.kind() {
Kind::Parse(Parse::VersionH2) => { Kind::Parse(Parse::VersionH2) if self.fallback => {
self.upgrade_h2(); self.upgrade_h2();
continue; continue;
} }
@@ -435,7 +467,7 @@ where
Err(e) => { Err(e) => {
debug!("error polling connection protocol: {}", e); debug!("error polling connection protocol: {}", e);
match *e.kind() { match *e.kind() {
Kind::Parse(Parse::VersionH2) => { Kind::Parse(Parse::VersionH2) if self.fallback => {
self.upgrade_h2(); self.upgrade_h2();
continue; continue;
} }

View File

@@ -167,6 +167,14 @@ impl<I> Builder<I> {
} }
} }
/// Sets whether HTTP/1 is required.
///
/// Default is `false`.
pub fn http1_only(mut self, val: bool) -> Self {
self.protocol.http1_only(val);
self
}
/// Sets whether HTTP/2 is required. /// Sets whether HTTP/2 is required.
/// ///
/// Default is `false`. /// Default is `false`.