vtubestudio/service/
retry.rs1use crate::data::{RequestEnvelope, ResponseEnvelope};
2use crate::error::{Error, ErrorKind};
3
4use futures_util::future;
5use tower::retry::{Policy, Retry};
6use tower::Layer;
7use tracing::debug;
8
9#[derive(Debug, Clone)]
13pub struct RetryPolicy {
14 retry_on_disconnect: bool,
15 retry_on_auth_error: bool,
16}
17
18impl RetryPolicy {
19 pub fn new() -> Self {
21 RetryPolicy {
22 retry_on_disconnect: true,
23 retry_on_auth_error: true,
24 }
25 }
26
27 pub fn on_disconnect(mut self, value: bool) -> Self {
29 self.retry_on_disconnect = value;
30 self
31 }
32
33 pub fn on_auth_error(mut self, value: bool) -> Self {
35 self.retry_on_auth_error = value;
36 self
37 }
38}
39
40impl<S> Layer<S> for RetryPolicy {
41 type Service = Retry<Self, S>;
42
43 fn layer(&self, service: S) -> Self::Service {
44 let policy = self.clone();
45 Retry::new(policy, service)
46 }
47}
48
49impl Policy<RequestEnvelope, ResponseEnvelope, Error> for RetryPolicy {
50 type Future = future::Ready<Self>;
51
52 fn retry(
53 &self,
54 req: &RequestEnvelope,
55 result: Result<&ResponseEnvelope, &Error>,
56 ) -> Option<Self::Future> {
57 Some(future::ready(match result {
58 Ok(resp) if resp.is_unauthenticated_error() && self.retry_on_auth_error => {
59 self.clone().on_auth_error(false)
60 }
61
62 Err(e) => {
63 if self.retry_on_auth_error && e.is_unauthenticated_error() {
64 debug!(
65 message_type = req.message_type.as_str(),
66 "Retrying request due to API auth error"
67 );
68 self.clone().on_auth_error(false)
69 } else if self.retry_on_disconnect && e.has_kind(ErrorKind::ConnectionDropped) {
70 debug!(
71 message_type = req.message_type.as_str(),
72 "Retrying request due to disconnection"
73 );
74 self.clone().on_disconnect(false)
75 } else {
76 return None;
77 }
78 }
79
80 _ => return None,
81 }))
82 }
83
84 fn clone_request(&self, req: &RequestEnvelope) -> Option<RequestEnvelope> {
85 Some(req.clone())
86 }
87}