diff options
Diffstat (limited to 'vendor/hyper/src/proto/h1/encode.rs')
| -rw-r--r-- | vendor/hyper/src/proto/h1/encode.rs | 660 |
1 files changed, 0 insertions, 660 deletions
diff --git a/vendor/hyper/src/proto/h1/encode.rs b/vendor/hyper/src/proto/h1/encode.rs deleted file mode 100644 index 2df0c396..00000000 --- a/vendor/hyper/src/proto/h1/encode.rs +++ /dev/null @@ -1,660 +0,0 @@ -use std::collections::HashMap; -use std::fmt; -use std::io::IoSlice; - -use bytes::buf::{Chain, Take}; -use bytes::{Buf, Bytes}; -use http::{ - header::{ - AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE, - CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING, - }, - HeaderMap, HeaderName, HeaderValue, -}; - -use super::io::WriteBuf; -use super::role::{write_headers, write_headers_title_case}; - -type StaticBuf = &'static [u8]; - -/// Encoders to handle different Transfer-Encodings. -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct Encoder { - kind: Kind, - is_last: bool, -} - -#[derive(Debug)] -pub(crate) struct EncodedBuf<B> { - kind: BufKind<B>, -} - -#[derive(Debug)] -pub(crate) struct NotEof(u64); - -#[derive(Debug, PartialEq, Clone)] -enum Kind { - /// An Encoder for when Transfer-Encoding includes `chunked`. - Chunked(Option<Vec<HeaderValue>>), - /// An Encoder for when Content-Length is set. - /// - /// Enforces that the body is not longer than the Content-Length header. - Length(u64), - /// An Encoder for when neither Content-Length nor Chunked encoding is set. - /// - /// 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. - #[cfg(feature = "server")] - CloseDelimited, -} - -#[derive(Debug)] -enum BufKind<B> { - Exact(B), - Limited(Take<B>), - Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>), - ChunkedEnd(StaticBuf), - Trailers(Chain<Chain<StaticBuf, Bytes>, StaticBuf>), -} - -impl Encoder { - fn new(kind: Kind) -> Encoder { - Encoder { - kind, - is_last: false, - } - } - pub(crate) fn chunked() -> Encoder { - Encoder::new(Kind::Chunked(None)) - } - - pub(crate) fn length(len: u64) -> Encoder { - Encoder::new(Kind::Length(len)) - } - - #[cfg(feature = "server")] - pub(crate) fn close_delimited() -> Encoder { - Encoder::new(Kind::CloseDelimited) - } - - pub(crate) fn into_chunked_with_trailing_fields(self, trailers: Vec<HeaderValue>) -> Encoder { - match self.kind { - Kind::Chunked(_) => Encoder { - kind: Kind::Chunked(Some(trailers)), - is_last: self.is_last, - }, - _ => self, - } - } - - pub(crate) fn is_eof(&self) -> bool { - matches!(self.kind, Kind::Length(0)) - } - - #[cfg(feature = "server")] - pub(crate) fn set_last(mut self, is_last: bool) -> Self { - self.is_last = is_last; - self - } - - pub(crate) fn is_last(&self) -> bool { - self.is_last - } - - pub(crate) fn is_close_delimited(&self) -> bool { - match self.kind { - #[cfg(feature = "server")] - Kind::CloseDelimited => true, - _ => false, - } - } - - pub(crate) fn is_chunked(&self) -> bool { - matches!(self.kind, Kind::Chunked(_)) - } - - pub(crate) fn end<B>(&self) -> Result<Option<EncodedBuf<B>>, NotEof> { - match self.kind { - Kind::Length(0) => Ok(None), - Kind::Chunked(_) => Ok(Some(EncodedBuf { - kind: BufKind::ChunkedEnd(b"0\r\n\r\n"), - })), - #[cfg(feature = "server")] - Kind::CloseDelimited => Ok(None), - Kind::Length(n) => Err(NotEof(n)), - } - } - - pub(crate) fn encode<B>(&mut self, msg: B) -> EncodedBuf<B> - where - B: Buf, - { - let len = msg.remaining(); - debug_assert!(len > 0, "encode() called with empty buf"); - - let kind = match self.kind { - Kind::Chunked(_) => { - trace!("encoding chunked {}B", len); - let buf = ChunkSize::new(len) - .chain(msg) - .chain(b"\r\n" as &'static [u8]); - BufKind::Chunked(buf) - } - Kind::Length(ref mut remaining) => { - trace!("sized write, len = {}", len); - if len as u64 > *remaining { - let limit = *remaining as usize; - *remaining = 0; - BufKind::Limited(msg.take(limit)) - } else { - *remaining -= len as u64; - BufKind::Exact(msg) - } - } - #[cfg(feature = "server")] - Kind::CloseDelimited => { - trace!("close delimited write {}B", len); - BufKind::Exact(msg) - } - }; - EncodedBuf { kind } - } - - pub(crate) fn encode_trailers<B>( - &self, - trailers: HeaderMap, - title_case_headers: bool, - ) -> Option<EncodedBuf<B>> { - trace!("encoding trailers"); - match &self.kind { - Kind::Chunked(Some(allowed_trailer_fields)) => { - let allowed_trailer_field_map = allowed_trailer_field_map(allowed_trailer_fields); - - let mut cur_name = None; - let mut allowed_trailers = HeaderMap::new(); - - for (opt_name, value) in trailers { - if let Some(n) = opt_name { - cur_name = Some(n); - } - let name = cur_name.as_ref().expect("current header name"); - - if allowed_trailer_field_map.contains_key(name.as_str()) { - if is_valid_trailer_field(name) { - allowed_trailers.insert(name, value); - } else { - debug!("trailer field is not valid: {}", &name); - } - } else { - debug!("trailer header name not found in trailer header: {}", &name); - } - } - - let mut buf = Vec::new(); - if title_case_headers { - write_headers_title_case(&allowed_trailers, &mut buf); - } else { - write_headers(&allowed_trailers, &mut buf); - } - - if buf.is_empty() { - return None; - } - - Some(EncodedBuf { - kind: BufKind::Trailers(b"0\r\n".chain(Bytes::from(buf)).chain(b"\r\n")), - }) - } - Kind::Chunked(None) => { - debug!("attempted to encode trailers, but the trailer header is not set"); - None - } - _ => { - debug!("attempted to encode trailers for non-chunked response"); - None - } - } - } - - pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool - where - B: Buf, - { - let len = msg.remaining(); - debug_assert!(len > 0, "encode() called with empty buf"); - - match self.kind { - Kind::Chunked(_) => { - trace!("encoding chunked {}B", len); - let buf = ChunkSize::new(len) - .chain(msg) - .chain(b"\r\n0\r\n\r\n" as &'static [u8]); - dst.buffer(buf); - !self.is_last - } - Kind::Length(remaining) => { - use std::cmp::Ordering; - - trace!("sized write, len = {}", len); - match (len as u64).cmp(&remaining) { - Ordering::Equal => { - dst.buffer(msg); - !self.is_last - } - Ordering::Greater => { - dst.buffer(msg.take(remaining as usize)); - !self.is_last - } - Ordering::Less => { - dst.buffer(msg); - false - } - } - } - #[cfg(feature = "server")] - Kind::CloseDelimited => { - trace!("close delimited write {}B", len); - dst.buffer(msg); - false - } - } - } -} - -fn is_valid_trailer_field(name: &HeaderName) -> bool { - !matches!( - *name, - AUTHORIZATION - | CACHE_CONTROL - | CONTENT_ENCODING - | CONTENT_LENGTH - | CONTENT_RANGE - | CONTENT_TYPE - | HOST - | MAX_FORWARDS - | SET_COOKIE - | TRAILER - | TRANSFER_ENCODING - | TE - ) -} - -fn allowed_trailer_field_map(allowed_trailer_fields: &Vec<HeaderValue>) -> HashMap<String, ()> { - let mut trailer_map = HashMap::new(); - - for header_value in allowed_trailer_fields { - if let Ok(header_str) = header_value.to_str() { - let items: Vec<&str> = header_str.split(',').map(|item| item.trim()).collect(); - - for item in items { - trailer_map.entry(item.to_string()).or_insert(()); - } - } - } - - trailer_map -} - -impl<B> Buf for EncodedBuf<B> -where - B: Buf, -{ - #[inline] - fn remaining(&self) -> usize { - match self.kind { - BufKind::Exact(ref b) => b.remaining(), - BufKind::Limited(ref b) => b.remaining(), - BufKind::Chunked(ref b) => b.remaining(), - BufKind::ChunkedEnd(ref b) => b.remaining(), - BufKind::Trailers(ref b) => b.remaining(), - } - } - - #[inline] - fn chunk(&self) -> &[u8] { - match self.kind { - BufKind::Exact(ref b) => b.chunk(), - BufKind::Limited(ref b) => b.chunk(), - BufKind::Chunked(ref b) => b.chunk(), - BufKind::ChunkedEnd(ref b) => b.chunk(), - BufKind::Trailers(ref b) => b.chunk(), - } - } - - #[inline] - fn advance(&mut self, cnt: usize) { - match self.kind { - BufKind::Exact(ref mut b) => b.advance(cnt), - BufKind::Limited(ref mut b) => b.advance(cnt), - BufKind::Chunked(ref mut b) => b.advance(cnt), - BufKind::ChunkedEnd(ref mut b) => b.advance(cnt), - BufKind::Trailers(ref mut b) => b.advance(cnt), - } - } - - #[inline] - fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { - match self.kind { - BufKind::Exact(ref b) => b.chunks_vectored(dst), - BufKind::Limited(ref b) => b.chunks_vectored(dst), - BufKind::Chunked(ref b) => b.chunks_vectored(dst), - BufKind::ChunkedEnd(ref b) => b.chunks_vectored(dst), - BufKind::Trailers(ref b) => b.chunks_vectored(dst), - } - } -} - -#[cfg(target_pointer_width = "32")] -const USIZE_BYTES: usize = 4; - -#[cfg(target_pointer_width = "64")] -const USIZE_BYTES: usize = 8; - -// each byte will become 2 hex -const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2; - -#[derive(Clone, Copy)] -struct ChunkSize { - bytes: [u8; CHUNK_SIZE_MAX_BYTES + 2], - pos: u8, - len: u8, -} - -impl ChunkSize { - fn new(len: usize) -> ChunkSize { - use std::fmt::Write; - let mut size = ChunkSize { - bytes: [0; CHUNK_SIZE_MAX_BYTES + 2], - pos: 0, - len: 0, - }; - write!(&mut size, "{:X}\r\n", len).expect("CHUNK_SIZE_MAX_BYTES should fit any usize"); - size - } -} - -impl Buf for ChunkSize { - #[inline] - fn remaining(&self) -> usize { - (self.len - self.pos).into() - } - - #[inline] - fn chunk(&self) -> &[u8] { - &self.bytes[self.pos.into()..self.len.into()] - } - - #[inline] - fn advance(&mut self, cnt: usize) { - assert!(cnt <= self.remaining()); - self.pos += cnt as u8; // just asserted cnt fits in u8 - } -} - -impl fmt::Debug for ChunkSize { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ChunkSize") - .field("bytes", &&self.bytes[..self.len.into()]) - .field("pos", &self.pos) - .finish() - } -} - -impl fmt::Write for ChunkSize { - fn write_str(&mut self, num: &str) -> fmt::Result { - use std::io::Write; - (&mut self.bytes[self.len.into()..]) - .write_all(num.as_bytes()) - .expect("&mut [u8].write() cannot error"); - self.len += num.len() as u8; // safe because bytes is never bigger than 256 - Ok(()) - } -} - -impl<B: Buf> From<B> for EncodedBuf<B> { - fn from(buf: B) -> Self { - EncodedBuf { - kind: BufKind::Exact(buf), - } - } -} - -impl<B: Buf> From<Take<B>> for EncodedBuf<B> { - fn from(buf: Take<B>) -> Self { - EncodedBuf { - kind: BufKind::Limited(buf), - } - } -} - -impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> { - fn from(buf: Chain<Chain<ChunkSize, B>, StaticBuf>) -> Self { - EncodedBuf { - kind: BufKind::Chunked(buf), - } - } -} - -impl fmt::Display for NotEof { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "early end, expected {} more bytes", self.0) - } -} - -impl std::error::Error for NotEof {} - -#[cfg(test)] -mod tests { - use bytes::BufMut; - use http::{ - header::{ - AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE, - CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING, - }, - HeaderMap, HeaderName, HeaderValue, - }; - - use super::super::io::Cursor; - use super::Encoder; - - #[test] - fn chunked() { - let mut encoder = Encoder::chunked(); - let mut dst = Vec::new(); - - let msg1 = b"foo bar".as_ref(); - let buf1 = encoder.encode(msg1); - dst.put(buf1); - assert_eq!(dst, b"7\r\nfoo bar\r\n"); - - let msg2 = b"baz quux herp".as_ref(); - let buf2 = encoder.encode(msg2); - dst.put(buf2); - - assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n"); - - let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap(); - dst.put(end); - - assert_eq!( - dst, - b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref() - ); - } - - #[test] - fn length() { - let max_len = 8; - let mut encoder = Encoder::length(max_len as u64); - let mut dst = Vec::new(); - - let msg1 = b"foo bar".as_ref(); - let buf1 = encoder.encode(msg1); - dst.put(buf1); - - assert_eq!(dst, b"foo bar"); - assert!(!encoder.is_eof()); - encoder.end::<()>().unwrap_err(); - - let msg2 = b"baz".as_ref(); - let buf2 = encoder.encode(msg2); - dst.put(buf2); - - assert_eq!(dst.len(), max_len); - assert_eq!(dst, b"foo barb"); - assert!(encoder.is_eof()); - assert!(encoder.end::<()>().unwrap().is_none()); - } - - #[cfg(feature = "server")] - #[test] - fn eof() { - let mut encoder = Encoder::close_delimited(); - let mut dst = Vec::new(); - - let msg1 = b"foo bar".as_ref(); - let buf1 = encoder.encode(msg1); - dst.put(buf1); - - assert_eq!(dst, b"foo bar"); - assert!(!encoder.is_eof()); - encoder.end::<()>().unwrap(); - - let msg2 = b"baz".as_ref(); - let buf2 = encoder.encode(msg2); - dst.put(buf2); - - assert_eq!(dst, b"foo barbaz"); - assert!(!encoder.is_eof()); - encoder.end::<()>().unwrap(); - } - - #[test] - fn chunked_with_valid_trailers() { - let encoder = Encoder::chunked(); - let trailers = vec![HeaderValue::from_static("chunky-trailer")]; - let encoder = encoder.into_chunked_with_trailing_fields(trailers); - - let headers = HeaderMap::from_iter(vec![ - ( - HeaderName::from_static("chunky-trailer"), - HeaderValue::from_static("header data"), - ), - ( - HeaderName::from_static("should-not-be-included"), - HeaderValue::from_static("oops"), - ), - ]); - - let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap(); - - let mut dst = Vec::new(); - dst.put(buf1); - assert_eq!(dst, b"0\r\nchunky-trailer: header data\r\n\r\n"); - } - - #[test] - fn chunked_with_multiple_trailer_headers() { - let encoder = Encoder::chunked(); - let trailers = vec![ - HeaderValue::from_static("chunky-trailer"), - HeaderValue::from_static("chunky-trailer-2"), - ]; - let encoder = encoder.into_chunked_with_trailing_fields(trailers); - - let headers = HeaderMap::from_iter(vec![ - ( - HeaderName::from_static("chunky-trailer"), - HeaderValue::from_static("header data"), - ), - ( - HeaderName::from_static("chunky-trailer-2"), - HeaderValue::from_static("more header data"), - ), - ]); - - let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap(); - - let mut dst = Vec::new(); - dst.put(buf1); - assert_eq!( - dst, - b"0\r\nchunky-trailer: header data\r\nchunky-trailer-2: more header data\r\n\r\n" - ); - } - - #[test] - fn chunked_with_no_trailer_header() { - let encoder = Encoder::chunked(); - - let headers = HeaderMap::from_iter(vec![( - HeaderName::from_static("chunky-trailer"), - HeaderValue::from_static("header data"), - )]); - - assert!(encoder - .encode_trailers::<&[u8]>(headers.clone(), false) - .is_none()); - - let trailers = vec![]; - let encoder = encoder.into_chunked_with_trailing_fields(trailers); - - assert!(encoder.encode_trailers::<&[u8]>(headers, false).is_none()); - } - - #[test] - fn chunked_with_invalid_trailers() { - let encoder = Encoder::chunked(); - - let trailers = format!( - "{},{},{},{},{},{},{},{},{},{},{},{}", - AUTHORIZATION, - CACHE_CONTROL, - CONTENT_ENCODING, - CONTENT_LENGTH, - CONTENT_RANGE, - CONTENT_TYPE, - HOST, - MAX_FORWARDS, - SET_COOKIE, - TRAILER, - TRANSFER_ENCODING, - TE, - ); - let trailers = vec![HeaderValue::from_str(&trailers).unwrap()]; - let encoder = encoder.into_chunked_with_trailing_fields(trailers); - - let mut headers = HeaderMap::new(); - headers.insert(AUTHORIZATION, HeaderValue::from_static("header data")); - headers.insert(CACHE_CONTROL, HeaderValue::from_static("header data")); - headers.insert(CONTENT_ENCODING, HeaderValue::from_static("header data")); - headers.insert(CONTENT_LENGTH, HeaderValue::from_static("header data")); - headers.insert(CONTENT_RANGE, HeaderValue::from_static("header data")); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("header data")); - headers.insert(HOST, HeaderValue::from_static("header data")); - headers.insert(MAX_FORWARDS, HeaderValue::from_static("header data")); - headers.insert(SET_COOKIE, HeaderValue::from_static("header data")); - headers.insert(TRAILER, HeaderValue::from_static("header data")); - headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("header data")); - headers.insert(TE, HeaderValue::from_static("header data")); - - assert!(encoder.encode_trailers::<&[u8]>(headers, true).is_none()); - } - - #[test] - fn chunked_with_title_case_headers() { - let encoder = Encoder::chunked(); - let trailers = vec![HeaderValue::from_static("chunky-trailer")]; - let encoder = encoder.into_chunked_with_trailing_fields(trailers); - - let headers = HeaderMap::from_iter(vec![( - HeaderName::from_static("chunky-trailer"), - HeaderValue::from_static("header data"), - )]); - let buf1 = encoder.encode_trailers::<&[u8]>(headers, true).unwrap(); - - let mut dst = Vec::new(); - dst.put(buf1); - assert_eq!(dst, b"0\r\nChunky-Trailer: header data\r\n\r\n"); - } -} |
