fix(client): check for drained stream in Response::drop
This commit is contained in:
		| @@ -116,7 +116,6 @@ impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector fo | ||||
|         Ok(PooledStream { | ||||
|             inner: Some((key, conn)), | ||||
|             is_closed: false, | ||||
|             is_drained: false, | ||||
|             pool: self.inner.clone() | ||||
|         }) | ||||
|     } | ||||
| @@ -130,20 +129,13 @@ impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector fo | ||||
| pub struct PooledStream<S> { | ||||
|     inner: Option<(Key, S)>, | ||||
|     is_closed: bool, | ||||
|     is_drained: bool, | ||||
|     pool: Arc<Mutex<PoolImpl<S>>> | ||||
| } | ||||
|  | ||||
| impl<S: NetworkStream> Read for PooledStream<S> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.inner.as_mut().unwrap().1.read(buf) { | ||||
|             Ok(0) => { | ||||
|                 self.is_drained = true; | ||||
|                 Ok(0) | ||||
|             } | ||||
|             r => r | ||||
|         } | ||||
|         self.inner.as_mut().unwrap().1.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -174,8 +166,8 @@ impl<S: NetworkStream> NetworkStream for PooledStream<S> { | ||||
|  | ||||
| impl<S> Drop for PooledStream<S> { | ||||
|     fn drop(&mut self) { | ||||
|         trace!("PooledStream.drop, is_closed={}, is_drained={}", self.is_closed, self.is_drained); | ||||
|         if !self.is_closed && self.is_drained { | ||||
|         trace!("PooledStream.drop, is_closed={}", self.is_closed); | ||||
|         if !self.is_closed { | ||||
|             self.inner.take().map(|(key, conn)| { | ||||
|                 if let Ok(mut pool) = self.pool.lock() { | ||||
|                     pool.reuse(key, conn); | ||||
| @@ -205,13 +197,13 @@ mod tests { | ||||
|     fn test_connect_and_drop() { | ||||
|         let pool = mocked!(); | ||||
|         let key = key("127.0.0.1", 3000, "http"); | ||||
|         pool.connect("127.0.0.1", 3000, "http").unwrap().is_drained = true; | ||||
|         pool.connect("127.0.0.1", 3000, "http").unwrap(); | ||||
|         { | ||||
|             let locked = pool.inner.lock().unwrap(); | ||||
|             assert_eq!(locked.conns.len(), 1); | ||||
|             assert_eq!(locked.conns.get(&key).unwrap().len(), 1); | ||||
|         } | ||||
|         pool.connect("127.0.0.1", 3000, "http").unwrap().is_drained = true; //reused | ||||
|         pool.connect("127.0.0.1", 3000, "http").unwrap(); //reused | ||||
|         { | ||||
|             let locked = pool.inner.lock().unwrap(); | ||||
|             assert_eq!(locked.conns.len(), 1); | ||||
|   | ||||
| @@ -19,6 +19,7 @@ pub struct Response { | ||||
|     pub version: version::HttpVersion, | ||||
|     status_raw: RawStatus, | ||||
|     message: Box<HttpMessage>, | ||||
|     is_drained: bool, | ||||
| } | ||||
|  | ||||
| impl Response { | ||||
| @@ -43,6 +44,7 @@ impl Response { | ||||
|             headers: headers, | ||||
|             message: message, | ||||
|             status_raw: raw_status, | ||||
|             is_drained: false, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -50,34 +52,46 @@ impl Response { | ||||
|     pub fn status_raw(&self) -> &RawStatus { | ||||
|         &self.status_raw | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| impl Read for Response { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         let count = try!(self.message.read(buf)); | ||||
|         match self.message.read(buf) { | ||||
|             Ok(0) => { | ||||
|                 self.is_drained = true; | ||||
|                 Ok(0) | ||||
|             }, | ||||
|             r => r | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|         if count == 0 { | ||||
|             if !http::should_keep_alive(self.version, &self.headers) { | ||||
|                 try!(self.message.close_connection() | ||||
|                                  .map_err(|_| io::Error::new(io::ErrorKind::Other, | ||||
|                                                              "Error closing connection"))); | ||||
| impl Drop for Response { | ||||
|     fn drop(&mut self) { | ||||
|         // if not drained, theres old bits in the Reader. we can't reuse this, | ||||
|         // since those old bits would end up in new Responses | ||||
|         // | ||||
|         // otherwise, the response has been drained. we should check that the | ||||
|         // server has agreed to keep the connection open | ||||
|         trace!("Response.is_drained = {:?}", self.is_drained); | ||||
|         if !(self.is_drained && http::should_keep_alive(self.version, &self.headers)) { | ||||
|             trace!("closing connection"); | ||||
|             if let Err(e) = self.message.close_connection() { | ||||
|                 error!("error closing connection: {}", e); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(count) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::borrow::Cow::Borrowed; | ||||
|     use std::io::{self, Read}; | ||||
|  | ||||
|     use header::Headers; | ||||
|     use header::TransferEncoding; | ||||
|     use header::Encoding; | ||||
|     use http::RawStatus; | ||||
|     use http::HttpMessage; | ||||
|     use mock::MockStream; | ||||
|     use status; | ||||
|     use version; | ||||
| @@ -94,18 +108,10 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_into_inner() { | ||||
|         let res = Response { | ||||
|             status: status::StatusCode::Ok, | ||||
|             headers: Headers::new(), | ||||
|             version: version::HttpVersion::Http11, | ||||
|             message: Box::new(Http11Message::with_stream(Box::new(MockStream::new()))), | ||||
|             status_raw: RawStatus(200, Borrowed("OK")), | ||||
|         }; | ||||
|  | ||||
|         let message = res.message.downcast::<Http11Message>().ok().unwrap(); | ||||
|         let message: Box<HttpMessage> = Box::new(Http11Message::with_stream(Box::new(MockStream::new()))); | ||||
|         let message = message.downcast::<Http11Message>().ok().unwrap(); | ||||
|         let b = message.into_inner().downcast::<MockStream>().ok().unwrap(); | ||||
|         assert_eq!(b, Box::new(MockStream::new())); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user