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:
@@ -98,7 +98,6 @@ where I: AsyncRead + AsyncWrite,
|
||||
|
||||
pub fn can_read_head(&self) -> bool {
|
||||
match self.state.reading {
|
||||
//Reading::Init => true,
|
||||
Reading::Init => {
|
||||
if T::should_read_first() {
|
||||
true
|
||||
@@ -228,13 +227,19 @@ where I: AsyncRead + AsyncWrite,
|
||||
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());
|
||||
|
||||
if self.is_mid_message() {
|
||||
self.mid_message_detect_eof().map_err(::Error::new_io)
|
||||
self.mid_message_detect_eof()
|
||||
} 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 should only be called for Clients wanting to enter the idle
|
||||
// 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.is_mid_message());
|
||||
debug_assert!(T::is_client());
|
||||
|
||||
if !self.io.read_buf().is_empty() {
|
||||
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected bytes after message ended"))
|
||||
} 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(())
|
||||
},
|
||||
}
|
||||
return Err(::Error::new_unexpected_message());
|
||||
}
|
||||
|
||||
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.is_mid_message());
|
||||
|
||||
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 {
|
||||
self.try_io_read().map(|_| ())
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_io_read(&mut self) -> Poll<usize, io::Error> {
|
||||
match self.io.read_from_io() {
|
||||
Ok(Async::Ready(0)) => {
|
||||
trace!("try_io_read; found EOF on connection: {:?}", self.state);
|
||||
let must_error = !self.state.is_idle();
|
||||
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)
|
||||
}
|
||||
}
|
||||
fn force_io_read(&mut self) -> Poll<usize, io::Error> {
|
||||
self.io.read_from_io().map_err(|e| {
|
||||
trace!("force_io_read; io error = {:?}", e);
|
||||
self.state.close();
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -162,7 +162,6 @@ where
|
||||
self.conn.close_read();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
Ok(Async::Ready(None)) => {
|
||||
@@ -180,7 +179,7 @@ where
|
||||
// just drop, the body will close automatically
|
||||
}
|
||||
} 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));
|
||||
Ok(())
|
||||
} 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) => {
|
||||
@@ -497,7 +499,7 @@ where
|
||||
trace!("canceling queued request with connection error: {}", err);
|
||||
// in this case, the message was never even started, so it's safe to tell
|
||||
// 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(())
|
||||
} else {
|
||||
Err(err)
|
||||
|
||||
@@ -31,8 +31,21 @@ pub(crate) trait Http1Transaction {
|
||||
|
||||
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
|
||||
|
||||
fn should_error_on_parse_eof() -> bool;
|
||||
fn should_read_first() -> bool;
|
||||
fn is_client() -> 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() {}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ impl Http1Transaction for Server {
|
||||
if wrote_len {
|
||||
warn!("unexpected content-length found, canceling");
|
||||
rewind(dst);
|
||||
return Err(::Error::new_header());
|
||||
return Err(::Error::new_user_header());
|
||||
}
|
||||
match msg.body {
|
||||
Some(BodyLength::Known(known_len)) => {
|
||||
@@ -354,7 +354,7 @@ impl Http1Transaction for Server {
|
||||
if fold.0 != len {
|
||||
warn!("multiple Content-Length values found: [{}, {}]", fold.0, len);
|
||||
rewind(dst);
|
||||
return Err(::Error::new_header());
|
||||
return Err(::Error::new_user_header());
|
||||
}
|
||||
folded = Some(fold);
|
||||
} else {
|
||||
@@ -363,7 +363,7 @@ impl Http1Transaction for Server {
|
||||
} else {
|
||||
warn!("illegal Content-Length value: {:?}", value);
|
||||
rewind(dst);
|
||||
return Err(::Error::new_header());
|
||||
return Err(::Error::new_user_header());
|
||||
}
|
||||
}
|
||||
if let Some((len, value)) = folded {
|
||||
@@ -403,7 +403,7 @@ impl Http1Transaction for Server {
|
||||
if wrote_len {
|
||||
warn!("unexpected transfer-encoding found, canceling");
|
||||
rewind(dst);
|
||||
return Err(::Error::new_header());
|
||||
return Err(::Error::new_user_header());
|
||||
}
|
||||
// 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) {
|
||||
@@ -531,11 +531,7 @@ impl Http1Transaction for Server {
|
||||
Some(msg)
|
||||
}
|
||||
|
||||
fn should_error_on_parse_eof() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn should_read_first() -> bool {
|
||||
fn is_server() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -692,13 +688,9 @@ impl Http1Transaction for Client {
|
||||
None
|
||||
}
|
||||
|
||||
fn should_error_on_parse_eof() -> bool {
|
||||
fn is_client() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn should_read_first() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
|
||||
@@ -100,7 +100,7 @@ where
|
||||
Ok(Async::Ready(Some((req, cb)))) => {
|
||||
// check that future hasn't been canceled already
|
||||
if cb.is_canceled() {
|
||||
trace!("request canceled");
|
||||
trace!("request callback is canceled");
|
||||
continue;
|
||||
}
|
||||
let (head, body) = req.into_parts();
|
||||
@@ -159,11 +159,11 @@ where
|
||||
|
||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
||||
|
||||
Ok(Async::Ready(None)) |
|
||||
Err(_) => {
|
||||
Ok(Async::Ready(None)) => {
|
||||
trace!("client::dispatch::Sender dropped");
|
||||
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)) {
|
||||
Some(0) => {}
|
||||
Some(_) => break,
|
||||
None => return Err(::Error::new_canceled(None::<::Error>)),
|
||||
None => return Err(::Error::new_canceled()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user