feat(http1): Add higher-level HTTP upgrade support to Client and Server (#1563)
- Adds `Body::on_upgrade()` that returns an `OnUpgrade` future.
- Adds `hyper::upgrade` module containing types for dealing with
  upgrades.
- Adds `server::conn::Connection::with_upgrades()` method to enable
  these upgrades when using lower-level API (because of a missing
  `Send` bound on the transport generic).
- Client connections are automatically enabled.
- Optimizes request parsing, to make up for extra work to look for
  upgrade requests.
  - Returns a smaller `DecodedLength` type instead of the fatter
    `Decoder`, which should also allow a couple fewer branches.
  - Removes the `Decode::Ignore` wrapper enum, and instead ignoring
    1xx responses is handled directly in the response parsing code.
Ref #1563 
Closes #1395
			
			
This commit is contained in:
		
							
								
								
									
										120
									
								
								src/proto/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								src/proto/mod.rs
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| //! Pieces pertaining to the HTTP message protocol. | ||||
| use http::{HeaderMap, Method, StatusCode, Uri, Version}; | ||||
|  | ||||
| pub(crate) use self::h1::{dispatch, Conn, ClientTransaction, ClientUpgradeTransaction, ServerTransaction}; | ||||
| pub(crate) use self::h1::{dispatch, Conn, ServerTransaction}; | ||||
| use self::body_length::DecodedLength; | ||||
|  | ||||
| pub(crate) mod h1; | ||||
| pub(crate) mod h2; | ||||
|  | ||||
|  | ||||
| /// An Incoming Message head. Includes request/status line, and headers. | ||||
| #[derive(Clone, Debug, Default, PartialEq)] | ||||
| pub struct MessageHead<S> { | ||||
| @@ -27,34 +27,6 @@ pub struct RequestLine(pub Method, pub Uri); | ||||
| /// An incoming response message. | ||||
| pub type ResponseHead = MessageHead<StatusCode>; | ||||
|  | ||||
| /* | ||||
| impl<S> MessageHead<S> { | ||||
|     pub fn should_keep_alive(&self) -> bool { | ||||
|         should_keep_alive(self.version, &self.headers) | ||||
|     } | ||||
|  | ||||
|     pub fn expecting_continue(&self) -> bool { | ||||
|         expecting_continue(self.version, &self.headers) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Checks if a connection should be kept alive. | ||||
| #[inline] | ||||
| pub fn should_keep_alive(version: Version, headers: &HeaderMap) -> bool { | ||||
|     if version == Version::HTTP_10 { | ||||
|         headers::connection_keep_alive(headers) | ||||
|     } else { | ||||
|         !headers::connection_close(headers) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Checks if a connection is expecting a `100 Continue` before sending its body. | ||||
| #[inline] | ||||
| pub fn expecting_continue(version: Version, headers: &HeaderMap) -> bool { | ||||
|     version == Version::HTTP_11 && headers::expect_continue(headers) | ||||
| } | ||||
| */ | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum BodyLength { | ||||
|     /// Content-Length | ||||
| @@ -63,32 +35,72 @@ pub enum BodyLength { | ||||
|     Unknown, | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[test] | ||||
| fn test_should_keep_alive() { | ||||
|     let mut headers = HeaderMap::new(); | ||||
|  | ||||
|     assert!(!should_keep_alive(Version::HTTP_10, &headers)); | ||||
|     assert!(should_keep_alive(Version::HTTP_11, &headers)); | ||||
|  | ||||
|     headers.insert("connection", ::http::header::HeaderValue::from_static("close")); | ||||
|     assert!(!should_keep_alive(Version::HTTP_10, &headers)); | ||||
|     assert!(!should_keep_alive(Version::HTTP_11, &headers)); | ||||
|  | ||||
|     headers.insert("connection", ::http::header::HeaderValue::from_static("keep-alive")); | ||||
|     assert!(should_keep_alive(Version::HTTP_10, &headers)); | ||||
|     assert!(should_keep_alive(Version::HTTP_11, &headers)); | ||||
| /// Status of when an Disaptcher future completes. | ||||
| pub(crate) enum Dispatched { | ||||
|     /// Dispatcher completely shutdown connection. | ||||
|     Shutdown, | ||||
|     /// Dispatcher has pending upgrade, and so did not shutdown. | ||||
|     Upgrade(::upgrade::Pending), | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_expecting_continue() { | ||||
|     let mut headers = HeaderMap::new(); | ||||
| /// A separate module to encapsulate the invariants of the DecodedLength type. | ||||
| mod body_length { | ||||
|     use std::fmt; | ||||
|  | ||||
|     assert!(!expecting_continue(Version::HTTP_10, &headers)); | ||||
|     assert!(!expecting_continue(Version::HTTP_11, &headers)); | ||||
|     #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||||
|     pub(crate) struct DecodedLength(u64); | ||||
|  | ||||
|     headers.insert("expect", ::http::header::HeaderValue::from_static("100-continue")); | ||||
|     assert!(!expecting_continue(Version::HTTP_10, &headers)); | ||||
|     assert!(expecting_continue(Version::HTTP_11, &headers)); | ||||
|     const MAX_LEN: u64 = ::std::u64::MAX - 2; | ||||
|  | ||||
|     impl DecodedLength { | ||||
|         pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX); | ||||
|         pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1); | ||||
|         pub(crate) const ZERO: DecodedLength = DecodedLength(0); | ||||
|  | ||||
|         #[cfg(test)] | ||||
|         pub(crate) fn new(len: u64) -> Self { | ||||
|             debug_assert!(len <= MAX_LEN); | ||||
|             DecodedLength(len) | ||||
|         } | ||||
|  | ||||
|         /// Takes the length as a content-length without other checks. | ||||
|         /// | ||||
|         /// Should only be called if previously confirmed this isn't | ||||
|         /// CLOSE_DELIMITED or CHUNKED. | ||||
|         #[inline] | ||||
|         pub(crate) fn danger_len(self) -> u64 { | ||||
|             debug_assert!(self.0 < Self::CHUNKED.0); | ||||
|             self.0 | ||||
|         } | ||||
|  | ||||
|         /// Converts to an Option<u64> representing a Known or Unknown length. | ||||
|         pub(crate) fn into_opt(self) -> Option<u64> { | ||||
|             match self { | ||||
|                 DecodedLength::CHUNKED | | ||||
|                 DecodedLength::CLOSE_DELIMITED => None, | ||||
|                 DecodedLength(known) => Some(known) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// Checks the `u64` is within the maximum allowed for content-length. | ||||
|         pub(crate) fn checked_new(len: u64) -> Result<Self, ::error::Parse> { | ||||
|             if len <= MAX_LEN { | ||||
|                 Ok(DecodedLength(len)) | ||||
|             } else { | ||||
|                 warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN); | ||||
|                 Err(::error::Parse::TooLarge) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl fmt::Display for DecodedLength { | ||||
|         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|             match *self { | ||||
|                 DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"), | ||||
|                 DecodedLength::CHUNKED => f.write_str("chunked encoding"), | ||||
|                 DecodedLength::ZERO => f.write_str("empty"), | ||||
|                 DecodedLength(n) => write!(f, "content-length ({} bytes)", n), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user