/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-3.3.0/src/request_ext.rs
Line | Count | Source |
1 | | use crate::config::typestate::RequestExtScope; |
2 | | use crate::config::{Config, ConfigBuilder, RequestLevelConfig}; |
3 | | use crate::{Agent, AsSendBody, Body, Error, http}; |
4 | | use std::ops::Deref; |
5 | | use ureq_proto::http::{Request, Response}; |
6 | | |
7 | | /// Extension trait for [`http::Request<impl AsSendBody>`]. |
8 | | /// |
9 | | /// Adds additional convenience methods to the `Request` that are not available |
10 | | /// in the plain http API. |
11 | | pub trait RequestExt<S> |
12 | | where |
13 | | S: AsSendBody, |
14 | | { |
15 | | /// Allows configuring the request behaviour, starting with the default [`Agent`]. |
16 | | /// |
17 | | /// This method allows configuring the request by using the default Agent, and performing |
18 | | /// additional configurations on top. |
19 | | /// This method returns a `WithAgent` struct that it is possible to call `configure()` and `run()` |
20 | | /// on to configure the request behaviour, or run the request. |
21 | | /// |
22 | | /// # Example |
23 | | /// |
24 | | /// ``` |
25 | | /// use ureq::{http, RequestExt, Error}; |
26 | | /// |
27 | | /// let request: Result<http::Response<_>, Error> = http::Request::builder() |
28 | | /// .method(http::Method::GET) |
29 | | /// .uri("http://foo.bar") |
30 | | /// .body(()) |
31 | | /// .unwrap() |
32 | | /// .with_default_agent() |
33 | | /// .configure() |
34 | | /// .http_status_as_error(false) |
35 | | /// .run(); |
36 | | /// ``` |
37 | 0 | fn with_default_agent(self) -> WithAgent<'static, S> |
38 | 0 | where |
39 | 0 | Self: Sized, |
40 | | { |
41 | 0 | let agent = Agent::new_with_defaults(); |
42 | 0 | Self::with_agent(self, agent) |
43 | 0 | } |
44 | | |
45 | | /// Allows configuring this request behaviour, using a specific [`Agent`]. |
46 | | /// |
47 | | /// This method allows configuring the request by using a user-provided `Agent` and performing |
48 | | /// additional configurations on top. |
49 | | /// This method returns a `WithAgent` struct that it is possible to call `configure()` and `run()` |
50 | | /// on to configure the request behaviour, or run the request. |
51 | | /// |
52 | | /// # Example |
53 | | /// |
54 | | /// ``` |
55 | | /// use ureq::{http, Agent, RequestExt, Error}; |
56 | | /// use std::time::Duration; |
57 | | /// let agent = Agent::config_builder() |
58 | | /// .timeout_global(Some(Duration::from_secs(30))) |
59 | | /// .build() |
60 | | /// .new_agent(); |
61 | | /// |
62 | | /// let request: Result<http::Response<_>, Error> = http::Request::builder() |
63 | | /// .method(http::Method::GET) |
64 | | /// .uri("http://foo.bar") |
65 | | /// .body(()) |
66 | | /// .unwrap() |
67 | | /// .with_agent(&agent) |
68 | | /// .run(); |
69 | | /// ``` |
70 | | /// # Example with further customizations |
71 | | /// |
72 | | /// In this example we use a specific agent, but apply a request-specific configuration on top. |
73 | | /// |
74 | | /// ``` |
75 | | /// use ureq::{http, Agent, RequestExt, Error}; |
76 | | /// use std::time::Duration; |
77 | | /// let mut agent = Agent::config_builder() |
78 | | /// .timeout_global(Some(Duration::from_secs(30))) |
79 | | /// .build() |
80 | | /// .new_agent(); |
81 | | /// |
82 | | /// let request: Result<http::Response<_>, Error> = http::Request::builder() |
83 | | /// .method(http::Method::GET) |
84 | | /// .uri("http://foo.bar") |
85 | | /// .body(()) |
86 | | /// .unwrap() |
87 | | /// .with_agent(&agent) |
88 | | /// .configure() |
89 | | /// .http_status_as_error(false) |
90 | | /// .run(); |
91 | | /// ``` |
92 | | fn with_agent<'a>(self, agent: impl Into<AgentRef<'a>>) -> WithAgent<'a, S>; |
93 | | } |
94 | | |
95 | | /// Wrapper struct that holds a [`Request`] associated with an [`Agent`]. |
96 | | pub struct WithAgent<'a, S: AsSendBody> { |
97 | | pub(crate) agent: AgentRef<'a>, |
98 | | pub(crate) request: Request<S>, |
99 | | } |
100 | | |
101 | | impl<'a, S: AsSendBody> WithAgent<'a, S> { |
102 | | /// Returns a [`ConfigBuilder`] for configuring the request. |
103 | | /// |
104 | | /// This allows setting additional request-specific options before sending the request. |
105 | 0 | pub fn configure(self) -> ConfigBuilder<RequestExtScope<'a, S>> { |
106 | 0 | ConfigBuilder(RequestExtScope(self)) |
107 | 0 | } |
108 | | |
109 | | /// Executes the request using the associated [`Agent`]. |
110 | 0 | pub fn run(self) -> Result<Response<Body>, Error> { |
111 | 0 | self.agent.run(self.request) |
112 | 0 | } |
113 | | } |
114 | | |
115 | | impl<'a, S: AsSendBody> WithAgent<'a, S> { |
116 | 0 | pub(crate) fn request_level_config(&mut self) -> &mut Config { |
117 | 0 | let request_level_config = self |
118 | 0 | .request |
119 | 0 | .extensions_mut() |
120 | 0 | .get_mut::<RequestLevelConfig>(); |
121 | | |
122 | 0 | if request_level_config.is_none() { |
123 | 0 | self.request |
124 | 0 | .extensions_mut() |
125 | 0 | .insert(self.agent.new_request_level_config()); |
126 | 0 | } |
127 | | |
128 | | // Unwrap is safe because of the above check |
129 | 0 | let req_level: &mut RequestLevelConfig = self |
130 | 0 | .request |
131 | 0 | .extensions_mut() |
132 | 0 | .get_mut::<RequestLevelConfig>() |
133 | 0 | .unwrap(); |
134 | | |
135 | 0 | &mut req_level.0 |
136 | 0 | } |
137 | | } |
138 | | |
139 | | /// Reference type to hold an owned or borrowed [`Agent`]. |
140 | | pub enum AgentRef<'a> { |
141 | | Owned(Agent), |
142 | | Borrowed(&'a Agent), |
143 | | } |
144 | | |
145 | | impl<S: AsSendBody> RequestExt<S> for http::Request<S> { |
146 | 0 | fn with_agent<'a>(self, agent: impl Into<AgentRef<'a>>) -> WithAgent<'a, S> { |
147 | 0 | WithAgent { |
148 | 0 | agent: agent.into(), |
149 | 0 | request: self, |
150 | 0 | } |
151 | 0 | } |
152 | | } |
153 | | |
154 | | impl From<Agent> for AgentRef<'static> { |
155 | 0 | fn from(value: Agent) -> Self { |
156 | 0 | AgentRef::Owned(value) |
157 | 0 | } |
158 | | } |
159 | | |
160 | | impl<'a> From<&'a Agent> for AgentRef<'a> { |
161 | 0 | fn from(value: &'a Agent) -> Self { |
162 | 0 | AgentRef::Borrowed(value) |
163 | 0 | } |
164 | | } |
165 | | |
166 | | impl Deref for AgentRef<'_> { |
167 | | type Target = Agent; |
168 | | |
169 | 0 | fn deref(&self) -> &Self::Target { |
170 | 0 | match self { |
171 | 0 | AgentRef::Owned(agent) => agent, |
172 | 0 | AgentRef::Borrowed(agent) => agent, |
173 | | } |
174 | 0 | } |
175 | | } |
176 | | |
177 | | #[cfg(test)] |
178 | | mod tests { |
179 | | use super::*; |
180 | | use crate::config::RequestLevelConfig; |
181 | | use std::time::Duration; |
182 | | |
183 | | #[test] |
184 | | fn configure_request_with_default_agent() { |
185 | | // Create `http` crate request and configure with trait |
186 | | let request = http::Request::builder() |
187 | | .method(http::Method::GET) |
188 | | .uri("http://foo.bar") |
189 | | .body(()) |
190 | | .unwrap() |
191 | | .with_default_agent() |
192 | | .configure() |
193 | | .https_only(true) |
194 | | .build(); |
195 | | |
196 | | // Assert that the request-level configuration has been set |
197 | | let request_config = request |
198 | | .request |
199 | | .extensions() |
200 | | .get::<RequestLevelConfig>() |
201 | | .cloned() |
202 | | .unwrap(); |
203 | | |
204 | | assert!(request_config.0.https_only()); |
205 | | } |
206 | | |
207 | | #[test] |
208 | | fn configure_request_default_agent_2() { |
209 | | // Create `http` crate request and configure with trait |
210 | | let request = http::Request::builder() |
211 | | .method(http::Method::GET) |
212 | | .uri("http://foo.bar") |
213 | | .body(()) |
214 | | .unwrap() |
215 | | .with_default_agent() |
216 | | .configure() |
217 | | .https_only(false) |
218 | | .build(); |
219 | | |
220 | | // Assert that the request-level configuration has been set |
221 | | let request_config = request |
222 | | .request |
223 | | .extensions() |
224 | | .get::<RequestLevelConfig>() |
225 | | .cloned() |
226 | | .unwrap(); |
227 | | |
228 | | assert!(!request_config.0.https_only()); |
229 | | } |
230 | | |
231 | | #[test] |
232 | | fn configure_request_default_agent_3() { |
233 | | // Create `http` crate request |
234 | | let request = http::Request::builder() |
235 | | .method(http::Method::POST) |
236 | | .uri("http://foo.bar") |
237 | | .body("Some body") |
238 | | .unwrap(); |
239 | | |
240 | | // Configure with the trait |
241 | | let request = request |
242 | | .with_default_agent() |
243 | | .configure() |
244 | | .http_status_as_error(true) |
245 | | .build(); |
246 | | |
247 | | let request_config = request |
248 | | .request |
249 | | .extensions() |
250 | | .get::<RequestLevelConfig>() |
251 | | .cloned() |
252 | | .unwrap(); |
253 | | |
254 | | assert!(request_config.0.http_status_as_error()); |
255 | | } |
256 | | |
257 | | #[test] |
258 | | fn configure_request_default_agent_4() { |
259 | | // Create `http` crate request |
260 | | let request = http::Request::builder() |
261 | | .method(http::Method::POST) |
262 | | .uri("http://foo.bar") |
263 | | .body("Some body") |
264 | | .unwrap(); |
265 | | |
266 | | // Configure with the trait |
267 | | let request = request |
268 | | .with_default_agent() |
269 | | .configure() |
270 | | .http_status_as_error(false) |
271 | | .build(); |
272 | | |
273 | | let request_config = request |
274 | | .request |
275 | | .extensions() |
276 | | .get::<RequestLevelConfig>() |
277 | | .cloned() |
278 | | .unwrap(); |
279 | | |
280 | | assert!(!request_config.0.http_status_as_error()); |
281 | | } |
282 | | |
283 | | #[test] |
284 | | fn configure_request_specified_agent() { |
285 | | // Create `http` crate request |
286 | | let request = http::Request::builder() |
287 | | .method(http::Method::POST) |
288 | | .uri("http://foo.bar") |
289 | | .body("Some body") |
290 | | .unwrap(); |
291 | | |
292 | | // Configure with the trait |
293 | | let agent = Agent::config_builder() |
294 | | .timeout_per_call(Some(Duration::from_secs(60))) |
295 | | .build() |
296 | | .new_agent(); |
297 | | |
298 | | let request = request |
299 | | .with_agent(&agent) |
300 | | .configure() |
301 | | .http_status_as_error(false) |
302 | | .build(); |
303 | | |
304 | | let request_config = request |
305 | | .request |
306 | | .extensions() |
307 | | .get::<RequestLevelConfig>() |
308 | | .cloned() |
309 | | .unwrap(); |
310 | | |
311 | | // The request-level config is the agent defaults + the explicitly configured stuff |
312 | | assert!(!request_config.0.http_status_as_error()); |
313 | | assert_eq!( |
314 | | request_config.0.timeouts().per_call, |
315 | | Some(Duration::from_secs(60)) |
316 | | ); |
317 | | } |
318 | | } |