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:
		| @@ -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); | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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, | ||||
|         }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user