fix(lib): properly handle body streaming errors
This commit is contained in:
		| @@ -235,18 +235,31 @@ where I: AsyncRead + AsyncWrite, | ||||
|  | ||||
|         let (reading, ret) = match self.state.reading { | ||||
|             Reading::Body(ref mut decoder) => { | ||||
|                 let slice = try_ready!(decoder.decode(&mut self.io)); | ||||
|                 if !slice.is_empty() { | ||||
|                     return Ok(Async::Ready(Some(super::Chunk::from(slice)))); | ||||
|                 } else if decoder.is_eof() { | ||||
|                     debug!("incoming body completed"); | ||||
|                     (Reading::KeepAlive, Ok(Async::Ready(None))) | ||||
|                 } else { | ||||
|                     trace!("decode stream unexpectedly ended"); | ||||
|                     //TODO: Should this return an UnexpectedEof? | ||||
|                     (Reading::Closed, Ok(Async::Ready(None))) | ||||
|                 match decoder.decode(&mut self.io) { | ||||
|                     Ok(Async::Ready(slice)) => { | ||||
|                         let chunk = if !slice.is_empty() { | ||||
|                             Some(super::Chunk::from(slice)) | ||||
|                         } else { | ||||
|                             None | ||||
|                         }; | ||||
|                         let reading = if decoder.is_eof() { | ||||
|                             debug!("incoming body completed"); | ||||
|                             Reading::KeepAlive | ||||
|                         } else if chunk.is_some() { | ||||
|                             Reading::Body(decoder.clone()) | ||||
|                         } else { | ||||
|                             trace!("decode stream unexpectedly ended"); | ||||
|                             //TODO: Should this return an UnexpectedEof? | ||||
|                             Reading::Closed | ||||
|                         }; | ||||
|                         (reading, Ok(Async::Ready(chunk))) | ||||
|                     }, | ||||
|                     Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||
|                     Err(e) => { | ||||
|                         trace!("decode stream error: {}", e); | ||||
|                         (Reading::Closed, Err(e)) | ||||
|                     }, | ||||
|                 } | ||||
|  | ||||
|             }, | ||||
|             _ => unreachable!("read_body invalid state: {:?}", self.state.reading), | ||||
|         }; | ||||
|   | ||||
| @@ -85,66 +85,25 @@ where | ||||
|             if self.is_closing { | ||||
|                 return Ok(Async::Ready(())); | ||||
|             } else if self.conn.can_read_head() { | ||||
|                 // can dispatch receive, or does it still care about, an incoming message? | ||||
|                 match self.dispatch.poll_ready() { | ||||
|                     Ok(Async::Ready(())) => (), | ||||
|                     Ok(Async::NotReady) => unreachable!("dispatch not ready when conn is"), | ||||
|                     Err(()) => { | ||||
|                         trace!("dispatch no longer receiving messages"); | ||||
|                         self.close(); | ||||
|                         return Ok(Async::Ready(())); | ||||
|                     } | ||||
|                 } | ||||
|                 // dispatch is ready for a message, try to read one | ||||
|                 match self.conn.read_head() { | ||||
|                     Ok(Async::Ready(Some((head, has_body)))) => { | ||||
|                         let body = if has_body { | ||||
|                             let (mut tx, rx) = super::body::channel(); | ||||
|                             let _ = tx.poll_ready(); // register this task if rx is dropped | ||||
|                             self.body_tx = Some(tx); | ||||
|                             Some(rx) | ||||
|                         } else { | ||||
|                             None | ||||
|                         }; | ||||
|                         self.dispatch.recv_msg(Ok((head, body)))?; | ||||
|                     }, | ||||
|                     Ok(Async::Ready(None)) => { | ||||
|                         // read eof, conn will start to shutdown automatically | ||||
|                         return Ok(Async::Ready(())); | ||||
|                     } | ||||
|                     Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||
|                     Err(err) => { | ||||
|                         debug!("read_head error: {}", err); | ||||
|                         self.dispatch.recv_msg(Err(err))?; | ||||
|                         // if here, the dispatcher gave the user the error | ||||
|                         // somewhere else. we still need to shutdown, but | ||||
|                         // not as a second error. | ||||
|                         return Ok(Async::Ready(())); | ||||
|                     } | ||||
|                 } | ||||
|                 try_ready!(self.poll_read_head()); | ||||
|             } else if self.conn.can_write_continue() { | ||||
|                 try_nb!(self.conn.flush()); | ||||
|             } else if let Some(mut body) = self.body_tx.take() { | ||||
|                 let can_read_body = self.conn.can_read_body(); | ||||
|                 match body.poll_ready() { | ||||
|                     Ok(Async::Ready(())) => (), | ||||
|                     Ok(Async::NotReady) => { | ||||
|                         self.body_tx = Some(body); | ||||
|                         return Ok(Async::NotReady); | ||||
|                     }, | ||||
|                     Err(_canceled) => { | ||||
|                         // user doesn't care about the body | ||||
|                         // so we should stop reading | ||||
|                         if can_read_body { | ||||
|                 if self.conn.can_read_body() { | ||||
|                     match body.poll_ready() { | ||||
|                         Ok(Async::Ready(())) => (), | ||||
|                         Ok(Async::NotReady) => { | ||||
|                             self.body_tx = Some(body); | ||||
|                             return Ok(Async::NotReady); | ||||
|                         }, | ||||
|                         Err(_canceled) => { | ||||
|                             // user doesn't care about the body | ||||
|                             // so we should stop reading | ||||
|                             trace!("body receiver dropped before eof, closing"); | ||||
|                             self.conn.close_read(); | ||||
|                             return Ok(Async::Ready(())); | ||||
|                         } | ||||
|                         // else the conn body is done, and user dropped, | ||||
|                         // so everything is fine! | ||||
|                     } | ||||
|                 } | ||||
|                 if can_read_body { | ||||
|                     match self.conn.read_body() { | ||||
|                         Ok(Async::Ready(Some(chunk))) => { | ||||
|                             match body.start_send(Ok(chunk)) { | ||||
| @@ -183,6 +142,47 @@ where | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn poll_read_head(&mut self) -> Poll<(), ::Error> { | ||||
|         // can dispatch receive, or does it still care about, an incoming message? | ||||
|         match self.dispatch.poll_ready() { | ||||
|             Ok(Async::Ready(())) => (), | ||||
|             Ok(Async::NotReady) => unreachable!("dispatch not ready when conn is"), | ||||
|             Err(()) => { | ||||
|                 trace!("dispatch no longer receiving messages"); | ||||
|                 self.close(); | ||||
|                 return Ok(Async::Ready(())); | ||||
|             } | ||||
|         } | ||||
|         // dispatch is ready for a message, try to read one | ||||
|         match self.conn.read_head() { | ||||
|             Ok(Async::Ready(Some((head, has_body)))) => { | ||||
|                 let body = if has_body { | ||||
|                     let (mut tx, rx) = super::body::channel(); | ||||
|                     let _ = tx.poll_ready(); // register this task if rx is dropped | ||||
|                     self.body_tx = Some(tx); | ||||
|                     Some(rx) | ||||
|                 } else { | ||||
|                     None | ||||
|                 }; | ||||
|                 self.dispatch.recv_msg(Ok((head, body)))?; | ||||
|                 Ok(Async::Ready(())) | ||||
|             }, | ||||
|             Ok(Async::Ready(None)) => { | ||||
|                 // read eof, conn will start to shutdown automatically | ||||
|                 Ok(Async::Ready(())) | ||||
|             } | ||||
|             Ok(Async::NotReady) => Ok(Async::NotReady), | ||||
|             Err(err) => { | ||||
|                 debug!("read_head error: {}", err); | ||||
|                 self.dispatch.recv_msg(Err(err))?; | ||||
|                 // if here, the dispatcher gave the user the error | ||||
|                 // somewhere else. we still need to shutdown, but | ||||
|                 // not as a second error. | ||||
|                 Ok(Async::Ready(())) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn poll_write(&mut self) -> Poll<(), ::Error> { | ||||
|         loop { | ||||
|             if self.is_closing { | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| use std::error::Error as StdError; | ||||
| use std::fmt; | ||||
| use std::usize; | ||||
| use std::io; | ||||
| @@ -97,7 +98,7 @@ impl Decoder { | ||||
|                     if num > *remaining { | ||||
|                         *remaining = 0; | ||||
|                     } else if num == 0 { | ||||
|                         return Err(io::Error::new(io::ErrorKind::Other, "early eof")); | ||||
|                         return Err(io::Error::new(io::ErrorKind::UnexpectedEof, IncompleteBody)); | ||||
|                     } else { | ||||
|                         *remaining -= num; | ||||
|                     } | ||||
| @@ -262,7 +263,7 @@ impl ChunkedState { | ||||
|  | ||||
|         if count == 0 { | ||||
|             *rem = 0; | ||||
|             return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")); | ||||
|             return Err(io::Error::new(io::ErrorKind::UnexpectedEof, IncompleteBody)); | ||||
|         } | ||||
|         *buf = Some(slice); | ||||
|         *rem -= count as u64; | ||||
| @@ -300,9 +301,23 @@ impl ChunkedState { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct IncompleteBody; | ||||
|  | ||||
| impl fmt::Display for IncompleteBody { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str(self.description()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl StdError for IncompleteBody { | ||||
|     fn description(&self) -> &str { | ||||
|         "end of file before message length reached" | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::error::Error; | ||||
|     use std::io; | ||||
|     use std::io::Write; | ||||
|     use super::Decoder; | ||||
| @@ -422,8 +437,7 @@ mod tests { | ||||
|         let mut decoder = Decoder::length(10); | ||||
|         assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); | ||||
|         let e = decoder.decode(&mut bytes).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::Other); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -436,7 +450,6 @@ mod tests { | ||||
|         assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); | ||||
|         let e = decoder.decode(&mut bytes).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user