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
22pub struct HttpResponse<B = BoxBody> {
24 res: Response<B>,
25 error: Option<Error>,
26}
27
28impl HttpResponse<BoxBody> {
29 #[inline]
31 pub fn new(status: StatusCode) -> Self {
32 Self {
33 res: Response::new(status),
34 error: None,
35 }
36 }
37
38 #[inline]
40 pub fn build(status: StatusCode) -> HttpResponseBuilder {
41 HttpResponseBuilder::new(status)
42 }
43
44 #[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 #[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 #[inline]
66 pub fn head(&self) -> &ResponseHead {
67 self.res.head()
68 }
69
70 #[inline]
72 pub fn head_mut(&mut self) -> &mut ResponseHead {
73 self.res.head_mut()
74 }
75
76 #[inline]
78 pub fn error(&self) -> Option<&Error> {
79 self.error.as_ref()
80 }
81
82 #[inline]
84 pub fn status(&self) -> StatusCode {
85 self.res.status()
86 }
87
88 #[inline]
90 pub fn status_mut(&mut self) -> &mut StatusCode {
91 self.res.status_mut()
92 }
93
94 #[inline]
96 pub fn headers(&self) -> &HeaderMap {
97 self.res.headers()
98 }
99
100 #[inline]
102 pub fn headers_mut(&mut self) -> &mut HeaderMap {
103 self.res.headers_mut()
104 }
105
106 #[cfg(feature = "cookies")]
108 pub fn cookies(&self) -> CookieIter<'_> {
109 CookieIter {
110 iter: self.headers().get_all(header::SET_COOKIE),
111 }
112 }
113
114 #[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 #[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 #[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 headers.append(header::SET_COOKIE, v);
187 }
188
189 count
190 }
191
192 #[inline]
194 pub fn upgrade(&self) -> bool {
195 self.res.upgrade()
196 }
197
198 pub fn keep_alive(&self) -> bool {
200 self.res.keep_alive()
201 }
202
203 #[inline]
205 pub fn extensions(&self) -> Ref<'_, Extensions> {
206 self.res.extensions()
207 }
208
209 #[inline]
211 pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {
212 self.res.extensions_mut()
213 }
214
215 #[inline]
217 pub fn body(&self) -> &B {
218 self.res.body()
219 }
220
221 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 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 pub fn drop_body(self) -> HttpResponse<()> {
248 HttpResponse {
249 res: self.res.drop_body(),
250 error: self.error,
251 }
252 }
253
254 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 #[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 #[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 #[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 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 res.res
326 }
327}
328
329#[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 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}