Add ureq::request_url and Agent::request_url. (#226)

These let a user pass an already-parsed Url.
This commit is contained in:
Jacob Hoffman-Andrews
2020-11-21 22:11:15 -08:00
committed by GitHub
parent 0321ea043d
commit e92bf0b4bb
3 changed files with 102 additions and 12 deletions

View File

@@ -1,5 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use url::Url;
use crate::pool::ConnectionPool; use crate::pool::ConnectionPool;
use crate::proxy::Proxy; use crate::proxy::Proxy;
use crate::request::Request; use crate::request::Request;
@@ -94,15 +96,21 @@ impl Agent {
AgentBuilder::new().build() AgentBuilder::new().build()
} }
/// Request by providing the HTTP verb such as `GET`, `POST`... /// Make a request with the HTTP verb as a parameter.
///
/// This allows making requests with verbs that don't have a dedicated
/// method.
///
/// If you've got an already-parsed [Url], try [request_url][Agent::request_url].
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), ureq::Error> { /// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true); /// # ureq::is_test(true);
/// use ureq::Response;
/// let agent = ureq::agent(); /// let agent = ureq::agent();
/// ///
/// let resp = agent /// let resp: Response = agent
/// .request("GET", "http://httpbin.org/status/200") /// .request("OPTIONS", "http://example.com/")
/// .call()?; /// .call()?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -111,6 +119,30 @@ impl Agent {
Request::new(self.clone(), method.into(), path.into()) Request::new(self.clone(), method.into(), path.into())
} }
/// Make a request using an already-parsed [Url].
///
/// This is useful if you've got a parsed Url from some other source, or if
/// you want to parse the URL and then modify it before making the request.
/// If you'd just like to pass a String or a `&str`, try [request][Agent::request].
///
/// ```
/// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true);
/// use {url::Url, ureq::Response};
/// let agent = ureq::agent();
///
/// let mut url: Url = "http://example.com/some-page".parse().unwrap();
/// url.set_path("/robots.txt");
/// let resp: Response = agent
/// .request_url("GET", &url)
/// .call()?;
/// # Ok(())
/// # }
/// ```
pub fn request_url(&self, method: &str, url: &Url) -> Request {
Request::with_url(self.clone(), method.into(), url.clone())
}
/// Make a GET request from this agent. /// Make a GET request from this agent.
pub fn get(&self, path: &str) -> Request { pub fn get(&self, path: &str) -> Request {
self.request("GET", path) self.request("GET", path)

View File

@@ -207,6 +207,7 @@ mod cookies;
#[cfg(feature = "json")] #[cfg(feature = "json")]
pub use serde_json::json; pub use serde_json::json;
use url::Url;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
@@ -265,18 +266,46 @@ pub fn agent() -> Agent {
return testserver::test_agent(); return testserver::test_agent();
} }
/// Make a request setting the HTTP method via a string. /// Make a request with the HTTP verb as a parameter.
///
/// This allows making requests with verbs that don't have a dedicated
/// method.
///
/// If you've got an already-parsed [Url], try [request_url][request_url].
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), ureq::Error> { /// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true); /// # ureq::is_test(true);
/// ureq::request("GET", "http://example.com").call()?; /// let resp: ureq::Response = ureq::request("OPTIONS", "http://example.com/")
/// .call()?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn request(method: &str, path: &str) -> Request { pub fn request(method: &str, path: &str) -> Request {
agent().request(method, path) agent().request(method, path)
} }
/// Make a request using an already-parsed [Url].
///
/// This is useful if you've got a parsed Url from some other source, or if
/// you want to parse the URL and then modify it before making the request.
/// If you'd just like to pass a String or a `&str`, try [request][request()].
///
/// ```
/// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true);
/// use url::Url;
/// let agent = ureq::agent();
///
/// let mut url: Url = "http://example.com/some-page".parse().unwrap();
/// url.set_path("/robots.txt");
/// let resp: ureq::Response = ureq::request_url("GET", &url)
/// .call()?;
/// # Ok(())
/// # }
/// ```
pub fn request_url(method: &str, url: &Url) -> Request {
agent().request_url(method, url)
}
/// Make a GET request. /// Make a GET request.
pub fn get(path: &str) -> Request { pub fn get(path: &str) -> Request {

View File

@@ -15,6 +15,12 @@ use super::SerdeValue;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone)]
enum Urlish {
Url(Url),
Str(String),
}
/// Request instances are builders that creates a request. /// Request instances are builders that creates a request.
/// ///
/// ``` /// ```
@@ -30,12 +36,21 @@ pub type Result<T> = std::result::Result<T, Error>;
pub struct Request { pub struct Request {
agent: Agent, agent: Agent,
method: String, method: String,
url: String, url: Urlish,
error_on_non_2xx: bool, error_on_non_2xx: bool,
headers: Vec<Header>, headers: Vec<Header>,
query_params: Vec<(String, String)>, query_params: Vec<(String, String)>,
} }
impl fmt::Display for Urlish {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Urlish::Url(u) => write!(f, "{}", u),
Urlish::Str(s) => write!(f, "{}", s),
}
}
}
impl fmt::Debug for Request { impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
@@ -51,7 +66,18 @@ impl Request {
Request { Request {
agent, agent,
method, method,
url, url: Urlish::Str(url),
headers: vec![],
error_on_non_2xx: true,
query_params: vec![],
}
}
pub(crate) fn with_url(agent: Agent, method: String, url: Url) -> Request {
Request {
agent,
method,
url: Urlish::Url(url),
headers: vec![], headers: vec![],
error_on_non_2xx: true, error_on_non_2xx: true,
query_params: vec![], query_params: vec![],
@@ -79,11 +105,14 @@ impl Request {
for h in &self.headers { for h in &self.headers {
h.validate()?; h.validate()?;
} }
let mut url: Url = self.url.parse().map_err(|e: url::ParseError| { let mut url: Url = match self.url.clone() {
ErrorKind::BadUrl Urlish::Url(u) => u,
.msg(&format!("failed to parse URL '{}'", self.url)) Urlish::Str(s) => s.parse().map_err(|e: url::ParseError| {
.src(e) ErrorKind::BadUrl
})?; .msg(&format!("failed to parse URL '{}'", self.url))
.src(e)
})?,
};
for (name, value) in self.query_params.clone() { for (name, value) in self.query_params.clone() {
url.query_pairs_mut().append_pair(&name, &value); url.query_pairs_mut().append_pair(&name, &value);
} }