feat(server): support HTTP1 and HTTP2 automatically

If an HTTP/1 connection has a parse error, but it starts with the HTTP2 preface, converts the connection automatically into an HTTP2 server connection.

Closes #1486
This commit is contained in:
estk
2018-05-10 14:23:42 -07:00
committed by Sean McArthur
parent 18f4dd2406
commit bc6af88a32
9 changed files with 302 additions and 16 deletions

View File

@@ -12,6 +12,7 @@ use proto::{BodyLength, Decode, Http1Transaction, MessageHead};
use super::io::{Buffered};
use super::{EncodedBuf, Encoder, Decoder};
const H2_PREFACE: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
/// This handles a connection, which will have been established over an
/// `AsyncRead + AsyncWrite` (like a socket), and will likely include multiple
@@ -107,6 +108,11 @@ where I: AsyncRead + AsyncWrite,
T::should_error_on_parse_eof() && !self.state.is_idle()
}
fn has_h2_prefix(&self) -> bool {
let read_buf = self.io.read_buf();
read_buf.len() >= 24 && read_buf[..24] == *H2_PREFACE
}
pub fn read_head(&mut self) -> Poll<Option<(MessageHead<T::Incoming>, bool)>, ::Error> {
debug_assert!(self.can_read_head());
trace!("Conn::read_head");
@@ -124,6 +130,7 @@ where I: AsyncRead + AsyncWrite,
self.io.consume_leading_lines();
let was_mid_parse = e.is_parse() || !self.io.read_buf().is_empty();
return if was_mid_parse || must_error {
// We check if the buf contains the h2 Preface
debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len());
self.on_parse_error(e)
.map(|()| Async::NotReady)
@@ -529,8 +536,12 @@ where I: AsyncRead + AsyncWrite,
// - Client: there is nothing we can do
// - Server: if Response hasn't been written yet, we can send a 4xx response
fn on_parse_error(&mut self, err: ::Error) -> ::Result<()> {
match self.state.writing {
Writing::Init => {
if self.has_h2_prefix() {
return Err(::Error::new_version_h2())
}
if let Some(msg) = T::on_error(&err) {
self.write_head(msg, None);
self.state.error = Some(err);

View File

@@ -332,6 +332,9 @@ impl<S> Server<S> where S: Service {
service: service,
}
}
pub fn into_service(self) -> S {
self.service
}
}
impl<S, Bs> Dispatch for Server<S>

View File

@@ -186,14 +186,14 @@ where
use ::error::{Kind, Parse};
let status = match *err.kind() {
Kind::Parse(Parse::Method) |
Kind::Parse(Parse::Version) |
Kind::Parse(Parse::Header) |
Kind::Parse(Parse::Uri) => {
Kind::Parse(Parse::Uri) |
Kind::Parse(Parse::Version) => {
StatusCode::BAD_REQUEST
},
Kind::Parse(Parse::TooLarge) => {
StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE
}
},
_ => return None,
};