acick_util/service/
session.rs1use std::io::Write as _;
2use std::time::Duration;
3
4use anyhow::Context as _;
5use reqwest::blocking::{Client, Request, RequestBuilder, Response};
6use retry::{delay, retry, OperationResult};
7
8use crate::abs_path::AbsPathBuf;
9use crate::service::CookieStorage;
10use crate::{Console, Error, Result};
11
12pub struct RetryRequestBuilder<'a> {
13 inner: RequestBuilder,
14 client: &'a Client,
15 cookies_path: &'a AbsPathBuf,
16 retry_limit: usize,
17 retry_interval: Duration,
18}
19
20impl<'a> RetryRequestBuilder<'a> {
21 pub fn retry_send(mut self, cnsl: &mut Console) -> Result<Response> {
22 let retry_interval = self.retry_interval.as_millis() as u64;
23 let durations = delay::Fixed::from_millis(retry_interval).take(self.retry_limit);
24 retry(durations, || self.send(cnsl)).map_err(|err| match err {
25 retry::Error::Operation { error, .. } => error,
26 retry::Error::Internal(msg) => Error::msg(msg),
27 })
28 }
29
30 fn send(&mut self, cnsl: &mut Console) -> OperationResult<Response, Error> {
31 let result = self
32 .inner
33 .try_clone()
34 .ok_or_else(|| Error::msg("Could not create request"))
35 .and_then(|builder| Ok(builder.build()?))
36 .context("Could not build request")
37 .and_then(|req| self.exec_session_pretty(req, cnsl));
38 match result {
39 Ok(res) => {
40 if res.status().is_server_error() {
41 OperationResult::Retry(Error::msg("Received server error"))
42 } else {
43 OperationResult::Ok(res)
44 }
45 }
46 Err(err) => OperationResult::Retry(err),
47 }
48 }
49
50 fn exec_session_pretty(&mut self, req: Request, cnsl: &mut Console) -> Result<Response> {
51 write!(cnsl, "{:7} {} ... ", req.method().as_str(), req.url()).unwrap_or(());
52 let result = self.exec_session(req).context("Could not send request");
53 match &result {
54 Ok(res) => writeln!(cnsl, "{}", res.status()),
55 Err(_) => writeln!(cnsl, "failed"),
56 }
57 .unwrap_or(());
58 result
59 }
60
61 fn exec_session(&self, mut request: Request) -> Result<Response> {
62 let mut storage =
63 CookieStorage::open(self.cookies_path).context("Could not open cookie storage")?;
64 storage
65 .load_into(&mut request)
66 .context("Could not load cookies into request")?;
67 let response = self.client.execute(request)?;
68 storage
69 .store_from(&response)
70 .context("Could not store cookies from response")?;
71 Ok(response)
72 }
73}
74
75pub trait WithRetry {
76 fn with_retry<'a>(
77 self,
78 client: &'a Client,
79 cookies_path: &'a AbsPathBuf,
80 retry_limit: usize,
81 retry_interval: Duration,
82 ) -> RetryRequestBuilder<'a>;
83}
84
85impl WithRetry for RequestBuilder {
86 fn with_retry<'a>(
87 self,
88 client: &'a Client,
89 cookies_path: &'a AbsPathBuf,
90 retry_limit: usize,
91 retry_interval: Duration,
92 ) -> RetryRequestBuilder<'a> {
93 RetryRequestBuilder {
94 inner: self,
95 client,
96 cookies_path,
97 retry_limit,
98 retry_interval,
99 }
100 }
101}