feat(lib): update Tokio, bytes, http, h2, and http-body
This commit is contained in:
		| @@ -5,7 +5,7 @@ use std::marker::PhantomData; | ||||
| use bytes::{Buf, Bytes}; | ||||
| use http::{HeaderMap, Method, Version}; | ||||
| use http::header::{HeaderValue, CONNECTION}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use crate::Chunk; | ||||
| use crate::common::{Pin, Poll, Unpin, task}; | ||||
| @@ -915,7 +915,11 @@ mod tests { | ||||
|         *conn.io.read_buf_mut() = ::bytes::BytesMut::from(&s[..]); | ||||
|         conn.state.cached_headers = Some(HeaderMap::with_capacity(2)); | ||||
|  | ||||
|         let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap(); | ||||
|         let mut rt = tokio::runtime::Builder::new() | ||||
|             .enable_all() | ||||
|             .basic_scheduler() | ||||
|             .build() | ||||
|             .unwrap(); | ||||
|  | ||||
|         b.iter(|| { | ||||
|             rt.block_on(futures_util::future::poll_fn(|cx| { | ||||
|   | ||||
| @@ -328,7 +328,7 @@ impl StdError for IncompleteBody { | ||||
| mod tests { | ||||
|     use std::time::Duration; | ||||
|     use std::pin::Pin; | ||||
|     use tokio_io::AsyncRead; | ||||
|     use tokio::io::AsyncRead; | ||||
|     use super::*; | ||||
|  | ||||
|     impl<'a> MemRead for &'a [u8] { | ||||
| @@ -336,7 +336,7 @@ mod tests { | ||||
|             let n = ::std::cmp::min(len, self.len()); | ||||
|             if n > 0 { | ||||
|                 let (a, b) = self.split_at(n); | ||||
|                 let buf = Bytes::from(a); | ||||
|                 let buf = Bytes::copy_from_slice(a); | ||||
|                 *self = b; | ||||
|                 Poll::Ready(Ok(buf)) | ||||
|             } else { | ||||
| @@ -349,7 +349,7 @@ mod tests { | ||||
|         fn read_mem(&mut self, cx: &mut task::Context<'_>, len: usize) -> Poll<io::Result<Bytes>> { | ||||
|             let mut v = vec![0; len]; | ||||
|             let n = ready!(Pin::new(self).poll_read(cx, &mut v)?); | ||||
|             Poll::Ready(Ok(Bytes::from(&v[..n]))) | ||||
|             Poll::Ready(Ok(Bytes::copy_from_slice(&v[..n]))) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ use std::error::Error as StdError; | ||||
|  | ||||
| use bytes::{Buf, Bytes}; | ||||
| use http::{Request, Response, StatusCode}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use crate::body::{Body, Payload}; | ||||
| use crate::common::{Future, Never, Poll, Pin, Unpin, task}; | ||||
| @@ -605,7 +605,7 @@ mod tests { | ||||
|     fn client_read_bytes_before_writing_request() { | ||||
|         let _ = pretty_env_logger::try_init(); | ||||
|  | ||||
|         tokio_test::task::mock(|cx| { | ||||
|         tokio_test::task::spawn(()).enter(|cx, _| { | ||||
|  | ||||
|             let (io, mut handle) = tokio_test::io::Builder::new() | ||||
|                 .build_with_handle(); | ||||
| @@ -637,36 +637,32 @@ mod tests { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn body_empty_chunks_ignored() { | ||||
|     #[tokio::test] | ||||
|     async fn body_empty_chunks_ignored() { | ||||
|         let _ = pretty_env_logger::try_init(); | ||||
|  | ||||
|         tokio_test::clock::mock(|_timer| { | ||||
|             tokio_test::task::mock(|cx| { | ||||
|                 let io = tokio_test::io::Builder::new() | ||||
|                     // no reading or writing, just be blocked for the test... | ||||
|                     .wait(Duration::from_secs(5)) | ||||
|                     .build(); | ||||
|         let io = tokio_test::io::Builder::new() | ||||
|             // no reading or writing, just be blocked for the test... | ||||
|             .wait(Duration::from_secs(5)) | ||||
|             .build(); | ||||
|  | ||||
|                 let (mut tx, rx) = crate::client::dispatch::channel(); | ||||
|                 let conn = Conn::<_, crate::Chunk, ClientTransaction>::new(io); | ||||
|                 let mut dispatcher = Dispatcher::new(Client::new(rx), conn); | ||||
|         let (mut tx, rx) = crate::client::dispatch::channel(); | ||||
|         let conn = Conn::<_, crate::Chunk, ClientTransaction>::new(io); | ||||
|         let mut dispatcher = tokio_test::task::spawn(Dispatcher::new(Client::new(rx), conn)); | ||||
|  | ||||
|                 // First poll is needed to allow tx to send... | ||||
|                 assert!(Pin::new(&mut dispatcher).poll(cx).is_pending()); | ||||
|         // First poll is needed to allow tx to send... | ||||
|         assert!(dispatcher.poll().is_pending()); | ||||
|  | ||||
|                 let body = { | ||||
|                     let (mut tx, body) = crate::Body::channel(); | ||||
|                     tx.try_send_data("".into()).unwrap(); | ||||
|                     body | ||||
|                 }; | ||||
|         let body = { | ||||
|             let (mut tx, body) = crate::Body::channel(); | ||||
|             tx.try_send_data("".into()).unwrap(); | ||||
|             body | ||||
|         }; | ||||
|  | ||||
|                 let _res_rx = tx.try_send(crate::Request::new(body)).unwrap(); | ||||
|         let _res_rx = tx.try_send(crate::Request::new(body)).unwrap(); | ||||
|  | ||||
|                 // Ensure conn.write_body wasn't called with the empty chunk. | ||||
|                 // If it is, it will trigger an assertion. | ||||
|                 assert!(Pin::new(&mut dispatcher).poll(cx).is_pending()); | ||||
|             }); | ||||
|         }); | ||||
|         // Ensure conn.write_body wasn't called with the empty chunk. | ||||
|         // If it is, it will trigger an assertion. | ||||
|         assert!(dispatcher.poll().is_pending()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| use std::fmt; | ||||
| use std::io::IoSlice; | ||||
|  | ||||
| use bytes::{Buf, IntoBuf}; | ||||
| use bytes::buf::{Chain, Take}; | ||||
| use iovec::IoVec; | ||||
| use bytes::Buf; | ||||
| use bytes::buf::ext::{BufExt, Chain, Take}; | ||||
|  | ||||
| use crate::common::StaticBuf; | ||||
| use super::io::WriteBuf; | ||||
|  | ||||
| type StaticBuf = &'static [u8]; | ||||
|  | ||||
| /// Encoders to handle different Transfer-Encodings. | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct Encoder { | ||||
| @@ -84,17 +85,16 @@ impl Encoder { | ||||
|         match self.kind { | ||||
|             Kind::Length(0) => Ok(None), | ||||
|             Kind::Chunked => Ok(Some(EncodedBuf { | ||||
|                 kind: BufKind::ChunkedEnd(StaticBuf(b"0\r\n\r\n")), | ||||
|                 kind: BufKind::ChunkedEnd(b"0\r\n\r\n"), | ||||
|             })), | ||||
|             _ => Err(NotEof), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn encode<B>(&mut self, msg: B) -> EncodedBuf<B::Buf> | ||||
|     pub fn encode<B>(&mut self, msg: B) -> EncodedBuf<B> | ||||
|     where | ||||
|         B: IntoBuf, | ||||
|         B: Buf, | ||||
|     { | ||||
|         let msg = msg.into_buf(); | ||||
|         let len = msg.remaining(); | ||||
|         debug_assert!(len > 0, "encode() called with empty buf"); | ||||
|  | ||||
| @@ -103,7 +103,7 @@ impl Encoder { | ||||
|                 trace!("encoding chunked {}B", len); | ||||
|                 let buf = ChunkSize::new(len) | ||||
|                     .chain(msg) | ||||
|                     .chain(StaticBuf(b"\r\n")); | ||||
|                     .chain(b"\r\n" as &'static [u8]); | ||||
|                 BufKind::Chunked(buf) | ||||
|             }, | ||||
|             Kind::Length(ref mut remaining) => { | ||||
| @@ -127,11 +127,10 @@ impl Encoder { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B::Buf>>) -> bool | ||||
|     pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool | ||||
|     where | ||||
|         B: IntoBuf, | ||||
|         B: Buf, | ||||
|     { | ||||
|         let msg = msg.into_buf(); | ||||
|         let len = msg.remaining(); | ||||
|         debug_assert!(len > 0, "encode() called with empty buf"); | ||||
|  | ||||
| @@ -140,7 +139,7 @@ impl Encoder { | ||||
|                 trace!("encoding chunked {}B", len); | ||||
|                 let buf = ChunkSize::new(len) | ||||
|                     .chain(msg) | ||||
|                     .chain(StaticBuf(b"\r\n0\r\n\r\n")); | ||||
|                     .chain(b"\r\n0\r\n\r\n" as &'static [u8]); | ||||
|                 dst.buffer(buf); | ||||
|                 !self.is_last | ||||
|             }, | ||||
| @@ -176,11 +175,10 @@ impl Encoder { | ||||
|     /// This is used in conjunction with Payload::__hyper_full_data(), which | ||||
|     /// means we can trust that the buf has the correct size (the buf itself | ||||
|     /// was checked to make the headers). | ||||
|     pub(super) fn danger_full_buf<B>(self, msg: B, dst: &mut WriteBuf<EncodedBuf<B::Buf>>) | ||||
|     pub(super) fn danger_full_buf<B>(self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) | ||||
|     where | ||||
|         B: IntoBuf, | ||||
|         B: Buf, | ||||
|     { | ||||
|         let msg = msg.into_buf(); | ||||
|         debug_assert!(msg.remaining() > 0, "encode() called with empty buf"); | ||||
|         debug_assert!(match self.kind { | ||||
|             Kind::Length(len) => len == msg.remaining() as u64, | ||||
| @@ -193,7 +191,7 @@ impl Encoder { | ||||
|                 trace!("encoding chunked {}B", len); | ||||
|                 let buf = ChunkSize::new(len) | ||||
|                     .chain(msg) | ||||
|                     .chain(StaticBuf(b"\r\n0\r\n\r\n")); | ||||
|                     .chain(b"\r\n0\r\n\r\n" as &'static [u8]); | ||||
|                 dst.buffer(buf); | ||||
|             }, | ||||
|             _ => { | ||||
| @@ -238,12 +236,12 @@ where | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize { | ||||
|     fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { | ||||
|         match self.kind { | ||||
|             BufKind::Exact(ref b) => b.bytes_vec(dst), | ||||
|             BufKind::Limited(ref b) => b.bytes_vec(dst), | ||||
|             BufKind::Chunked(ref b) => b.bytes_vec(dst), | ||||
|             BufKind::ChunkedEnd(ref b) => b.bytes_vec(dst), | ||||
|             BufKind::Exact(ref b) => b.bytes_vectored(dst), | ||||
|             BufKind::Limited(ref b) => b.bytes_vectored(dst), | ||||
|             BufKind::Chunked(ref b) => b.bytes_vectored(dst), | ||||
|             BufKind::ChunkedEnd(ref b) => b.bytes_vectored(dst), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ use std::cell::Cell; | ||||
| use std::cmp; | ||||
| use std::collections::VecDeque; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::io::{self, IoSlice}; | ||||
|  | ||||
| use bytes::{Buf, BufMut, Bytes, BytesMut}; | ||||
| use iovec::IoVec; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use crate::common::{Pin, Poll, Unpin, task}; | ||||
| use super::{Http1Transaction, ParseContext, ParsedMessage}; | ||||
| @@ -105,6 +104,12 @@ where | ||||
|         &mut self.read_buf | ||||
|     } | ||||
|  | ||||
|     /// Return the "allocated" available space, not the potential space | ||||
|     /// that could be allocated in the future. | ||||
|     fn read_buf_remaining_mut(&self) -> usize { | ||||
|         self.read_buf.capacity() - self.read_buf.len() | ||||
|     } | ||||
|  | ||||
|     pub fn headers_buf(&mut self) -> &mut Vec<u8> { | ||||
|         let buf = self.write_buf.headers_mut(); | ||||
|         &mut buf.bytes | ||||
| @@ -170,7 +175,7 @@ where | ||||
|     pub fn poll_read_from_io(&mut self, cx: &mut task::Context<'_>) -> Poll<io::Result<usize>> { | ||||
|         self.read_blocked = false; | ||||
|         let next = self.read_buf_strategy.next(); | ||||
|         if self.read_buf.remaining_mut() < next { | ||||
|         if self.read_buf_remaining_mut() < next { | ||||
|             self.read_buf.reserve(next); | ||||
|         } | ||||
|         match Pin::new(&mut self.io).poll_read_buf(cx, &mut self.read_buf) { | ||||
| @@ -520,9 +525,9 @@ impl<B: Buf> Buf for WriteBuf<B> { | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize { | ||||
|         let n = self.headers.bytes_vec(dst); | ||||
|         self.queue.bytes_vec(&mut dst[n..]) + n | ||||
|     fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { | ||||
|         let n = self.headers.bytes_vectored(dst); | ||||
|         self.queue.bytes_vectored(&mut dst[n..]) + n | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -562,9 +567,9 @@ impl<'a, B: Buf> Buf for WriteBufAuto<'a, B> { | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize { | ||||
|     fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { | ||||
|         self.bytes_vec_called.set(true); | ||||
|         self.inner.bytes_vec(dst) | ||||
|         self.inner.bytes_vectored(dst) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -638,13 +643,13 @@ impl<T: Buf> Buf for BufDeque<T> { | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize { | ||||
|     fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { | ||||
|         if dst.is_empty() { | ||||
|             return 0; | ||||
|         } | ||||
|         let mut vecs = 0; | ||||
|         for buf in &self.bufs { | ||||
|             vecs += buf.bytes_vec(&mut dst[vecs..]); | ||||
|             vecs += buf.bytes_vectored(&mut dst[vecs..]); | ||||
|             if vecs == dst.len() { | ||||
|                 break; | ||||
|             } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ macro_rules! header_name { | ||||
|         { | ||||
|             match HeaderName::from_bytes($bytes) { | ||||
|                 Ok(name) => name, | ||||
|                 Err(_) => panic!("illegal header name from httparse: {:?}", ::bytes::Bytes::from($bytes)), | ||||
|                 Err(_) => panic!("illegal header name from httparse: {:?}", ::bytes::Bytes::copy_from_slice($bytes)), | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -40,7 +40,7 @@ macro_rules! header_value { | ||||
|         #[cfg(debug_assertions)] | ||||
|         { | ||||
|             let __hvb: ::bytes::Bytes = $bytes; | ||||
|             match HeaderValue::from_shared(__hvb.clone()) { | ||||
|             match HeaderValue::from_maybe_shared(__hvb.clone()) { | ||||
|                 Ok(name) => name, | ||||
|                 Err(_) => panic!("illegal header value from httparse: {:?}", __hvb), | ||||
|             } | ||||
| @@ -50,7 +50,7 @@ macro_rules! header_value { | ||||
|         { | ||||
|             // Unsafe: httparse already validated header value | ||||
|             unsafe { | ||||
|                 HeaderValue::from_shared_unchecked($bytes) | ||||
|                 HeaderValue::from_maybe_shared_unchecked($bytes) | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| @@ -153,7 +153,7 @@ impl Http1Transaction for Server { | ||||
|  | ||||
|         for header in &headers_indices[..headers_len] { | ||||
|             let name = header_name!(&slice[header.name.0..header.name.1]); | ||||
|             let value = header_value!(slice.slice(header.value.0, header.value.1)); | ||||
|             let value = header_value!(slice.slice(header.value.0..header.value.1)); | ||||
|  | ||||
|             match name { | ||||
|                 header::TRANSFER_ENCODING => { | ||||
| @@ -302,10 +302,40 @@ impl Http1Transaction for Server { | ||||
|  | ||||
|         let mut encoder = Encoder::length(0); | ||||
|         let mut wrote_date = false; | ||||
|         'headers: for (name, mut values) in msg.head.headers.drain() { | ||||
|             match name { | ||||
|         let mut cur_name = None; | ||||
|         let mut is_name_written = false; | ||||
|         let mut must_write_chunked = false; | ||||
|         let mut prev_con_len = None; | ||||
|  | ||||
|         macro_rules! handle_is_name_written { | ||||
|             () => ({ | ||||
|                 if is_name_written { | ||||
|                     // we need to clean up and write the newline | ||||
|                     debug_assert_ne!( | ||||
|                         &dst[dst.len() - 2 ..], | ||||
|                         b"\r\n", | ||||
|                         "previous header wrote newline but set is_name_written" | ||||
|                     ); | ||||
|  | ||||
|                     if must_write_chunked { | ||||
|                         extend(dst, b", chunked\r\n"); | ||||
|                     } else { | ||||
|                         extend(dst, b"\r\n"); | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         'headers: for (opt_name, value) in msg.head.headers.drain() { | ||||
|             if let Some(n) = opt_name { | ||||
|                 cur_name = Some(n); | ||||
|                 handle_is_name_written!(); | ||||
|                 is_name_written = false; | ||||
|             } | ||||
|             let name = cur_name.as_ref().expect("current header name"); | ||||
|             match *name { | ||||
|                 header::CONTENT_LENGTH => { | ||||
|                     if wrote_len { | ||||
|                     if wrote_len && !is_name_written { | ||||
|                         warn!("unexpected content-length found, canceling"); | ||||
|                         rewind(dst); | ||||
|                         return Err(crate::Error::new_user_header()); | ||||
| @@ -319,77 +349,56 @@ impl Http1Transaction for Server { | ||||
|                             // | ||||
|                             // In debug builds, we'll assert they are the | ||||
|                             // same to help developers find bugs. | ||||
|                             encoder = Encoder::length(known_len); | ||||
|  | ||||
|                             #[cfg(debug_assertions)] | ||||
|                             { | ||||
|                                 let mut folded = None::<(u64, HeaderValue)>; | ||||
|                                 for value in values { | ||||
|                                     if let Some(len) = headers::content_length_parse(&value) { | ||||
|                                         if let Some(fold) = folded { | ||||
|                                             if fold.0 != len { | ||||
|                                                 panic!("multiple Content-Length values found: [{}, {}]", fold.0, len); | ||||
|                                             } | ||||
|                                             folded = Some(fold); | ||||
|                                         } else { | ||||
|                                             folded = Some((len, value)); | ||||
|                                         } | ||||
|                                     } else { | ||||
|                                         panic!("illegal Content-Length value: {:?}", value); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 if let Some((len, value)) = folded { | ||||
|                                 if let Some(len) = headers::content_length_parse(&value) { | ||||
|                                     assert!( | ||||
|                                         len == known_len, | ||||
|                                         "payload claims content-length of {}, custom content-length header claims {}", | ||||
|                                         known_len, | ||||
|                                         len, | ||||
|                                     ); | ||||
|                                     extend(dst, b"content-length: "); | ||||
|                                     extend(dst, value.as_bytes()); | ||||
|                                     extend(dst, b"\r\n"); | ||||
|                                     wrote_len = true; | ||||
|                                     continue 'headers; | ||||
|                                 } else { | ||||
|                                     // No values in content-length... ignore? | ||||
|                                     continue 'headers; | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                             if !is_name_written { | ||||
|                                 encoder = Encoder::length(known_len); | ||||
|                                 extend(dst, b"content-length: "); | ||||
|                                 extend(dst, value.as_bytes()); | ||||
|                                 wrote_len = true; | ||||
|                                 is_name_written = true; | ||||
|                             } | ||||
|                             continue 'headers; | ||||
|                         }, | ||||
|                         Some(BodyLength::Unknown) => { | ||||
|                             // The Payload impl didn't know how long the | ||||
|                             // body is, but a length header was included. | ||||
|                             // We have to parse the value to return our | ||||
|                             // Encoder... | ||||
|                             let mut folded = None::<(u64, HeaderValue)>; | ||||
|                             for value in values { | ||||
|                                 if let Some(len) = headers::content_length_parse(&value) { | ||||
|                                     if let Some(fold) = folded { | ||||
|                                         if fold.0 != len { | ||||
|                                             warn!("multiple Content-Length values found: [{}, {}]", fold.0, len); | ||||
|                                             rewind(dst); | ||||
|                                             return Err(crate::Error::new_user_header()); | ||||
|                                         } | ||||
|                                         folded = Some(fold); | ||||
|                                     } else { | ||||
|                                         folded = Some((len, value)); | ||||
|  | ||||
|                             if let Some(len) = headers::content_length_parse(&value) { | ||||
|                                 if let Some(prev) = prev_con_len { | ||||
|                                     if prev != len { | ||||
|                                         warn!("multiple Content-Length values found: [{}, {}]", prev, len); | ||||
|                                         rewind(dst); | ||||
|                                         return Err(crate::Error::new_user_header()); | ||||
|                                     } | ||||
|                                     debug_assert!(is_name_written); | ||||
|                                     continue 'headers; | ||||
|                                 } else { | ||||
|                                     warn!("illegal Content-Length value: {:?}", value); | ||||
|                                     rewind(dst); | ||||
|                                     return Err(crate::Error::new_user_header()); | ||||
|                                     // we haven't written content-lenght yet! | ||||
|                                     encoder = Encoder::length(len); | ||||
|                                     extend(dst, b"content-length: "); | ||||
|                                     extend(dst, value.as_bytes()); | ||||
|                                     wrote_len = true; | ||||
|                                     is_name_written = true; | ||||
|                                     prev_con_len = Some(len); | ||||
|                                     continue 'headers; | ||||
|                                 } | ||||
|                             } | ||||
|                             if let Some((len, value)) = folded { | ||||
|                                 encoder = Encoder::length(len); | ||||
|                                 extend(dst, b"content-length: "); | ||||
|                                 extend(dst, value.as_bytes()); | ||||
|                                 extend(dst, b"\r\n"); | ||||
|                                 wrote_len = true; | ||||
|                                 continue 'headers; | ||||
|                             } else { | ||||
|                                 // No values in content-length... ignore? | ||||
|                                 continue 'headers; | ||||
|                                 warn!("illegal Content-Length value: {:?}", value); | ||||
|                                 rewind(dst); | ||||
|                                 return Err(crate::Error::new_user_header()); | ||||
|                             } | ||||
|                         }, | ||||
|                         None => { | ||||
| @@ -402,10 +411,8 @@ impl Http1Transaction for Server { | ||||
|                             if msg.req_method == &Some(Method::HEAD) { | ||||
|                                 debug_assert_eq!(encoder, Encoder::length(0)); | ||||
|                             } else { | ||||
|                                 for value in values { | ||||
|                                     if value.as_bytes() != b"0" { | ||||
|                                         warn!("content-length value found, but empty body provided: {:?}", value); | ||||
|                                     } | ||||
|                                 if value.as_bytes() != b"0" { | ||||
|                                     warn!("content-length value found, but empty body provided: {:?}", value); | ||||
|                                 } | ||||
|                                 continue 'headers; | ||||
|                             } | ||||
| @@ -414,7 +421,7 @@ impl Http1Transaction for Server { | ||||
|                     wrote_len = true; | ||||
|                 }, | ||||
|                 header::TRANSFER_ENCODING => { | ||||
|                     if wrote_len { | ||||
|                     if wrote_len && !is_name_written { | ||||
|                         warn!("unexpected transfer-encoding found, canceling"); | ||||
|                         rewind(dst); | ||||
|                         return Err(crate::Error::new_user_header()); | ||||
| @@ -424,44 +431,36 @@ impl Http1Transaction for Server { | ||||
|                         continue; | ||||
|                     } | ||||
|                     wrote_len = true; | ||||
|                     encoder = Encoder::chunked(); | ||||
|                     // Must check each value, because `chunked` needs to be the | ||||
|                     // last encoding, or else we add it. | ||||
|                     must_write_chunked = !headers::is_chunked_(&value); | ||||
|  | ||||
|                     extend(dst, b"transfer-encoding: "); | ||||
|  | ||||
|                     let mut saw_chunked; | ||||
|                     if let Some(te) = values.next() { | ||||
|                         extend(dst, te.as_bytes()); | ||||
|                         saw_chunked = headers::is_chunked_(&te); | ||||
|                         for value in values { | ||||
|                             extend(dst, b", "); | ||||
|                             extend(dst, value.as_bytes()); | ||||
|                             saw_chunked = headers::is_chunked_(&value); | ||||
|                         } | ||||
|                         if !saw_chunked { | ||||
|                             extend(dst, b", chunked\r\n"); | ||||
|                         } else { | ||||
|                             extend(dst, b"\r\n"); | ||||
|                         } | ||||
|                     if !is_name_written { | ||||
|                         encoder = Encoder::chunked(); | ||||
|                         is_name_written = true; | ||||
|                         extend(dst, b"transfer-encoding: "); | ||||
|                         extend(dst, value.as_bytes()); | ||||
|                     } else { | ||||
|                         // zero lines? add a chunked line then | ||||
|                         extend(dst, b"chunked\r\n"); | ||||
|                         extend(dst, b", "); | ||||
|                         extend(dst, value.as_bytes()); | ||||
|                     } | ||||
|                     continue 'headers; | ||||
|                 }, | ||||
|                 header::CONNECTION => { | ||||
|                     if !is_last { | ||||
|                         for value in values { | ||||
|                             extend(dst, name.as_str().as_bytes()); | ||||
|                             extend(dst, b": "); | ||||
|                             extend(dst, value.as_bytes()); | ||||
|                             extend(dst, b"\r\n"); | ||||
|  | ||||
|                             if headers::connection_close(&value) { | ||||
|                                 is_last = true; | ||||
|                             } | ||||
|                         if headers::connection_close(&value) { | ||||
|                             is_last = true; | ||||
|                         } | ||||
|                         continue 'headers; | ||||
|                     } | ||||
|                     if !is_name_written { | ||||
|                         is_name_written = true; | ||||
|                         extend(dst, b"connection: "); | ||||
|                         extend(dst, value.as_bytes()); | ||||
|                     } else { | ||||
|                         extend(dst, b", "); | ||||
|                         extend(dst, value.as_bytes()); | ||||
|                     } | ||||
|                     continue 'headers; | ||||
|                 }, | ||||
|                 header::DATE => { | ||||
|                     wrote_date = true; | ||||
| @@ -470,14 +469,21 @@ impl Http1Transaction for Server { | ||||
|             } | ||||
|             //TODO: this should perhaps instead combine them into | ||||
|             //single lines, as RFC7230 suggests is preferable. | ||||
|             for value in values { | ||||
|                 extend(dst, name.as_str().as_bytes()); | ||||
|                 extend(dst, b": "); | ||||
|                 extend(dst, value.as_bytes()); | ||||
|                 extend(dst, b"\r\n"); | ||||
|             } | ||||
|  | ||||
|             // non-special write Name and Value | ||||
|             debug_assert!( | ||||
|                 !is_name_written, | ||||
|                 "{:?} set is_name_written and didn't continue loop", | ||||
|                 name, | ||||
|             ); | ||||
|             extend(dst, name.as_str().as_bytes()); | ||||
|             extend(dst, b": "); | ||||
|             extend(dst, value.as_bytes()); | ||||
|             extend(dst, b"\r\n"); | ||||
|         } | ||||
|  | ||||
|         handle_is_name_written!(); | ||||
|  | ||||
|         if !wrote_len { | ||||
|             encoder = match msg.body { | ||||
|                 Some(BodyLength::Unknown) => { | ||||
| @@ -629,7 +635,7 @@ impl Http1Transaction for Client { | ||||
|             headers.reserve(headers_len); | ||||
|             for header in &headers_indices[..headers_len] { | ||||
|                 let name = header_name!(&slice[header.name.0..header.name.1]); | ||||
|                 let value = header_value!(slice.slice(header.value.0, header.value.1)); | ||||
|                 let value = header_value!(slice.slice(header.value.0..header.value.1)); | ||||
|  | ||||
|                 if let header::CONNECTION = name { | ||||
|                     // keep_alive was previously set to default for Version | ||||
| @@ -820,8 +826,7 @@ impl Client { | ||||
|  | ||||
|         // If the user set a transfer-encoding, respect that. Let's just | ||||
|         // make sure `chunked` is the final encoding. | ||||
|         let encoder = match headers.entry(header::TRANSFER_ENCODING) | ||||
|             .expect("TRANSFER_ENCODING is valid HeaderName") { | ||||
|         let encoder = match headers.entry(header::TRANSFER_ENCODING) { | ||||
|             Entry::Occupied(te) => { | ||||
|                 should_remove_con_len = true; | ||||
|                 if headers::is_chunked(te.iter()) { | ||||
| @@ -906,8 +911,7 @@ fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { | ||||
|     // so perhaps only do that while the user is developing/testing. | ||||
|  | ||||
|     if cfg!(debug_assertions) { | ||||
|         match headers.entry(header::CONTENT_LENGTH) | ||||
|             .expect("CONTENT_LENGTH is valid HeaderName") { | ||||
|         match headers.entry(header::CONTENT_LENGTH) { | ||||
|             Entry::Occupied(mut cl) => { | ||||
|                 // Internal sanity check, we should have already determined | ||||
|                 // that the header was illegal before calling this function. | ||||
| @@ -1067,7 +1071,7 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_parse_request() { | ||||
|         let _ = pretty_env_logger::try_init(); | ||||
|         let mut raw = BytesMut::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec()); | ||||
|         let mut raw = BytesMut::from("GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); | ||||
|         let mut method = None; | ||||
|         let msg = Server::parse(&mut raw, ParseContext { | ||||
|             cached_headers: &mut None, | ||||
| @@ -1086,7 +1090,7 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_parse_response() { | ||||
|         let _ = pretty_env_logger::try_init(); | ||||
|         let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec()); | ||||
|         let mut raw = BytesMut::from("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); | ||||
|         let ctx = ParseContext { | ||||
|             cached_headers: &mut None, | ||||
|             req_method: &mut Some(crate::Method::GET), | ||||
| @@ -1101,7 +1105,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_request_errors() { | ||||
|         let mut raw = BytesMut::from(b"GET htt:p// HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec()); | ||||
|         let mut raw = BytesMut::from("GET htt:p// HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); | ||||
|         let ctx = ParseContext { | ||||
|             cached_headers: &mut None, | ||||
|             req_method: &mut None, | ||||
| @@ -1480,7 +1484,7 @@ mod tests { | ||||
|     #[bench] | ||||
|     fn bench_parse_incoming(b: &mut Bencher) { | ||||
|         let mut raw = BytesMut::from( | ||||
|             b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ | ||||
|             &b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ | ||||
|             I_wonder/Hard_to_write_in_an_uri_after_all/you_have_to_make\ | ||||
|             _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\ | ||||
|             foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1\r\nHost: \ | ||||
| @@ -1496,7 +1500,7 @@ mod tests { | ||||
|             X-Content-Duration: None\r\nX-Content-Security-Policy: None\ | ||||
|             \r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \ | ||||
|             Something important obviously\r\nX-Requested-With: Nothing\ | ||||
|             \r\n\r\n".to_vec() | ||||
|             \r\n\r\n"[..] | ||||
|         ); | ||||
|         let len = raw.len(); | ||||
|         let mut headers = Some(HeaderMap::new()); | ||||
| @@ -1526,7 +1530,7 @@ mod tests { | ||||
|     #[bench] | ||||
|     fn bench_parse_short(b: &mut Bencher) { | ||||
|         let s = &b"GET / HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"[..]; | ||||
|         let mut raw = BytesMut::from(s.to_vec()); | ||||
|         let mut raw = BytesMut::from(s); | ||||
|         let len = raw.len(); | ||||
|         let mut headers = Some(HeaderMap::new()); | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ use futures_channel::{mpsc, oneshot}; | ||||
| use futures_util::future::{self, FutureExt as _, TryFutureExt as _, Either}; | ||||
| use futures_util::stream::StreamExt as _; | ||||
| use h2::client::{Builder, SendRequest}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use crate::headers::content_length_parse_all; | ||||
| use crate::body::Payload; | ||||
| @@ -71,7 +71,7 @@ where | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     exec.execute(conn_task)?; | ||||
|     exec.execute(conn_task); | ||||
|  | ||||
|     Ok(ClientTask { | ||||
|         conn_drop_ref, | ||||
| @@ -155,7 +155,7 @@ where | ||||
|                                         drop(conn_drop_ref); | ||||
|                                         x | ||||
|                                     }); | ||||
|                                 self.executor.execute(pipe)?; | ||||
|                                 self.executor.execute(pipe); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| @@ -175,7 +175,7 @@ where | ||||
|                                 } | ||||
|                             } | ||||
|                         }); | ||||
|                     self.executor.execute(cb.send_when(fut))?; | ||||
|                     self.executor.execute(cb.send_when(fut)); | ||||
|                     continue; | ||||
|                 }, | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use std::marker::Unpin; | ||||
| use pin_project::{pin_project, project}; | ||||
| use h2::Reason; | ||||
| use h2::server::{Builder, Connection, Handshake, SendResponse}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use crate::body::Payload; | ||||
| use crate::common::exec::H2Exec; | ||||
| @@ -175,7 +175,7 @@ where | ||||
|                             crate::Body::h2(stream, content_length) | ||||
|                         }); | ||||
|                         let fut = H2Stream::new(service.call(req), respond); | ||||
|                         exec.execute_h2stream(fut)?; | ||||
|                         exec.execute_h2stream(fut); | ||||
|                     }, | ||||
|                     Some(Err(e)) => { | ||||
|                         return Poll::Ready(Err(crate::Error::new_h2(e))); | ||||
| @@ -285,7 +285,6 @@ where | ||||
|                     res | ||||
|                         .headers_mut() | ||||
|                         .entry(::http::header::DATE) | ||||
|                         .expect("DATE is a valid HeaderName") | ||||
|                         .or_insert_with(crate::proto::h1::date::update_and_header_value); | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user