refactor(error): improve organization of Error kinds
- Placed all cases of "unexpected bytes" errors into the `UnexpectedMessage` variant. - Placed all cases of "unexpected EOF" errors into the `IncompleteMessage` variant. Description is now generic about "connection closed before message completed", instead of mentioning "request" or "response. - Added `Error::is_incomplete_message()` accessor to help checking for unexpected closures. - Renamed some variants to be clearer when viewing the `Debug` format. - Collected all "user" errors into an internal `User` enum, to prevent forgetting to update the `is_user()` method.
This commit is contained in:
@@ -235,7 +235,7 @@ where
|
|||||||
},
|
},
|
||||||
Err(_req) => {
|
Err(_req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = ::Error::new_canceled(Some("connection was not ready"));
|
let err = ::Error::new_canceled().with("connection was not ready");
|
||||||
Either::B(future::err(err))
|
Either::B(future::err(err))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -262,7 +262,7 @@ where
|
|||||||
},
|
},
|
||||||
Err(req) => {
|
Err(req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = ::Error::new_canceled(Some("connection was not ready"));
|
let err = ::Error::new_canceled().with("connection was not ready");
|
||||||
Either::B(future::err((err, Some(req))))
|
Either::B(future::err((err, Some(req))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,7 +322,7 @@ where
|
|||||||
},
|
},
|
||||||
Err(req) => {
|
Err(req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = ::Error::new_canceled(Some("connection was not ready"));
|
let err = ::Error::new_canceled().with("connection was not ready");
|
||||||
Either::B(future::err((err, Some(req))))
|
Either::B(future::err((err, Some(req))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ struct Envelope<T, U>(Option<(T, Callback<T, U>)>);
|
|||||||
impl<T, U> Drop for Envelope<T, U> {
|
impl<T, U> Drop for Envelope<T, U> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some((val, cb)) = self.0.take() {
|
if let Some((val, cb)) = self.0.take() {
|
||||||
let _ = cb.send(Err((::Error::new_canceled(None::<::Error>), Some(val))));
|
let _ = cb.send(Err((::Error::new_canceled().with("connection closed"), Some(val))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -498,7 +498,7 @@ where C: Connect + Sync + 'static,
|
|||||||
let connecting = match pool.connecting(&pool_key, ver) {
|
let connecting = match pool.connecting(&pool_key, ver) {
|
||||||
Some(lock) => lock,
|
Some(lock) => lock,
|
||||||
None => {
|
None => {
|
||||||
let canceled = ::Error::new_canceled(Some("HTTP/2 connection in progress"));
|
let canceled = ::Error::new_canceled().with("HTTP/2 connection in progress");
|
||||||
return Either::B(future::err(canceled));
|
return Either::B(future::err(canceled));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -517,7 +517,7 @@ where C: Connect + Sync + 'static,
|
|||||||
None => {
|
None => {
|
||||||
// Another connection has already upgraded,
|
// Another connection has already upgraded,
|
||||||
// the pool checkout should finish up for us.
|
// the pool checkout should finish up for us.
|
||||||
let canceled = ::Error::new_canceled(Some("ALPN upgraded to HTTP/2"));
|
let canceled = ::Error::new_canceled().with("ALPN upgraded to HTTP/2");
|
||||||
return Either::B(future::err(canceled));
|
return Either::B(future::err(canceled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,14 +577,14 @@ impl<T: Poolable> Checkout<T> {
|
|||||||
if value.is_open() {
|
if value.is_open() {
|
||||||
Ok(Async::Ready(Some(self.pool.reuse(&self.key, value))))
|
Ok(Async::Ready(Some(self.pool.reuse(&self.key, value))))
|
||||||
} else {
|
} else {
|
||||||
Err(::Error::new_canceled(Some(CANCELED)))
|
Err(::Error::new_canceled().with(CANCELED))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::NotReady) => {
|
||||||
self.waiter = Some(rx);
|
self.waiter = Some(rx);
|
||||||
Ok(Async::NotReady)
|
Ok(Async::NotReady)
|
||||||
},
|
},
|
||||||
Err(_canceled) => Err(::Error::new_canceled(Some(CANCELED))),
|
Err(_canceled) => Err(::Error::new_canceled().with(CANCELED)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
@@ -654,7 +654,7 @@ impl<T: Poolable> Future for Checkout<T> {
|
|||||||
if let Some(pooled) = self.checkout() {
|
if let Some(pooled) = self.checkout() {
|
||||||
Ok(Async::Ready(pooled))
|
Ok(Async::Ready(pooled))
|
||||||
} else if !self.pool.is_enabled() {
|
} else if !self.pool.is_enabled() {
|
||||||
Err(::Error::new_canceled(Some("pool is disabled")))
|
Err(::Error::new_canceled().with("pool is disabled"))
|
||||||
} else {
|
} else {
|
||||||
Ok(Async::NotReady)
|
Ok(Async::NotReady)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,10 +103,7 @@ fn conn_reset_after_write() {
|
|||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}).map_err(|e: ::std::io::Error| panic!("srv2 poll_fn error: {}", e));
|
}).map_err(|e: ::std::io::Error| panic!("srv2 poll_fn error: {}", e));
|
||||||
let err = rt.block_on(res2.join(srv2)).expect_err("res2");
|
let err = rt.block_on(res2.join(srv2)).expect_err("res2");
|
||||||
match err.kind() {
|
assert!(err.is_incomplete_message(), "{:?}", err);
|
||||||
&::error::Kind::Incomplete => (),
|
|
||||||
other => panic!("expected Incomplete, found {:?}", other)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
217
src/error.rs
217
src/error.rs
@@ -25,14 +25,15 @@ struct ErrorImpl {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) enum Kind {
|
pub(crate) enum Kind {
|
||||||
Parse(Parse),
|
Parse(Parse),
|
||||||
|
User(User),
|
||||||
/// A message reached EOF, but is not complete.
|
/// A message reached EOF, but is not complete.
|
||||||
Incomplete,
|
IncompleteMessage,
|
||||||
/// A client connection received a response when not waiting for one.
|
/// A connection received a message (or bytes) when not waiting for one.
|
||||||
MismatchedResponse,
|
UnexpectedMessage,
|
||||||
/// A pending item was dropped before ever being processed.
|
/// A pending item was dropped before ever being processed.
|
||||||
Canceled,
|
Canceled,
|
||||||
/// Indicates a connection is closed.
|
/// Indicates a channel (client or body sender) is closed.
|
||||||
Closed,
|
ChannelClosed,
|
||||||
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
||||||
Io,
|
Io,
|
||||||
/// Error occurred while connecting.
|
/// Error occurred while connecting.
|
||||||
@@ -42,22 +43,40 @@ pub(crate) enum Kind {
|
|||||||
Listen,
|
Listen,
|
||||||
/// Error accepting on an Incoming stream.
|
/// Error accepting on an Incoming stream.
|
||||||
Accept,
|
Accept,
|
||||||
/// Error calling user's NewService::new_service().
|
|
||||||
NewService,
|
|
||||||
/// Error from future of user's Service::call().
|
|
||||||
Service,
|
|
||||||
/// Error while reading a body from connection.
|
/// Error while reading a body from connection.
|
||||||
Body,
|
Body,
|
||||||
/// Error while writing a body to connection.
|
/// Error while writing a body to connection.
|
||||||
BodyWrite,
|
BodyWrite,
|
||||||
/// Error calling user's Payload::poll_data().
|
|
||||||
BodyUser,
|
|
||||||
/// Error calling AsyncWrite::shutdown()
|
/// Error calling AsyncWrite::shutdown()
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
|
||||||
/// A general error from h2.
|
/// A general error from h2.
|
||||||
Http2,
|
Http2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum Parse {
|
||||||
|
Method,
|
||||||
|
Version,
|
||||||
|
VersionH2,
|
||||||
|
Uri,
|
||||||
|
Header,
|
||||||
|
TooLarge,
|
||||||
|
Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum User {
|
||||||
|
/// Error calling user's Payload::poll_data().
|
||||||
|
Body,
|
||||||
|
/// Error calling user's MakeService.
|
||||||
|
MakeService,
|
||||||
|
/// Error from future of user's Service.
|
||||||
|
Service,
|
||||||
|
/// User tried to send a certain header in an unexpected context.
|
||||||
|
///
|
||||||
|
/// For example, sending both `content-length` and `transfer-encoding`.
|
||||||
|
UnexpectedHeader,
|
||||||
/// User tried to create a Request with bad version.
|
/// User tried to create a Request with bad version.
|
||||||
UnsupportedVersion,
|
UnsupportedVersion,
|
||||||
/// User tried to create a CONNECT Request with the Client.
|
/// User tried to create a CONNECT Request with the Client.
|
||||||
@@ -77,26 +96,6 @@ pub(crate) enum Kind {
|
|||||||
Execute,
|
Execute,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(crate) enum Parse {
|
|
||||||
Method,
|
|
||||||
Version,
|
|
||||||
VersionH2,
|
|
||||||
Uri,
|
|
||||||
Header,
|
|
||||||
TooLarge,
|
|
||||||
Status,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum User {
|
|
||||||
VersionNotSupported,
|
|
||||||
MethodNotSupported,
|
|
||||||
InvalidRequestUri,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
/// Returns true if this was an HTTP parse error.
|
/// Returns true if this was an HTTP parse error.
|
||||||
pub fn is_parse(&self) -> bool {
|
pub fn is_parse(&self) -> bool {
|
||||||
@@ -109,16 +108,7 @@ impl Error {
|
|||||||
/// Returns true if this error was caused by user code.
|
/// Returns true if this error was caused by user code.
|
||||||
pub fn is_user(&self) -> bool {
|
pub fn is_user(&self) -> bool {
|
||||||
match self.inner.kind {
|
match self.inner.kind {
|
||||||
Kind::BodyUser |
|
Kind::User(_) => true,
|
||||||
Kind::NewService |
|
|
||||||
Kind::Service |
|
|
||||||
Kind::Closed |
|
|
||||||
Kind::UnsupportedVersion |
|
|
||||||
Kind::UnsupportedRequestMethod |
|
|
||||||
Kind::UnsupportedStatusCode |
|
|
||||||
Kind::AbsoluteUriRequired |
|
|
||||||
Kind::NoUpgrade |
|
|
||||||
Kind::Execute => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +120,7 @@ impl Error {
|
|||||||
|
|
||||||
/// Returns true if a sender's channel is closed.
|
/// Returns true if a sender's channel is closed.
|
||||||
pub fn is_closed(&self) -> bool {
|
pub fn is_closed(&self) -> bool {
|
||||||
self.inner.kind == Kind::Closed
|
self.inner.kind == Kind::ChannelClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this was an error from `Connect`.
|
/// Returns true if this was an error from `Connect`.
|
||||||
@@ -138,6 +128,11 @@ impl Error {
|
|||||||
self.inner.kind == Kind::Connect
|
self.inner.kind == Kind::Connect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the connection closed before a message could complete.
|
||||||
|
pub fn is_incomplete_message(&self) -> bool {
|
||||||
|
self.inner.kind == Kind::IncompleteMessage
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg_attr(error_source, deprecated(note = "use Error::source instead"))]
|
#[cfg_attr(error_source, deprecated(note = "use Error::source instead"))]
|
||||||
pub fn cause2(&self) -> Option<&(StdError + 'static + Sync + Send)> {
|
pub fn cause2(&self) -> Option<&(StdError + 'static + Sync + Send)> {
|
||||||
@@ -149,15 +144,20 @@ impl Error {
|
|||||||
self.inner.cause
|
self.inner.cause
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(kind: Kind, cause: Option<Cause>) -> Error {
|
pub(crate) fn new(kind: Kind) -> Error {
|
||||||
Error {
|
Error {
|
||||||
inner: Box::new(ErrorImpl {
|
inner: Box::new(ErrorImpl {
|
||||||
kind,
|
kind,
|
||||||
cause,
|
cause: None,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
|
||||||
|
self.inner.cause = Some(cause.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn kind(&self) -> &Kind {
|
pub(crate) fn kind(&self) -> &Kind {
|
||||||
&self.inner.kind
|
&self.inner.kind
|
||||||
}
|
}
|
||||||
@@ -201,114 +201,122 @@ impl Error {
|
|||||||
h2::Reason::INTERNAL_ERROR
|
h2::Reason::INTERNAL_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_canceled<E: Into<Cause>>(cause: Option<E>) -> Error {
|
pub(crate) fn new_canceled() -> Error {
|
||||||
Error::new(Kind::Canceled, cause.map(Into::into))
|
Error::new(Kind::Canceled)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_incomplete() -> Error {
|
pub(crate) fn new_incomplete() -> Error {
|
||||||
Error::new(Kind::Incomplete, None)
|
Error::new(Kind::IncompleteMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_too_large() -> Error {
|
pub(crate) fn new_too_large() -> Error {
|
||||||
Error::new(Kind::Parse(Parse::TooLarge), None)
|
Error::new(Kind::Parse(Parse::TooLarge))
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new_header() -> Error {
|
|
||||||
Error::new(Kind::Parse(Parse::Header), None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_version_h2() -> Error {
|
pub(crate) fn new_version_h2() -> Error {
|
||||||
Error::new(Kind::Parse(Parse::VersionH2), None)
|
Error::new(Kind::Parse(Parse::VersionH2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_mismatched_response() -> Error {
|
pub(crate) fn new_unexpected_message() -> Error {
|
||||||
Error::new(Kind::MismatchedResponse, None)
|
Error::new(Kind::UnexpectedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_io(cause: io::Error) -> Error {
|
pub(crate) fn new_io(cause: io::Error) -> Error {
|
||||||
Error::new(Kind::Io, Some(cause.into()))
|
Error::new(Kind::Io).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
pub(crate) fn new_listen<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_listen<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Listen, Some(cause.into()))
|
Error::new(Kind::Listen).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Accept, Some(cause.into()))
|
Error::new(Kind::Accept).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Connect, Some(cause.into()))
|
Error::new(Kind::Connect).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_closed() -> Error {
|
pub(crate) fn new_closed() -> Error {
|
||||||
Error::new(Kind::Closed, None)
|
Error::new(Kind::ChannelClosed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_body<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_body<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Body, Some(cause.into()))
|
Error::new(Kind::Body).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::BodyWrite, Some(cause.into()))
|
Error::new(Kind::BodyWrite).with(cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_user(user: User) -> Error {
|
||||||
|
Error::new(Kind::User(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_user_header() -> Error {
|
||||||
|
Error::new_user(User::UnexpectedHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_unsupported_version() -> Error {
|
pub(crate) fn new_user_unsupported_version() -> Error {
|
||||||
Error::new(Kind::UnsupportedVersion, None)
|
Error::new_user(User::UnsupportedVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_unsupported_request_method() -> Error {
|
pub(crate) fn new_user_unsupported_request_method() -> Error {
|
||||||
Error::new(Kind::UnsupportedRequestMethod, None)
|
Error::new_user(User::UnsupportedRequestMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_unsupported_status_code() -> Error {
|
pub(crate) fn new_user_unsupported_status_code() -> Error {
|
||||||
Error::new(Kind::UnsupportedStatusCode, None)
|
Error::new_user(User::UnsupportedStatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_absolute_uri_required() -> Error {
|
pub(crate) fn new_user_absolute_uri_required() -> Error {
|
||||||
Error::new(Kind::AbsoluteUriRequired, None)
|
Error::new_user(User::AbsoluteUriRequired)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_no_upgrade() -> Error {
|
pub(crate) fn new_user_no_upgrade() -> Error {
|
||||||
Error::new(Kind::NoUpgrade, None)
|
Error::new_user(User::NoUpgrade)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_manual_upgrade() -> Error {
|
pub(crate) fn new_user_manual_upgrade() -> Error {
|
||||||
Error::new(Kind::ManualUpgrade, None)
|
Error::new_user(User::ManualUpgrade)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_new_service<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::NewService, Some(cause.into()))
|
Error::new_user(User::MakeService).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Service, Some(cause.into()))
|
Error::new_user(User::Service).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::BodyUser, Some(cause.into()))
|
Error::new_user(User::Body).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_shutdown(cause: io::Error) -> Error {
|
pub(crate) fn new_shutdown(cause: io::Error) -> Error {
|
||||||
Error::new(Kind::Shutdown, Some(Box::new(cause)))
|
Error::new(Kind::Shutdown).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_execute<E: Into<Cause>>(cause: E) -> Error {
|
pub(crate) fn new_execute<E: Into<Cause>>(cause: E) -> Error {
|
||||||
Error::new(Kind::Execute, Some(cause.into()))
|
Error::new_user(User::Execute).with(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_h2(cause: ::h2::Error) -> Error {
|
pub(crate) fn new_h2(cause: ::h2::Error) -> Error {
|
||||||
Error::new(Kind::Http2, Some(Box::new(cause)))
|
if cause.is_io() {
|
||||||
|
Error::new_io(cause.into_io().expect("h2::Error::is_io"))
|
||||||
|
} else {
|
||||||
|
Error::new(Kind::Http2).with(cause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Error {
|
impl fmt::Debug for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut f = f.debug_struct("Error");
|
let mut f = f.debug_tuple("Error");
|
||||||
f.field("kind", &self.inner.kind);
|
f.field(&self.inner.kind);
|
||||||
if let Some(ref cause) = self.inner.cause {
|
if let Some(ref cause) = self.inner.cause {
|
||||||
f.field("cause", cause);
|
f.field(cause);
|
||||||
}
|
}
|
||||||
f.finish()
|
f.finish()
|
||||||
}
|
}
|
||||||
@@ -327,37 +335,38 @@ impl fmt::Display for Error {
|
|||||||
impl StdError for Error {
|
impl StdError for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self.inner.kind {
|
match self.inner.kind {
|
||||||
Kind::Parse(Parse::Method) => "invalid Method specified",
|
Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
|
||||||
Kind::Parse(Parse::Version) => "invalid HTTP version specified",
|
Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
|
||||||
Kind::Parse(Parse::VersionH2) => "invalid HTTP version specified (Http2)",
|
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
|
||||||
Kind::Parse(Parse::Uri) => "invalid URI",
|
Kind::Parse(Parse::Uri) => "invalid URI",
|
||||||
Kind::Parse(Parse::Header) => "invalid Header provided",
|
Kind::Parse(Parse::Header) => "invalid HTTP header parsed",
|
||||||
Kind::Parse(Parse::TooLarge) => "message head is too large",
|
Kind::Parse(Parse::TooLarge) => "message head is too large",
|
||||||
Kind::Parse(Parse::Status) => "invalid Status provided",
|
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
|
||||||
Kind::Incomplete => "parsed HTTP message from remote is incomplete",
|
Kind::IncompleteMessage => "connection closed before message completed",
|
||||||
Kind::MismatchedResponse => "response received without matching request",
|
Kind::UnexpectedMessage => "received unexpected message from connection",
|
||||||
Kind::Closed => "connection closed",
|
Kind::ChannelClosed => "channel closed",
|
||||||
Kind::Connect => "an error occurred trying to connect",
|
Kind::Connect => "error trying to connect",
|
||||||
Kind::Canceled => "an operation was canceled internally before starting",
|
Kind::Canceled => "operation was canceled",
|
||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
Kind::Listen => "error creating server listener",
|
Kind::Listen => "error creating server listener",
|
||||||
Kind::Accept => "error accepting connection",
|
Kind::Accept => "error accepting connection",
|
||||||
Kind::NewService => "calling user's new_service failed",
|
|
||||||
Kind::Service => "error from user's server service",
|
|
||||||
Kind::Body => "error reading a body from connection",
|
Kind::Body => "error reading a body from connection",
|
||||||
Kind::BodyWrite => "error writing a body to connection",
|
Kind::BodyWrite => "error writing a body to connection",
|
||||||
Kind::BodyUser => "error from user's Payload stream",
|
|
||||||
Kind::Shutdown => "error shutting down connection",
|
Kind::Shutdown => "error shutting down connection",
|
||||||
Kind::Http2 => "http2 general error",
|
Kind::Http2 => "http2 error",
|
||||||
Kind::UnsupportedVersion => "request has unsupported HTTP version",
|
Kind::Io => "connection error",
|
||||||
Kind::UnsupportedRequestMethod => "request has unsupported HTTP method",
|
|
||||||
Kind::UnsupportedStatusCode => "response has 1xx status code, not supported by server",
|
|
||||||
Kind::AbsoluteUriRequired => "client requires absolute-form URIs",
|
|
||||||
Kind::NoUpgrade => "no upgrade available",
|
|
||||||
Kind::ManualUpgrade => "upgrade expected but low level API in use",
|
|
||||||
Kind::Execute => "executor failed to spawn task",
|
|
||||||
|
|
||||||
Kind::Io => "an IO error occurred",
|
Kind::User(User::Body) => "error from user's Payload stream",
|
||||||
|
Kind::User(User::MakeService) => "error from user's MakeService",
|
||||||
|
Kind::User(User::Service) => "error from user's Service",
|
||||||
|
Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
|
||||||
|
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
|
||||||
|
Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
|
||||||
|
Kind::User(User::UnsupportedStatusCode) => "response has 1xx status code, not supported by server",
|
||||||
|
Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
|
||||||
|
Kind::User(User::NoUpgrade) => "no upgrade available",
|
||||||
|
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
|
||||||
|
Kind::User(User::Execute) => "executor failed to spawn task",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +392,7 @@ impl StdError for Error {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl From<Parse> for Error {
|
impl From<Parse> for Error {
|
||||||
fn from(err: Parse) -> Error {
|
fn from(err: Parse) -> Error {
|
||||||
Error::new(Kind::Parse(err), None)
|
Error::new(Kind::Parse(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,8 +447,14 @@ impl AssertSendSync for Error {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::mem;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_size_of() {
|
||||||
|
assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn h2_reason_unknown() {
|
fn h2_reason_unknown() {
|
||||||
let closed = Error::new_closed();
|
let closed = Error::new_closed();
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
|
|
||||||
pub fn can_read_head(&self) -> bool {
|
pub fn can_read_head(&self) -> bool {
|
||||||
match self.state.reading {
|
match self.state.reading {
|
||||||
//Reading::Init => true,
|
|
||||||
Reading::Init => {
|
Reading::Init => {
|
||||||
if T::should_read_first() {
|
if T::should_read_first() {
|
||||||
true
|
true
|
||||||
@@ -228,13 +227,19 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_keep_alive(&mut self) -> Result<(), ::Error> {
|
pub fn wants_read_again(&mut self) -> bool {
|
||||||
|
let ret = self.state.notify_read;
|
||||||
|
self.state.notify_read = false;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_keep_alive(&mut self) -> Poll<(), ::Error> {
|
||||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||||
|
|
||||||
if self.is_mid_message() {
|
if self.is_mid_message() {
|
||||||
self.mid_message_detect_eof().map_err(::Error::new_io)
|
self.mid_message_detect_eof()
|
||||||
} else {
|
} else {
|
||||||
self.require_empty_read().map_err(::Error::new_io)
|
self.require_empty_read()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,87 +250,65 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wants_read_again(&mut self) -> bool {
|
|
||||||
let ret = self.state.notify_read;
|
|
||||||
self.state.notify_read = false;
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will check to make sure the io object read is empty.
|
// This will check to make sure the io object read is empty.
|
||||||
//
|
//
|
||||||
// This should only be called for Clients wanting to enter the idle
|
// This should only be called for Clients wanting to enter the idle
|
||||||
// state.
|
// state.
|
||||||
fn require_empty_read(&mut self) -> io::Result<()> {
|
fn require_empty_read(&mut self) -> Poll<(), ::Error> {
|
||||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||||
|
debug_assert!(!self.is_mid_message());
|
||||||
|
debug_assert!(T::is_client());
|
||||||
|
|
||||||
if !self.io.read_buf().is_empty() {
|
if !self.io.read_buf().is_empty() {
|
||||||
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected bytes after message ended"))
|
return Err(::Error::new_unexpected_message());
|
||||||
} else {
|
|
||||||
match self.try_io_read()? {
|
|
||||||
Async::Ready(0) => {
|
|
||||||
// case handled in try_io_read
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Async::Ready(n) => {
|
|
||||||
debug!("received {} bytes on an idle connection", n);
|
|
||||||
let desc = if self.state.is_idle() {
|
|
||||||
"unexpected bytes after message ended"
|
|
||||||
} else {
|
|
||||||
"unexpected bytes before writing message"
|
|
||||||
};
|
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, desc))
|
|
||||||
},
|
|
||||||
Async::NotReady => {
|
|
||||||
trace!("read_keep_alive check: ok, nothing to read yet");
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let num_read = try_ready!(self.force_io_read().map_err(::Error::new_io));
|
||||||
|
|
||||||
|
if num_read == 0 {
|
||||||
|
let ret = if self.should_error_on_eof() {
|
||||||
|
trace!("found unexpected EOF on busy connection: {:?}", self.state);
|
||||||
|
Err(::Error::new_incomplete())
|
||||||
|
} else {
|
||||||
|
trace!("found EOF on idle connection, closing");
|
||||||
|
Ok(Async::Ready(()))
|
||||||
|
};
|
||||||
|
|
||||||
|
// order is important: should_error needs state BEFORE close_read
|
||||||
|
self.state.close_read();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("received unexpected {} bytes on an idle connection", num_read);
|
||||||
|
Err(::Error::new_unexpected_message())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mid_message_detect_eof(&mut self) -> io::Result<()> {
|
fn mid_message_detect_eof(&mut self) -> Poll<(), ::Error> {
|
||||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||||
|
debug_assert!(self.is_mid_message());
|
||||||
|
|
||||||
if self.state.allow_half_close || !self.io.read_buf().is_empty() {
|
if self.state.allow_half_close || !self.io.read_buf().is_empty() {
|
||||||
Ok(())
|
return Ok(Async::NotReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_read = try_ready!(self.force_io_read().map_err(::Error::new_io));
|
||||||
|
|
||||||
|
if num_read == 0 {
|
||||||
|
trace!("found unexpected EOF on busy connection: {:?}", self.state);
|
||||||
|
self.state.close_read();
|
||||||
|
Err(::Error::new_incomplete())
|
||||||
} else {
|
} else {
|
||||||
self.try_io_read().map(|_| ())
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_io_read(&mut self) -> Poll<usize, io::Error> {
|
fn force_io_read(&mut self) -> Poll<usize, io::Error> {
|
||||||
match self.io.read_from_io() {
|
self.io.read_from_io().map_err(|e| {
|
||||||
Ok(Async::Ready(0)) => {
|
trace!("force_io_read; io error = {:?}", e);
|
||||||
trace!("try_io_read; found EOF on connection: {:?}", self.state);
|
self.state.close();
|
||||||
let must_error = !self.state.is_idle();
|
e
|
||||||
let ret = if must_error {
|
})
|
||||||
let desc = if self.is_mid_message() {
|
|
||||||
"unexpected EOF waiting for response"
|
|
||||||
} else {
|
|
||||||
"unexpected EOF before writing message"
|
|
||||||
};
|
|
||||||
Err(io::Error::new(io::ErrorKind::UnexpectedEof, desc))
|
|
||||||
} else {
|
|
||||||
Ok(Async::Ready(0))
|
|
||||||
};
|
|
||||||
|
|
||||||
// order is important: must_error needs state BEFORE close_read
|
|
||||||
self.state.close_read();
|
|
||||||
ret
|
|
||||||
},
|
|
||||||
Ok(Async::Ready(n)) => {
|
|
||||||
Ok(Async::Ready(n))
|
|
||||||
},
|
|
||||||
Ok(Async::NotReady) => {
|
|
||||||
Ok(Async::NotReady)
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
trace!("try_io_read; error = {}", e);
|
|
||||||
self.state.close();
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ where
|
|||||||
self.conn.close_read();
|
self.conn.close_read();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(Async::Ready(None)) => {
|
Ok(Async::Ready(None)) => {
|
||||||
@@ -180,7 +179,7 @@ where
|
|||||||
// just drop, the body will close automatically
|
// just drop, the body will close automatically
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.conn.read_keep_alive().map(Async::Ready);
|
return self.conn.read_keep_alive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,7 +485,10 @@ where
|
|||||||
let _ = cb.send(Ok(res));
|
let _ = cb.send(Ok(res));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(::Error::new_mismatched_response())
|
// Getting here is likely a bug! An error should have happened
|
||||||
|
// in Conn::require_empty_read() before ever parsing a
|
||||||
|
// full message!
|
||||||
|
Err(::Error::new_unexpected_message())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -497,7 +499,7 @@ where
|
|||||||
trace!("canceling queued request with connection error: {}", err);
|
trace!("canceling queued request with connection error: {}", err);
|
||||||
// in this case, the message was never even started, so it's safe to tell
|
// in this case, the message was never even started, so it's safe to tell
|
||||||
// the user that the request was completely canceled
|
// the user that the request was completely canceled
|
||||||
let _ = cb.send(Err((::Error::new_canceled(Some(err)), Some(req))));
|
let _ = cb.send(Err((::Error::new_canceled().with(err), Some(req))));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
|||||||
@@ -31,8 +31,21 @@ pub(crate) trait Http1Transaction {
|
|||||||
|
|
||||||
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
|
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
|
||||||
|
|
||||||
fn should_error_on_parse_eof() -> bool;
|
fn is_client() -> bool {
|
||||||
fn should_read_first() -> bool;
|
!Self::is_server()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_server() -> bool {
|
||||||
|
!Self::is_client()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_error_on_parse_eof() -> bool {
|
||||||
|
Self::is_client()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_read_first() -> bool {
|
||||||
|
Self::is_server()
|
||||||
|
}
|
||||||
|
|
||||||
fn update_date() {}
|
fn update_date() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ impl Http1Transaction for Server {
|
|||||||
if wrote_len {
|
if wrote_len {
|
||||||
warn!("unexpected content-length found, canceling");
|
warn!("unexpected content-length found, canceling");
|
||||||
rewind(dst);
|
rewind(dst);
|
||||||
return Err(::Error::new_header());
|
return Err(::Error::new_user_header());
|
||||||
}
|
}
|
||||||
match msg.body {
|
match msg.body {
|
||||||
Some(BodyLength::Known(known_len)) => {
|
Some(BodyLength::Known(known_len)) => {
|
||||||
@@ -354,7 +354,7 @@ impl Http1Transaction for Server {
|
|||||||
if fold.0 != len {
|
if fold.0 != len {
|
||||||
warn!("multiple Content-Length values found: [{}, {}]", fold.0, len);
|
warn!("multiple Content-Length values found: [{}, {}]", fold.0, len);
|
||||||
rewind(dst);
|
rewind(dst);
|
||||||
return Err(::Error::new_header());
|
return Err(::Error::new_user_header());
|
||||||
}
|
}
|
||||||
folded = Some(fold);
|
folded = Some(fold);
|
||||||
} else {
|
} else {
|
||||||
@@ -363,7 +363,7 @@ impl Http1Transaction for Server {
|
|||||||
} else {
|
} else {
|
||||||
warn!("illegal Content-Length value: {:?}", value);
|
warn!("illegal Content-Length value: {:?}", value);
|
||||||
rewind(dst);
|
rewind(dst);
|
||||||
return Err(::Error::new_header());
|
return Err(::Error::new_user_header());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((len, value)) = folded {
|
if let Some((len, value)) = folded {
|
||||||
@@ -403,7 +403,7 @@ impl Http1Transaction for Server {
|
|||||||
if wrote_len {
|
if wrote_len {
|
||||||
warn!("unexpected transfer-encoding found, canceling");
|
warn!("unexpected transfer-encoding found, canceling");
|
||||||
rewind(dst);
|
rewind(dst);
|
||||||
return Err(::Error::new_header());
|
return Err(::Error::new_user_header());
|
||||||
}
|
}
|
||||||
// check that we actually can send a chunked body...
|
// check that we actually can send a chunked body...
|
||||||
if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) {
|
if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) {
|
||||||
@@ -531,11 +531,7 @@ impl Http1Transaction for Server {
|
|||||||
Some(msg)
|
Some(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_error_on_parse_eof() -> bool {
|
fn is_server() -> bool {
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_read_first() -> bool {
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,13 +688,9 @@ impl Http1Transaction for Client {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_error_on_parse_eof() -> bool {
|
fn is_client() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_read_first() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ where
|
|||||||
Ok(Async::Ready(Some((req, cb)))) => {
|
Ok(Async::Ready(Some((req, cb)))) => {
|
||||||
// check that future hasn't been canceled already
|
// check that future hasn't been canceled already
|
||||||
if cb.is_canceled() {
|
if cb.is_canceled() {
|
||||||
trace!("request canceled");
|
trace!("request callback is canceled");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (head, body) = req.into_parts();
|
let (head, body) = req.into_parts();
|
||||||
@@ -159,11 +159,11 @@ where
|
|||||||
|
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
||||||
|
|
||||||
Ok(Async::Ready(None)) |
|
Ok(Async::Ready(None)) => {
|
||||||
Err(_) => {
|
|
||||||
trace!("client::dispatch::Sender dropped");
|
trace!("client::dispatch::Sender dropped");
|
||||||
return Ok(Async::Ready(Dispatched::Shutdown));
|
return Ok(Async::Ready(Dispatched::Shutdown));
|
||||||
}
|
},
|
||||||
|
Err(never) => match never {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ where
|
|||||||
match try_ready!(self.body_tx.poll_capacity().map_err(::Error::new_body_write)) {
|
match try_ready!(self.body_tx.poll_capacity().map_err(::Error::new_body_write)) {
|
||||||
Some(0) => {}
|
Some(0) => {}
|
||||||
Some(_) => break,
|
Some(_) => break,
|
||||||
None => return Err(::Error::new_canceled(None::<::Error>)),
|
None => return Err(::Error::new_canceled()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -708,7 +708,7 @@ where
|
|||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!("make_service closed");
|
trace!("make_service closed");
|
||||||
return Err(::Error::new_user_new_service(e));
|
return Err(::Error::new_user_make_service(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,8 +876,8 @@ pub(crate) mod spawn_all {
|
|||||||
let conn = try_ready!(connecting
|
let conn = try_ready!(connecting
|
||||||
.poll()
|
.poll()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
let err = ::Error::new_user_new_service(err);
|
let err = ::Error::new_user_make_service(err);
|
||||||
debug!("connection error: {}", err);
|
debug!("connecting error: {}", err);
|
||||||
}));
|
}));
|
||||||
let connected = watcher.watch(conn.with_upgrades());
|
let connected = watcher.watch(conn.with_upgrades());
|
||||||
State::Connected(connected)
|
State::Connected(connected)
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ impl Future for OnUpgrade {
|
|||||||
Ok(Async::Ready(Ok(upgraded))) => Ok(Async::Ready(upgraded)),
|
Ok(Async::Ready(Ok(upgraded))) => Ok(Async::Ready(upgraded)),
|
||||||
Ok(Async::Ready(Err(err))) => Err(err),
|
Ok(Async::Ready(Err(err))) => Err(err),
|
||||||
Err(_oneshot_canceled) => Err(
|
Err(_oneshot_canceled) => Err(
|
||||||
::Error::new_canceled(Some(UpgradeExpected(())))
|
::Error::new_canceled().with(UpgradeExpected(()))
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
None => Err(::Error::new_user_no_upgrade()),
|
None => Err(::Error::new_user_no_upgrade()),
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ test! {
|
|||||||
method: GET,
|
method: GET,
|
||||||
url: "http://{addr}/err",
|
url: "http://{addr}/err",
|
||||||
},
|
},
|
||||||
error: |err| err.to_string() == "parsed HTTP message from remote is incomplete",
|
error: |err| err.is_incomplete_message(),
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
|
|||||||
Reference in New Issue
Block a user