fastly/http/response.rs
1//! HTTP responses.
2
3pub(crate) mod handle;
4
5use self::handle::ResponseHandle;
6use super::cache;
7use super::request::BackgroundRevalidation;
8use super::{
9 body::{self, Body, StreamingBody},
10 LazyHandle, Request,
11};
12use crate::{
13 backend::Backend,
14 convert::{Borrowable, ToHeaderName, ToHeaderValue, ToStatusCode},
15 experimental::BodyExt,
16 handle::BodyHandle,
17};
18use fastly_shared::{FramingHeadersMode, HttpKeepaliveMode};
19use http::header::{self, HeaderName, HeaderValue};
20use http::{StatusCode, Version};
21use mime::Mime;
22use serde::de::DeserializeOwned;
23use serde::Serialize;
24use std::time::Duration;
25use std::{
26 borrow::Cow,
27 io::{BufRead, Write},
28 net::SocketAddr,
29};
30
31/// An HTTP response, including body, headers, and status code.
32///
33/// # Sending to the client
34///
35/// Each execution of a Compute program may send a single response back to the client:
36///
37/// - [`Response::send_to_client()`]
38/// - [`Response::stream_to_client()`]
39///
40/// If no response is explicitly sent by the program, a default `200 OK` response is sent.
41///
42/// # Creation and conversion
43///
44/// Responses can be created programmatically:
45///
46/// - [`Response::new()`]
47/// - [`Response::from_body()`]
48/// - [`Response::from_status()`]
49///
50/// Responses are also returned from backend requests:
51///
52/// - [`Request::send()`]
53/// - [`Request::send_async()`]
54/// - [`Request::send_async_streaming()`]
55///
56/// For interoperability with other Rust libraries, [`Response`] can be converted to and from the
57/// [`http`] crate's [`http::Response`] type using the [`From`][`Response::from()`] and
58/// [`Into`][`Response::into()`] traits.
59///
60/// # Builder-style methods
61///
62/// [`Response`] can be used as a
63/// [builder](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html), allowing responses to
64/// be constructed and used through method chaining. Methods with the `with_` name prefix, such as
65/// [`with_header()`][`Self::with_header()`], return `Self` to allow chaining. The builder style is
66/// typically most useful when constructing and using a response in a single expression. For
67/// example:
68///
69/// ```no_run
70/// # use fastly::Response;
71/// Response::new()
72/// .with_header("my-header", "hello!")
73/// .with_header("my-other-header", "Здравствуйте!")
74/// .send_to_client();
75/// ```
76///
77/// # Setter methods
78///
79/// Setter methods, such as [`set_header()`][`Self::set_header()`], are prefixed by `set_`, and can
80/// be used interchangeably with the builder-style methods, allowing you to mix and match styles
81/// based on what is most convenient for your program. Setter methods tend to work better than
82/// builder-style methods when constructing a value involves conditional branches or loops. For
83/// example:
84///
85/// ```no_run
86/// # use fastly::Response;
87/// # let needs_translation = true;
88/// let mut resp = Response::new().with_header("my-header", "hello!");
89/// if needs_translation {
90/// resp.set_header("my-other-header", "Здравствуйте!");
91/// }
92/// resp.send_to_client();
93/// ```
94#[derive(Debug)]
95pub struct Response {
96 pub(crate) lazy_handle: LazyHandle<ResponseHandle>,
97 body: Option<Body>,
98 pub(crate) metadata: FastlyResponseMetadata,
99 // currently kept out of `FastlyResponseMetadata` due to not being `Sync`
100 pub(crate) sent_req: Option<Request>,
101 /// Used purely for its drop implementation, this field represents a
102 /// background revalidation request and open cache transaction that will be
103 /// completed once the containing `Response` is dropped; that allows for
104 /// calling `send_to_client` on a stale response to complete normal
105 /// execution quickly, and _then_ to implicitly complete backend
106 /// revalidation.
107 pub(crate) background_revalidation: Option<BackgroundRevalidation>,
108}
109
110/// Anything that we need to make a full roundtrip through the `http` types that doesn't have a more
111/// concrete corresponding type.
112#[derive(Clone, Debug)]
113pub(crate) struct FastlyResponseMetadata {
114 framing_headers_mode: FramingHeadersMode,
115 http_keepalive_mode: HttpKeepaliveMode,
116 remote_addr: Option<SocketAddr>,
117 pub(crate) backend: Option<Backend>,
118 pub(crate) cache_options: Option<cache::WriteOptions>,
119 pub(crate) cache_storage_action: Option<cache::HttpStorageAction>,
120 pub(crate) cache_hits: Option<u64>,
121}
122
123impl FastlyResponseMetadata {
124 fn new() -> Self {
125 Self {
126 remote_addr: None,
127 framing_headers_mode: FramingHeadersMode::Automatic,
128 http_keepalive_mode: HttpKeepaliveMode::Automatic,
129 backend: None,
130 cache_options: None,
131 cache_storage_action: None,
132 cache_hits: None,
133 }
134 }
135}
136
137impl Response {
138 /// Create a new [`Response`].
139 ///
140 /// The new response is created with status code `200 OK`, no headers, and an empty body.
141 pub fn new() -> Self {
142 Self {
143 lazy_handle: LazyHandle::detached()
144 .with_field(StatusCode::OK)
145 .with_field(Version::HTTP_11)
146 .finish(),
147 body: None,
148 metadata: FastlyResponseMetadata::new(),
149 sent_req: None,
150 background_revalidation: None,
151 }
152 }
153
154 /// Return whether the response is from a backend request.
155 pub fn is_from_backend(&self) -> bool {
156 self.metadata.backend.is_some()
157 }
158
159 /// Make a new response with the same headers, status, and version of this response, but no
160 /// body.
161 ///
162 /// If you also need to clone the response body, use
163 /// [`clone_with_body()`][`Self::clone_with_body()`]
164 ///
165 /// # Examples
166 ///
167 /// ```no_run
168 /// # use fastly::Response;
169 /// let original = Response::from_body("hello")
170 /// .with_header("hello", "world!")
171 /// .with_status(418);
172 /// let new = original.clone_without_body();
173 /// assert_eq!(original.get_header("hello"), new.get_header("hello"));
174 /// assert_eq!(original.get_status(), new.get_status());
175 /// assert_eq!(original.get_version(), new.get_version());
176 /// assert!(original.has_body());
177 /// assert!(!new.has_body());
178 /// ```
179 pub fn clone_without_body(&self) -> Response {
180 Self {
181 lazy_handle: self.lazy_handle.clone(),
182 body: None,
183 metadata: self.metadata.clone(),
184 sent_req: self.sent_req.as_ref().map(Request::clone_without_body),
185 background_revalidation: None,
186 }
187 }
188
189 /// Clone this response by reading in its body, and then writing the same body to the original
190 /// and the cloned response.
191 ///
192 /// This method requires mutable access to this response because reading from and writing to the
193 /// body can involve an HTTP connection.
194 ///
195 #[doc = include_str!("../../docs/snippets/clones-body.md")]
196 ///
197 /// # Examples
198 ///
199 /// ```no_run
200 /// # use fastly::Response;
201 /// let mut original = Response::from_body("hello")
202 /// .with_header("hello", "world!")
203 /// .with_status(418);
204 /// let mut new = original.clone_with_body();
205 /// assert_eq!(original.get_header("hello"), new.get_header("hello"));
206 /// assert_eq!(original.get_status(), new.get_status());
207 /// assert_eq!(original.get_version(), new.get_version());
208 /// assert_eq!(original.take_body_bytes(), new.take_body_bytes());
209 /// ```
210 pub fn clone_with_body(&mut self) -> Response {
211 let mut new_resp = self.clone_without_body();
212 if self.has_body() {
213 let mut body = self.take_body();
214
215 for chunk in body.read_chunks(4096) {
216 let chunk = chunk.expect("can read body chunk");
217 new_resp
218 .get_body_mut()
219 .write_all(&chunk)
220 .expect("failed to write to cloned body");
221 self.get_body_mut()
222 .write_all(&chunk)
223 .expect("failed to write to cloned body");
224 }
225
226 if let Ok(trailers) = body.get_trailers() {
227 for (k, v) in trailers.iter() {
228 self.get_body_mut().append_trailer(k, v);
229 new_resp.get_body_mut().append_trailer(k, v);
230 }
231 }
232 }
233 new_resp
234 }
235
236 /// Create a new [`Response`] with the given value as the body.
237 ///
238 #[doc = include_str!("../../docs/snippets/body-argument.md")]
239 ///
240 /// # Examples
241 ///
242 /// ```no_run
243 /// # use fastly::Response;
244 /// let resp = Response::from_body("hello");
245 /// assert_eq!(&resp.into_body_str(), "hello");
246 /// ```
247 ///
248 /// ```no_run
249 /// # use fastly::Response;
250 /// let body_bytes: &[u8] = &[1, 2, 3];
251 /// let resp = Response::from_body(body_bytes);
252 /// assert_eq!(resp.into_body_bytes().as_slice(), body_bytes);
253 /// ```
254 pub fn from_body(body: impl Into<Body>) -> Self {
255 Self::new().with_body(body)
256 }
257
258 /// Create a new response with the given status code.
259 ///
260 #[doc = include_str!("../../docs/snippets/body-argument.md")]
261 ///
262 /// # Examples
263 ///
264 /// ```no_run
265 /// # use fastly::Response;
266 /// use fastly::http::StatusCode;
267 /// let resp = Response::from_status(StatusCode::NOT_FOUND);
268 /// assert_eq!(resp.get_status().as_u16(), 404);
269 /// ```
270 ///
271 /// ```no_run
272 /// # use fastly::Response;
273 /// use fastly::http::StatusCode;
274 /// let resp = Response::from_status(404);
275 /// assert_eq!(resp.get_status(), StatusCode::NOT_FOUND);
276 /// ```
277 pub fn from_status(status: impl ToStatusCode) -> Self {
278 Self::new().with_status(status)
279 }
280
281 /// Create a 303 See Other response with the given value as the `Location` header.
282 ///
283 /// # Examples
284 ///
285 /// ```no_run
286 /// # use fastly::Response;
287 /// # use http::{header, StatusCode};
288 /// let resp = Response::see_other("https://www.fastly.com");
289 /// assert_eq!(resp.get_status(), StatusCode::SEE_OTHER);
290 /// assert_eq!(resp.get_header_str(header::LOCATION).unwrap(), "https://www.fastly.com");
291 /// ```
292 pub fn see_other(destination: impl ToHeaderValue) -> Self {
293 Self::new()
294 .with_status(StatusCode::SEE_OTHER)
295 .with_header(header::LOCATION, destination)
296 }
297
298 /// Create a 308 Permanent Redirect response with the given value as the `Location` header.
299 ///
300 /// # Examples
301 ///
302 /// ```no_run
303 /// # use fastly::Response;
304 /// # use http::{header, StatusCode};
305 /// let resp = Response::redirect("https://www.fastly.com");
306 /// assert_eq!(resp.get_status(), StatusCode::PERMANENT_REDIRECT);
307 /// assert_eq!(resp.get_header_str(header::LOCATION).unwrap(), "https://www.fastly.com");
308 /// ```
309 pub fn redirect(destination: impl ToHeaderValue) -> Self {
310 Self::new()
311 .with_status(StatusCode::PERMANENT_REDIRECT)
312 .with_header(header::LOCATION, destination)
313 }
314
315 /// Create a 307 Temporary Redirect response with the given value as the `Location` header.
316 ///
317 /// # Examples
318 ///
319 /// ```no_run
320 /// # use fastly::Response;
321 /// # use http::{header, StatusCode};
322 /// let resp = Response::temporary_redirect("https://www.fastly.com");
323 /// assert_eq!(resp.get_status(), StatusCode::TEMPORARY_REDIRECT);
324 /// assert_eq!(resp.get_header_str(header::LOCATION).unwrap(), "https://www.fastly.com");
325 /// ```
326 pub fn temporary_redirect(destination: impl ToHeaderValue) -> Self {
327 Self::new()
328 .with_status(StatusCode::TEMPORARY_REDIRECT)
329 .with_header(header::LOCATION, destination)
330 }
331
332 /// Builder-style equivalent of [`set_body()`][`Self::set_body()`].
333 pub fn with_body(mut self, body: impl Into<Body>) -> Self {
334 self.set_body(body);
335 self
336 }
337
338 /// Returns `true` if this response has a body.
339 pub fn has_body(&self) -> bool {
340 self.body.is_some()
341 }
342
343 /// Get a mutable reference to the body of this response.
344 ///
345 #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
346 ///
347 /// # Examples
348 ///
349 /// ```no_run
350 /// # use fastly::Response;
351 /// use std::io::Write;
352 ///
353 /// let mut resp = Response::from_body("hello,");
354 /// write!(resp.get_body_mut(), " world!").unwrap();
355 /// assert_eq!(&resp.into_body_str(), "hello, world!");
356 /// ```
357 pub fn get_body_mut(&mut self) -> &mut Body {
358 self.body.get_or_insert_with(|| Body::new())
359 }
360
361 /// Get a shared reference to the body of this response if it has one, otherwise return `None`.
362 ///
363 /// # Examples
364 ///
365 /// ```no_run
366 /// # use fastly::Response;
367 /// use std::io::Write;
368 ///
369 /// let mut resp = Response::new();
370 /// assert!(resp.try_get_body_mut().is_none());
371 ///
372 /// resp.set_body("hello,");
373 /// write!(resp.try_get_body_mut().expect("body now exists"), " world!").unwrap();
374 /// assert_eq!(&resp.into_body_str(), "hello, world!");
375 /// ```
376 pub fn try_get_body_mut(&mut self) -> Option<&mut Body> {
377 self.body.as_mut()
378 }
379
380 /// Get a prefix of this response's body containing up to the given number of bytes.
381 ///
382 /// See [`Body::get_prefix_mut()`] for details.
383 pub fn get_body_prefix_mut(&mut self, length: usize) -> body::Prefix {
384 self.get_body_mut().get_prefix_mut(length)
385 }
386
387 /// Get a prefix of this response's body as a string containing up to the given number of bytes.
388 ///
389 /// See [`Body::get_prefix_str_mut()`] for details.
390 ///
391 /// # Panics
392 ///
393 /// If the prefix contains invalid UTF-8 bytes, this function will panic. The exception to this
394 /// is if the bytes are invalid because a multi-byte codepoint is cut off by the requested
395 /// prefix length. In this case, the invalid bytes are left off the end of the prefix.
396 ///
397 /// To explicitly handle the possibility of invalid UTF-8 bytes, use
398 /// [`try_get_body_prefix_str_mut()`][`Self::try_get_body_prefix_str_mut()`], which returns an
399 /// error on failure rather than panicking.
400 pub fn get_body_prefix_str_mut(&mut self, length: usize) -> body::PrefixString {
401 self.get_body_mut().get_prefix_str_mut(length)
402 }
403
404 /// Try to get a prefix of the body as a string containing up to the given number of bytes.
405 ///
406 /// See [`Body::try_get_prefix_str_mut()`] for details.
407 pub fn try_get_body_prefix_str_mut(
408 &mut self,
409 length: usize,
410 ) -> Result<body::PrefixString, std::str::Utf8Error> {
411 self.get_body_mut().try_get_prefix_str_mut(length)
412 }
413
414 /// Set the given value as the response's body.
415 #[doc = include_str!("../../docs/snippets/body-argument.md")]
416 #[doc = include_str!("../../docs/snippets/discards-body.md")]
417 pub fn set_body(&mut self, body: impl Into<Body>) {
418 self.body = Some(body.into());
419 }
420
421 /// Take and return the body from this response.
422 ///
423 /// After calling this method, this response will no longer have a body.
424 ///
425 #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
426 pub fn take_body(&mut self) -> Body {
427 self.body.take().unwrap_or_else(|| Body::new())
428 }
429
430 /// Take and return the body from this response if it has one, otherwise return `None`.
431 ///
432 /// After calling this method, this response will no longer have a body.
433 pub fn try_take_body(&mut self) -> Option<Body> {
434 self.body.take()
435 }
436
437 /// Append another [`Body`] to the body of this response without reading or writing any body
438 /// contents.
439 ///
440 /// If this response does not have a body, the appended body is set as the response's body.
441 ///
442 #[doc = include_str!("../../docs/snippets/body-append-constant-time.md")]
443 ///
444 /// This method should be used when combining bodies that have not necessarily been read yet,
445 /// such as a body returned from a backend response. To append contents that are already in
446 /// memory as strings or bytes, use [`get_body_mut()`][`Self::get_body_mut()`] to write the
447 /// contents to the end of the body.
448 ///
449 /// # Examples
450 ///
451 /// ```no_run
452 /// # use fastly::{Request, Response};
453 /// let mut resp = Response::from_body("hello! backend says: ");
454 /// let backend_resp = Request::get("https://example.com/").send("example_backend").unwrap();
455 /// resp.append_body(backend_resp.into_body());
456 /// resp.send_to_client();
457 /// ```
458 pub fn append_body(&mut self, other: Body) {
459 if let Some(ref mut body) = &mut self.body {
460 body.append(other);
461 } else {
462 self.body = Some(other);
463 }
464 }
465
466 /// Consume the response and return its body as a byte vector.
467 ///
468 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
469 /// # Examples
470 ///
471 /// ```no_run
472 /// # use fastly::Response;
473 /// let resp = Response::from_body(b"hello, world!".to_vec());
474 /// let bytes = resp.into_body_bytes();
475 /// assert_eq!(&bytes, b"hello, world!");
476 pub fn into_body_bytes(mut self) -> Vec<u8> {
477 self.take_body_bytes()
478 }
479
480 /// Consume the response and return its body as a string.
481 ///
482 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
483 ///
484 /// # Panics
485 ///
486 #[doc = include_str!("../../docs/snippets/panics-reqresp-intobody-utf8.md")]
487 ///
488 /// # Examples
489 ///
490 /// ```no_run
491 /// # use fastly::Response;
492 /// let resp = Response::from_body("hello, world!");
493 /// let string = resp.into_body_str();
494 /// assert_eq!(&string, "hello, world!");
495 /// ```
496 pub fn into_body_str(mut self) -> String {
497 self.take_body_str()
498 }
499
500 /// Consume the response and return its body as a string, including invalid characters.
501 ///
502 #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
503 ///
504 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
505 ///
506 /// # Examples
507 ///
508 /// ```no_run
509 /// # use fastly::Response;
510 /// let mut resp = Response::new();
511 /// resp.set_body_octet_stream(b"\xF0\x90\x80 hello, world!");
512 /// let string = resp.into_body_str_lossy();
513 /// assert_eq!(&string, "� hello, world!");
514 /// ```
515 pub fn into_body_str_lossy(mut self) -> String {
516 self.take_body_str_lossy()
517 }
518
519 /// Consume the response and return its body.
520 ///
521 #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
522 pub fn into_body(self) -> Body {
523 self.body.unwrap_or_else(|| Body::new())
524 }
525
526 /// Consume the response and return its body if it has one, otherwise return `None`.
527 pub fn try_into_body(self) -> Option<Body> {
528 self.body
529 }
530
531 /// Builder-style equivalent of [`set_body_text_plain()`][`Self::set_body_text_plain()`].
532 pub fn with_body_text_plain(mut self, body: &str) -> Self {
533 self.set_body_text_plain(body);
534 self
535 }
536
537 /// Set the given string as the response's body with content type `text/plain; charset=UTF-8`.
538 ///
539 #[doc = include_str!("../../docs/snippets/discards-body.md")]
540 #[doc = include_str!("../../docs/snippets/sets-text-plain.md")]
541 ///
542 /// # Examples
543 ///
544 /// ```no_run
545 /// # use fastly::Response;
546 /// let mut resp = Response::new();
547 /// resp.set_body_text_plain("hello, world!");
548 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::TEXT_PLAIN_UTF_8));
549 /// assert_eq!(&resp.into_body_str(), "hello, world!");
550 /// ```
551 pub fn set_body_text_plain(&mut self, body: &str) {
552 self.body = Some(Body::from(body));
553 self.set_content_type(mime::TEXT_PLAIN_UTF_8);
554 }
555
556 /// Builder-style equivalent of [`set_body_text_html()`][`Self::set_body_text_html()`].
557 pub fn with_body_text_html(mut self, body: &str) -> Self {
558 self.set_body_text_html(body);
559 self
560 }
561
562 /// Set the given string as the request's body with content type `text/html; charset=UTF-8`.
563 ///
564 #[doc = include_str!("../../docs/snippets/discards-body.md")]
565 #[doc = include_str!("../../docs/snippets/sets-text-html.md")]
566 ///
567 /// # Examples
568 ///
569 /// ```no_run
570 /// # use fastly::Response;
571 /// let mut resp = Response::new();
572 /// resp.set_body_text_html("<p>hello, world!</p>");
573 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::TEXT_HTML_UTF_8));
574 /// assert_eq!(&resp.into_body_str(), "<p>hello, world!</p>");
575 /// ```
576 pub fn set_body_text_html(&mut self, body: &str) {
577 self.body = Some(Body::from(body));
578 self.set_content_type(mime::TEXT_HTML_UTF_8);
579 }
580
581 /// Take and return the body from this response as a string.
582 ///
583 /// After calling this method, this response will no longer have a body.
584 ///
585 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
586 ///
587 /// # Panics
588 ///
589 #[doc = include_str!("../../docs/snippets/panics-reqresp-takebody-utf8.md")]
590 ///
591 /// # Examples
592 ///
593 /// ```no_run
594 /// # use fastly::Response;
595 /// let mut resp = Response::from_body("hello, world!");
596 /// let string = resp.take_body_str();
597 /// assert!(resp.try_take_body().is_none());
598 /// assert_eq!(&string, "hello, world!");
599 /// ```
600 pub fn take_body_str(&mut self) -> String {
601 if let Some(body) = self.try_take_body() {
602 body.into_string()
603 } else {
604 String::new()
605 }
606 }
607
608 /// Take and return the body from this response as a string, including invalid characters.
609 ///
610 #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
611 ///
612 /// After calling this method, this response will no longer have a body.
613 ///
614 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
615 ///
616 /// # Examples
617 ///
618 /// ```no_run
619 /// # use fastly::Response;
620 /// let mut resp = Response::new();
621 /// resp.set_body_octet_stream(b"\xF0\x90\x80 hello, world!");
622 /// let string = resp.take_body_str_lossy();
623 /// assert!(resp.try_take_body().is_none());
624 /// assert_eq!(&string, "� hello, world!");
625 /// ```
626 pub fn take_body_str_lossy(&mut self) -> String {
627 if let Some(body) = self.try_take_body() {
628 String::from_utf8_lossy(&body.into_bytes()).to_string()
629 } else {
630 String::new()
631 }
632 }
633
634 /// Return a [`Lines`][`std::io::Lines`] iterator that reads the response body a line at a time.
635 ///
636 /// # Examples
637 ///
638 /// ```no_run
639 /// # use fastly::{Body, Response};
640 /// use std::io::Write;
641 ///
642 /// fn remove_es(resp: &mut Response) {
643 /// let mut no_es = Body::new();
644 /// for line in resp.read_body_lines() {
645 /// writeln!(no_es, "{}", line.unwrap().replace("e", "")).unwrap();
646 /// }
647 /// resp.set_body(no_es);
648 /// }
649 /// ```
650 pub fn read_body_lines(&mut self) -> std::io::Lines<&mut Body> {
651 self.get_body_mut().lines()
652 }
653
654 /// Builder-style equivalent of [`set_body_octet_stream()`][`Self::set_body_octet_stream()`].
655 pub fn with_body_octet_stream(mut self, body: &[u8]) -> Self {
656 self.set_body_octet_stream(body);
657 self
658 }
659
660 /// Set the given bytes as the response's body.
661 ///
662 #[doc = include_str!("../../docs/snippets/discards-body.md")]
663 #[doc = include_str!("../../docs/snippets/sets-app-octet-stream.md")]
664 ///
665 /// # Examples
666 ///
667 /// ```no_run
668 /// # use fastly::Response;
669 /// let mut resp = Response::new();
670 /// resp.set_body_octet_stream(b"hello, world!");
671 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::APPLICATION_OCTET_STREAM));
672 /// assert_eq!(&resp.into_body_bytes(), b"hello, world!");
673 /// ```
674 pub fn set_body_octet_stream(&mut self, body: &[u8]) {
675 self.body = Some(Body::from(body));
676 self.set_content_type(mime::APPLICATION_OCTET_STREAM);
677 }
678
679 /// Take and return the body from this response as a string.
680 ///
681 /// After calling this method, this response will no longer have a body.
682 ///
683 #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
684 ///
685 /// # Examples
686 ///
687 /// ```no_run
688 /// # use fastly::Response;
689 /// let mut resp = Response::from_body(b"hello, world!".to_vec());
690 /// let bytes = resp.take_body_bytes();
691 /// assert!(resp.try_take_body().is_none());
692 /// assert_eq!(&bytes, b"hello, world!");
693 /// ```
694 pub fn take_body_bytes(&mut self) -> Vec<u8> {
695 if let Some(body) = self.try_take_body() {
696 body.into_bytes()
697 } else {
698 Vec::new()
699 }
700 }
701
702 /// Return an iterator that reads the response body in chunks of at most the given number of
703 /// bytes.
704 ///
705 /// If `chunk_size` does not evenly divide the length of the body, then the last chunk will not
706 /// have length `chunk_size`.
707 ///
708 /// # Examples
709 ///
710 /// ```no_run
711 /// # use fastly::{Body, Response};
712 /// use std::io::Write;
713 /// fn remove_0s(resp: &mut Response) {
714 /// let mut no_0s = Body::new();
715 /// for chunk in resp.read_body_chunks(4096) {
716 /// let mut chunk = chunk.unwrap();
717 /// chunk.retain(|b| *b != 0);
718 /// no_0s.write_all(&chunk).unwrap();
719 /// }
720 /// resp.set_body(no_0s);
721 /// }
722 /// ```
723 pub fn read_body_chunks<'a>(
724 &'a mut self,
725 chunk_size: usize,
726 ) -> impl Iterator<Item = Result<Vec<u8>, std::io::Error>> + 'a {
727 self.get_body_mut().read_chunks(chunk_size)
728 }
729
730 /// Builder-style equivalent of [`set_body_json()`][Self::set_body_json()`].
731 pub fn with_body_json(mut self, value: &impl Serialize) -> Result<Self, serde_json::Error> {
732 self.set_body_json(value)?;
733 Ok(self)
734 }
735
736 /// Convert the given value to JSON and set that JSON as the response's body.
737 ///
738 /// The given value must implement [`serde::Serialize`]. You can either implement that trait for
739 /// your own custom type, or use [`serde_json::Value`] to create untyped JSON values. See
740 /// [`serde_json`] for details.
741 ///
742 #[doc = include_str!("../../docs/snippets/discards-body.md")]
743 #[doc = include_str!("../../docs/snippets/sets-app-json.md")]
744 ///
745 /// # Errors
746 ///
747 /// This method returns [`serde_json::Error`] if serialization fails.
748 ///
749 /// # Examples
750 ///
751 /// Using a type that derives [`serde::Serialize`]:
752 ///
753 /// ```no_run
754 /// # use fastly::Response;
755 /// #[derive(serde::Serialize)]
756 /// struct MyData {
757 /// name: String,
758 /// count: u64,
759 /// }
760 /// let my_data = MyData { name: "Computers".to_string(), count: 1024 };
761 /// let mut resp = Response::new();
762 /// resp.set_body_json(&my_data).unwrap();
763 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::APPLICATION_JSON));
764 /// assert_eq!(&resp.into_body_str(), r#"{"name":"Computers","count":1024}"#);
765 /// ```
766 ///
767 /// Using untyped JSON and the [`serde_json::json`] macro:
768 ///
769 /// ```no_run
770 /// # use fastly::Response;
771 /// let my_data = serde_json::json!({
772 /// "name": "Computers",
773 /// "count": 1024,
774 /// });
775 /// let mut resp = Response::new();
776 /// resp.set_body_json(&my_data).unwrap();
777 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::APPLICATION_JSON));
778 /// assert_eq!(&resp.into_body_str(), r#"{"count":1024,"name":"Computers"}"#);
779 /// ```
780 pub fn set_body_json(&mut self, value: &impl Serialize) -> Result<(), serde_json::Error> {
781 self.body = Some(Body::new());
782 serde_json::to_writer(self.get_body_mut(), value)?;
783 self.set_content_type(mime::APPLICATION_JSON);
784 Ok(())
785 }
786
787 /// Take the response body and attempt to parse it as a JSON value.
788 ///
789 /// The return type must implement [`serde::Deserialize`] without any non-static lifetimes. You
790 /// can either implement that trait for your own custom type, or use [`serde_json::Value`] to
791 /// deserialize untyped JSON values. See [`serde_json`] for details.
792 ///
793 /// After calling this method, this response will no longer have a body.
794 ///
795 /// # Errors
796 ///
797 /// This method returns [`serde_json::Error`] if deserialization fails.
798 ///
799 /// # Examples
800 ///
801 /// Using a type that derives [`serde::de::DeserializeOwned`]:
802 ///
803 /// ```no_run
804 /// # use fastly::Response;
805 /// #[derive(serde::Deserialize)]
806 /// struct MyData {
807 /// name: String,
808 /// count: u64,
809 /// }
810 /// let mut resp = Response::from_body(r#"{"name":"Computers","count":1024}"#);
811 /// let my_data = resp.take_body_json::<MyData>().unwrap();
812 /// assert_eq!(&my_data.name, "Computers");
813 /// assert_eq!(my_data.count, 1024);
814 /// ```
815 ///
816 /// Using untyped JSON with [`serde_json::Value`]:
817 ///
818 /// ```no_run
819 /// # use fastly::Response;
820 /// let my_data = serde_json::json!({
821 /// "name": "Computers",
822 /// "count": 1024,
823 /// });
824 /// let mut resp = Response::from_body(r#"{"name":"Computers","count":1024}"#);
825 /// let my_data = resp.take_body_json::<serde_json::Value>().unwrap();
826 /// assert_eq!(my_data["name"].as_str(), Some("Computers"));
827 /// assert_eq!(my_data["count"].as_u64(), Some(1024));
828 /// ```
829 pub fn take_body_json<T: DeserializeOwned>(&mut self) -> Result<T, serde_json::Error> {
830 if let Some(body) = self.try_take_body() {
831 serde_json::from_reader(body)
832 } else {
833 serde_json::from_reader(std::io::empty())
834 }
835 }
836
837 /// Builder-style equivalent of [`set_body_form()`][`Self::set_body_form()`].
838 pub fn with_body_form(
839 mut self,
840 value: &impl Serialize,
841 ) -> Result<Self, serde_urlencoded::ser::Error> {
842 self.set_body_form(value)?;
843 Ok(self)
844 }
845
846 /// Convert the given value to `application/x-www-form-urlencoded` format and set that data as
847 /// the response's body.
848 ///
849 /// The given value must implement [`serde::Serialize`]; see the trait documentation for
850 /// details.
851 ///
852 #[doc = include_str!("../../docs/snippets/discards-body.md")]
853 ///
854 /// # Content type
855 ///
856 /// This method sets the content type to `application/x-www-form-urlencoded`.
857 ///
858 /// # Errors
859 ///
860 /// This method returns [`serde_urlencoded::ser::Error`] if serialization fails.
861 ///
862 /// # Examples
863 ///
864 /// ```no_run
865 /// # use fastly::Response;
866 /// #[derive(serde::Serialize)]
867 /// struct MyData {
868 /// name: String,
869 /// count: u64,
870 /// }
871 /// let my_data = MyData { name: "Computers".to_string(), count: 1024 };
872 /// let mut resp = Response::new();
873 /// resp.set_body_form(&my_data).unwrap();
874 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::APPLICATION_WWW_FORM_URLENCODED));
875 /// assert_eq!(&resp.into_body_str(), "name=Computers&count=1024");
876 /// ```
877 pub fn set_body_form(
878 &mut self,
879 value: &impl Serialize,
880 ) -> Result<(), serde_urlencoded::ser::Error> {
881 self.body = Some(Body::new());
882 let s = serde_urlencoded::to_string(value)?;
883 self.set_body(s);
884 self.set_content_type(mime::APPLICATION_WWW_FORM_URLENCODED);
885 Ok(())
886 }
887
888 /// Take the response body and attempt to parse it as a `application/x-www-form-urlencoded`
889 /// formatted string.
890 ///
891 #[doc = include_str!("../../docs/snippets/returns-deserializeowned.md")]
892 ///
893 /// After calling this method, this response will no longer have a body.
894 ///
895 /// # Errors
896 ///
897 /// This method returns [`serde_urlencoded::de::Error`] if deserialization fails.
898 ///
899 /// # Examples
900 ///
901 /// ```no_run
902 /// # use fastly::Response;
903 /// #[derive(serde::Deserialize)]
904 /// struct MyData {
905 /// name: String,
906 /// count: u64,
907 /// }
908 /// let mut resp = Response::from_body("name=Computers&count=1024");
909 /// let my_data = resp.take_body_form::<MyData>().unwrap();
910 /// assert_eq!(&my_data.name, "Computers");
911 /// assert_eq!(my_data.count, 1024);
912 /// ```
913 pub fn take_body_form<T: DeserializeOwned>(
914 &mut self,
915 ) -> Result<T, serde_urlencoded::de::Error> {
916 if let Some(body) = self.try_take_body() {
917 serde_urlencoded::from_reader(body)
918 } else {
919 serde_urlencoded::from_reader(std::io::empty())
920 }
921 }
922
923 /// Get the MIME type described by the response's
924 /// [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
925 /// header, or `None` if that header is absent or contains an invalid MIME type.
926 ///
927 /// # Examples
928 ///
929 /// ```no_run
930 /// # use fastly::Response;
931 /// let resp = Response::new().with_body_text_plain("hello, world!");
932 /// assert_eq!(resp.get_content_type(), Some(fastly::mime::TEXT_PLAIN_UTF_8));
933 /// ```
934 pub fn get_content_type(&self) -> Option<Mime> {
935 self.get_header_str(http::header::CONTENT_TYPE)
936 .and_then(|v| v.parse().ok())
937 }
938
939 /// Builder-style equivalent of [`set_content_type()`][`Self::set_content_type()`].
940 pub fn with_content_type(mut self, mime: Mime) -> Self {
941 self.set_content_type(mime);
942 self
943 }
944
945 /// Set the MIME type described by the response's
946 /// [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
947 /// header.
948 ///
949 /// Any existing `Content-Type` header values will be overwritten.
950 ///
951 /// # Examples
952 ///
953 /// ```no_run
954 /// # use fastly::Response;
955 /// let mut resp = Response::new().with_body("hello,world!");
956 /// resp.set_content_type(fastly::mime::TEXT_CSV_UTF_8);
957 /// ```
958 pub fn set_content_type(&mut self, mime: Mime) {
959 self.set_header(http::header::CONTENT_TYPE, mime.as_ref())
960 }
961
962 /// Get the value of the response's
963 /// [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
964 /// header, if it exists.
965 pub fn get_content_length(&self) -> Option<usize> {
966 self.get_header(http::header::CONTENT_LENGTH)
967 .and_then(|v| v.to_str().ok())
968 .and_then(|v| v.parse().ok())
969 }
970
971 /// Returns whether the given header name is present in the response.
972 ///
973 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
974 ///
975 /// # Examples
976 ///
977 /// ```no_run
978 /// # use fastly::Response;
979 /// let resp = Response::new().with_header("hello", "world!");
980 /// assert!(resp.contains_header("hello"));
981 /// assert!(!resp.contains_header("not-present"));
982 /// ```
983 pub fn contains_header(&self, name: impl ToHeaderName) -> bool {
984 !self.lazy_handle.get_header_values(name).is_empty()
985 }
986
987 /// Builder-style equivalent of [`append_header()`][`Self::append_header()`].
988 pub fn with_header(mut self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
989 self.append_header(name, value);
990 self
991 }
992
993 /// Builder-style equivalent of [`set_header()`][`Self::set_header()`].
994 pub fn with_set_header(mut self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
995 self.set_header(name, value);
996 self
997 }
998
999 /// Get the value of a header as a string, or `None` if the header is not present.
1000 ///
1001 /// If there are multiple values for the header, only one is returned, which may be any of the
1002 /// values. See [`get_header_all_str()`][`Self::get_header_all_str()`] if you need to get all of
1003 /// the values.
1004 ///
1005 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1006 ///
1007 /// # Panics
1008 ///
1009 #[doc = include_str!("../../docs/snippets/panics-reqresp-header-utf8.md")]
1010 ///
1011 /// # Examples
1012 ///
1013 /// ```no_run
1014 /// # use fastly::Response;
1015 /// let resp = Response::new().with_header("hello", "world!");
1016 /// assert_eq!(resp.get_header_str("hello"), Some("world"));
1017 /// ```
1018 pub fn get_header_str<'a>(&self, name: impl ToHeaderName) -> Option<&str> {
1019 let name = name.into_borrowable();
1020 if let Some(hdr) = self.get_header(name.as_ref()) {
1021 Some(
1022 std::str::from_utf8(hdr.as_bytes()).unwrap_or_else(|_| {
1023 panic!("invalid UTF-8 HTTP header value for header: {}", name)
1024 }),
1025 )
1026 } else {
1027 None
1028 }
1029 }
1030
1031 /// Get the value of a header as a string, including invalid characters, or `None` if the header
1032 /// is not present.
1033 ///
1034 #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1035 ///
1036 /// If there are multiple values for the header, only one is returned, which may be any of the
1037 /// values. See [`get_header_all_str_lossy()`][`Self::get_header_all_str_lossy()`] if you need
1038 /// to get all of the values.
1039 ///
1040 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1041 ///
1042 /// # Examples
1043 ///
1044 /// ```no_run
1045 /// # use fastly::Response;
1046 /// # use http::header::HeaderValue;
1047 /// # use std::borrow::Cow;
1048 /// let header_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1049 /// let resp = Response::new().with_header("hello", header_value);
1050 /// assert_eq!(resp.get_header_str_lossy("hello"), Some(Cow::from("� world")));
1051 /// ```
1052 pub fn get_header_str_lossy(&self, name: impl ToHeaderName) -> Option<Cow<'_, str>> {
1053 self.get_header(name)
1054 .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()))
1055 }
1056
1057 /// Get the value of a header, or `None` if the header is not present.
1058 ///
1059 /// If there are multiple values for the header, only one is returned, which may be any of the
1060 /// values. See [`get_header_all()`][`Self::get_header_all()`] if you need to get all of the
1061 /// values.
1062 ///
1063 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1064 ///
1065 /// # Examples
1066 ///
1067 /// Handling UTF-8 values explicitly:
1068 ///
1069 /// ```no_run
1070 /// # use fastly::Response;
1071 /// # use fastly::http::HeaderValue;
1072 /// let resp = Response::new().with_header("hello", "world!");
1073 /// assert_eq!(resp.get_header("hello"), Some(&HeaderValue::from_static("world")));
1074 /// ```
1075 ///
1076 /// Safely handling invalid UTF-8 values:
1077 ///
1078 /// ```no_run
1079 /// # use fastly::Response;
1080 /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1081 /// let resp = Response::new().with_header("hello", invalid_utf8);
1082 /// assert_eq!(resp.get_header("hello").unwrap().as_bytes(), invalid_utf8);
1083 /// ```
1084 pub fn get_header<'a>(&self, name: impl ToHeaderName) -> Option<&HeaderValue> {
1085 self.lazy_handle.get_header_values(name).first()
1086 }
1087
1088 /// Get all values of a header as strings, or an empty vector if the header is not present.
1089 ///
1090 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1091 ///
1092 /// # Panics
1093 ///
1094 #[doc = include_str!("../../docs/snippets/panics-reqresp-headers-utf8.md")]
1095 ///
1096 /// # Examples
1097 ///
1098 /// ```no_run
1099 /// # use fastly::Response;
1100 /// let resp = Response::new()
1101 /// .with_header("hello", "world!")
1102 /// .with_header("hello", "universe!");
1103 /// let values = resp.get_header_all_str("hello");
1104 /// assert_eq!(values.len(), 2);
1105 /// assert!(values.contains(&"world!"));
1106 /// assert!(values.contains(&"universe!"));
1107 /// ```
1108 pub fn get_header_all_str<'a>(&self, name: impl ToHeaderName) -> Vec<&str> {
1109 let name = name.into_borrowable();
1110 self.get_header_all(name.as_ref())
1111 .map(|v| {
1112 std::str::from_utf8(v.as_bytes())
1113 .unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name))
1114 })
1115 .collect()
1116 }
1117
1118 /// Get an iterator of all the response's header names and values.
1119 ///
1120 /// # Examples
1121 ///
1122 /// You can turn the iterator into a collection, like [`Vec`]:
1123 ///
1124 /// ```no_run
1125 /// # use fastly::Response;
1126 /// # use fastly::http::header::{HeaderName, HeaderValue};
1127 /// let resp = Response::new()
1128 /// .with_header("hello", "world!")
1129 /// .with_header("hello", "universe!");
1130 ///
1131 /// let headers: Vec<(&HeaderName, &HeaderValue)> = resp.get_headers().collect();
1132 /// assert_eq!(headers.len(), 2);
1133 /// assert!(headers.contains(&(&HeaderName::from_static("hello"), &HeaderValue::from_static("world!"))));
1134 /// assert!(headers.contains(&(&HeaderName::from_static("hello"), &HeaderValue::from_static("universe!"))));
1135 /// ```
1136 ///
1137 /// You can use the iterator in a loop:
1138 ///
1139 /// ```no_run
1140 /// # use fastly::Response;
1141 /// let resp = Response::new()
1142 /// .with_header("hello", "world!")
1143 /// .with_header("hello", "universe!");
1144 ///
1145 /// for (n, v) in resp.get_headers() {
1146 /// println!("Header - {}: {:?}", n, v);
1147 /// }
1148 /// ```
1149 pub fn get_headers(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
1150 self.lazy_handle.iter()
1151 }
1152
1153 /// Get all values of a header as strings, including invalid characters, or an empty vector if the header is not present.
1154 ///
1155 #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1156 ///
1157 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1158 ///
1159 /// # Examples
1160 ///
1161 /// ```no_run
1162 /// # use std::borrow::Cow;
1163 /// # use http::header::HeaderValue;
1164 /// # use fastly::Response;
1165 /// let world_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1166 /// let universe_value = HeaderValue::from_bytes(b"\xF0\x90\x80 universe!").unwrap();
1167 /// let resp = Response::new()
1168 /// .with_header("hello", world_value)
1169 /// .with_header("hello", universe_value);
1170 /// let values = resp.get_header_all_str_lossy("hello");
1171 /// assert_eq!(values.len(), 2);
1172 /// assert!(values.contains(&Cow::from("� world!")));
1173 /// assert!(values.contains(&Cow::from("� universe!")));
1174 /// ```
1175 pub fn get_header_all_str_lossy(&self, name: impl ToHeaderName) -> Vec<Cow<'_, str>> {
1176 self.get_header_all(name)
1177 .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()))
1178 .collect()
1179 }
1180
1181 /// Get an iterator of all the values of a header.
1182 ///
1183 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1184 ///
1185 /// # Examples
1186 ///
1187 /// You can turn the iterator into collection, like [`Vec`]:
1188 ///
1189 /// ```no_run
1190 /// # use fastly::Response;
1191 /// # use fastly::http::HeaderValue;
1192 /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1193 /// let resp = Response::new()
1194 /// .with_header("hello", "world!")
1195 /// .with_header("hello", invalid_utf8);
1196 ///
1197 /// let values: Vec<&HeaderValue> = resp.get_header_all("hello").collect();
1198 /// assert_eq!(values.len(), 2);
1199 /// assert!(values.contains(&&HeaderValue::from_static("world!")));
1200 /// assert!(values.contains(&&HeaderValue::from_bytes(invalid_utf8).unwrap()));
1201 /// ```
1202 ///
1203 /// You can use the iterator in a loop:
1204 ///
1205 /// ```no_run
1206 /// # use fastly::Response;
1207 /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1208 /// let resp = Response::new()
1209 /// .with_header("hello", "world!")
1210 /// .with_header("hello", invalid_utf8);
1211 ///
1212 /// for value in resp.get_header_all("hello") {
1213 /// if let Ok(s) = std::str::from_utf8(value.as_bytes()) {
1214 /// println!("hello, {}", s);
1215 /// } else {
1216 /// println!("hello, invalid UTF-8!");
1217 /// }
1218 /// }
1219 /// ```
1220 pub fn get_header_all<'a>(
1221 &'a self,
1222 name: impl ToHeaderName,
1223 ) -> impl Iterator<Item = &'a HeaderValue> {
1224 self.lazy_handle.get_header_values(name).into_iter()
1225 }
1226
1227 /// Get all of the response's header names as strings, or an empty vector if no headers are
1228 /// present.
1229 ///
1230 /// # Examples
1231 ///
1232 /// ```no_run
1233 /// # use fastly::Response;
1234 /// let resp = Response::new()
1235 /// .with_header("hello", "world!")
1236 /// .with_header("goodbye", "latency!");
1237 /// let names = resp.get_header_names_str();
1238 /// assert_eq!(names.len(), 2);
1239 /// assert!(names.contains(&"hello"));
1240 /// assert!(names.contains(&"goodbye"));
1241 /// ```
1242 pub fn get_header_names_str(&self) -> Vec<&str> {
1243 self.get_header_names().map(|n| n.as_str()).collect()
1244 }
1245
1246 /// Get an iterator of all the response's header names.
1247 ///
1248 /// # Examples
1249 ///
1250 /// You can turn the iterator into a collection, like [`Vec`]:
1251 ///
1252 /// ```no_run
1253 /// # use fastly::Response;
1254 /// # use fastly::http::header::HeaderName;
1255 /// let resp = Response::new()
1256 /// .with_header("hello", "world!")
1257 /// .with_header("goodbye", "latency!");
1258 ///
1259 /// let values: Vec<&HeaderName> = resp.get_header_names().collect();
1260 /// assert_eq!(values.len(), 2);
1261 /// assert!(values.contains(&&HeaderName::from_static("hello")));
1262 /// assert!(values.contains(&&HeaderName::from_static("goodbye")));
1263 /// ```
1264 ///
1265 /// You can use the iterator in a loop:
1266 ///
1267 /// ```no_run
1268 /// # use fastly::Response;
1269 /// let resp = Response::new()
1270 /// .with_header("hello", "world!")
1271 /// .with_header("goodbye", "latency!");
1272 ///
1273 /// for name in resp.get_header_names() {
1274 /// println!("saw header: {:?}", name);
1275 /// }
1276 /// ```
1277 pub fn get_header_names(&self) -> impl Iterator<Item = &HeaderName> {
1278 self.lazy_handle.get_header_names()
1279 }
1280
1281 /// Set a response header to the given value, discarding any previous values for the given
1282 /// header name.
1283 ///
1284 #[doc = include_str!("../../docs/snippets/header-name-value-argument.md")]
1285 ///
1286 /// # Examples
1287 ///
1288 /// ```no_run
1289 /// # use fastly::Response;
1290 /// let mut resp = Response::new();
1291 ///
1292 /// resp.set_header("hello", "world!");
1293 /// assert_eq!(resp.get_header_str("hello"), Some("world!"));
1294 ///
1295 /// resp.set_header("hello", "universe!");
1296 ///
1297 /// let values = resp.get_header_all_str("hello");
1298 /// assert_eq!(values.len(), 1);
1299 /// assert!(!values.contains(&"world!"));
1300 /// assert!(values.contains(&"universe!"));
1301 /// ```
1302 pub fn set_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
1303 self.lazy_handle
1304 .set_header(name.into_owned(), value.into_owned());
1305 }
1306
1307 /// Add a response header with given value.
1308 ///
1309 /// Unlike [`set_header()`][`Self::set_header()`], this does not discard existing values for the
1310 /// same header name.
1311 ///
1312 #[doc = include_str!("../../docs/snippets/header-name-value-argument.md")]
1313 ///
1314 /// # Examples
1315 ///
1316 /// ```no_run
1317 /// # use fastly::Response;
1318 /// let mut resp = Response::new();
1319 ///
1320 /// resp.set_header("hello", "world!");
1321 /// assert_eq!(resp.get_header_str("hello"), Some("world!"));
1322 ///
1323 /// resp.append_header("hello", "universe!");
1324 ///
1325 /// let values = resp.get_header_all_str("hello");
1326 /// assert_eq!(values.len(), 2);
1327 /// assert!(values.contains(&"world!"));
1328 /// assert!(values.contains(&"universe!"));
1329 /// ```
1330 pub fn append_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
1331 self.lazy_handle
1332 .append_header_value(name.into_borrowable().as_ref(), value);
1333 }
1334
1335 /// Remove all response headers of the given name, and return one of the removed header values
1336 /// if any were present.
1337 ///
1338 /// If the header has multiple values, one is returned arbitrarily. To get all of the removed
1339 /// header values, or to get a specific value, use
1340 /// [`get_header_all()`][`Self::get_header_all()`].
1341 ///
1342 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1343 ///
1344 /// # Examples
1345 ///
1346 /// ```no_run
1347 /// # use fastly::Response;
1348 /// # use fastly::http::HeaderValue;
1349 /// let mut resp = Response::new().with_header("hello", "world!");
1350 /// assert_eq!(resp.get_header_str("hello"), Some("world!"));
1351 /// assert_eq!(resp.remove_header("hello"), Some(HeaderValue::from_static("world!")));
1352 /// assert!(resp.remove_header("not-present").is_none());
1353 /// ```
1354 pub fn remove_header(&mut self, name: impl ToHeaderName) -> Option<HeaderValue> {
1355 self.lazy_handle
1356 .remove_header(name.into_borrowable().as_ref())
1357 }
1358
1359 /// Remove all response headers of the given name, and return one of the removed header values
1360 /// as a string if any were present.
1361 ///
1362 #[doc = include_str!("../../docs/snippets/removes-one-header.md")]
1363 ///
1364 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1365 ///
1366 /// # Panics
1367 ///
1368 #[doc = include_str!("../../docs/snippets/panics-reqresp-remove-header-utf8.md")]
1369 ///
1370 /// # Examples
1371 ///
1372 /// ```no_run
1373 /// # use fastly::Response;
1374 /// let mut resp = Response::new().with_header("hello", "world!");
1375 /// assert_eq!(resp.get_header_str("hello"), Some("world!"));
1376 /// assert_eq!(resp.remove_header_str("hello"), Some("world!".to_string()));
1377 /// assert!(resp.remove_header_str("not-present").is_none());
1378 /// ```
1379 pub fn remove_header_str(&mut self, name: impl ToHeaderName) -> Option<String> {
1380 let name = name.into_borrowable();
1381 if let Some(hdr) = self.remove_header(name.as_ref()) {
1382 Some(
1383 std::str::from_utf8(hdr.as_bytes())
1384 .map(|s| s.to_owned())
1385 .unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name)),
1386 )
1387 } else {
1388 None
1389 }
1390 }
1391
1392 /// Remove all response headers of the given name, and return one of the removed header values
1393 /// as a string, including invalid characters, if any were present.
1394 ///
1395 #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1396 ///
1397 #[doc = include_str!("../../docs/snippets/removes-one-header.md")]
1398 ///
1399 #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1400 ///
1401 /// # Examples
1402 ///
1403 /// ```no_run
1404 /// # use fastly::Response;
1405 /// # use http::header::HeaderValue;
1406 /// # use std::borrow::Cow;
1407 /// let header_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1408 /// let mut resp = Response::new().with_header("hello", header_value);
1409 /// assert_eq!(resp.get_header_str_lossy("hello"), Some(Cow::from("� world")));
1410 /// assert_eq!(resp.remove_header_str_lossy("hello"), Some(String::from("� world")));
1411 /// assert!(resp.remove_header_str_lossy("not-present").is_none());
1412 /// ```
1413 pub fn remove_header_str_lossy(&mut self, name: impl ToHeaderName) -> Option<String> {
1414 self.remove_header(name)
1415 .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()).into_owned())
1416 }
1417
1418 /// Builder-style equivalent of [`set_status()`][`Self::set_status()`].
1419 pub fn with_status(mut self, status: impl ToStatusCode) -> Self {
1420 self.set_status(status);
1421 self
1422 }
1423
1424 /// Get the HTTP status code of the response.
1425 pub fn get_status(&self) -> StatusCode {
1426 *self.lazy_handle.get_field::<StatusCode>()
1427 }
1428
1429 /// Set the HTTP status code of the response.
1430 ///
1431 #[doc = include_str!("../../docs/snippets/statuscode-argument.md")]
1432 ///
1433 /// # Examples
1434 ///
1435 /// Using the constants from [`StatusCode`]:
1436 ///
1437 /// ```no_run
1438 /// # use fastly::Response;
1439 /// use fastly::http::StatusCode;
1440 ///
1441 /// let mut resp = Response::from_body("not found!");
1442 /// resp.set_status(StatusCode::NOT_FOUND);
1443 /// resp.send_to_client();
1444 /// ```
1445 ///
1446 /// Using a `u16`:
1447 ///
1448 /// ```no_run
1449 /// # use fastly::Response;
1450 /// let mut resp = Response::from_body("not found!");
1451 /// resp.set_status(404);
1452 /// resp.send_to_client();
1453 /// ```
1454 pub fn set_status(&mut self, status: impl ToStatusCode) {
1455 self.lazy_handle
1456 .put_field::<StatusCode>(status.to_status_code());
1457 }
1458
1459 /// Builder-style equivalent of [`set_version()`][`Self::set_version()`].
1460 pub fn with_version(mut self, version: Version) -> Self {
1461 self.set_version(version);
1462 self
1463 }
1464
1465 /// Get the HTTP version of this response.
1466 pub fn get_version(&self) -> Version {
1467 *self.lazy_handle.get_field::<Version>()
1468 }
1469
1470 /// Set the HTTP version of this response.
1471 pub fn set_version(&mut self, version: Version) {
1472 self.lazy_handle.put_field::<Version>(version);
1473 }
1474
1475 /// Sets how `Content-Length` and `Transfer-Encoding` will be determined when sending this
1476 /// request.
1477 ///
1478 /// See [`FramingHeadersMode`] for details on the options.
1479 pub fn set_framing_headers_mode(&mut self, mode: FramingHeadersMode) {
1480 self.metadata.framing_headers_mode = mode;
1481 }
1482
1483 /// Builder-style equivalent of
1484 /// [`set_framing_headers_mode()`][`Self::set_framing_headers_mode()`].
1485 pub fn with_framing_headers_mode(mut self, mode: FramingHeadersMode) -> Self {
1486 self.set_framing_headers_mode(mode);
1487 self
1488 }
1489
1490 /// Sets whether the client is encouraged to stop using the current connection and to open
1491 /// a new one for the next request.
1492 ///
1493 /// See [`HttpKeepaliveMode`] for details on the options.
1494 ///
1495 /// While this method never fails, Compute may not always respect the specificed
1496 /// [`HttpKeepaliveMode`]. In those cases, the `Response` being configured will otherwise be
1497 /// intact, and Compute will default to [`HttpKeepaliveMode::Automatic`].
1498 #[doc(hidden)]
1499 pub fn set_http_keepalive_mode(&mut self, mode: HttpKeepaliveMode) {
1500 self.metadata.http_keepalive_mode = mode;
1501 }
1502
1503 /// Builder-style equivalent of
1504 /// [`set_http_keepalive_mode()`][`Self::set_http_keepalive_mode()`].
1505 #[doc(hidden)]
1506 pub fn with_http_keepalive_mode(mut self, mode: HttpKeepaliveMode) -> Self {
1507 self.set_http_keepalive_mode(mode);
1508 self
1509 }
1510
1511 /// Get the name of the [`Backend`] this response came from, or `None` if the response is
1512 /// synthetic.
1513 ///
1514 /// # Examples
1515 ///
1516 /// From a backend response:
1517 ///
1518 /// ```no_run
1519 /// # use fastly::Request;
1520 /// let backend_resp = Request::get("https://example.com/").send("example_backend").unwrap();
1521 /// assert_eq!(backend_resp.get_backend_name(), Some("example_backend"));
1522 /// ```
1523 ///
1524 /// From a synthetic response:
1525 ///
1526 /// ```no_run
1527 /// # use fastly::Response;
1528 /// let synthetic_resp = Response::new();
1529 /// assert!(synthetic_resp.get_backend_name().is_none());
1530 /// ```
1531 pub fn get_backend_name(&self) -> Option<&str> {
1532 self.get_backend().map(|be| be.name())
1533 }
1534
1535 /// Get the backend this response came from, or `None` if the response is synthetic.
1536 ///
1537 /// # Examples
1538 ///
1539 /// From a backend response:
1540 ///
1541 /// ```no_run
1542 /// # use fastly::{Backend, Request};
1543 /// let backend_resp = Request::get("https://example.com/").send("example_backend").unwrap();
1544 /// assert_eq!(backend_resp.get_backend(), Some(&Backend::from_name("example_backend").unwrap()));
1545 /// ```
1546 ///
1547 /// From a synthetic response:
1548 ///
1549 /// ```no_run
1550 /// # use fastly::Response;
1551 /// let synthetic_resp = Response::new();
1552 /// assert!(synthetic_resp.get_backend().is_none());
1553 /// ```
1554 pub fn get_backend(&self) -> Option<&Backend> {
1555 self.metadata.backend.as_ref()
1556 }
1557
1558 /// Get the address of the backend this response came from, or `None` when the response is
1559 /// synthetic or cached.
1560 ///
1561 /// # Examples
1562 ///
1563 /// From a backend response:
1564 ///
1565 /// ```no_run
1566 /// # use fastly::Request;
1567 /// # use std::net::SocketAddr;
1568 /// let backend_resp = Request::get("https://example.com/")
1569 /// .with_pass(true)
1570 /// .send("example_backend")
1571 /// .unwrap();
1572 /// assert_eq!(backend_resp.get_backend_addr(), Some(&"127.0.0.1:443".parse::<SocketAddr>().unwrap()));
1573 /// ```
1574 ///
1575 /// From a synthetic response:
1576 ///
1577 /// ```no_run
1578 /// # use fastly::Response;
1579 /// let synthetic_resp = Response::new();
1580 /// assert!(synthetic_resp.get_backend_addr().is_none());
1581 /// ```
1582 pub fn get_backend_addr(&self) -> Option<&SocketAddr> {
1583 self.metadata.remote_addr.as_ref()
1584 }
1585
1586 /// Get the request this response came from, or `None` if the response is synthetic.
1587 ///
1588 /// Note that the returned request will only have the headers and metadata of the original
1589 /// request, as the body is consumed when sending the request.
1590 ///
1591 /// This method only returns a reference to the backend request. To instead take and return the
1592 /// owned request (for example, to subsequently send the request again), use
1593 /// [`take_backend_request()`][`Self::take_backend_request()`].
1594 ///
1595 /// # Examples
1596 ///
1597 /// From a backend response:
1598 ///
1599 /// ```no_run
1600 /// # use fastly::Request;
1601 /// let backend_resp = Request::post("https://example.com/")
1602 /// .with_body("hello")
1603 /// .send("example_backend")
1604 /// .unwrap();
1605 /// let backend_req = backend_resp.get_backend_request().expect("response is not synthetic");
1606 /// assert_eq!(backend_req.get_url_str(), "https://example.com/");
1607 /// assert!(!backend_req.has_body());
1608 /// ```
1609 ///
1610 /// From a synthetic response:
1611 ///
1612 /// ```no_run
1613 /// # use fastly::Response;
1614 /// let synthetic_resp = Response::new();
1615 /// assert!(synthetic_resp.get_backend_request().is_none());
1616 /// ```
1617 pub fn get_backend_request(&self) -> Option<&Request> {
1618 self.sent_req.as_ref()
1619 }
1620
1621 /// Take and return the request this response came from, or `None` if the response is synthetic.
1622 ///
1623 /// Note that the returned request will only have the headers and metadata of the original
1624 /// request, as the body is consumed when sending the request.
1625 ///
1626 /// # Examples
1627 ///
1628 /// From a backend response:
1629 ///
1630 /// ```no_run
1631 /// # use fastly::Request;
1632 /// let mut backend_resp = Request::post("https://example.com/")
1633 /// .with_body("hello")
1634 /// .send("example_backend")
1635 /// .unwrap();
1636 /// let backend_req = backend_resp.take_backend_request().expect("response is not synthetic");
1637 /// assert_eq!(backend_req.get_url_str(), "https://example.com/");
1638 /// assert!(!backend_req.has_body());
1639 /// backend_req.with_body("goodbye").send("example_backend").unwrap();
1640 /// ```
1641 ///
1642 /// From a synthetic response:
1643 ///
1644 /// ```no_run
1645 /// # use fastly::Response;
1646 /// let mut synthetic_resp = Response::new();
1647 /// assert!(synthetic_resp.take_backend_request().is_none());
1648 /// ```
1649 pub fn take_backend_request(&mut self) -> Option<Request> {
1650 self.sent_req.take()
1651 }
1652
1653 /// Begin sending the response to the client.
1654 ///
1655 /// This method returns as soon as the response header begins sending to the client, and
1656 /// transmission of the response will continue in the background.
1657 ///
1658 /// Once this method is called, nothing else may be added to the response body. To stream
1659 /// additional data to a response body after it begins to send, use
1660 /// [`stream_to_client`][`Self::stream_to_client()`].
1661 ///
1662 /// # Panics
1663 ///
1664 /// This method panics if another response has already been sent to the client by this method,
1665 /// by [`stream_to_client()`][`Self::stream_to_client()`], or by the equivalent methods of
1666 /// [`ResponseHandle`].
1667 ///
1668 #[doc = include_str!("../../docs/snippets/explicit-send-fastly-main.md")]
1669 ///
1670 /// # Examples
1671 ///
1672 /// Sending a backend response without modification:
1673 ///
1674 /// ```no_run
1675 /// # use fastly::Request;
1676 /// Request::get("https://example.com/").send("example_backend").unwrap().send_to_client();
1677 /// ```
1678 ///
1679 /// Removing a header from a backend response before sending to the client:
1680 ///
1681 /// ```no_run
1682 /// # use fastly::Request;
1683 /// let mut backend_resp = Request::get("https://example.com/").send("example_backend").unwrap();
1684 /// backend_resp.remove_header("bad-header");
1685 /// backend_resp.send_to_client();
1686 /// ```
1687 ///
1688 /// Sending a synthetic response:
1689 ///
1690 /// ```no_run
1691 /// # use fastly::Response;
1692 /// Response::from_body("hello, world!").send_to_client();
1693 /// ```
1694 pub fn send_to_client(self) {
1695 let res = self.send_to_client_impl(false, true);
1696 debug_assert!(res.is_none());
1697 }
1698
1699 /// Begin sending the response to the client, and return a [`StreamingBody`] that can accept
1700 /// further data to send.
1701 ///
1702 /// The client connection must be closed when finished writing the response by calling
1703 /// [`StreamingBody::finish()`].
1704 ///
1705 /// This method is most useful for programs that do some sort of processing or inspection of a
1706 /// potentially-large backend response body. Streaming allows the program to operate on small
1707 /// parts of the body rather than having to read it all into memory at once.
1708 ///
1709 /// This method returns as soon as the response header begins sending to the client, and
1710 /// transmission of the response will continue in the background.
1711 ///
1712 /// # Panics
1713 ///
1714 /// This method panics if another response has already been sent to the client by this method,
1715 /// by [`send_to_client()`][`Self::send_to_client()`], or by the equivalent methods of
1716 /// [`ResponseHandle`].
1717 ///
1718 #[doc = include_str!("../../docs/snippets/explicit-send-fastly-main.md")]
1719 ///
1720 /// # Examples
1721 ///
1722 /// Count the number of lines in a UTF-8 backend response body while sending it to the client:
1723 ///
1724 /// ```no_run
1725 /// # use fastly::Request;
1726 /// use std::io::{BufRead, Write};
1727 ///
1728 /// let mut backend_resp = Request::get("https://example.com/").send("example_backend").unwrap();
1729 /// // Take the body so we can iterate through its lines later
1730 /// let backend_resp_body = backend_resp.take_body();
1731 /// // Start sending the backend response to the client with a now-empty body
1732 /// let mut client_body = backend_resp.stream_to_client();
1733 ///
1734 /// let mut num_lines = 0;
1735 /// for line in backend_resp_body.lines() {
1736 /// let line = line.unwrap();
1737 /// num_lines += 1;
1738 /// // Write the line to the streaming client body
1739 /// client_body.write_all(line.as_bytes()).unwrap();
1740 /// }
1741 /// // Finish the streaming body to close the client connection
1742 /// client_body.finish().unwrap();
1743 ///
1744 /// println!("backend response body contained {} lines", num_lines);
1745 /// ```
1746 pub fn stream_to_client(self) -> StreamingBody {
1747 let res = self.send_to_client_impl(true, true);
1748 // streaming = true means we always get back a `Some`
1749 res.expect("streaming body is present")
1750 }
1751
1752 /// Send a response to the client.
1753 ///
1754 /// This will return a [`StreamingBody`] if and only if `streaming` is true. If a response has
1755 /// already been sent to the client, and `panic_on_multiple_send` is `true`, this function will
1756 /// panic.
1757 ///
1758 /// This method is public, but hidden from generated documentation in order to support the
1759 /// implementation of [`panic_with_status()`].
1760 #[doc(hidden)]
1761 pub fn send_to_client_impl(
1762 mut self,
1763 streaming: bool,
1764 panic_on_multiple_send: bool,
1765 ) -> Option<StreamingBody> {
1766 assert_single_downstream_response_is_sent(panic_on_multiple_send);
1767
1768 // we want to ensure that revalidation completes _after_ sending to the client
1769 let revalidation = self.background_revalidation.take();
1770 let (resp_handle, body_handle) = self.into_handles();
1771
1772 // Send the response to the client using the appropriate method based on the `streaming` flag.
1773 let ret = if streaming {
1774 Some(resp_handle.stream_to_client(body_handle).into())
1775 } else {
1776 resp_handle.send_to_client(body_handle);
1777 None
1778 };
1779
1780 drop(revalidation);
1781 ret
1782 }
1783
1784 /// Create a [`Response`] from the a [`ResponseHandle`] and a [`BodyHandle`].
1785 ///
1786 /// The extra metadata associated with a backend response is not tracked by the low-level handle
1787 /// APIs. As a result, methods like [`get_backend()`][`Self::get_backend()`] and
1788 /// [`get_backend_request()`][`Self::get_backend_request()`] will always return `None` for a
1789 /// request created from handles.
1790 pub fn from_handles(resp_handle: ResponseHandle, body_handle: BodyHandle) -> Self {
1791 Response {
1792 body: Some(body_handle.into()),
1793 ..Self::from_response_handle(resp_handle)
1794 }
1795 }
1796
1797 pub(crate) fn from_response_handle(resp_handle: ResponseHandle) -> Self {
1798 Response {
1799 body: None,
1800 metadata: FastlyResponseMetadata {
1801 remote_addr: resp_handle.remote_addr().ok().flatten(),
1802 ..FastlyResponseMetadata::new()
1803 },
1804 lazy_handle: LazyHandle::from_handle(resp_handle)
1805 .with_field_lazy::<StatusCode>()
1806 .with_field_lazy::<Version>()
1807 .finish(),
1808 sent_req: None,
1809 background_revalidation: None,
1810 }
1811 }
1812
1813 pub(crate) fn from_backend_resp(
1814 resp_handle: ResponseHandle,
1815 body_handle: BodyHandle,
1816 backend: Backend,
1817 sent_req: Request,
1818 ) -> Self {
1819 let mut resp = Self::from_handles(resp_handle, body_handle);
1820 resp.metadata.backend = Some(backend);
1821 resp.sent_req = Some(sent_req);
1822 resp
1823 }
1824
1825 pub(crate) fn get_response_handle(&self) -> &ResponseHandle {
1826 self.lazy_handle.get_handle()
1827 }
1828
1829 pub(crate) fn into_response_handle(self) -> ResponseHandle {
1830 self.lazy_handle.into_handle()
1831 }
1832
1833 /// Create a [`ResponseHandle`]/[`BodyHandle`] pair from a [`Response`].
1834 ///
1835 /// The extra metadata associated with a backend response is not tracked by the low-level handle
1836 /// APIs. As a result, converting to handles will cause the backend and request associated with
1837 /// a backend response to be lost.
1838 pub fn into_handles(mut self) -> (ResponseHandle, BodyHandle) {
1839 // Convert to a body handle, or create an empty body handle if none is set.
1840 let body_handle = if let Some(body) = self.try_take_body() {
1841 body.into_handle()
1842 } else {
1843 BodyHandle::new()
1844 };
1845
1846 let mut resp_handle = self.lazy_handle.into_handle();
1847 resp_handle.set_framing_headers_mode(self.metadata.framing_headers_mode);
1848 // If we are not permitted to set a keepalive mode on the response, proceed anyway. This
1849 // does not impede the integrity of a response a client would like to send, and could be
1850 // for relatively inoccuous reasons such as Compute determining a WASM program may not have
1851 // useful input on the state of a client connection.
1852 //
1853 // Ignoring the error here is how the fallible nature of
1854 // `Response::set_http_keepalive_mode` is upheld: `ResponseHandle::set_http_keepalive_mode`
1855 // is fallible on the condition that Compute denies a request to set a particular keepalive
1856 // mode. If this lower-level call succeeds, Compute will do as promised; it's just that we
1857 // allow this failure in support of a simpler high-level interface. It's almost certainly
1858 // overkill to fail response handling just because we'll retain a default posture of
1859 // keeping a client connection alive.
1860 let _ = resp_handle.set_http_keepalive_mode(self.metadata.http_keepalive_mode);
1861 (resp_handle, body_handle)
1862 }
1863
1864 /// Implements non-standard response header logic to match existing host-side behaviors.
1865 pub(crate) fn with_fastly_cache_headers(mut self, original_req: &Request) -> Self {
1866 use super::header::fastly::*;
1867
1868 if let Some(hits) = self.metadata.cache_hits {
1869 self.lazy_handle
1870 .append_header_value_with_comma(&X_CACHE, VAL_HIT);
1871 self.lazy_handle
1872 .append_header_value_with_comma(&X_CACHE_HITS, hits.to_string());
1873 } else {
1874 self.lazy_handle
1875 .append_header_value_with_comma(&X_CACHE, VAL_MISS);
1876 self.lazy_handle
1877 .append_header_value_with_comma(&X_CACHE_HITS, VAL_0);
1878 }
1879
1880 let should_remove_surrogate_headers =
1881 !original_req.contains_header(FASTLY_FF) && !original_req.contains_header(FASTLY_DEBUG);
1882 if should_remove_surrogate_headers {
1883 self.remove_header(SURROGATE_KEY);
1884 self.remove_header(SURROGATE_CONTROL);
1885 }
1886
1887 self
1888 }
1889
1890 /// Whether or not this response was stored in the cache.
1891 fn was_cached(&self) -> bool {
1892 matches!(
1893 self.metadata.cache_storage_action,
1894 Some(cache::HttpStorageAction::Insert | cache::HttpStorageAction::Update)
1895 )
1896 }
1897
1898 /// Get the Time to Live (TTL) in the cache for this response, if it is cached.
1899 ///
1900 /// The TTL provides the duration of "freshness" for the cached response
1901 /// after it is inserted into the cache. If the response is stale,
1902 /// the TTL is 0 (i.e. this returns `Some(0)`.
1903 ///
1904 /// Returns `None` if the response is not cached.
1905 pub fn get_ttl(&self) -> Option<Duration> {
1906 if self.was_cached() {
1907 return None;
1908 }
1909 let options = self.metadata.cache_options.as_ref()?;
1910 Some(options.max_age.saturating_sub(options.initial_age))
1911 }
1912
1913 /// The current age of the response, if it is cached.
1914 ///
1915 /// Returns `None` if the response is not cached.
1916 pub fn get_age(&self) -> Option<Duration> {
1917 if self.was_cached() {
1918 return None;
1919 }
1920 Some(self.metadata.cache_options.as_ref()?.initial_age)
1921 }
1922
1923 /// The time for which the response can safely be used despite being considered stale, if it is cached.
1924 ///
1925 /// Returns `None` if the response is not cached.
1926 pub fn get_stale_while_revalidate(&self) -> Option<Duration> {
1927 if self.was_cached() {
1928 return None;
1929 }
1930 Some(self.metadata.cache_options.as_ref()?.stale_while_revalidate)
1931 }
1932}
1933
1934impl Into<http::Response<Body>> for Response {
1935 fn into(mut self) -> http::Response<Body> {
1936 let mut resp = http::Response::new(self.body.take().unwrap_or_else(|| Body::new()));
1937 *resp.status_mut() = self.get_status();
1938 *resp.version_mut() = self.get_version();
1939 resp.extensions_mut().insert(self.metadata);
1940 *resp.headers_mut() = self.lazy_handle.into();
1941 resp
1942 }
1943}
1944
1945impl From<http::Response<Body>> for Response {
1946 fn from(from: http::Response<Body>) -> Self {
1947 let (mut parts, body) = from.into_parts();
1948 let metadata: FastlyResponseMetadata = parts
1949 .extensions
1950 .remove()
1951 .unwrap_or_else(FastlyResponseMetadata::new);
1952 Response {
1953 lazy_handle: LazyHandle::detached()
1954 .with_field(parts.status)
1955 .with_field(parts.version)
1956 .with_headers(parts.headers)
1957 .finish(),
1958 body: Some(body),
1959 metadata,
1960 sent_req: None,
1961 background_revalidation: None,
1962 }
1963 }
1964}
1965
1966impl From<(ResponseHandle, BodyHandle)> for Response {
1967 fn from(pair: (ResponseHandle, BodyHandle)) -> Self {
1968 Self::from_handles(pair.0, pair.1)
1969 }
1970}
1971
1972/// Send a response to the client with the given HTTP status code, and then panic.
1973///
1974/// By default, Rust panics will cause a generic `500 Internal Server Error` response to be sent to
1975/// the client, if a response has not already been sent. This macro allows you to customize the
1976/// status code, although the response is still generic.
1977///
1978/// The syntax is similar to [`panic!()`], but takes an optional first argument that must implement
1979/// [`ToStatusCode`], such as [`StatusCode`] or [`u16`]. The optional message and format arguments
1980/// are passed to [`panic!()`] unchanged, and so will be printed to the logging endpoint specified
1981/// by [`set_panic_endpoint()`][`crate::log::set_panic_endpoint()`].
1982///
1983/// # Examples
1984///
1985/// ```no_run
1986/// # use fastly::{Request, panic_with_status};
1987/// let req = Request::get("https://example.com/bad_path");
1988/// if req.get_path().starts_with("bad") {
1989/// panic_with_status!(403, "forbade request to a bad path: {}", req.get_url_str());
1990/// }
1991/// ```
1992#[macro_export]
1993macro_rules! panic_with_status {
1994 () => {
1995 $crate::panic_with_status!($crate::http::StatusCode::INTERNAL_SERVER_ERROR)
1996 };
1997 ($status:expr) => {{
1998 $crate::Response::new().with_status($status).send_to_client_impl(false, false);
1999 panic!();
2000 }};
2001 ($status:expr, $($arg:tt)*) => {{
2002 $crate::Response::new().with_status($status).send_to_client_impl(false, false);
2003 panic!($($arg)*);
2004 }};
2005}
2006
2007/// Make sure a single response is sent to the downstream request
2008#[doc(hidden)]
2009pub(crate) fn assert_single_downstream_response_is_sent(panic_on_multiple_send: bool) {
2010 use std::sync::atomic::{AtomicBool, Ordering};
2011
2012 /// A flag representing whether or not we have sent a response to the client.
2013 static SENT: AtomicBool = AtomicBool::new(false);
2014
2015 // Set our sent flag, and panic if we have already sent a response.
2016 if SENT.swap(true, Ordering::SeqCst) && panic_on_multiple_send {
2017 panic!("cannot send more than one client response per execution");
2018 }
2019}