1use reqwest::header::USER_AGENT;
2use reqwest::Client;
3
4use crate::config::Config;
5use crate::yubicoerror::YubicoError;
6use crate::{build_request, Request, Result};
7use futures::stream::FuturesUnordered;
8use futures::StreamExt;
9use std::sync::Arc;
10
11pub async fn verify_async<S>(otp: S, config: Config) -> Result<()>
12where
13 S: Into<String>,
14{
15 AsyncVerifier::new(config)?.verify(otp).await
16}
17
18pub struct AsyncVerifier {
19 client: Client,
20 config: Config,
21}
22
23impl AsyncVerifier {
24 pub fn new(config: Config) -> Result<AsyncVerifier> {
25 let client =
26 if config.proxy_url != "" && config.proxy_username == "" {
27 AsyncVerifier::get_client_proxy(config.clone())?
28 } else if config.proxy_url != "" && config.proxy_username != "" {
29 AsyncVerifier::get_client_proxy_with_auth(config.clone())?
30 } else {
31 Client::builder().timeout(config.request_timeout).build()?
32 };
33
34 Ok(AsyncVerifier { client, config })
35 }
36
37 pub async fn verify<S>(&self, otp: S) -> Result<()>
38 where
39 S: Into<String>,
40 {
41 let request = Arc::new(build_request(otp, &self.config)?); let mut responses = FuturesUnordered::new();
44 self.config
45 .api_hosts
46 .iter()
47 .for_each(|api_host| responses.push(self.request(request.clone(), api_host)));
48
49 let mut errors = vec![];
50
51 while let Some(response) = responses.next().await {
52 match response {
53 Ok(()) => return Ok(()),
54 Err(err @ YubicoError::ReplayedRequest) => errors.push(err),
55 Err(YubicoError::HTTPStatusCode(code)) => {
56 errors.push(YubicoError::HTTPStatusCode(code))
57 }
58 Err(err) => return Err(err),
59 }
60 }
61
62 Err(YubicoError::MultipleErrors(errors))
63 }
64
65 async fn request(&self, request: Arc<Request>, api_host: &str) -> Result<()> {
66 let url = request.build_url(api_host);
67 let http_request = self
68 .client
69 .get(&url)
70 .header(USER_AGENT, self.config.user_agent.clone());
71
72 let response = http_request.send().await?;
73 let status_code = response.status();
74
75 if !status_code.is_success() {
76 return Err(YubicoError::HTTPStatusCode(status_code));
77 }
78
79 let text = response.text().await?;
80
81 request.response_verifier.verify_response(text)
82 }
83
84 fn get_client_proxy(config: Config) -> Result<Client> {
85 Ok(Client::builder()
86 .timeout(config.request_timeout)
87 .proxy(reqwest::Proxy::all(&config.proxy_url)?).build()?)
88 }
89
90 fn get_client_proxy_with_auth(config: Config) -> Result<Client> {
91 let proxy = reqwest::Proxy::all(&config.proxy_url)?
92 .basic_auth(&config.proxy_username, &config.proxy_password);
93 Ok(Client::builder().timeout(config.request_timeout).proxy(proxy).build()?)
94 }
95}