diff --git a/src/body.rs b/src/body.rs index 5ec8526..a7a2d97 100644 --- a/src/body.rs +++ b/src/body.rs @@ -43,11 +43,21 @@ impl Default for Payload { } } +/// The size of the body. +/// +/// *Internal API* +#[derive(Debug)] +pub(crate) enum BodySize { + Empty, + Unknown, + Known(u64), +} + /// Payloads are turned into this type where we can hold both a size and the reader. /// /// *Internal API* pub(crate) struct SizedReader { - pub size: Option, + pub size: BodySize, pub reader: Box, } @@ -58,7 +68,7 @@ impl fmt::Debug for SizedReader { } impl SizedReader { - fn new(size: Option, reader: Box) -> Self { + fn new(size: BodySize, reader: Box) -> Self { SizedReader { size, reader } } } @@ -66,7 +76,7 @@ impl SizedReader { impl Payload { pub fn into_read(self) -> SizedReader { match self { - Payload::Empty => SizedReader::new(Some(0), Box::new(empty())), + Payload::Empty => SizedReader::new(BodySize::Empty, Box::new(empty())), Payload::Text(text, _charset) => { #[cfg(feature = "charset")] let bytes = { @@ -79,20 +89,20 @@ impl Payload { let bytes = text.into_bytes(); let len = bytes.len(); let cursor = Cursor::new(bytes); - SizedReader::new(Some(len), Box::new(cursor)) + SizedReader::new(BodySize::Known(len as u64), Box::new(cursor)) } #[cfg(feature = "json")] Payload::JSON(v) => { let bytes = serde_json::to_vec(&v).expect("Bad JSON in payload"); let len = bytes.len(); let cursor = Cursor::new(bytes); - SizedReader::new(Some(len), Box::new(cursor)) + SizedReader::new(BodySize::Known(len as u64), Box::new(cursor)) } - Payload::Reader(read) => SizedReader::new(None, read), + Payload::Reader(read) => SizedReader::new(BodySize::Unknown, read), Payload::Bytes(bytes) => { let len = bytes.len(); let cursor = Cursor::new(bytes); - SizedReader::new(Some(len), Box::new(cursor)) + SizedReader::new(BodySize::Known(len as u64), Box::new(cursor)) } } } diff --git a/src/request.rs b/src/request.rs index 12848bd..31478cc 100644 --- a/src/request.rs +++ b/src/request.rs @@ -8,6 +8,7 @@ use qstring::QString; use url::{form_urlencoded, Url}; use crate::agent::{self, Agent, AgentState}; +use crate::body::BodySize; use crate::body::{Payload, SizedReader}; use crate::error::Error; use crate::header::{self, Header}; @@ -604,8 +605,14 @@ impl Request { // Sized bodies are retryable only if they are zero-length because of // coincidences of the current implementation - the function responsible // for retries doesn't have a way to replay a Payload. - let no_body = body.size.is_none() || body.size.unwrap() > 0; - idempotent && no_body + let retryable_body = match body.size { + BodySize::Unknown => false, + BodySize::Known(0) => true, + BodySize::Known(_) => false, + BodySize::Empty => true, + }; + + idempotent && retryable_body } } diff --git a/src/unit.rs b/src/unit.rs index 1f5d9fc..4616f28 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -8,7 +8,7 @@ use url::Url; use cookie::{Cookie, CookieJar}; use crate::agent::AgentState; -use crate::body::{self, Payload, SizedReader}; +use crate::body::{self, BodySize, Payload, SizedReader}; use crate::header; use crate::resolve::ArcResolver; use crate::stream::{self, connect_test, Stream}; @@ -56,15 +56,17 @@ impl Unit { // Content-Length, // otherwise, use the chunked Transfer-Encoding (only if no other Transfer-Encoding // has been set - if let Some(size) = body.size { - if size != 0 { - extra.push(Header::new("Content-Length", &format!("{}", size))); + match body.size { + BodySize::Known(size) => { + extra.push(Header::new("Content-Length", &format!("{}", size))) } - } else { - if !is_transfer_encoding_set { - extra.push(Header::new("Transfer-Encoding", "chunked")); - is_chunked = true; + BodySize::Unknown => { + if !is_transfer_encoding_set { + extra.push(Header::new("Transfer-Encoding", "chunked")); + is_chunked = true; + } } + BodySize::Empty => {} } }