From cb1764ddfe3002c048982afcf053d3b28fcffc52 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Mon, 23 Dec 2019 11:38:31 -0800 Subject: [PATCH] Don't set User-Agent header by default --- src/async_impl/client.rs | 51 ++++++++++++++++++++++++++++++++++++---- src/blocking/client.rs | 31 ++++++++++++++++++++++++ src/connect.rs | 20 +++++++++------- tests/client.rs | 24 ++++++++++++++++++- 4 files changed, 111 insertions(+), 15 deletions(-) diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index a578eab06..6bd44ef41 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::net::IpAddr; use std::sync::Arc; #[cfg(feature = "cookies")] @@ -36,8 +37,6 @@ use crate::tls::TlsBackend; use crate::{Certificate, Identity}; use crate::{IntoUrl, Method, Proxy, StatusCode, Url}; -static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); - /// An asynchronous `Client` to make Requests with. /// /// The Client has various configuration values to tweak, but the defaults @@ -85,6 +84,7 @@ struct Config { nodelay: bool, #[cfg(feature = "cookies")] cookie_store: Option, + error: Option, } impl Default for ClientBuilder { @@ -99,11 +99,11 @@ impl ClientBuilder { /// This is the same as `Client::builder()`. pub fn new() -> ClientBuilder { let mut headers: HeaderMap = HeaderMap::with_capacity(2); - headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT)); headers.insert(ACCEPT, HeaderValue::from_static("*/*")); ClientBuilder { config: Config { + error: None, gzip: cfg!(feature = "gzip"), headers, #[cfg(feature = "native-tls")] @@ -143,6 +143,11 @@ impl ClientBuilder { /// cannot load the system configuration. pub fn build(self) -> crate::Result { let config = self.config; + + if let Some(err) = config.error { + return Err(err); + } + let mut proxies = config.proxies; if config.auto_sys_proxy { proxies.push(Proxy::system()); @@ -151,8 +156,8 @@ impl ClientBuilder { let mut connector = { #[cfg(feature = "__tls")] - fn user_agent(headers: &HeaderMap) -> HeaderValue { - headers[USER_AGENT].clone() + fn user_agent(headers: &HeaderMap) -> Option { + headers.get(USER_AGENT).cloned() } #[cfg(feature = "__tls")] @@ -270,6 +275,42 @@ impl ClientBuilder { // Higher-level options + + /// Sets the `User-Agent` header to be used by this client. + /// + /// # Example + /// + /// ```rust + /// # async fn doc() -> Result<(), reqwest::Error> { + /// // Name your user agent after your app? + /// static APP_USER_AGENT: &str = concat!( + /// env!("CARGO_PKG_NAME"), + /// "/", + /// env!("CARGO_PKG_VERSION"), + /// ); + /// + /// let client = reqwest::Client::builder() + /// .user_agent(APP_USER_AGENT) + /// .build()?; + /// let res = client.get("https://www.rust-lang.org").send().await?; + /// # Ok(()) + /// # } + /// ``` + 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. /// /// # Example diff --git a/src/blocking/client.rs b/src/blocking/client.rs index 0f29d22aa..c5f187820 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::fmt; use std::future::Future; use std::net::IpAddr; @@ -5,6 +6,7 @@ use std::sync::Arc; use std::thread; use std::time::Duration; +use http::header::HeaderValue; use log::{error, trace}; use tokio::sync::{mpsc, oneshot}; @@ -85,6 +87,35 @@ impl ClientBuilder { // Higher-level options + + /// Sets the `User-Agent` header to be used by this client. + /// + /// # Example + /// + /// ```rust + /// # fn doc() -> Result<(), reqwest::Error> { + /// // Name your user agent after your app? + /// static APP_USER_AGENT: &str = concat!( + /// env!("CARGO_PKG_NAME"), + /// "/", + /// env!("CARGO_PKG_VERSION"), + /// ); + /// + /// let client = reqwest::blocking::Client::builder() + /// .user_agent(APP_USER_AGENT) + /// .build()?; + /// let res = client.get("https://www.rust-lang.org").send()?; + /// # Ok(()) + /// # } + /// ``` + pub fn user_agent(self, value: V) -> ClientBuilder + where + V: TryInto, + V::Error: Into, + { + self.with_inner(move |inner| inner.user_agent(value)) + } + /// Sets the default headers for every request. /// /// # Example diff --git a/src/connect.rs b/src/connect.rs index a0cf8675e..7dc87d336 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -41,7 +41,7 @@ pub(crate) struct Connector { #[cfg(feature = "__tls")] nodelay: bool, #[cfg(feature = "__tls")] - user_agent: HeaderValue, + user_agent: Option, } #[derive(Clone)] @@ -82,7 +82,7 @@ impl Connector { pub(crate) fn new_default_tls( tls: TlsConnectorBuilder, proxies: Arc>, - user_agent: HeaderValue, + user_agent: Option, local_addr: T, nodelay: bool, ) -> crate::Result @@ -108,7 +108,7 @@ impl Connector { pub(crate) fn new_rustls_tls( tls: rustls::ClientConfig, proxies: Arc>, - user_agent: HeaderValue, + user_agent: Option, local_addr: T, nodelay: bool, ) -> crate::Result @@ -515,7 +515,7 @@ async fn tunnel( mut conn: T, host: String, port: u16, - user_agent: HeaderValue, + user_agent: Option, auth: Option, ) -> Result where @@ -534,9 +534,11 @@ where // user-agent - buf.extend_from_slice(b"User-Agent: "); - buf.extend_from_slice(user_agent.as_bytes()); - buf.extend_from_slice(b"\r\n"); + if let Some(user_agent) = user_agent { + buf.extend_from_slice(b"User-Agent: "); + buf.extend_from_slice(user_agent.as_bytes()); + buf.extend_from_slice(b"\r\n"); + } // proxy-authorization @@ -888,8 +890,8 @@ mod tests { }}; } - fn ua() -> http::header::HeaderValue { - http::header::HeaderValue::from_static(TUNNEL_UA) + fn ua() -> Option { + Some(http::header::HeaderValue::from_static(TUNNEL_UA)) } #[test] diff --git a/tests/client.rs b/tests/client.rs index b273a89df..1013efbe6 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -11,7 +11,7 @@ async fn auto_headers() { assert_eq!(req.method(), "GET"); assert_eq!(req.headers()["accept"], "*/*"); - assert_eq!(req.headers()["user-agent"], DEFAULT_USER_AGENT); + assert_eq!(req.headers().get("user-agent"), None); if cfg!(feature = "gzip") { assert_eq!(req.headers()["accept-encoding"], "gzip"); } @@ -28,6 +28,28 @@ async fn auto_headers() { assert_eq!(res.remote_addr(), Some(server.addr())); } +#[tokio::test] +async fn user_agent() { + let server = server::http(move |req| { + async move { + assert_eq!(req.headers()["user-agent"], "reqwest-test-agent"); + http::Response::default() + } + }); + + let url = format!("http://{}/ua", server.addr()); + let res = reqwest::Client::builder() + .user_agent("reqwest-test-agent") + .build() + .expect("client builder") + .get(&url) + .send() + .await + .expect("request"); + + assert_eq!(res.status(), reqwest::StatusCode::OK); +} + #[tokio::test] async fn response_text() { let _ = env_logger::try_init();