Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_text/tests/annealer_corpus/actix_response.rs

12.3 KiB, 1 run

created by r1870400018:11678, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use std::{
2 cell::{Ref, RefMut},
3 fmt,
4};
5
6use actix_http::{
7 body::{BoxBody, EitherBody, MessageBody},
8 header::HeaderMap,
9 Extensions, Response, ResponseHead, StatusCode,
10};
11#[cfg(feature = "cookies")]
12use {
13 actix_http::{
14 error::HttpError,
15 header::{self, HeaderValue},
16 },
17 cookie::Cookie,
18};
19
20use crate::{error::Error, HttpRequest, HttpResponseBuilder, Responder};
21
22/// An outgoing response.
23pub struct HttpResponse<B = BoxBody> {
24 res: Response<B>,
25 error: Option<Error>,
26}
27
28impl HttpResponse<BoxBody> {
29 /// Constructs a response.
30 #[inline]
31 pub fn new(status: StatusCode) -> Self {
32 Self {
33 res: Response::new(status),
34 error: None,
35 }
36 }
37
38 /// Constructs a response builder with specific HTTP status.
39 #[inline]
40 pub fn build(status: StatusCode) -> HttpResponseBuilder {
41 HttpResponseBuilder::new(status)
42 }
43
44 /// Create an error response.
45 #[inline]
46 pub fn from_error(error: impl Into<Error>) -> Self {
47 let error = error.into();
48 let mut response = error.as_response_error().error_response();
49 response.error = Some(error);
50 response
51 }
52}
53
54impl<B> HttpResponse<B> {
55 /// Constructs a response with body
56 #[inline]
57 pub fn with_body(status: StatusCode, body: B) -> Self {
58 Self {
59 res: Response::with_body(status, body),
60 error: None,
61 }
62 }
63
64 /// Returns a reference to response head.
65 #[inline]
66 pub fn head(&self) -> &ResponseHead {
67 self.res.head()
68 }
69
70 /// Returns a mutable reference to response head.
71 #[inline]
72 pub fn head_mut(&mut self) -> &mut ResponseHead {
73 self.res.head_mut()
74 }
75
76 /// The source `error` for this response
77 #[inline]
78 pub fn error(&self) -> Option<&Error> {
79 self.error.as_ref()
80 }
81
82 /// Get the response status code
83 #[inline]
84 pub fn status(&self) -> StatusCode {
85 self.res.status()
86 }
87
88 /// Set the `StatusCode` for this response
89 #[inline]
90 pub fn status_mut(&mut self) -> &mut StatusCode {
91 self.res.status_mut()
92 }
93
94 /// Get the headers from the response
95 #[inline]
96 pub fn headers(&self) -> &HeaderMap {
97 self.res.headers()
98 }
99
100 /// Get a mutable reference to the headers
101 #[inline]
102 pub fn headers_mut(&mut self) -> &mut HeaderMap {
103 self.res.headers_mut()
104 }
105
106 /// Get an iterator for the cookies set by this response.
107 #[cfg(feature = "cookies")]
108 pub fn cookies(&self) -> CookieIter<'_> {
109 CookieIter {
110 iter: self.headers().get_all(header::SET_COOKIE),
111 }
112 }
113
114 /// Add a cookie to this response.
115 ///
116 /// # Errors
117 /// Returns an error if the cookie results in a malformed `Set-Cookie` header.
118 #[cfg(feature = "cookies")]
119 pub fn add_cookie(&mut self, cookie: &Cookie<'_>) -> Result<(), HttpError> {
120 HeaderValue::from_str(&cookie.to_string())
121 .map(|cookie| self.headers_mut().append(header::SET_COOKIE, cookie))
122 .map_err(Into::into)
123 }
124
125 /// Add a "removal" cookie to the response that matches attributes of given cookie.
126 ///
127 /// This will cause browsers/clients to remove stored cookies with this name.
128 ///
129 /// The `Set-Cookie` header added to the response will have:
130 /// - name matching given cookie;
131 /// - domain matching given cookie;
132 /// - path matching given cookie;
133 /// - an empty value;
134 /// - a max-age of `0`;
135 /// - an expiration date far in the past.
136 ///
137 /// If the cookie you're trying to remove has an explicit path or domain set, those attributes
138 /// will need to be included in the cookie passed in here.
139 ///
140 /// # Errors
141 /// Returns an error if the given name results in a malformed `Set-Cookie` header.
142 #[cfg(feature = "cookies")]
143 pub fn add_removal_cookie(&mut self, cookie: &Cookie<'_>) -> Result<(), HttpError> {
144 let mut removal_cookie = cookie.to_owned();
145 removal_cookie.make_removal();
146
147 HeaderValue::from_str(&removal_cookie.to_string())
148 .map(|cookie| self.headers_mut().append(header::SET_COOKIE, cookie))
149 .map_err(Into::into)
150 }
151
152 /// Remove all cookies with the given name from this response.
153 ///
154 /// Returns the number of cookies removed.
155 ///
156 /// This method can _not_ cause a browser/client to delete any of its stored cookies. Its only
157 /// purpose is to delete cookies that were added to this response using [`add_cookie`]
158 /// and [`add_removal_cookie`]. Use [`add_removal_cookie`] to send a "removal" cookie.
159 ///
160 /// [`add_cookie`]: Self::add_cookie
161 /// [`add_removal_cookie`]: Self::add_removal_cookie
162 #[cfg(feature = "cookies")]
163 pub fn del_cookie(&mut self, name: &str) -> usize {
164 let headers = self.headers_mut();
165
166 let vals: Vec<HeaderValue> = headers
167 .get_all(header::SET_COOKIE)
168 .map(|v| v.to_owned())
169 .collect();
170
171 headers.remove(header::SET_COOKIE);
172
173 let mut count: usize = 0;
174
175 for v in vals {
176 if let Ok(s) = v.to_str() {
177 if let Ok(c) = Cookie::parse_encoded(s) {
178 if c.name() == name {
179 count += 1;
180 continue;
181 }
182 }
183 }
184
185 // put set-cookie header head back if it does not validate
186 headers.append(header::SET_COOKIE, v);
187 }
188
189 count
190 }
191
192 /// Connection upgrade status
193 #[inline]
194 pub fn upgrade(&self) -> bool {
195 self.res.upgrade()
196 }
197
198 /// Keep-alive status for this connection
199 pub fn keep_alive(&self) -> bool {
200 self.res.keep_alive()
201 }
202
203 /// Returns reference to the response-local data/extensions container.
204 #[inline]
205 pub fn extensions(&self) -> Ref<'_, Extensions> {
206 self.res.extensions()
207 }
208
209 /// Returns reference to the response-local data/extensions container.
210 #[inline]
211 pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {
212 self.res.extensions_mut()
213 }
214
215 /// Returns a reference to this response's body.
216 #[inline]
217 pub fn body(&self) -> &B {
218 self.res.body()
219 }
220
221 /// Sets new body.
222 pub fn set_body<B2>(self, body: B2) -> HttpResponse<B2> {
223 HttpResponse {
224 res: self.res.set_body(body),
225 error: self.error,
226 }
227 }
228
229 /// Returns split head and body.
230 ///
231 /// # Implementation Notes
232 /// Due to internal performance optimizations, the first element of the returned tuple is an
233 /// `HttpResponse` as well but only contains the head of the response this was called on.
234 pub fn into_parts(self) -> (HttpResponse<()>, B) {
235 let (head, body) = self.res.into_parts();
236
237 (
238 HttpResponse {
239 res: head,
240 error: self.error,
241 },
242 body,
243 )
244 }
245
246 /// Drops body and returns new response.
247 pub fn drop_body(self) -> HttpResponse<()> {
248 HttpResponse {
249 res: self.res.drop_body(),
250 error: self.error,
251 }
252 }
253
254 /// Map the current body type to another using a closure, returning a new response.
255 ///
256 /// Closure receives the response head and the current body type.
257 pub fn map_body<F, B2>(self, f: F) -> HttpResponse<B2>
258 where
259 F: FnOnce(&mut ResponseHead, B) -> B2,
260 {
261 HttpResponse {
262 res: self.res.map_body(f),
263 error: self.error,
264 }
265 }
266
267 /// Map the current body type `B` to `EitherBody::Left(B)`.
268 ///
269 /// Useful for middleware which can generate their own responses.
270 #[inline]
271 pub fn map_into_left_body<R>(self) -> HttpResponse<EitherBody<B, R>> {
272 self.map_body(|_, body| EitherBody::left(body))
273 }
274
275 /// Map the current body type `B` to `EitherBody::Right(B)`.
276 ///
277 /// Useful for middleware which can generate their own responses.
278 #[inline]
279 pub fn map_into_right_body<L>(self) -> HttpResponse<EitherBody<L, B>> {
280 self.map_body(|_, body| EitherBody::right(body))
281 }
282
283 /// Map the current body to a type-erased `BoxBody`.
284 #[inline]
285 pub fn map_into_boxed_body(self) -> HttpResponse<BoxBody>
286 where
287 B: MessageBody + 'static,
288 {
289 self.map_body(|_, body| body.boxed())
290 }
291
292 /// Returns the response body, dropping all other parts.
293 pub fn into_body(self) -> B {
294 self.res.into_body()
295 }
296}
297
298impl<B> fmt::Debug for HttpResponse<B>
299where
300 B: MessageBody,
301{
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 f.debug_struct("HttpResponse")
304 .field("error", &self.error)
305 .field("res", &self.res)
306 .finish()
307 }
308}
309
310impl<B> From<Response<B>> for HttpResponse<B> {
311 fn from(res: Response<B>) -> Self {
312 HttpResponse { res, error: None }
313 }
314}
315
316impl From<Error> for HttpResponse {
317 fn from(err: Error) -> Self {
318 HttpResponse::from_error(err)
319 }
320}
321
322impl<B> From<HttpResponse<B>> for Response<B> {
323 fn from(res: HttpResponse<B>) -> Self {
324 // this impl will always be called as part of dispatcher
325 res.res
326 }
327}
328
329// Rationale for cfg(test): this impl causes false positives on a clippy lint (async_yields_async)
330// when returning an HttpResponse from an async function/closure and it's not very useful outside of
331// tests anyway.
332#[cfg(test)]
333mod response_fut_impl {
334 use std::{
335 future::Future,
336 mem,
337 pin::Pin,
338 task::{Context, Poll},
339 };
340
341 use super::*;
342
343 // Future is only implemented for BoxBody payload type because it's the most useful for making
344 // simple handlers without async blocks. Making it generic over all MessageBody types requires a
345 // future impl on Response which would cause its body field to be, undesirably, Option<B>.
346 //
347 // This impl is not particularly efficient due to the Response construction and should probably
348 // not be invoked if performance is important. Prefer an async fn/block in such cases.
349 impl Future for HttpResponse<BoxBody> {
350 type Output = Result<Response<BoxBody>, Error>;
351
352 fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
353 if let Some(err) = self.error.take() {
354 return Poll::Ready(Err(err));
355 }
356
357 Poll::Ready(Ok(mem::replace(
358 &mut self.res,
359 Response::new(StatusCode::default()),
360 )))
361 }
362 }
363}
364
365impl<B> Responder for HttpResponse<B>
366where
367 B: MessageBody + 'static,
368{
369 type Body = B;
370
371 #[inline]
372 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
373 self
374 }
375}
376
377#[cfg(feature = "cookies")]
378pub struct CookieIter<'a> {
379 iter: std::slice::Iter<'a, HeaderValue>,
380}
381
382#[cfg(feature = "cookies")]
383impl<'a> Iterator for CookieIter<'a> {
384 type Item = Cookie<'a>;
385
386 #[inline]
387 fn next(&mut self) -> Option<Cookie<'a>> {
388 for v in self.iter.by_ref() {
389 if let Ok(c) = Cookie::parse_encoded(v.to_str().ok()?) {
390 return Some(c);
391 }
392 }
393 None
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use static_assertions::assert_impl_all;
400
401 use super::*;
402 use crate::http::header::COOKIE;
403
404 assert_impl_all!(HttpResponse: Responder);
405 assert_impl_all!(HttpResponse<String>: Responder);
406 assert_impl_all!(HttpResponse<&'static str>: Responder);
407 assert_impl_all!(HttpResponse<crate::body::None>: Responder);
408
409 #[test]
410 fn test_debug() {
411 let resp = HttpResponse::Ok()
412 .append_header((COOKIE, HeaderValue::from_static("cookie1=value1; ")))
413 .append_header((COOKIE, HeaderValue::from_static("cookie2=value2; ")))
414 .finish();
415 let dbg = format!("{:?}", resp);
416 assert!(dbg.contains("HttpResponse"));
417 }
418}
419
420#[cfg(test)]
421#[cfg(feature = "cookies")]
422mod cookie_tests {
423 use super::*;
424
425 #[test]
426 fn removal_cookies() {
427 let mut res = HttpResponse::Ok().finish();
428 let cookie = Cookie::new("foo", "");
429 res.add_removal_cookie(&cookie).unwrap();
430 let set_cookie_hdr = res.headers().get(header::SET_COOKIE).unwrap();
431 assert_eq!(
432 &set_cookie_hdr.as_bytes()[..25],
433 &b"foo=; Max-Age=0; Expires="[..],
434 "unexpected set-cookie value: {:?}",
435 set_cookie_hdr.to_str()
436 );
437 }
438}