Files
hyper/src/proto/mod.rs
Sean McArthur 5d3c472228 feat(error): revamp hyper::Error type
**The `Error` is now an opaque struct**, which allows for more variants to
be added freely, and the internal representation to change without being
breaking changes.

For inspecting an `Error`, there are several `is_*` methods to check for
certain classes of errors, such as `Error::is_parse()`. The `cause` can
also be inspected, like before. This likely seems like a downgrade, but
more inspection can be added as needed!

The `Error` now knows about more states, which gives much more context
around when a certain error occurs. This is also expressed in the
description and `fmt` messages.

**Most places where a user would provide an error to hyper can now pass
any error type** (`E: Into<Box<std::error::Error>>`). This error is passed
back in relevant places, and can be useful for logging. This should make
it much clearer about what error a user should provide to hyper: any it
feels is relevant!

Closes #1128
Closes #1130
Closes #1431
Closes #1338

BREAKING CHANGE: `Error` is no longer an enum to pattern match over, or
  to construct. Code will need to be updated accordingly.

  For body streams or `Service`s, inference might be unable to determine
  what error type you mean to return. Starting in Rust 1.26, you could
  just label that as `!` if you never return an error.
2018-04-10 14:29:34 -07:00

135 lines
4.1 KiB
Rust

//! Pieces pertaining to the HTTP message protocol.
use bytes::BytesMut;
use http::{HeaderMap, Method, StatusCode, Uri, Version};
use headers;
pub use self::body::Body;
pub use self::chunk::Chunk;
pub(crate) use self::h1::{dispatch, Conn};
pub mod body;
mod chunk;
mod h1;
//mod h2;
/// An Incoming Message head. Includes request/status line, and headers.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MessageHead<S> {
/// HTTP version of the message.
pub version: Version,
/// Subject (request line or status line) of Incoming message.
pub subject: S,
/// Headers of the Incoming message.
pub headers: HeaderMap,
}
/// An incoming request message.
pub type RequestHead = MessageHead<RequestLine>;
#[derive(Debug, Default, PartialEq)]
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)
}
pub(crate) type ServerTransaction = h1::role::Server<h1::role::YesUpgrades>;
//pub type ServerTransaction = h1::role::Server<h1::role::NoUpgrades>;
//pub type ServerUpgradeTransaction = h1::role::Server<h1::role::YesUpgrades>;
pub(crate) type ClientTransaction = h1::role::Client<h1::role::NoUpgrades>;
pub(crate) type ClientUpgradeTransaction = h1::role::Client<h1::role::YesUpgrades>;
pub(crate) trait Http1Transaction {
type Incoming;
type Outgoing: Default;
fn parse(bytes: &mut BytesMut) -> ParseResult<Self::Incoming>;
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decode>;
fn encode(
head: MessageHead<Self::Outgoing>,
body: Option<BodyLength>,
method: &mut Option<Method>,
dst: &mut Vec<u8>,
) -> ::Result<h1::Encoder>;
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
fn should_error_on_parse_eof() -> bool;
fn should_read_first() -> bool;
}
pub(crate) type ParseResult<T> = Result<Option<(MessageHead<T>, usize)>, ::error::Parse>;
#[derive(Debug)]
pub enum BodyLength {
/// Content-Length
Known(u64),
/// Transfer-Encoding: chunked (if h1)
Unknown,
}
#[derive(Debug)]
pub enum Decode {
/// Decode normally.
Normal(h1::Decoder),
/// After this decoder is done, HTTP is done.
Final(h1::Decoder),
/// A header block that should be ignored, like unknown 1xx responses.
Ignore,
}
#[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));
}
#[test]
fn test_expecting_continue() {
let mut headers = HeaderMap::new();
assert!(!expecting_continue(Version::HTTP_10, &headers));
assert!(!expecting_continue(Version::HTTP_11, &headers));
headers.insert("expect", ::http::header::HeaderValue::from_static("100-continue"));
assert!(!expecting_continue(Version::HTTP_10, &headers));
assert!(expecting_continue(Version::HTTP_11, &headers));
}