reqwest_netrc/
lib.rs

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}