From 3b2efcfc13e004dde46d8aeb02810334c58f2bc5 Mon Sep 17 00:00:00 2001 From: Hakim Taklanti Date: Sat, 20 Jan 2024 22:14:30 +0100 Subject: [PATCH] feat: add reqwest-netrc --- Cargo.toml | 9 +++- README.md | 2 +- reqwest-netrc/Cargo.toml | 22 ++++++++ reqwest-netrc/src/lib.rs | 106 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 reqwest-netrc/Cargo.toml create mode 100644 reqwest-netrc/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 7c9ccee..986cd4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "netrc" +name = "rust-netrc" version = "0.1.0" description = "netrc support for Rust with support for reqwest" keywords = ["netrc", "reqwest", "http", "client"] @@ -12,5 +12,12 @@ documentation = "https://docs.rs/rust-netrc" license = "MIT" exclude = ["/.github"] +[lib] +name = "netrc" +path = "src/lib.rs" + [dependencies] thiserror = "1.0.56" + +[workspace] +members = ["reqwest-netrc"] diff --git a/README.md b/README.md index faa0bca..db74cf5 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ fn main() { } ``` -With [Reqwest](reqwest-url): +With `reqwest`: TODO diff --git a/reqwest-netrc/Cargo.toml b/reqwest-netrc/Cargo.toml new file mode 100644 index 0000000..69e604e --- /dev/null +++ b/reqwest-netrc/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "reqwest-netrc" +version = "0.1.0" +description = "netrc support for Rust with support for reqwest" +keywords = ["netrc", "reqwest", "http", "client"] +authors = ["Hakim Taklanti "] +edition = "2021" +categories = ["network-programming"] +readme = "../README.md" +repository = "https://github.com/gribouille/netrc" +documentation = "https://docs.rs/rust-netrc" +license = "MIT" + + +[dependencies] +rust-netrc = { path = "..", version = "0.1.0" } +reqwest-middleware = "0.2.4" + +[dev-dependencies] +tokio = { version = "1.35.1", features = ["macros"] } +wiremock = "0.5.22" +reqwest = "0.11.23" diff --git a/reqwest-netrc/src/lib.rs b/reqwest-netrc/src/lib.rs new file mode 100644 index 0000000..f171cc7 --- /dev/null +++ b/reqwest-netrc/src/lib.rs @@ -0,0 +1,106 @@ +use netrc::{Netrc, Result}; +use reqwest_middleware::{RequestBuilder, RequestInitialiser}; +use std::path::Path; + +pub struct NetrcMiddleware { + nrc: Netrc, +} + +impl NetrcMiddleware { + pub fn new() -> Result { + Netrc::new().map(|nrc| NetrcMiddleware { nrc }) + } + + pub fn from_file(file: &Path) -> Result { + Netrc::from_file(file).map(|nrc| NetrcMiddleware { nrc }) + } +} + +impl RequestInitialiser for NetrcMiddleware { + fn init(&self, req: RequestBuilder) -> RequestBuilder { + match req.try_clone() { + Some(nr) => req + .try_clone() + .unwrap() + .build() + .ok() + .and_then(|r| { + r.url() + .host_str() + .and_then(|host| { + self.nrc + .hosts + .get(host) + .or_else(|| self.nrc.hosts.get("default")) + }) + .map(|auth| { + nr.basic_auth( + &auth.login, + if auth.password.is_empty() { + None + } else { + Some(&auth.password) + }, + ) + }) + }) + .unwrap_or(req), + None => req, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use reqwest::Client; + use reqwest_middleware::ClientBuilder; + use std::path::PathBuf; + use wiremock::matchers::{basic_auth, method, path}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + const NETRC: &str = r#"default login myuser password mypassword"#; + + fn create_netrc_file() -> PathBuf { + let dest = std::env::temp_dir().join("netrc"); + if !dest.exists() { + std::fs::write(&dest, NETRC).unwrap(); + } + dest + } + + #[tokio::test] + async fn test_init() { + let server = MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/hello")) + .and(basic_auth("myuser", "mypassword")) + .respond_with(ResponseTemplate::new(200)) + .mount(&server) + .await; + + let status = ClientBuilder::new(Client::builder().build().unwrap()) + .build() + .get(format!("{}/hello", &server.uri())) + .send() + .await + .unwrap() + .status(); + + assert_eq!(status, 404); + + let file = create_netrc_file(); + + let status = ClientBuilder::new(Client::builder().build().unwrap()) + .with_init(NetrcMiddleware::from_file(file.as_path()).unwrap()) + .build() + .get(format!("{}/hello", &server.uri())) + .send() + .await + .unwrap() + .status(); + + assert_eq!(status, 200); + } +}