diff --git a/Cargo.toml b/Cargo.toml index f82660c..5a18d3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_client" -version = "0.1.0" +version = "0.2.0" authors = ["Vladimir Burdukov "] edition = "2018" diff --git a/src/lib.rs b/src/lib.rs index 8cc9e49..43998e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,12 @@ -use curl::easy::Easy; +use std::borrow::Borrow; +use std::thread; + +use futures::channel::oneshot; + use serde::de::DeserializeOwned; use serde_json; -use std::borrow::Borrow; + +use curl::easy::Easy; use url::{ParseError, Url}; #[derive(Debug)] @@ -28,20 +33,18 @@ pub struct HttpClient { } impl HttpClient { - // Public API - pub fn new(base_url: &str) -> Result { let base_url = Url::parse(base_url)?; Ok(HttpClient { base_url }) } - pub fn get(&self, path: &str) -> Result { - self.do_get(self.prepare_url_with_path(path)) + pub async fn get(&self, path: &str) -> Result { + self.do_get(self.prepare_url_with_path(path)).await } - pub fn get_with_params(&self, path: &str, iter: I) -> Result + pub async fn get_with_params(&self, path: &str, iter: I) -> Result where - T: DeserializeOwned, + T: DeserializeOwned + Send + 'static, I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef, @@ -49,7 +52,7 @@ impl HttpClient { { let mut url = self.prepare_url_with_path(path); url.query_pairs_mut().extend_pairs(iter); - self.do_get(url) + self.do_get(url).await } // Private API @@ -60,33 +63,39 @@ impl HttpClient { url } - fn do_get(&self, url: Url) -> Result { - let mut response = Vec::new(); - let mut easy = Easy::new(); - easy.url(url.as_str()).unwrap(); - - { - let mut transfer = easy.transfer(); - transfer - .write_function(|data| { - response.extend_from_slice(data); - Ok(data.len()) - }) - .unwrap(); - transfer.perform().unwrap(); - } - - let code = easy.response_code().unwrap(); - - if code >= 200 && code < 300 { - let response: T = serde_json::from_slice(&response) - .map_err(|err| Error::from(err)) - .unwrap(); - - Ok(response) - } else { - eprintln!("{}", String::from_utf8_lossy(&response)); - Err(Error::HttpError(code)) - } + async fn do_get(&self, url: Url) -> Result { + let (tx, rx) = oneshot::channel::>(); + + thread::spawn(move || { + let mut response = Vec::new(); + let mut easy = Easy::new(); + easy.url(url.as_str()).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer + .write_function(|data| { + response.extend_from_slice(data); + Ok(data.len()) + }) + .unwrap(); + transfer.perform().unwrap(); + } + + let code = easy.response_code().unwrap(); + + if code >= 200 && code < 300 { + let response: T = serde_json::from_slice(&response) + .map_err(|err| Error::from(err)) + .unwrap(); + + let _ = tx.send(Ok(response)); + } else { + eprintln!("{}", String::from_utf8_lossy(&response)); + let _ = tx.send(Err(Error::HttpError(code))); + } + }); + + rx.await.unwrap() } } diff --git a/tests/get.rs b/tests/get.rs index 44a7b1b..f21efff 100644 --- a/tests/get.rs +++ b/tests/get.rs @@ -1,3 +1,4 @@ +use futures::executor::block_on; use http_client::HttpClient; use serde_derive::Deserialize; @@ -11,11 +12,28 @@ fn test_get() { let http_client = HttpClient::new("https://httpbin.org/").unwrap(); assert_eq!( - http_client.get::("/get").unwrap().url, + block_on(http_client.get::("/get")).unwrap().url, "https://httpbin.org/get" ); } +#[test] +fn test_get_join() { + #[derive(Deserialize)] + struct Response { + url: String, + } + + let http_client = HttpClient::new("https://httpbin.org/").unwrap(); + let results = block_on(futures::future::join( + http_client.get::("/delay/2"), + http_client.get::("/delay/1"), + )); + + assert_eq!(results.0.unwrap().url, "https://httpbin.org/delay/2"); + assert_eq!(results.1.unwrap().url, "https://httpbin.org/delay/1"); +} + #[test] fn test_get_with_params() { use std::collections::HashMap; @@ -29,9 +47,8 @@ fn test_get_with_params() { let http_client = HttpClient::new("https://httpbin.org/").unwrap(); let params = vec![("key1", "value1"), ("key2", "value2")]; - let response = http_client - .get_with_params::("/get", params) - .unwrap(); + let response = + block_on(http_client.get_with_params::("/get", params)).unwrap(); assert_eq!( response.url, diff --git a/tests/http_error.rs b/tests/http_error.rs index e4e658b..ab056d5 100644 --- a/tests/http_error.rs +++ b/tests/http_error.rs @@ -1,3 +1,4 @@ +use futures::executor::block_on; use http_client::{Error, HttpClient}; use serde_derive::Deserialize; @@ -8,7 +9,7 @@ fn test_404() { let http_client = HttpClient::new("https://httpbin.org/").unwrap(); - match http_client.get::("/status/404").unwrap_err() { + match block_on(http_client.get::("/status/404")).unwrap_err() { Error::HttpError(404) => (), error => panic!( r#"assertion failed: