diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc08078..b01c2b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,7 @@ jobs: - "feat.: gzip" - "feat.: brotli" - "feat.: json" + - "feat.: multipart" - "feat.: stream" - "feat.: socks/default-tls" - "feat.: socks/rustls-tls" @@ -84,21 +85,21 @@ jobs: - name: windows / stable-x86_64-msvc os: windows-latest target: x86_64-pc-windows-msvc - features: "--features blocking,gzip,brotli,json" + features: "--features blocking,gzip,brotli,json,multipart" - name: windows / stable-i686-msvc os: windows-latest target: i686-pc-windows-msvc - features: "--features blocking,gzip,brotli,json" + features: "--features blocking,gzip,brotli,json,multipart" - name: windows / stable-x86_64-gnu os: windows-latest rust: stable-x86_64-pc-windows-gnu target: x86_64-pc-windows-gnu - features: "--features blocking,gzip,brotli,json" + features: "--features blocking,gzip,brotli,json,multipart" - name: windows / stable-i686-gnu os: windows-latest rust: stable-i686-pc-windows-gnu target: i686-pc-windows-gnu - features: "--features blocking,gzip,brotli,json" + features: "--features blocking,gzip,brotli,json,multipart" - name: "feat.: default-tls disabled" features: "--no-default-features" @@ -120,6 +121,8 @@ jobs: features: "--features brotli" - name: "feat.: json" features: "--features json" + - name: "feat.: multipart" + features: "--features multipart" - name: "feat.: stream" features: "--features stream" - name: "feat.: socks/default-tls" diff --git a/Cargo.toml b/Cargo.toml index 5bc1686..808206d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ features = [ "blocking", "cookies", "json", + "multipart", ] [features] @@ -49,6 +50,8 @@ brotli = ["async-compression", "async-compression/brotli", "tokio-util"] json = ["serde_json"] +multipart = ["mime_guess"] + trust-dns = ["trust-dns-resolver"] stream = [] @@ -74,9 +77,13 @@ url = "2.2" bytes = "1.0" serde = "1.0" serde_urlencoded = "0.7" -mime_guess = "2.0" + +# Optional deps... + ## json serde_json = { version = "1.0", optional = true } +## multipart +mime_guess = { version = "2.0", default-features = false, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] base64 = "0.13" @@ -206,3 +213,8 @@ required-features = ["gzip"] name = "brotli" path = "tests/brotli.rs" required-features = ["brotli"] + +[[test]] +name = "multipart" +path = "tests/multipart.rs" +required-features = ["multipart"] diff --git a/src/async_impl/body.rs b/src/async_impl/body.rs index 2c16166..5d12d05 100644 --- a/src/async_impl/body.rs +++ b/src/async_impl/body.rs @@ -152,6 +152,7 @@ impl Body { ImplStream(self) } + #[cfg(feature = "multipart")] pub(crate) fn content_length(&self) -> Option { match self.inner { Inner::Reusable(ref bytes) => Some(bytes.len() as u64), diff --git a/src/async_impl/mod.rs b/src/async_impl/mod.rs index 15e6ce2..1be0b66 100644 --- a/src/async_impl/mod.rs +++ b/src/async_impl/mod.rs @@ -9,6 +9,7 @@ pub(crate) use self::decoder::Decoder; pub mod body; pub mod client; pub mod decoder; +#[cfg(feature = "multipart")] pub mod multipart; pub(crate) mod request; mod response; diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs index fa2f405..fdb542b 100644 --- a/src/async_impl/request.rs +++ b/src/async_impl/request.rs @@ -11,9 +11,12 @@ use serde_json; use super::body::Body; use super::client::{Client, Pending}; +#[cfg(feature = "multipart")] use super::multipart; use super::response::Response; -use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_LENGTH, CONTENT_TYPE}; +use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; +#[cfg(feature = "multipart")] +use crate::header::CONTENT_LENGTH; use crate::{Method, Url}; use http::{Request as HttpRequest, request::Parts}; @@ -260,6 +263,7 @@ impl RequestBuilder { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "multipart")] pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder { let mut builder = self.header( CONTENT_TYPE, diff --git a/src/blocking/body.rs b/src/blocking/body.rs index c42e418..a3c3d62 100644 --- a/src/blocking/body.rs +++ b/src/blocking/body.rs @@ -1,7 +1,9 @@ use std::fmt; use std::fs::File; use std::future::Future; -use std::io::{self, Cursor, Read}; +use std::io::{self, Read}; +#[cfg(feature = "multipart")] +use std::io::Cursor; use std::mem; use std::ptr; @@ -113,6 +115,7 @@ impl Body { } } + #[cfg(feature = "multipart")] pub(crate) fn len(&self) -> Option { match self.kind { Kind::Reader(_, len) => len, @@ -120,6 +123,7 @@ impl Body { } } + #[cfg(feature = "multipart")] pub(crate) fn into_reader(self) -> Reader { match self.kind { Kind::Reader(r, _) => Reader::Reader(r), @@ -236,11 +240,13 @@ impl<'a> fmt::Debug for DebugLength<'a> { } } +#[cfg(feature = "multipart")] pub(crate) enum Reader { Reader(Box), Bytes(Cursor), } +#[cfg(feature = "multipart")] impl Read for Reader { fn read(&mut self, buf: &mut [u8]) -> io::Result { match *self { diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs index 4948cfb..550716d 100644 --- a/src/blocking/mod.rs +++ b/src/blocking/mod.rs @@ -59,6 +59,7 @@ mod body; mod client; +#[cfg(feature = "multipart")] pub mod multipart; mod request; mod response; diff --git a/src/blocking/request.rs b/src/blocking/request.rs index 4c74cf1..72a1426 100644 --- a/src/blocking/request.rs +++ b/src/blocking/request.rs @@ -10,6 +10,7 @@ use serde_json; use serde_urlencoded; use super::body::{self, Body}; +#[cfg(feature = "multipart")] use super::multipart; use super::Client; use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; @@ -508,6 +509,7 @@ impl RequestBuilder { /// ``` /// /// See [`multipart`](multipart/) for more examples. + #[cfg(feature = "multipart")] pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder { let mut builder = self.header( CONTENT_TYPE, diff --git a/src/lib.rs b/src/lib.rs index 2b59a3e..3fff272 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,7 @@ //! - **gzip**: Provides response body gzip decompression. //! - **brotli**: Provides response body brotli decompression. //! - **json**: Provides serialization and deserialization for JSON bodies. +//! - **multipart**: Provides functionality for multipart forms. //! - **stream**: Adds support for `futures::Stream`. //! - **socks**: Provides SOCKS5 proxy support. //! - **trust-dns**: Enables a trust-dns async resolver instead of default @@ -290,11 +291,13 @@ if_hyper! { doctest!("../README.md"); pub use self::async_impl::{ - multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response, ResponseBuilderExt, + Body, Client, ClientBuilder, Request, RequestBuilder, Response, ResponseBuilderExt, }; pub use self::proxy::Proxy; #[cfg(feature = "__tls")] pub use self::tls::{Certificate, Identity}; + #[cfg(feature = "multipart")] + pub use self::async_impl::multipart; mod async_impl; @@ -316,5 +319,7 @@ if_wasm! { mod wasm; mod util; - pub use self::wasm::{multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response}; + pub use self::wasm::{Body, Client, ClientBuilder, Request, RequestBuilder, Response}; + #[cfg(feature = "multipart")] + pub use self::wasm::multipart; } diff --git a/src/wasm/body.rs b/src/wasm/body.rs index ab2160f..ebcf180 100644 --- a/src/wasm/body.rs +++ b/src/wasm/body.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "multipart")] use super::multipart::Form; /// dox use bytes::Bytes; @@ -18,6 +19,7 @@ pub struct Body { enum Inner { Bytes(Bytes), + #[cfg(feature = "multipart")] Multipart(Form), } @@ -30,6 +32,7 @@ impl Body { let js_value: &JsValue = body_array.as_ref(); Ok(js_value.to_owned()) } + #[cfg(feature = "multipart")] Inner::Multipart(form) => { let form_data = form.to_form_data()?; let js_value: &JsValue = form_data.as_ref(); @@ -39,6 +42,7 @@ impl Body { } #[inline] + #[cfg(feature = "multipart")] pub(crate) fn from_form(f: Form) -> Body { Self { inner: Inner::Multipart(f), @@ -48,6 +52,7 @@ impl Body { pub(crate) fn is_empty(&self) -> bool { match &self.inner { Inner::Bytes(bytes) => bytes.is_empty(), + #[cfg(feature = "multipart")] Inner::Multipart(form) => form.is_empty(), } } diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index b0107d3..a0ba070 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -5,6 +5,7 @@ mod client; mod request; mod response; /// TODO +#[cfg(feature = "multipart")] pub mod multipart; pub use self::body::Body; diff --git a/src/wasm/request.rs b/src/wasm/request.rs index df78083..b7c7990 100644 --- a/src/wasm/request.rs +++ b/src/wasm/request.rs @@ -191,6 +191,7 @@ impl RequestBuilder { } /// TODO + #[cfg(feature = "multipart")] pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder { if let Ok(ref mut req) = self.request { *req.body_mut() = Some(Body::from_form(multipart))