1use std::{
2 cell::{Ref, RefMut},
3 future::Future,
4 pin::Pin,
5 task::{Context, Poll},
6};
7
8use actix_http::{error::HttpError, Response, ResponseHead};
9use bytes::Bytes;
10use futures_core::Stream;
11use serde::Serialize;
12
13use crate::{
14 body::{BodyStream, BoxBody, MessageBody},
15 dev::Extensions,
16 error::{Error, JsonPayloadError},
17 http::{
18 header::{self, HeaderName, TryIntoHeaderPair, TryIntoHeaderValue},
19 ConnectionType, StatusCode,
20 },
21 BoxError, HttpRequest, HttpResponse, Responder,
22};
23
24pub struct HttpResponseBuilder {
28 res: Option<Response<BoxBody>>,
29 error: Option<HttpError>,
30}
31
32impl HttpResponseBuilder {
33 #[inline]
34 pub fn new(status: StatusCode) -> Self {
36 Self {
37 res: Some(Response::with_body(status, BoxBody::new(()))),
38 error: None,
39 }
40 }
41
42 #[inline]
44 pub fn status(&mut self, status: StatusCode) -> &mut Self {
45 if let Some(parts) = self.inner() {
46 parts.status = status;
47 }
48 self
49 }
50
51 pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
62 if let Some(parts) = self.inner() {
63 match header.try_into_pair() {
64 Ok((key, value)) => {
65 parts.headers.insert(key, value);
66 }
67 Err(err) => self.error = Some(err.into()),
68 };
69 }
70
71 self
72 }
73
74 pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
86 if let Some(parts) = self.inner() {
87 match header.try_into_pair() {
88 Ok((key, value)) => parts.headers.append(key, value),
89 Err(err) => self.error = Some(err.into()),
90 };
91 }
92
93 self
94 }
95
96 #[doc(hidden)]
98 #[deprecated(
99 since = "4.0.0",
100 note = "Replaced with `insert_header((key, value))`. Will be removed in v5."
101 )]
102 pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
103 where
104 K: TryInto<HeaderName>,
105 K::Error: Into<HttpError>,
106 V: TryIntoHeaderValue,
107 {
108 if self.error.is_some() {
109 return self;
110 }
111
112 match (key.try_into(), value.try_into_value()) {
113 (Ok(name), Ok(value)) => return self.insert_header((name, value)),
114 (Err(err), _) => self.error = Some(err.into()),
115 (_, Err(err)) => self.error = Some(err.into()),
116 }
117
118 self
119 }
120
121 #[doc(hidden)]
123 #[deprecated(
124 since = "4.0.0",
125 note = "Replaced with `append_header((key, value))`. Will be removed in v5."
126 )]
127 pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
128 where
129 K: TryInto<HeaderName>,
130 K::Error: Into<HttpError>,
131 V: TryIntoHeaderValue,
132 {
133 if self.error.is_some() {
134 return self;
135 }
136
137 match (key.try_into(), value.try_into_value()) {
138 (Ok(name), Ok(value)) => return self.append_header((name, value)),
139 (Err(err), _) => self.error = Some(err.into()),
140 (_, Err(err)) => self.error = Some(err.into()),
141 }
142
143 self
144 }
145
146 #[inline]
148 pub fn reason(&mut self, reason: &'static str) -> &mut Self {
149 if let Some(parts) = self.inner() {
150 parts.reason = Some(reason);
151 }
152 self
153 }
154
155 #[inline]
157 pub fn keep_alive(&mut self) -> &mut Self {
158 if let Some(parts) = self.inner() {
159 parts.set_connection_type(ConnectionType::KeepAlive);
160 }
161 self
162 }
163
164 #[inline]
166 pub fn upgrade<V>(&mut self, value: V) -> &mut Self
167 where
168 V: TryIntoHeaderValue,
169 {
170 if let Some(parts) = self.inner() {
171 parts.set_connection_type(ConnectionType::Upgrade);
172 }
173
174 if let Ok(value) = value.try_into_value() {
175 self.insert_header((header::UPGRADE, value));
176 }
177
178 self
179 }
180
181 #[inline]
183 pub fn force_close(&mut self) -> &mut Self {
184 if let Some(parts) = self.inner() {
185 parts.set_connection_type(ConnectionType::Close);
186 }
187 self
188 }
189
190 #[inline]
192 pub fn no_chunking(&mut self, len: u64) -> &mut Self {
193 let mut buf = itoa::Buffer::new();
194 self.insert_header((header::CONTENT_LENGTH, buf.format(len)));
195
196 if let Some(parts) = self.inner() {
197 parts.no_chunking(true);
198 }
199 self
200 }
201
202 #[inline]
204 pub fn content_type<V>(&mut self, value: V) -> &mut Self
205 where
206 V: TryIntoHeaderValue,
207 {
208 if let Some(parts) = self.inner() {
209 match value.try_into_value() {
210 Ok(value) => {
211 parts.headers.insert(header::CONTENT_TYPE, value);
212 }
213 Err(err) => self.error = Some(err.into()),
214 };
215 }
216 self
217 }
218
219 #[cfg(feature = "cookies")]
257 pub fn cookie(&mut self, cookie: cookie::Cookie<'_>) -> &mut Self {
258 match cookie.to_string().try_into_value() {
259 Ok(hdr_val) => self.append_header((header::SET_COOKIE, hdr_val)),
260 Err(err) => {
261 self.error = Some(err.into());
262 self
263 }
264 }
265 }
266
267 #[inline]
269 pub fn extensions(&self) -> Ref<'_, Extensions> {
270 self.res
271 .as_ref()
272 .expect("cannot reuse response builder")
273 .extensions()
274 }
275
276 #[inline]
278 pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {
279 self.res
280 .as_mut()
281 .expect("cannot reuse response builder")
282 .extensions_mut()
283 }
284
285 pub fn body<B>(&mut self, body: B) -> HttpResponse<BoxBody>
292 where
293 B: MessageBody + 'static,
294 {
295 match self.message_body(body) {
296 Ok(res) => res.map_into_boxed_body(),
297 Err(err) => HttpResponse::from_error(err),
298 }
299 }
300
301 pub fn message_body<B>(&mut self, body: B) -> Result<HttpResponse<B>, Error> {
305 if let Some(err) = self.error.take() {
306 return Err(err.into());
307 }
308
309 let res = self
310 .res
311 .take()
312 .expect("cannot reuse response builder")
313 .set_body(body);
314
315 Ok(HttpResponse::from(res))
316 }
317
318 #[inline]
322 pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
323 where
324 S: Stream<Item = Result<Bytes, E>> + 'static,
325 E: Into<BoxError> + 'static,
326 {
327 self.body(BodyStream::new(stream))
328 }
329
330 pub fn json(&mut self, value: impl Serialize) -> HttpResponse {
334 match serde_json::to_string(&value) {
335 Ok(body) => {
336 let contains = if let Some(parts) = self.inner() {
337 parts.headers.contains_key(header::CONTENT_TYPE)
338 } else {
339 true
340 };
341
342 if !contains {
343 self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
344 }
345
346 self.body(body)
347 }
348 Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
349 }
350 }
351
352 #[inline]
356 pub fn finish(&mut self) -> HttpResponse {
357 self.body(())
358 }
359
360 pub fn take(&mut self) -> Self {
362 Self {
363 res: self.res.take(),
364 error: self.error.take(),
365 }
366 }
367
368 fn inner(&mut self) -> Option<&mut ResponseHead> {
369 if self.error.is_some() {
370 return None;
371 }
372
373 self.res.as_mut().map(Response::head_mut)
374 }
375}
376
377impl From<HttpResponseBuilder> for HttpResponse {
378 fn from(mut builder: HttpResponseBuilder) -> Self {
379 builder.finish()
380 }
381}
382
383impl From<HttpResponseBuilder> for Response<BoxBody> {
384 fn from(mut builder: HttpResponseBuilder) -> Self {
385 builder.finish().into()
386 }
387}
388
389impl Future for HttpResponseBuilder {
390 type Output = Result<HttpResponse, Error>;
391
392 fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
393 Poll::Ready(Ok(self.finish()))
394 }
395}
396
397impl Responder for HttpResponseBuilder {
398 type Body = BoxBody;
399
400 #[inline]
401 fn respond_to(mut self, _: &HttpRequest) -> HttpResponse<Self::Body> {
402 self.finish()
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409 use crate::{
410 body,
411 http::header::{HeaderValue, CONTENT_TYPE},
412 test::assert_body_eq,
413 };
414
415 #[test]
416 fn test_basic_builder() {
417 let resp = HttpResponse::Ok()
418 .insert_header(("X-TEST", "value"))
419 .finish();
420 assert_eq!(resp.status(), StatusCode::OK);
421 }
422
423 #[test]
424 fn test_upgrade() {
425 let resp = HttpResponseBuilder::new(StatusCode::OK)
426 .upgrade("websocket")
427 .finish();
428 assert!(resp.upgrade());
429 assert_eq!(
430 resp.headers().get(header::UPGRADE).unwrap(),
431 HeaderValue::from_static("websocket")
432 );
433 }
434
435 #[test]
436 fn test_force_close() {
437 let resp = HttpResponseBuilder::new(StatusCode::OK)
438 .force_close()
439 .finish();
440 assert!(!resp.keep_alive())
441 }
442
443 #[test]
444 fn test_content_type() {
445 let resp = HttpResponseBuilder::new(StatusCode::OK)
446 .content_type("text/plain")
447 .body(Bytes::new());
448 assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
449 }
450
451 #[actix_rt::test]
452 async fn test_json() {
453 let res = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]);
454 let ct = res.headers().get(CONTENT_TYPE).unwrap();
455 assert_eq!(ct, HeaderValue::from_static("application/json"));
456 assert_body_eq!(res, br#"["v1","v2","v3"]"#);
457
458 let res = HttpResponse::Ok().json(["v1", "v2", "v3"]);
459 let ct = res.headers().get(CONTENT_TYPE).unwrap();
460 assert_eq!(ct, HeaderValue::from_static("application/json"));
461 assert_body_eq!(res, br#"["v1","v2","v3"]"#);
462
463 let res = HttpResponse::Ok()
465 .insert_header((CONTENT_TYPE, "text/json"))
466 .json(["v1", "v2", "v3"]);
467 let ct = res.headers().get(CONTENT_TYPE).unwrap();
468 assert_eq!(ct, HeaderValue::from_static("text/json"));
469 assert_body_eq!(res, br#"["v1","v2","v3"]"#);
470 }
471
472 #[actix_rt::test]
473 async fn test_serde_json_in_body() {
474 let resp = HttpResponse::Ok()
475 .body(serde_json::to_vec(&serde_json::json!({ "test-key": "test-value" })).unwrap());
476
477 assert_eq!(
478 body::to_bytes(resp.into_body()).await.unwrap().as_ref(),
479 br#"{"test-key":"test-value"}"#
480 );
481 }
482
483 #[test]
484 fn response_builder_header_insert_kv() {
485 let mut res = HttpResponse::Ok();
486 res.insert_header(("Content-Type", "application/octet-stream"));
487 let res = res.finish();
488
489 assert_eq!(
490 res.headers().get("Content-Type"),
491 Some(&HeaderValue::from_static("application/octet-stream"))
492 );
493 }
494
495 #[test]
496 fn response_builder_header_insert_typed() {
497 let mut res = HttpResponse::Ok();
498 res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
499 let res = res.finish();
500
501 assert_eq!(
502 res.headers().get("Content-Type"),
503 Some(&HeaderValue::from_static("application/octet-stream"))
504 );
505 }
506
507 #[test]
508 fn response_builder_header_append_kv() {
509 let mut res = HttpResponse::Ok();
510 res.append_header(("Content-Type", "application/octet-stream"));
511 res.append_header(("Content-Type", "application/json"));
512 let res = res.finish();
513
514 let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect();
515 assert_eq!(headers.len(), 2);
516 assert!(headers.contains(&HeaderValue::from_static("application/octet-stream")));
517 assert!(headers.contains(&HeaderValue::from_static("application/json")));
518 }
519
520 #[test]
521 fn response_builder_header_append_typed() {
522 let mut res = HttpResponse::Ok();
523 res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
524 res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
525 let res = res.finish();
526
527 let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect();
528 assert_eq!(headers.len(), 2);
529 assert!(headers.contains(&HeaderValue::from_static("application/octet-stream")));
530 assert!(headers.contains(&HeaderValue::from_static("application/json")));
531 }
532}