1use netrc::{Netrc, Result};
2use reqwest_middleware::{RequestBuilder, RequestInitialiser};
3use std::path::Path;
4
5pub struct NetrcMiddleware {
6 nrc: Netrc,
7}
8
9impl NetrcMiddleware {
10 pub fn new() -> Result<Self> {
11 Netrc::new().map(|nrc| NetrcMiddleware { nrc })
12 }
13
14 pub fn from_file(file: &Path) -> Result<Self> {
15 Netrc::from_file(file).map(|nrc| NetrcMiddleware { nrc })
16 }
17}
18
19impl RequestInitialiser for NetrcMiddleware {
20 fn init(&self, req: RequestBuilder) -> RequestBuilder {
21 match req.try_clone() {
22 Some(nr) => req
23 .try_clone()
24 .unwrap()
25 .build()
26 .ok()
27 .and_then(|r| {
28 r.url()
29 .host_str()
30 .and_then(|host| {
31 self.nrc
32 .hosts
33 .get(host)
34 .or_else(|| self.nrc.hosts.get("default"))
35 })
36 .map(|auth| {
37 nr.basic_auth(
38 &auth.login,
39 if auth.password.is_empty() {
40 None
41 } else {
42 Some(&auth.password)
43 },
44 )
45 })
46 })
47 .unwrap_or(req),
48 None => req,
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use reqwest::Client;
57 use reqwest_middleware::ClientBuilder;
58 use std::path::PathBuf;
59 use wiremock::matchers::{basic_auth, method, path};
60 use wiremock::{Mock, MockServer, ResponseTemplate};
61
62 const NETRC: &str = r#"default login myuser password mypassword"#;
63
64 fn create_netrc_file() -> PathBuf {
65 let dest = std::env::temp_dir().join("netrc");
66 if !dest.exists() {
67 std::fs::write(&dest, NETRC).unwrap();
68 }
69 dest
70 }
71
72 #[tokio::test]
73 async fn test_init() {
74 let server = MockServer::start().await;
75
76 Mock::given(method("GET"))
77 .and(path("/hello"))
78 .and(basic_auth("myuser", "mypassword"))
79 .respond_with(ResponseTemplate::new(200))
80 .mount(&server)
81 .await;
82
83 let status = ClientBuilder::new(Client::builder().build().unwrap())
84 .build()
85 .get(format!("{}/hello", &server.uri()))
86 .send()
87 .await
88 .unwrap()
89 .status();
90
91 assert_eq!(status, 404);
92
93 let file = create_netrc_file();
94
95 let status = ClientBuilder::new(Client::builder().build().unwrap())
96 .with_init(NetrcMiddleware::from_file(file.as_path()).unwrap())
97 .build()
98 .get(format!("{}/hello", &server.uri()))
99 .send()
100 .await
101 .unwrap()
102 .status();
103
104 assert_eq!(status, 200);
105 }
106}