From 6c6170b1a7d4453ad88216a2b19a36ecf1bc4dae Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 13 Mar 2024 12:31:26 +0100 Subject: [PATCH] feat: Make encoding_rs an optional dependency called `charset` Closes #1785 --- Cargo.toml | 8 +++++--- src/async_impl/response.rs | 30 +++++++++++++++++++++++++++--- src/blocking/response.rs | 16 +++++++++++++++- src/lib.rs | 1 + tests/blocking.rs | 1 + 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1cd24621a..37e447379 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ features = [ ] [features] -default = ["default-tls", "http2", "macos-system-configuration"] +default = ["default-tls", "charset", "http2", "macos-system-configuration"] # Note: this doesn't enable the 'native-tls' feature, which adds specific # functionality for it. @@ -47,6 +47,8 @@ rustls-tls-native-roots = ["dep:rustls-native-certs", "__rustls"] blocking = ["futures-channel/sink", "futures-util/io", "futures-util/sink", "tokio/rt-multi-thread", "tokio/sync"] +charset = ["dep:encoding_rs"] + cookies = ["dep:cookie_crate", "dep:cookie_store"] gzip = ["dep:async-compression", "async-compression?/gzip", "dep:tokio-util"] @@ -60,7 +62,7 @@ json = ["dep:serde_json"] multipart = ["dep:mime_guess"] # Deprecated, remove this feature while bumping minor versions. -trust-dns = ["dep:trust-dns-resolver"] +trust-dns = [] hickory-dns = ["dep:hickory-resolver"] stream = ["tokio/fs", "dep:tokio-util", "dep:wasm-streams"] @@ -107,7 +109,7 @@ serde_json = { version = "1.0", optional = true } mime_guess = { version = "2.0", default-features = false, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -encoding_rs = "0.8" +encoding_rs = { version = "0.8", optional = true } http-body = "1" http-body-util = "0.1" hyper = { version = "1", features = ["http1", "client"] } diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index 1ee4a2f36..d2ddfc3a1 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -3,11 +3,9 @@ use std::net::SocketAddr; use std::pin::Pin; use bytes::Bytes; -use encoding_rs::{Encoding, UTF_8}; use http_body_util::BodyExt; use hyper::{HeaderMap, StatusCode, Version}; use hyper_util::client::legacy::connect::HttpInfo; -use mime::Mime; #[cfg(feature = "json")] use serde::de::DeserializeOwned; #[cfg(feature = "json")] @@ -21,6 +19,11 @@ use crate::async_impl::body::ResponseBody; #[cfg(feature = "cookies")] use crate::cookie; +#[cfg(feature = "charset")] +use encoding_rs::{Encoding, UTF_8}; +#[cfg(feature = "charset")] +use mime::Mime; + /// A Response to a submitted `Request`. pub struct Response { pub(super) res: hyper::Response, @@ -135,6 +138,11 @@ impl Response { /// /// Note that the BOM is stripped from the returned String. /// + /// # Note + /// + /// If the `charset` feature is disabled the method will only attempt to decode the + /// response as UTF-8, regardless of the given `Content-Type` + /// /// # Example /// /// ``` @@ -149,7 +157,17 @@ impl Response { /// # } /// ``` pub async fn text(self) -> crate::Result { - self.text_with_charset("utf-8").await + #[cfg(feature = "charset")] + { + self.text_with_charset("utf-8").await + } + + #[cfg(not(feature = "charset"))] + { + let full = self.bytes().await?; + let text = String::from_utf8_lossy(&full); + Ok(text.into_owned()) + } } /// Get the full response text given a specific encoding. @@ -164,6 +182,10 @@ impl Response { /// /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages /// + /// # Optional + /// + /// This requires the optional `encoding_rs` feature enabled. + /// /// # Example /// /// ``` @@ -177,6 +199,8 @@ impl Response { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "charset")] + #[cfg_attr(docsrs, doc(cfg(feature = "charset")))] pub async fn text_with_charset(self, default_encoding: &str) -> crate::Result { let content_type = self .headers() diff --git a/src/blocking/response.rs b/src/blocking/response.rs index 6ece95ba6..1a4685115 100644 --- a/src/blocking/response.rs +++ b/src/blocking/response.rs @@ -270,6 +270,11 @@ impl Response { /// Encoding is determined from the `charset` parameter of `Content-Type` header, /// and defaults to `utf-8` if not presented. /// + /// # Note + /// + /// If the `charset` feature is disabled the method will only attempt to decode the + /// response as UTF-8, regardless of the given `Content-Type` + /// /// # Example /// /// ```rust @@ -280,7 +285,10 @@ impl Response { /// # } /// ``` pub fn text(self) -> crate::Result { - self.text_with_charset("utf-8") + wait::timeout(self.inner.text(), self.timeout).map_err(|e| match e { + wait::Waited::TimedOut(e) => crate::error::decode(e), + wait::Waited::Inner(e) => e, + }) } /// Get the response text given a specific encoding. @@ -293,6 +301,10 @@ impl Response { /// /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages /// + /// # Optional + /// + /// This requires the optional `charset` feature enabled. + /// /// # Example /// /// ```rust @@ -303,6 +315,8 @@ impl Response { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "charset")] + #[cfg_attr(docsrs, doc(cfg(feature = "charset")))] pub fn text_with_charset(self, default_encoding: &str) -> crate::Result { wait::timeout(self.inner.text_with_charset(default_encoding), self.timeout).map_err(|e| { match e { diff --git a/src/lib.rs b/src/lib.rs index 2342fb948..ce4549dd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,6 +193,7 @@ //! - **rustls-tls-native-roots**: Enables TLS functionality provided by `rustls`, //! while using root certificates from the `rustls-native-certs` crate. //! - **blocking**: Provides the [blocking][] client API. +//! - **charset** *(enabled by default)*: Improved support for decoding text. //! - **cookies**: Provides cookie session support. //! - **gzip**: Provides response body gzip decompression. //! - **brotli**: Provides response body brotli decompression. diff --git a/tests/blocking.rs b/tests/blocking.rs index 8a59250bf..c4ea04bef 100644 --- a/tests/blocking.rs +++ b/tests/blocking.rs @@ -22,6 +22,7 @@ fn test_response_text() { } #[test] +#[cfg(feature = "charset")] fn test_response_non_utf_8_text() { let server = server::http(move |_req| async { http::Response::builder()