perf(h1): poll body less if is_end_stream
This commit is contained in:
		| @@ -451,43 +451,23 @@ where I: AsyncRead + AsyncWrite, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn write_body(&mut self, chunk: Option<B>) { | ||||
|     pub fn write_body(&mut self, chunk: B) { | ||||
|         debug_assert!(self.can_write_body() && self.can_buffer_body()); | ||||
|         // empty chunks should be discarded at Dispatcher level | ||||
|         debug_assert!(chunk.remaining() != 0); | ||||
|  | ||||
|         let state = match self.state.writing { | ||||
|             Writing::Body(ref mut encoder) => { | ||||
|                 if let Some(chunk) = chunk { | ||||
|                     if chunk.remaining() == 0 { | ||||
|                         return; | ||||
|                     } | ||||
|                 self.io.buffer(encoder.encode(chunk)); | ||||
|  | ||||
|                     let encoded = encoder.encode(chunk); | ||||
|                     self.io.buffer(encoded); | ||||
|  | ||||
|                     if encoder.is_eof() { | ||||
|                         if encoder.is_last() { | ||||
|                             Writing::Closed | ||||
|                         } else { | ||||
|                             Writing::KeepAlive | ||||
|                         } | ||||
|                 if encoder.is_eof() { | ||||
|                     if encoder.is_last() { | ||||
|                         Writing::Closed | ||||
|                     } else { | ||||
|                         return; | ||||
|                         Writing::KeepAlive | ||||
|                     } | ||||
|                 } else { | ||||
|                     // end of stream, that means we should try to eof | ||||
|                     match encoder.end() { | ||||
|                         Ok(end) => { | ||||
|                             if let Some(end) = end { | ||||
|                                 self.io.buffer(end); | ||||
|                             } | ||||
|                             if encoder.is_last() { | ||||
|                                 Writing::Closed | ||||
|                             } else { | ||||
|                                 Writing::KeepAlive | ||||
|                             } | ||||
|                         }, | ||||
|                         Err(_not_eof) => Writing::Closed, | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|             }, | ||||
|             _ => unreachable!("write_body invalid state: {:?}", self.state.writing), | ||||
| @@ -496,6 +476,61 @@ where I: AsyncRead + AsyncWrite, | ||||
|         self.state.writing = state; | ||||
|     } | ||||
|  | ||||
|     pub fn write_body_and_end(&mut self, chunk: B) { | ||||
|         debug_assert!(self.can_write_body() && self.can_buffer_body()); | ||||
|         // empty chunks should be discarded at Dispatcher level | ||||
|         debug_assert!(chunk.remaining() != 0); | ||||
|  | ||||
|         let state = match self.state.writing { | ||||
|             Writing::Body(ref mut encoder) => { | ||||
|                 self.io.buffer(encoder.encode(chunk)); | ||||
|                 match encoder.end() { | ||||
|                     Ok(end) => { | ||||
|                         if let Some(end) = end { | ||||
|                             self.io.buffer(end); | ||||
|                         } | ||||
|                         if encoder.is_last() { | ||||
|                             Writing::Closed | ||||
|                         } else { | ||||
|                             Writing::KeepAlive | ||||
|                         } | ||||
|                     }, | ||||
|                     Err(_not_eof) => Writing::Closed, | ||||
|                 } | ||||
|             }, | ||||
|             _ => unreachable!("write_body invalid state: {:?}", self.state.writing), | ||||
|         }; | ||||
|  | ||||
|         self.state.writing = state; | ||||
|     } | ||||
|  | ||||
|     pub fn end_body(&mut self) { | ||||
|         debug_assert!(self.can_write_body()); | ||||
|  | ||||
|         let state = match self.state.writing { | ||||
|             Writing::Body(ref mut encoder) => { | ||||
|                 // end of stream, that means we should try to eof | ||||
|                 match encoder.end() { | ||||
|                     Ok(end) => { | ||||
|                         if let Some(end) = end { | ||||
|                             self.io.buffer(end); | ||||
|                         } | ||||
|                         if encoder.is_last() { | ||||
|                             Writing::Closed | ||||
|                         } else { | ||||
|                             Writing::KeepAlive | ||||
|                         } | ||||
|                     }, | ||||
|                     Err(_not_eof) => Writing::Closed, | ||||
|                 } | ||||
|             }, | ||||
|             _ => return, | ||||
|         }; | ||||
|  | ||||
|         self.state.writing = state; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     // When we get a parse error, depending on what side we are, we might be able | ||||
|     // to write a response before closing the connection. | ||||
|     // | ||||
|   | ||||
| @@ -235,30 +235,39 @@ where | ||||
|             } else if !self.conn.can_buffer_body() { | ||||
|                 try_ready!(self.poll_flush()); | ||||
|             } else if let Some(mut body) = self.body_rx.take() { | ||||
|                 let chunk = match body.poll_data().map_err(::Error::new_user_body)? { | ||||
|                 if !self.conn.can_write_body() { | ||||
|                     trace!( | ||||
|                         "no more write body allowed, user body is_end_stream = {}", | ||||
|                         body.is_end_stream(), | ||||
|                     ); | ||||
|                     continue; | ||||
|                 } | ||||
|                 match body.poll_data().map_err(::Error::new_user_body)? { | ||||
|                     Async::Ready(Some(chunk)) => { | ||||
|                         self.body_rx = Some(body); | ||||
|                         chunk | ||||
|                         let eos = body.is_end_stream(); | ||||
|                         if eos { | ||||
|                             if chunk.remaining() == 0 { | ||||
|                                 trace!("discarding empty chunk"); | ||||
|                                 self.conn.end_body(); | ||||
|                             } else { | ||||
|                                 self.conn.write_body_and_end(chunk); | ||||
|                             } | ||||
|                         } else { | ||||
|                             self.body_rx = Some(body); | ||||
|                             if chunk.remaining() == 0 { | ||||
|                                 trace!("discarding empty chunk"); | ||||
|                                 continue; | ||||
|                             } | ||||
|                             self.conn.write_body(chunk); | ||||
|                         } | ||||
|                     }, | ||||
|                     Async::Ready(None) => { | ||||
|                         if self.conn.can_write_body() { | ||||
|                             self.conn.write_body(None); | ||||
|                         } | ||||
|                         continue; | ||||
|                         self.conn.end_body(); | ||||
|                     }, | ||||
|                     Async::NotReady => { | ||||
|                         self.body_rx = Some(body); | ||||
|                         return Ok(Async::NotReady); | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 if self.conn.can_write_body() { | ||||
|                     self.conn.write_body(Some(chunk)); | ||||
|                 // This allows when chunk is `None`, or `Some([])`. | ||||
|                 } else if chunk.remaining() == 0 { | ||||
|                     // ok | ||||
|                 } else { | ||||
|                     warn!("unexpected chunk when body cannot write"); | ||||
|                 } | ||||
|             } else { | ||||
|                 return Ok(Async::NotReady); | ||||
| @@ -531,4 +540,25 @@ mod tests { | ||||
|             Ok::<(), ()>(()) | ||||
|         }).wait().unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn body_empty_chunks_ignored() { | ||||
|         let _ = pretty_env_logger::try_init(); | ||||
|         ::futures::lazy(|| { | ||||
|             let io = AsyncIo::new_buf(vec![], 0); | ||||
|             let (mut tx, rx) = ::client::dispatch::channel(); | ||||
|             let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io); | ||||
|             let mut dispatcher = Dispatcher::new(Client::new(rx), conn); | ||||
|  | ||||
|             // First poll is needed to allow tx to send... | ||||
|             assert!(dispatcher.poll().expect("nothing is ready").is_not_ready()); | ||||
|  | ||||
|             let body = ::Body::wrap_stream(::futures::stream::once(Ok::<_, ::Error>(""))); | ||||
|  | ||||
|             let _res_rx = tx.try_send(::Request::new(body)).unwrap(); | ||||
|  | ||||
|             dispatcher.poll().expect("empty body shouldn't panic"); | ||||
|             Ok::<(), ()>(()) | ||||
|         }).wait().unwrap(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,7 +32,7 @@ enum Kind { | ||||
|     /// | ||||
|     /// This is mostly only used with HTTP/1.0 with a length. This kind requires | ||||
|     /// the connection to be closed when the body is finished. | ||||
|     Eof | ||||
|     CloseDelimited, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| @@ -58,8 +58,8 @@ impl Encoder { | ||||
|         Encoder::new(Kind::Length(len)) | ||||
|     } | ||||
|  | ||||
|     pub fn eof() -> Encoder { | ||||
|         Encoder::new(Kind::Eof) | ||||
|     pub fn close_delimited() -> Encoder { | ||||
|         Encoder::new(Kind::CloseDelimited) | ||||
|     } | ||||
|  | ||||
|     pub fn is_eof(&self) -> bool { | ||||
| @@ -112,8 +112,8 @@ impl Encoder { | ||||
|                     BufKind::Exact(msg) | ||||
|                 } | ||||
|             }, | ||||
|             Kind::Eof => { | ||||
|                 trace!("eof write {}B", len); | ||||
|             Kind::CloseDelimited => { | ||||
|                 trace!("close delimited write {}B", len); | ||||
|                 BufKind::Exact(msg) | ||||
|             } | ||||
|         }; | ||||
| @@ -323,7 +323,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn eof() { | ||||
|         let mut encoder = Encoder::eof(); | ||||
|         let mut encoder = Encoder::close_delimited(); | ||||
|         let mut dst = Vec::new(); | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -514,13 +514,13 @@ fn set_length(headers: &mut HeaderMap, body: BodyLength, can_chunked: bool) -> E | ||||
|         let encoder = if headers.remove(TRANSFER_ENCODING).is_some() { | ||||
|             trace!("removing illegal transfer-encoding header"); | ||||
|             should_remove_con_len = true; | ||||
|             Encoder::eof() | ||||
|             Encoder::close_delimited() | ||||
|         } else if let Some(len) = existing_con_len { | ||||
|             Encoder::length(len) | ||||
|         } else if let BodyLength::Known(len) = body { | ||||
|             set_content_length(headers, len) | ||||
|         } else { | ||||
|             Encoder::eof() | ||||
|             Encoder::close_delimited() | ||||
|         }; | ||||
|  | ||||
|         if should_remove_con_len && existing_con_len.is_some() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user