From 18dfac4fe2590f2e3a27e808c72fb26e6d9af98b Mon Sep 17 00:00:00 2001 From: bensadiku <43443348+bensadiku@users.noreply.github.com> Date: Thu, 15 Apr 2021 04:03:22 +0200 Subject: [PATCH] Add `RequestBuilder::version()` method to set HTTP version (#1243) Closes #1240 --- src/async_impl/client.rs | 3 +- src/async_impl/request.rs | 69 ++++++++++++++++++++++++++++++++++++--- src/blocking/request.rs | 45 +++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index bbefbd0..4bdad5d 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -1106,7 +1106,7 @@ impl Client { } pub(super) fn execute_request(&self, req: Request) -> Pending { - let (method, url, mut headers, body, timeout) = req.pieces(); + let (method, url, mut headers, body, timeout, version) = req.pieces(); if url.scheme() != "http" && url.scheme() != "https" { return Pending::new_err(error::url_bad_scheme(url)); } @@ -1157,6 +1157,7 @@ impl Client { let mut req = hyper::Request::builder() .method(method.clone()) .uri(uri) + .version(version) .body(body.into_stream()) .expect("valid request parts"); diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs index 3e089e9..504fd56 100644 --- a/src/async_impl/request.rs +++ b/src/async_impl/request.rs @@ -18,7 +18,7 @@ use super::response::Response; use crate::header::CONTENT_LENGTH; use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; use crate::{Method, Url}; -use http::{request::Parts, Request as HttpRequest}; +use http::{request::Parts, Request as HttpRequest, Version}; /// A request which can be executed with `Client::execute()`. pub struct Request { @@ -27,6 +27,7 @@ pub struct Request { headers: HeaderMap, body: Option, timeout: Option, + version: Version, } /// A builder to construct the properties of a `Request`. @@ -48,6 +49,7 @@ impl Request { headers: HeaderMap::new(), body: None, timeout: None, + version: Version::default(), } } @@ -111,6 +113,18 @@ impl Request { &mut self.timeout } + /// Get the http version. + #[inline] + pub fn version(&self) -> Version { + self.version + } + + /// Get a mutable reference to the http version. + #[inline] + pub fn version_mut(&mut self) -> &mut Version { + &mut self.version + } + /// Attempt to clone the request. /// /// `None` is returned if the request can not be cloned, i.e. if the body is a stream. @@ -122,12 +136,29 @@ impl Request { let mut req = Request::new(self.method().clone(), self.url().clone()); *req.timeout_mut() = self.timeout().cloned(); *req.headers_mut() = self.headers().clone(); + *req.version_mut() = self.version().clone(); req.body = body; Some(req) } - pub(super) fn pieces(self) -> (Method, Url, HeaderMap, Option, Option) { - (self.method, self.url, self.headers, self.body, self.timeout) + pub(super) fn pieces( + self, + ) -> ( + Method, + Url, + HeaderMap, + Option, + Option, + Version, + ) { + ( + self.method, + self.url, + self.headers, + self.body, + self.timeout, + self.version, + ) } } @@ -322,6 +353,14 @@ impl RequestBuilder { self } + /// Set HTTP version + pub fn version(mut self, version: Version) -> RequestBuilder { + if let Ok(ref mut req) = self.request { + req.version = version; + } + self + } + /// Send a form body. pub fn form(mut self, form: &T) -> RequestBuilder { let mut error = None; @@ -516,6 +555,7 @@ where method, uri, headers, + version, .. } = parts; let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?; @@ -525,13 +565,14 @@ where headers, body: Some(body.into()), timeout: None, + version: version, }) } } #[cfg(test)] mod tests { - use super::{Client, HttpRequest, Request}; + use super::{Client, HttpRequest, Request, Version}; use crate::Method; use serde::Serialize; use std::collections::BTreeMap; @@ -753,6 +794,26 @@ mod tests { assert_eq!(req.url().as_str(), "http://localhost/"); } + #[test] + fn set_http_request_version() { + let http_request = HttpRequest::builder() + .method("GET") + .uri("http://localhost/") + .header("User-Agent", "my-awesome-agent/1.0") + .version(Version::HTTP_11) + .body("test test test") + .unwrap(); + let req: Request = Request::try_from(http_request).unwrap(); + assert_eq!(req.body().is_none(), false); + let test_data = b"test test test"; + assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..])); + let headers = req.headers(); + assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0"); + assert_eq!(req.method(), Method::GET); + assert_eq!(req.url().as_str(), "http://localhost/"); + assert_eq!(req.version(), Version::HTTP_11); + } + /* use {body, Method}; use super::Client; diff --git a/src/blocking/request.rs b/src/blocking/request.rs index 7b36d3f..f0517ca 100644 --- a/src/blocking/request.rs +++ b/src/blocking/request.rs @@ -3,7 +3,7 @@ use std::fmt; use std::time::Duration; use base64::encode; -use http::{request::Parts, Request as HttpRequest}; +use http::{request::Parts, Request as HttpRequest, Version}; use serde::Serialize; #[cfg(feature = "json")] use serde_json; @@ -78,6 +78,18 @@ impl Request { self.inner.headers_mut() } + /// Get the http version. + #[inline] + pub fn version(&self) -> Version { + self.inner.version() + } + + /// Get a mutable reference to the http version. + #[inline] + pub fn version_mut(&mut self) -> &mut Version { + self.inner.version_mut() + } + /// Get the body. #[inline] pub fn body(&self) -> Option<&Body> { @@ -118,6 +130,7 @@ impl Request { }; let mut req = Request::new(self.method().clone(), self.url().clone()); *req.headers_mut() = self.headers().clone(); + *req.version_mut() = self.version().clone(); req.body = body; Some(req) } @@ -394,6 +407,14 @@ impl RequestBuilder { self } + /// Set HTTP version + pub fn version(mut self, version: Version) -> RequestBuilder { + if let Ok(ref mut req) = self.request { + *req.version_mut() = version; + } + self + } + /// Send a form body. /// /// Sets the body to the url encoded serialization of the passed value, @@ -640,7 +661,7 @@ fn fmt_request_fields<'a, 'b>( #[cfg(test)] mod tests { use super::super::{body, Client}; - use super::{HttpRequest, Request}; + use super::{HttpRequest, Request, Version}; use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST}; use crate::Method; use serde::Serialize; @@ -988,6 +1009,26 @@ mod tests { assert_eq!(req.url().as_str(), "http://localhost/"); } + #[test] + fn set_http_request_version() { + let http_request = HttpRequest::builder() + .method("GET") + .uri("http://localhost/") + .header("User-Agent", "my-awesome-agent/1.0") + .version(Version::HTTP_11) + .body("test test test") + .unwrap(); + let req: Request = Request::try_from(http_request).unwrap(); + assert_eq!(req.body().is_none(), false); + let test_data = b"test test test"; + assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..])); + let headers = req.headers(); + assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0"); + assert_eq!(req.method(), Method::GET); + assert_eq!(req.url().as_str(), "http://localhost/"); + assert_eq!(req.version(), Version::HTTP_11); + } + #[test] fn test_basic_auth_sensitive_header() { let client = Client::new();