Skip to main content

openwire_core/
policy.rs

1use std::sync::Arc;
2
3use http::{Method, StatusCode, Uri};
4
5use crate::WireError;
6
7pub trait RetryPolicy: Send + Sync + 'static {
8    fn should_retry(&self, ctx: &RetryContext<'_>) -> Option<&'static str>;
9
10    fn should_retry_response(&self, _ctx: &ResponseRetryContext<'_>) -> Option<&'static str> {
11        None
12    }
13}
14
15impl<T> RetryPolicy for Arc<T>
16where
17    T: RetryPolicy + ?Sized,
18{
19    fn should_retry(&self, ctx: &RetryContext<'_>) -> Option<&'static str> {
20        (**self).should_retry(ctx)
21    }
22
23    fn should_retry_response(&self, ctx: &ResponseRetryContext<'_>) -> Option<&'static str> {
24        (**self).should_retry_response(ctx)
25    }
26}
27
28pub struct RetryContext<'a> {
29    error: &'a WireError,
30    attempt: u32,
31    is_body_replayable: bool,
32    request_method: &'a Method,
33}
34
35impl<'a> RetryContext<'a> {
36    pub fn new(
37        error: &'a WireError,
38        attempt: u32,
39        is_body_replayable: bool,
40        request_method: &'a Method,
41    ) -> Self {
42        Self {
43            error,
44            attempt,
45            is_body_replayable,
46            request_method,
47        }
48    }
49
50    pub fn error(&self) -> &'a WireError {
51        self.error
52    }
53
54    pub fn attempt(&self) -> u32 {
55        self.attempt
56    }
57
58    pub fn is_body_replayable(&self) -> bool {
59        self.is_body_replayable
60    }
61
62    pub fn request_method(&self) -> &'a Method {
63        self.request_method
64    }
65}
66
67pub struct ResponseRetryContext<'a> {
68    response_status: StatusCode,
69    retry_count: u32,
70    is_body_replayable: bool,
71    request_method: &'a Method,
72    retry_after: Option<RetryAfter>,
73}
74
75impl<'a> ResponseRetryContext<'a> {
76    pub fn new(
77        response_status: StatusCode,
78        retry_count: u32,
79        is_body_replayable: bool,
80        request_method: &'a Method,
81        retry_after: Option<RetryAfter>,
82    ) -> Self {
83        Self {
84            response_status,
85            retry_count,
86            is_body_replayable,
87            request_method,
88            retry_after,
89        }
90    }
91
92    pub fn response_status(&self) -> StatusCode {
93        self.response_status
94    }
95
96    pub fn retry_count(&self) -> u32 {
97        self.retry_count
98    }
99
100    pub fn is_body_replayable(&self) -> bool {
101        self.is_body_replayable
102    }
103
104    pub fn request_method(&self) -> &'a Method {
105        self.request_method
106    }
107
108    pub fn retry_after(&self) -> Option<RetryAfter> {
109        self.retry_after
110    }
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114pub enum RetryAfter {
115    Immediate,
116    Delayed,
117    Invalid,
118}
119
120pub trait RedirectPolicy: Send + Sync + 'static {
121    fn should_redirect(&self, ctx: &RedirectContext<'_>) -> RedirectDecision;
122}
123
124impl<T> RedirectPolicy for Arc<T>
125where
126    T: RedirectPolicy + ?Sized,
127{
128    fn should_redirect(&self, ctx: &RedirectContext<'_>) -> RedirectDecision {
129        (**self).should_redirect(ctx)
130    }
131}
132
133pub struct RedirectContext<'a> {
134    request_method: &'a Method,
135    request_uri: &'a Uri,
136    response_status: StatusCode,
137    location: &'a Uri,
138    redirect_count: u32,
139    is_body_replayable: bool,
140}
141
142impl<'a> RedirectContext<'a> {
143    pub fn new(
144        request_method: &'a Method,
145        request_uri: &'a Uri,
146        response_status: StatusCode,
147        location: &'a Uri,
148        redirect_count: u32,
149        is_body_replayable: bool,
150    ) -> Self {
151        Self {
152            request_method,
153            request_uri,
154            response_status,
155            location,
156            redirect_count,
157            is_body_replayable,
158        }
159    }
160
161    pub fn request_method(&self) -> &'a Method {
162        self.request_method
163    }
164
165    pub fn request_uri(&self) -> &'a Uri {
166        self.request_uri
167    }
168
169    pub fn response_status(&self) -> StatusCode {
170        self.response_status
171    }
172
173    pub fn location(&self) -> &'a Uri {
174        self.location
175    }
176
177    pub fn redirect_count(&self) -> u32 {
178        self.redirect_count
179    }
180
181    pub fn is_body_replayable(&self) -> bool {
182        self.is_body_replayable
183    }
184}
185
186pub enum RedirectDecision {
187    Follow,
188    Stop,
189    Error(WireError),
190}