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}