diff --git a/Cargo.toml b/Cargo.toml index 612912629..1e517f824 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,8 +140,8 @@ tokio-socks = { version = "0.5.1", optional = true } trust-dns-resolver = { version = "0.23", optional = true, features = ["tokio-runtime"] } # HTTP/3 experimental support -h3 = { version="0.0.2", optional = true } -h3-quinn = { version="0.0.3", optional = true } +h3 = { version="0.0.3", optional = true } +h3-quinn = { version="0.0.4", optional = true } quinn = { version = "0.10", default-features = false, features = ["tls-rustls", "ring", "runtime-tokio"], optional = true } futures-channel = { version="0.3", optional = true} diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs index 487387545..0a6867418 100644 --- a/src/blocking/mod.rs +++ b/src/blocking/mod.rs @@ -16,7 +16,7 @@ //! //! # Making a GET request //! -//! For a single request, you can use the [`get`](get) shortcut method. +//! For a single request, you can use the [`get`] shortcut method. //! //! ```rust //! # use reqwest::{Error, Response}; @@ -30,19 +30,18 @@ //! # } //! ``` //! -//! Additionally, the blocking [`Response`](Response) struct implements Rust's +//! Additionally, the blocking [`Response`] struct implements Rust's //! `Read` trait, so many useful standard library and third party crates will //! have convenience methods that take a `Response` anywhere `T: Read` is //! acceptable. //! //! **NOTE**: If you plan to perform multiple requests, it is best to create a -//! [`Client`](Client) and reuse it, taking advantage of keep-alive connection -//! pooling. +//! [`Client`] and reuse it, taking advantage of keep-alive connection pooling. //! //! # Making POST requests (or setting request bodies) //! //! There are several ways you can set the body of a request. The basic one is -//! by using the `body()` method of a [`RequestBuilder`](RequestBuilder). This lets you set the +//! by using the `body()` method of a [`RequestBuilder`]. This lets you set the //! exact raw bytes of what the body should be. It accepts various types, //! including `String`, `Vec`, and `File`. If you wish to pass a custom //! Reader, you can use the `reqwest::blocking::Body::new()` constructor. diff --git a/src/blocking/multipart.rs b/src/blocking/multipart.rs index 9e7dfd3c7..5014b3975 100644 --- a/src/blocking/multipart.rs +++ b/src/blocking/multipart.rs @@ -1,7 +1,7 @@ //! multipart/form-data //! -//! To send a `multipart/form-data` body, a [`Form`](crate::blocking::multipart::Form) is built up, adding -//! fields or customized [`Part`](crate::blocking::multipart::Part)s, and then calling the +//! To send a `multipart/form-data` body, a [`Form`] is built up, adding +//! fields or customized [`Part`]s, and then calling the //! [`multipart`][builder] method on the `RequestBuilder`. //! //! # Example diff --git a/src/lib.rs b/src/lib.rs index 017cfea94..188ba4f02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,8 @@ //! It handles many of the things that most people just expect an HTTP client //! to do for them. //! -//! - Async and [blocking](blocking) Clients -//! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart](multipart) +//! - Async and [blocking] Clients +//! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart] //! - Customizable [redirect policy](#redirect-policies) //! - HTTP [Proxies](#proxies) //! - Uses system-native [TLS](#tls) @@ -138,7 +138,7 @@ //! `HTTP_PROXY` or `http_proxy` provide http proxies for http connections while //! `HTTPS_PROXY` or `https_proxy` provide HTTPS proxies for HTTPS connections. //! -//! These can be overwritten by adding a [`Proxy`](Proxy) to `ClientBuilder` +//! These can be overwritten by adding a [`Proxy`] to `ClientBuilder` //! i.e. `let proxy = reqwest::Proxy::http("https://secure.example")?;` //! or disabled by calling `ClientBuilder::no_proxy()`. //! @@ -155,16 +155,17 @@ //! Security-Framework on macOS, and OpenSSL on Linux. //! //! - Additional X509 certificates can be configured on a `ClientBuilder` with the -//! [`Certificate`](Certificate) type. +//! [`Certificate`] type. //! - Client certificates can be added to a `ClientBuilder` with the -//! [`Identity`][Identity] type. +//! [`Identity`] type. //! - Various parts of TLS can also be configured or even disabled on the //! `ClientBuilder`. //! //! ## WASM +//! //! The Client implementation automatically switches to the WASM one when the target_arch is wasm32, //! the usage is basically the same as the async api. Some of the features are disabled in wasm -//! : [`tls`](tls) [`cookie`](cookie) [`blocking`](blocking). +//! : [`tls`], [`cookie`], [`blocking`]. //! //! //! ## Optional Features diff --git a/src/proxy.rs b/src/proxy.rs index 93ad00c39..6e1bfcc73 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -943,7 +943,9 @@ fn parse_setting_from_dynamic_store( fn get_from_platform_impl() -> Result, Box> { let store = SCDynamicStoreBuilder::new("reqwest").build(); - let Some(proxies_map) = store.get_proxies() else { + let proxies_map = if let Some(proxies_map) = store.get_proxies() { + proxies_map + } else { return Ok(None); }; diff --git a/src/tls.rs b/src/tls.rs index e873939ab..52cc12862 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -5,9 +5,9 @@ //! Security-Framework on macOS, and OpenSSL on Linux. //! //! - Additional X509 certificates can be configured on a `ClientBuilder` with the -//! [`Certificate`](Certificate) type. +//! [`Certificate`] type. //! - Client certificates can be add to a `ClientBuilder` with the -//! [`Identity`][Identity] type. +//! [`Identity`] type. //! - Various parts of TLS can also be configured or even disabled on the //! `ClientBuilder`. diff --git a/src/wasm/client.rs b/src/wasm/client.rs index cfe97bf03..9f22a896b 100644 --- a/src/wasm/client.rs +++ b/src/wasm/client.rs @@ -1,5 +1,7 @@ -use http::{HeaderMap, Method}; +use http::header::USER_AGENT; +use http::{HeaderMap, HeaderValue, Method}; use js_sys::{Promise, JSON}; +use std::convert::TryInto; use std::{fmt, future::Future, sync::Arc}; use url::Url; use wasm_bindgen::prelude::{wasm_bindgen, UnwrapThrowExt as _}; @@ -266,12 +268,33 @@ impl ClientBuilder { /// Returns a 'Client' that uses this ClientBuilder configuration pub fn build(mut self) -> Result { + if let Some(err) = self.config.error { + return Err(err); + } + let config = std::mem::take(&mut self.config); Ok(Client { config: Arc::new(config), }) } + /// Sets the `User-Agent` header to be used by this client. + pub fn user_agent(mut self, value: V) -> ClientBuilder + where + V: TryInto, + V::Error: Into, + { + match value.try_into() { + Ok(value) => { + self.config.headers.insert(USER_AGENT, value); + } + Err(e) => { + self.config.error = Some(crate::error::builder(e.into())); + } + } + self + } + /// Sets the default headers for every request pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder { for (key, value) in headers.iter() { @@ -287,15 +310,17 @@ impl Default for ClientBuilder { } } -#[derive(Clone, Debug)] +#[derive(Debug)] struct Config { headers: HeaderMap, + error: Option, } impl Default for Config { fn default() -> Config { Config { headers: HeaderMap::new(), + error: None, } } } @@ -376,4 +401,47 @@ mod tests { "request headers don't change client defaults" ); } + + #[wasm_bindgen_test] + fn user_agent_header() { + use crate::header::USER_AGENT; + + let client = crate::Client::builder() + .user_agent("FooBar/1.2.3") + .build() + .expect("client"); + + let mut req = client + .get("https://www.example.com") + .build() + .expect("request"); + + // Merge the client headers with the request's one. + client.merge_headers(&mut req); + let headers1 = req.headers(); + + // Confirm that we have the `User-Agent` header set + assert_eq!( + headers1.get(USER_AGENT).unwrap(), + "FooBar/1.2.3", + "The user-agent header was not set: {req:#?}" + ); + + // Now we try to overwrite the `User-Agent` value + + let mut req2 = client + .get("https://www.example.com") + .header(USER_AGENT, "Another-User-Agent/42") + .build() + .expect("request 2"); + + client.merge_headers(&mut req2); + let headers2 = req2.headers(); + + assert_eq!( + headers2.get(USER_AGENT).expect("headers2 user agent"), + "Another-User-Agent/42", + "Was not able to overwrite the User-Agent value on the request-builder" + ); + } }