From 2cb94e15ed900b38ed142fa2f33d0e2f0b48812a Mon Sep 17 00:00:00 2001 From: Mikhail Grachev Date: Tue, 8 Feb 2022 20:50:19 +0300 Subject: [PATCH] feat: add http timeout Signed-off-by: Mikhail Grachev --- examples/crates.rs | 3 ++- examples/github.rs | 3 ++- examples/pypi.rs | 3 ++- src/http_client.rs | 26 ++++++++++++++++++++++++++ src/lib.rs | 18 +++++++++++++++--- src/registry.rs | 3 ++- src/registry/crates.rs | 7 ++++--- src/registry/github.rs | 12 ++++++------ src/registry/pypi.rs | 7 ++++--- 9 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 src/http_client.rs diff --git a/examples/crates.rs b/examples/crates.rs index c5d8c22..5f3c78d 100644 --- a/examples/crates.rs +++ b/examples/crates.rs @@ -5,7 +5,8 @@ fn main() { let pkg_name = "update-informer"; let current_version = "0.1.0"; - let informer = UpdateInformer::new(Crates, pkg_name, current_version, Duration::default()); + let informer = + UpdateInformer::new(Crates, pkg_name, current_version).interval(Duration::default()); if let Ok(Some(new_version)) = informer.check_version() { println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}"); diff --git a/examples/github.rs b/examples/github.rs index 2953373..7beed8f 100644 --- a/examples/github.rs +++ b/examples/github.rs @@ -5,7 +5,8 @@ fn main() { let pkg_name = "dotenv-linter/dotenv-linter"; let current_version = "3.1.0"; - let informer = UpdateInformer::new(GitHub, pkg_name, current_version, Duration::default()); + let informer = + UpdateInformer::new(GitHub, pkg_name, current_version).interval(Duration::default()); if let Ok(Some(new_version)) = informer.check_version() { println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}"); diff --git a/examples/pypi.rs b/examples/pypi.rs index 1411962..dc99697 100644 --- a/examples/pypi.rs +++ b/examples/pypi.rs @@ -5,7 +5,8 @@ fn main() { let pkg_name = "filprofiler"; let current_version = "2022.1.0"; - let informer = UpdateInformer::new(PyPI, pkg_name, current_version, Duration::default()); + let informer = + UpdateInformer::new(PyPI, pkg_name, current_version).interval(Duration::default()); if let Ok(Some(new_version)) = informer.check_version() { println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}"); diff --git a/src/http_client.rs b/src/http_client.rs new file mode 100644 index 0000000..a35408e --- /dev/null +++ b/src/http_client.rs @@ -0,0 +1,26 @@ +use crate::Error; +use serde::de::DeserializeOwned; +use std::time::Duration; + +pub(crate) struct HTTPClient { + request: ureq::Request, +} + +impl HTTPClient { + pub(crate) fn new(url: &str, timeout: Duration) -> Self { + Self { + request: ureq::builder().timeout(timeout).build().request("GET", url), + } + } + + pub(crate) fn add_header(mut self, key: &str, value: &str) -> Self { + self.request = self.request.set(key, value); + self + } + + pub(crate) fn call(self) -> Result { + let json = self.request.call()?.into_json()?; + + Ok(json) + } +} diff --git a/src/lib.rs b/src/lib.rs index 1cd73a2..a2fcfb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,12 +114,14 @@ //! informer.check_version(); //! ``` +use crate::http_client::HTTPClient; use crate::package::Package; use crate::registry::Registry; use crate::version::Version; use crate::version_file::VersionFile; use std::time::Duration; +mod http_client; mod package; mod version; mod version_file; @@ -145,6 +147,7 @@ pub struct UpdateInformer, V: AsRef> { name: N, version: V, interval: Duration, + timeout: Duration, } impl, V: AsRef> UpdateInformer { @@ -167,14 +170,23 @@ impl, V: AsRef> UpdateInformer { /// let version = env!("CARGO_PKG_VERSION"); /// let informer = UpdateInformer::new(Crates, name, version, Duration::from_secs(60 * 60 * 24)); /// ``` - pub fn new(registry: R, name: N, version: V, interval: Duration) -> Self { + pub fn new(registry: R, name: N, version: V) -> Self { Self { _registry: registry, name, version, - interval, + interval: Duration::from_secs(60 * 60 * 24), // Once a day + timeout: Duration::from_secs(5), } } + + pub fn interval(self, interval: Duration) -> Self { + Self { interval, ..self } + } + + pub fn timeout(self, timeout: Duration) -> Self { + Self { timeout, ..self } + } } impl, V: AsRef> Check for UpdateInformer { @@ -221,7 +233,7 @@ impl, V: AsRef> Check for UpdateInformer { latest_version_file.write_version(&v)?; v diff --git a/src/registry.rs b/src/registry.rs index 5553393..dc7b8ba 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -1,4 +1,5 @@ use crate::{Error, Package}; +use std::time::Duration; pub use crates::Crates; pub use github::GitHub; @@ -14,5 +15,5 @@ pub trait Registry { /// # Arguments /// /// * `pkg` - A `Package` struct. - fn get_latest_version(pkg: &Package) -> Result, Error>; + fn get_latest_version(pkg: &Package, timeout: Duration) -> Result, Error>; } diff --git a/src/registry/crates.rs b/src/registry/crates.rs index 4baa6cd..87e7439 100644 --- a/src/registry/crates.rs +++ b/src/registry/crates.rs @@ -1,6 +1,7 @@ use crate::registry::Registry; -use crate::{Error, Package}; +use crate::{Error, HTTPClient, Package}; use serde::Deserialize; +use std::time::Duration; #[cfg(test)] use mockito; @@ -32,10 +33,10 @@ fn get_base_url() -> String { } impl Registry for Crates { - fn get_latest_version(pkg: &Package) -> Result, Error> { + fn get_latest_version(pkg: &Package, timeout: Duration) -> Result, Error> { let url = format!("{}/{}/versions", get_base_url(), pkg); - let resp: Response = ureq::get(&url).call()?.into_json()?; + let resp: Response = HTTPClient::new(&url, timeout).call()?; if let Some(v) = resp.versions.first() { return Ok(Some(v.num.clone())); diff --git a/src/registry/github.rs b/src/registry/github.rs index a80df55..dd462e3 100644 --- a/src/registry/github.rs +++ b/src/registry/github.rs @@ -1,6 +1,7 @@ use crate::registry::Registry; -use crate::{Error, Package}; +use crate::{Error, HTTPClient, Package}; use serde::Deserialize; +use std::time::Duration; #[cfg(test)] use mockito; @@ -27,13 +28,12 @@ fn get_base_url() -> String { } impl Registry for GitHub { - fn get_latest_version(pkg: &Package) -> Result, Error> { + fn get_latest_version(pkg: &Package, timeout: Duration) -> Result, Error> { let url = format!("{}/{}/releases/latest", get_base_url(), pkg); - let resp: Response = ureq::get(&url) - .set("Accept", "application/vnd.github.v3+json") - .call()? - .into_json()?; + let resp: Response = HTTPClient::new(&url, timeout) + .add_header("Accept", "application/vnd.github.v3+json") + .call()?; if resp.tag_name.starts_with('v') { return Ok(Some(resp.tag_name[1..].to_string())); diff --git a/src/registry/pypi.rs b/src/registry/pypi.rs index 1c2c04a..6befe1f 100644 --- a/src/registry/pypi.rs +++ b/src/registry/pypi.rs @@ -1,6 +1,7 @@ use crate::registry::Registry; -use crate::{Error, Package}; +use crate::{Error, HTTPClient, Package}; use serde::Deserialize; +use std::time::Duration; #[cfg(test)] use mockito; @@ -30,10 +31,10 @@ fn get_base_url() -> String { } impl Registry for PyPI { - fn get_latest_version(pkg: &Package) -> Result, Error> { + fn get_latest_version(pkg: &Package, timeout: Duration) -> Result, Error> { let url = format!("{}/{}/json", get_base_url(), pkg); - let resp: Response = ureq::get(&url).call()?.into_json()?; + let resp: Response = HTTPClient::new(&url, timeout).call()?; if !resp.info.yanked { return Ok(Some(resp.info.version));