fastly/http/
request.rs

1//! HTTP requests.
2
3use self::handle::{ContentEncodings, RequestHandle};
4use super::body::{self, Body, StreamingBody};
5use super::cache::{self, CacheKey, HttpCacheError, HttpCacheHandle};
6use super::response::{assert_single_downstream_response_is_sent, Response};
7use super::{LazyHandle, INITIAL_HEADER_NAME_BUF_SIZE};
8use crate::convert::{Borrowable, ToBackend, ToHeaderName, ToHeaderValue, ToMethod, ToUrl};
9use crate::experimental::BodyExt;
10use crate::handle::BodyHandle;
11use crate::http::CandidateResponse;
12use fastly_shared::{CacheOverride, ClientCertVerifyResult, FastlyStatus, FramingHeadersMode};
13use fastly_sys::fastly_http_req::SendErrorDetail;
14use http::header::{HeaderName, HeaderValue};
15use http::{Method, Version};
16use mime::Mime;
17use pending::PendingRequestHandle;
18use serde::de::DeserializeOwned;
19use serde::Serialize;
20use std::borrow::Cow;
21use std::io::{BufRead, Write};
22use std::net::IpAddr;
23use std::sync::Arc;
24use thiserror::Error;
25use url::Url;
26
27pub use pending::{select, PendingRequest, PollResult};
28
29pub(crate) mod handle;
30pub(crate) mod pending;
31
32/// An HTTP request, including body, headers, method, and URL.
33///
34/// # Getting the client request
35///
36/// Call [`Request::from_client()`] to get the client request being handled by this execution of the
37/// Compute program.
38///
39/// # Creation and conversion
40///
41/// New requests can be created programmatically with [`Request::new()`]. In addition, there are
42/// convenience constructors like [`Request::get()`] which automatically select the appropriate
43/// method.
44///
45/// For interoperability with other Rust libraries, [`Request`] can be converted to and from the
46/// [`http`] crate's [`http::Request`] type using the [`From`][`Self::from()`] and
47/// [`Into`][`Self::into()`] traits.
48///
49/// # Sending backend requests
50///
51/// Requests can be sent to a backend in blocking or asynchronous fashion using
52/// [`send()`][`Self::send()`], [`send_async()`][`Self::send_async()`], or
53/// [`send_async_streaming()`][`Self::send_async_streaming()`].
54///
55/// # Builder-style methods
56///
57/// [`Request`] can be used as a
58/// [builder](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html), allowing requests to
59/// be constructed and used through method chaining. Methods with the `with_` name prefix, such as
60/// [`with_header()`][`Self::with_header()`], return `Self` to allow chaining. The builder style is
61/// typically most useful when constructing and using a request in a single expression. For example:
62///
63/// ```no_run
64/// # use fastly::{Error, Request};
65/// # fn f() -> Result<(), Error> {
66/// Request::get("https://example.com")
67///     .with_header("my-header", "hello!")
68///     .with_header("my-other-header", "Здравствуйте!")
69///     .send("example_backend")?;
70/// # Ok(()) }
71/// ```
72///
73/// # Setter methods
74///
75/// Setter methods, such as [`set_header()`][`Self::set_header()`], are prefixed by `set_`, and can
76/// be used interchangeably with the builder-style methods, allowing you to mix and match styles
77/// based on what is most convenient for your program. Setter methods tend to work better than
78/// builder-style methods when constructing a request involves conditional branches or loops. For
79/// example:
80///
81/// ```no_run
82/// # use fastly::{Error, Request};
83/// # fn f(needs_translation: bool) -> Result<(), Error> {
84/// let mut req = Request::get("https://example.com").with_header("my-header", "hello!");
85/// if needs_translation {
86///     req.set_header("my-other-header", "Здравствуйте!");
87/// }
88/// req.send("example_backend")?;
89/// # Ok(()) }
90/// ```
91#[derive(Debug)]
92pub struct Request {
93    lazy_handle: LazyHandle<RequestHandle>,
94    body: Option<Body>,
95    pub(crate) metadata: FastlyRequestMetadata,
96}
97
98/// Anything that we need to make a full roundtrip through the `http` types that doesn't have a more
99/// concrete corresponding type.
100#[derive(Debug, Clone)]
101pub(crate) struct FastlyRequestMetadata {
102    pub(crate) cache_override: CacheOverride,
103    is_from_client: bool,
104    auto_decompress_response: ContentEncodings,
105    framing_headers_mode: FramingHeadersMode,
106    pub(crate) override_cache_key: Option<CacheKeyGen>,
107    before_send: Option<BeforeSend>,
108    after_send: Option<AfterSend>,
109}
110
111impl FastlyRequestMetadata {
112    fn new() -> Self {
113        Self {
114            cache_override: CacheOverride::default(),
115            is_from_client: false,
116            auto_decompress_response: ContentEncodings::empty(),
117            framing_headers_mode: FramingHeadersMode::Automatic,
118            override_cache_key: None,
119            before_send: None,
120            after_send: None,
121        }
122    }
123
124    fn invoke_before_send(&self, req: &mut Request) -> Result<(), SendErrorCause> {
125        if let Some(f) = &self.before_send {
126            (f.before_send)(req)
127        } else {
128            Ok(())
129        }
130    }
131
132    fn flush_to_handle(&self, req_handle: &mut RequestHandle) {
133        req_handle.set_cache_override(&self.cache_override);
134        req_handle.set_auto_decompress_response(self.auto_decompress_response);
135        req_handle.set_framing_headers_mode(self.framing_headers_mode);
136    }
137}
138
139#[derive(Clone)]
140pub(crate) enum CacheKeyGen {
141    Lazy(Arc<dyn Fn(&Request) -> CacheKey + Send + Sync>),
142    Set(CacheKey),
143}
144
145impl std::fmt::Debug for CacheKeyGen {
146    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
147        match self {
148            CacheKeyGen::Lazy(f) => fmt.write_fmt(format_args!("Lazy({:?})", Arc::as_ptr(f))),
149            CacheKeyGen::Set(k) => {
150                const DIGITS: &[u8] = b"0123456789ABCDEF";
151                let mut hex = [0; 64];
152                for (i, b) in k.iter().enumerate() {
153                    hex[i * 2] = DIGITS[(b >> 4) as usize];
154                    hex[i * 2 + 1] = DIGITS[(b & 0xf) as usize];
155                }
156                fmt.write_fmt(format_args!("Set({})", std::str::from_utf8(&hex).unwrap()))
157            }
158        }
159    }
160}
161
162#[derive(Clone)]
163struct BeforeSend {
164    before_send: Arc<dyn Fn(&mut Request) -> Result<(), SendErrorCause> + Send + Sync>,
165}
166
167impl std::fmt::Debug for BeforeSend {
168    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
169        fmt.write_str("<closure>")
170    }
171}
172
173#[derive(Clone)]
174struct AfterSend {
175    after_send: Arc<dyn Fn(&mut CandidateResponse) -> Result<(), SendErrorCause> + Send + Sync>,
176}
177
178impl std::fmt::Debug for AfterSend {
179    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
180        fmt.write_str("<closure>")
181    }
182}
183
184impl Request {
185    /// Get the client request being handled by this execution of the Compute program.
186    ///
187    /// # Panics
188    ///
189    /// This method panics if the client request has already been retrieved by this method or by
190    /// [the low-level handle API][`crate::handle`].
191    ///
192    /// # Incompatibility with [`fastly::main`][`crate::main`]
193    ///
194    /// This method cannot be used with [`fastly::main`][`crate::main`], as that attribute
195    /// implicitly calls [`Request::from_client()`] to populate the request argument. Use an
196    /// undecorated `main()` function instead, along with [`Response::send_to_client()`] or
197    /// [`Response::stream_to_client()`] to send a response to the client.
198    pub fn from_client() -> Request {
199        let (req_handle, body_handle) = self::handle::client_request_and_body();
200        let mut req = Request::from_handles(req_handle, Some(body_handle));
201        req.metadata.is_from_client = true;
202        req
203    }
204
205    /// Return `true` if this request is from the client of this execution of the Compute
206    /// program.
207    pub fn is_from_client(&self) -> bool {
208        self.metadata.is_from_client
209    }
210
211    /// Create a new request with the given method and URL, no headers, and an empty body.
212    ///
213    /// # Argument type conversion
214    ///
215    /// The method and URL arguments can be any types that implement [`ToMethod`] and [`ToUrl`],
216    /// respectively. See those traits for details on which types can be used and when panics may
217    /// arise during conversion.
218    pub fn new(method: impl ToMethod, url: impl ToUrl) -> Self {
219        Self {
220            lazy_handle: LazyHandle::detached()
221                .with_field(Version::HTTP_11)
222                .with_field(method.into_owned())
223                .with_field(url.into_owned())
224                .finish(),
225            body: None,
226            metadata: FastlyRequestMetadata::new(),
227        }
228    }
229
230    /// Make a new request with the same method, url, headers, and version of this request, but no
231    /// body.
232    ///
233    /// If you also need to clone the request body, use
234    /// [`clone_with_body()`][`Self::clone_with_body()`]
235    ///
236    /// # Examples
237    ///
238    /// ```no_run
239    /// # use fastly::Request;
240    /// let original = Request::post("https://example.com")
241    ///     .with_header("hello", "world!")
242    ///     .with_body("hello");
243    /// let new = original.clone_without_body();
244    /// assert_eq!(original.get_method(), new.get_method());
245    /// assert_eq!(original.get_url(), new.get_url());
246    /// assert_eq!(original.get_header("hello"), new.get_header("hello"));
247    /// assert_eq!(original.get_version(), new.get_version());
248    /// assert!(original.has_body());
249    /// assert!(!new.has_body());
250    /// ```
251    pub fn clone_without_body(&self) -> Request {
252        Self {
253            lazy_handle: self.lazy_handle.clone(),
254            body: None,
255            metadata: self.metadata.clone(),
256        }
257    }
258
259    /// Clone this request by reading in its body, and then writing the same body to the original
260    /// and the cloned request.
261    ///
262    /// This method requires mutable access to this request because reading from and writing to the
263    /// body can involve an HTTP connection.
264    ///
265    #[doc = include_str!("../../docs/snippets/clones-body.md")]
266    ///
267    /// # Examples
268    ///
269    /// ```no_run
270    /// # use fastly::Request;
271    /// let mut original = Request::post("https://example.com")
272    ///     .with_header("hello", "world!")
273    ///     .with_body("hello");
274    /// let mut new = original.clone_with_body();
275    /// assert_eq!(original.get_method(), new.get_method());
276    /// assert_eq!(original.get_url(), new.get_url());
277    /// assert_eq!(original.get_header("hello"), new.get_header("hello"));
278    /// assert_eq!(original.get_version(), new.get_version());
279    /// assert_eq!(original.take_body_bytes(), new.take_body_bytes());
280    /// ```
281    pub fn clone_with_body(&mut self) -> Request {
282        let mut new_req = self.clone_without_body();
283        if self.has_body() {
284            let mut body = self.take_body();
285
286            for chunk in body.read_chunks(4096) {
287                let chunk = chunk.expect("can read body chunk");
288                new_req
289                    .get_body_mut()
290                    .write_all(&chunk)
291                    .expect("failed to write to cloned body");
292                self.get_body_mut()
293                    .write_all(&chunk)
294                    .expect("failed to write to cloned body");
295            }
296
297            if let Ok(trailers) = body.get_trailers() {
298                for (k, v) in trailers.iter() {
299                    self.get_body_mut().append_trailer(k, v);
300                    new_req.get_body_mut().append_trailer(k, v);
301                }
302            }
303        }
304        new_req
305    }
306
307    /// Create a new `GET` [`Request`] with the given URL, no headers, and an empty body.
308    ///
309    #[doc = include_str!("../../docs/snippets/url-argument.md")]
310    pub fn get(url: impl ToUrl) -> Self {
311        Self::new(Method::GET, url)
312    }
313
314    /// Create a new `HEAD` [`Request`] with the given URL, no headers, and an empty body.
315    ///
316    #[doc = include_str!("../../docs/snippets/url-argument.md")]
317    pub fn head(url: impl ToUrl) -> Self {
318        Self::new(Method::HEAD, url)
319    }
320
321    /// Create a new `POST` [`Request`] with the given URL, no headers, and an empty body.
322    ///
323    #[doc = include_str!("../../docs/snippets/url-argument.md")]
324    pub fn post(url: impl ToUrl) -> Self {
325        Self::new(Method::POST, url)
326    }
327
328    /// Create a new `PUT` [`Request`] with the given URL, no headers, and an empty body.
329    ///
330    #[doc = include_str!("../../docs/snippets/url-argument.md")]
331    pub fn put(url: impl ToUrl) -> Self {
332        Self::new(Method::PUT, url)
333    }
334
335    /// Create a new `DELETE` [`Request`] with the given URL, no headers, and an empty body.
336    ///
337    #[doc = include_str!("../../docs/snippets/url-argument.md")]
338    pub fn delete(url: impl ToUrl) -> Self {
339        Self::new(Method::DELETE, url)
340    }
341
342    /// Create a new `CONNECT` [`Request`] with the given URL, no headers, and an empty body.
343    ///
344    #[doc = include_str!("../../docs/snippets/url-argument.md")]
345    pub fn connect(url: impl ToUrl) -> Self {
346        Self::new(Method::CONNECT, url)
347    }
348
349    /// Create a new `OPTIONS` [`Request`] with the given URL, no headers, and an empty body.
350    ///
351    #[doc = include_str!("../../docs/snippets/url-argument.md")]
352    pub fn options(url: impl ToUrl) -> Self {
353        Self::new(Method::OPTIONS, url)
354    }
355
356    /// Create a new `TRACE` [`Request`] with the given URL, no headers, and an empty body.
357    ///
358    #[doc = include_str!("../../docs/snippets/url-argument.md")]
359    pub fn trace(url: impl ToUrl) -> Self {
360        Self::new(Method::TRACE, url)
361    }
362
363    /// Create a new `PATCH` [`Request`] with the given URL, no headers, and an empty body.
364    ///
365    #[doc = include_str!("../../docs/snippets/url-argument.md")]
366    pub fn patch(url: impl ToUrl) -> Self {
367        Self::new(Method::PATCH, url)
368    }
369
370    /// Builder-style equivalent of [`set_before_send()`][`Self::set_before_send()`].
371    pub fn with_before_send(
372        mut self,
373        f: impl Fn(&mut Request) -> Result<(), SendErrorCause> + Send + Sync + 'static,
374    ) -> Self {
375        self.set_before_send(f);
376        self
377    }
378
379    /// Sets a callback to be invoked if a request is going all the way to a
380    /// backend, allowing the request to be modified beforehand.
381    ///
382    /// This callback is useful when, for example, a backend requires an
383    /// additional header to be inserted, but that header is expensive to
384    /// produce. The callback will only be invoked if the original request
385    /// cannot be responded to from the cache, so the header is only computed
386    /// when it is truly needed.
387    pub fn set_before_send(
388        &mut self,
389        f: impl Fn(&mut Request) -> Result<(), SendErrorCause> + Send + Sync + 'static,
390    ) {
391        self.metadata.before_send = Some(BeforeSend {
392            before_send: Arc::new(f),
393        })
394    }
395
396    /// Builder-style equivalent of [`set_after_send()`][`Self::set_after_send()`].
397    pub fn with_after_send(
398        mut self,
399        f: impl Fn(&mut CandidateResponse) -> Result<(), SendErrorCause> + Send + Sync + 'static,
400    ) -> Self {
401        self.set_after_send(f);
402        self
403    }
404
405    /// Sets a callback to be invoked after a response is returned from a
406    /// backend, but before it is stored into the cache.
407    ///
408    /// This callback allows for cache properties like TTL to be customized
409    /// beyond what the backend resopnse headers specify. It also allows for the
410    /// response itself to be modified prior to storing into the cache.
411    pub fn set_after_send(
412        &mut self,
413        f: impl Fn(&mut CandidateResponse) -> Result<(), SendErrorCause> + Send + Sync + 'static,
414    ) {
415        self.metadata.after_send = Some(AfterSend {
416            after_send: Arc::new(f),
417        })
418    }
419
420    /// Retrieve a reponse for the request, either from cache or by sending it to the given backend
421    /// server. Returns once the response headers have been received, or an error occurs.
422    ///
423    #[doc = include_str!("../../docs/snippets/backend-argument.md")]
424    ///
425    /// # Examples
426    ///
427    /// Sending the client request to a backend without modification:
428    ///
429    /// ```no_run
430    /// # use fastly::Request;
431    /// let backend_resp = Request::from_client().send("example_backend").expect("request succeeds");
432    /// assert!(backend_resp.get_status().is_success());
433    /// ```
434    ///
435    /// Sending a synthetic request:
436    ///
437    /// ```no_run
438    /// # use fastly::Request;
439    /// let backend_resp = Request::get("https://example.com")
440    ///     .send("example_backend")
441    ///     .expect("request succeeds");
442    /// assert!(backend_resp.get_status().is_success());
443    /// ```
444    pub fn send(mut self, backend: impl ToBackend) -> Result<Response, SendError> {
445        let backend = backend.into_owned();
446        match validate_request(&self).and_then(|_| self.send_impl(backend.name())) {
447            Ok(mut resp) => {
448                resp.sent_req = Some(self);
449                resp.metadata.backend = Some(backend);
450                Ok(resp)
451            }
452            Err(err) => Err(SendError::new(backend.name(), self, err)),
453        }
454    }
455
456    fn must_use_guest_caching(&self) -> bool {
457        self.metadata.before_send.is_some() || self.metadata.after_send.is_some()
458    }
459
460    fn should_use_guest_caching(&self) -> Result<bool, SendErrorCause> {
461        if self.metadata.cache_override.is_pass() {
462            // Pass requests have to go through the host for now, because we
463            // don't have SDK access to the direct pass setting
464            Ok(false)
465        } else if self.get_method() == "PURGE" {
466            // We don't yet implement guest-side interpretation of URL purges
467            Ok(false)
468        } else if self.lazy_handle.get_handle().must_use_host_caching() {
469            // If the service cannot use guest-side caching, we cannot support send hooks:
470            if self.must_use_guest_caching() {
471                Err(SendErrorCause::HttpCacheApiUnsupported)
472            } else {
473                Ok(false)
474            }
475        } else {
476            Ok(true)
477        }
478    }
479
480    fn send_impl(&mut self, backend: &str) -> Result<Response, SendErrorCause> {
481        if self.should_use_guest_caching()? {
482            if !self.is_cacheable() {
483                return self
484                    .take_request_handle()
485                    .send_without_caching(self.take_body_handle(), backend)
486                    .map(|r| Response::from(r).with_fastly_cache_headers(self));
487            }
488
489            let options = cache::LookupOptions {
490                override_key: self.get_override_cache_key(),
491            };
492            // Clear out the cache key so that it's not inserted as a header
493            // (which is used for host-side caching mode)
494            self.metadata.override_cache_key = None;
495
496            let cache_handle = cache::transaction_lookup(&self.lazy_handle.get_handle(), &options)?;
497            // force the lookup to await in the host, retrieving any errors synchronously
498            cache_handle.wait()?;
499
500            // is there a "usable" cached response (i.e. fresh or within SWR period)
501            if let Ok(mut resp) = cache_handle.get_found_response(true) {
502                // if this is during SWR, we may be the "lucky winner" who is
503                // tasked with performing a background revalidation
504                if cache_handle.must_insert_or_update() {
505                    // since we don't have full `async fn` support, we begin
506                    // revalidation now, and put the data needed to complete it
507                    // into the _response_ we are about to return. when the
508                    // `background_revalidation` field is dropped (e.g. after
509                    // calling `Response::send_to_client()`), it will wait for
510                    // the backend response (if one hasn't arrived yet), invoke
511                    // any after-send hooks, and complete the cache transaction.
512                    resp.background_revalidation = self
513                        .send_async_for_caching(cache_handle, backend)
514                        .ok()
515                        .map(|pending| pending.into_background_revalidation());
516                }
517
518                // Meanwhile, whether fresh or in SWR, we can immediately return
519                // the cached response:
520                Ok(resp.with_fastly_cache_headers(self))
521            } else {
522                if !cache_handle.must_insert_or_update() {
523                    // Request collapsing has been disabled: pass the _original_ request through to the
524                    // origin without updating the cache.
525                    self.take_request_handle()
526                        .send_without_caching(self.take_body_handle(), backend)
527                        .map(|r| Response::from(r).with_fastly_cache_headers(self))
528                } else {
529                    self.send_async_for_caching(cache_handle, backend)?
530                        .into_candidate()?
531                        .apply_and_stream_back()
532                        .map(|r| Response::from(r).with_fastly_cache_headers(self))
533                }
534            }
535        } else {
536            self.take_request_handle()
537                .send(self.take_body_handle(), backend)
538                .map(Response::from)
539        }
540    }
541
542    /// Actually send a backend request, applying guest-side caching hooks.
543    ///
544    /// Returns a `PendingBackendRequestForCaching` (i.e. an async response) so
545    /// that completion can be performed asynchronously for background
546    /// revalidation.
547    fn send_async_for_caching(
548        &mut self,
549        cache_handle: HttpCacheHandle,
550        backend: &str,
551    ) -> Result<PendingBackendRequestForCaching, SendErrorCause> {
552        let mut req = Request::from(cache_handle.get_suggested_backend_request()?)
553            .with_body(self.take_body())
554            .with_metadata(self.metadata.clone());
555        self.metadata.invoke_before_send(&mut req)?;
556        let final_cache_override = req.metadata.cache_override.clone();
557        let (req_handle, req_body_handle) = req.into_handles();
558        let pending_req_handle = req_handle
559            .send_async_without_caching(req_body_handle.unwrap_or_else(BodyHandle::new), backend)?;
560        Ok(PendingBackendRequestForCaching {
561            cache_handle,
562            pending_req_handle,
563            after_send: self.metadata.after_send.clone(),
564            cache_override: final_cache_override,
565        })
566    }
567
568    /// Begin sending the request to the given backend server, and return a [`PendingRequest`] that
569    /// can yield the backend response or an error.
570    ///
571    /// This method returns as soon as the request begins sending to the backend, and transmission
572    /// of the request body and headers will continue in the background.
573    ///
574    /// This method allows for sending more than one request at once and receiving their responses
575    /// in arbitrary orders. See [`PendingRequest`] for more details on how to wait on, poll, or
576    /// select between pending requests.
577    ///
578    /// This method is also useful for sending requests where the response is unimportant, but the
579    /// request may take longer than the Compute program is able to run, as the request will
580    /// continue sending even after the program that initiated it exits.
581    ///
582    #[doc = include_str!("../../docs/snippets/backend-argument.md")]
583    ///
584    /// # Examples
585    ///
586    /// Sending a request to two backends and returning whichever response finishes first:
587    ///
588    /// ```no_run
589    /// # use fastly::Request;
590    /// let backend_resp_1 = Request::get("https://example.com/")
591    ///     .send_async("example_backend_1")
592    ///     .expect("request 1 begins sending");
593    /// let backend_resp_2 = Request::get("https://example.com/")
594    ///     .send_async("example_backend_2")
595    ///     .expect("request 2 begins sending");
596    /// let (resp, _) = fastly::http::request::select(vec![backend_resp_1, backend_resp_2]);
597    /// resp.expect("request succeeds").send_to_client();
598    /// ```
599    ///
600    /// Sending a long-running request and ignoring its result so that the program can exit before
601    /// it completes:
602    ///
603    /// ```no_run
604    /// # use fastly::Request;
605    /// # let some_large_file = vec![0u8];
606    /// let _ = Request::post("https://example.com")
607    ///     .with_body(some_large_file)
608    ///     .send_async("example_backend");
609    /// ```
610    pub fn send_async(self, backend: impl ToBackend) -> Result<PendingRequest, SendError> {
611        let backend = backend.into_owned();
612
613        // programmable HTTP caching is not currently support for async sending
614        if self.must_use_guest_caching() {
615            return Err(SendError::new(
616                backend.name(),
617                self,
618                SendErrorCause::HttpCacheApiUnsupported,
619            ));
620        }
621
622        // TODO 2024-07-31: this now forces request headers to be copied an extra
623        // time, while it used to be ~free. Once async fn support is added and
624        // pending request select is no longer present, we should be able to drop
625        // this forced clone and let users clone if needed.
626        let cloned = self.clone_without_body();
627
628        match validate_request(&self).and_then(|_| {
629            let (req_handle, body_handle) = self.into_handles();
630            let body_handle = body_handle.unwrap_or_else(BodyHandle::new);
631            req_handle.send_async(body_handle, backend.name())
632        }) {
633            Ok(pending_req_handle) => Ok(PendingRequest::new(pending_req_handle, backend, cloned)),
634            Err(err) => Err(SendError::new(backend.name(), cloned, err)),
635        }
636    }
637
638    /// Begin sending the request to the given backend server, and return a [`PendingRequest`] that
639    /// can yield the backend response or an error along with a [`StreamingBody`] that can accept
640    /// further data to send.
641    ///
642    /// The backend connection is only closed once [`StreamingBody::finish()`] is called. The
643    /// [`PendingRequest`] will not yield a [`Response`] until the [`StreamingBody`] is finished.
644    ///
645    /// This method is most useful for programs that do some sort of processing or inspection of a
646    /// potentially-large client request body. Streaming allows the program to operate on small
647    /// parts of the body rather than having to read it all into memory at once.
648    ///
649    /// This method returns as soon as the request begins sending to the backend, and transmission
650    /// of the request body and headers will continue in the background.
651    ///
652    /// # Examples
653    ///
654    /// Count the number of lines in a UTF-8 client request body while sending it to the backend:
655    ///
656    /// ```no_run
657    /// # use fastly::Request;
658    /// use std::io::{BufRead, Write};
659    ///
660    /// let mut req = Request::from_client();
661    /// // Take the body so we can iterate through its lines later
662    /// let req_body = req.take_body();
663    /// // Start sending the client request to the client with a now-empty body
664    /// let (mut backend_body, pending_req) = req
665    ///     .send_async_streaming("example_backend")
666    ///     .expect("request begins sending");
667    ///
668    /// let mut num_lines = 0;
669    /// for line in req_body.lines() {
670    ///     let line = line.unwrap();
671    ///     num_lines += 1;
672    ///     // Write the line to the streaming backend body
673    ///     backend_body.write_all(line.as_bytes()).unwrap();
674    /// }
675    /// // Finish the streaming body to allow the backend connection to close
676    /// backend_body.finish().unwrap();
677    ///
678    /// println!("client request body contained {} lines", num_lines);
679    /// ```
680    pub fn send_async_streaming(
681        self,
682        backend: impl ToBackend,
683    ) -> Result<(StreamingBody, PendingRequest), SendError> {
684        let backend = backend.into_owned();
685
686        // programmable HTTP caching is not currently support for async sending
687        if self.must_use_guest_caching() {
688            return Err(SendError::new(
689                backend.name(),
690                self,
691                SendErrorCause::HttpCacheApiUnsupported,
692            ));
693        }
694
695        // TODO 2024-07-31: this now forces request headers to be copied an extra
696        // time, while it used to be ~free. Once async fn support is added and
697        // pending request select is no longer present, we should be able to drop
698        // this forced clone and let users clone if needed.
699        let cloned = self.clone_without_body();
700
701        match validate_request(&self).and_then(|_| {
702            let (req_handle, body_handle) = self.into_handles();
703            let body_handle = body_handle.unwrap_or_else(BodyHandle::new);
704            req_handle.send_async_streaming(body_handle, backend.name())
705        }) {
706            Ok((streaming_body_handle, pending_req_handle)) => {
707                let pending_req = PendingRequest::new(pending_req_handle, backend, cloned);
708                Ok((streaming_body_handle.into(), pending_req))
709            }
710            Err(err) => Err(SendError::new(backend.name(), cloned, err)),
711        }
712    }
713
714    /// Builder-style equivalent of [`set_body()`][`Self::set_body()`].
715    pub fn with_body(mut self, body: impl Into<Body>) -> Self {
716        self.set_body(body);
717        self
718    }
719
720    /// Returns `true` if this request has a body.
721    pub fn has_body(&self) -> bool {
722        self.body.is_some()
723    }
724
725    /// Get a mutable reference to the body of this request.
726    ///
727    #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
728    ///
729    /// # Examples
730    ///
731    /// ```no_run
732    /// # use fastly::Request;
733    /// use std::io::Write;
734    ///
735    /// let mut req = Request::post("https://example.com").with_body("hello,");
736    /// write!(req.get_body_mut(), " world!").unwrap();
737    /// assert_eq!(&req.into_body_str(), "hello, world!");
738    /// ```
739    pub fn get_body_mut(&mut self) -> &mut Body {
740        self.body.get_or_insert_with(|| Body::new())
741    }
742
743    /// Get a shared reference to the body of this request if it has one, otherwise return `None`.
744    ///
745    /// # Examples
746    ///
747    /// ```no_run
748    /// # use fastly::Request;
749    /// use std::io::Write;
750    ///
751    /// let mut req = Request::post("https://example.com");
752    /// assert!(req.try_get_body_mut().is_none());
753    ///
754    /// req.set_body("hello,");
755    /// write!(req.try_get_body_mut().expect("body now exists"), " world!").unwrap();
756    /// assert_eq!(&req.into_body_str(), "hello, world!");
757    /// ```
758    pub fn try_get_body_mut(&mut self) -> Option<&mut Body> {
759        self.body.as_mut()
760    }
761
762    /// Get a prefix of this request's body containing up to the given number of bytes.
763    ///
764    /// See [`Body::get_prefix_mut()`] for details.
765    pub fn get_body_prefix_mut(&mut self, length: usize) -> body::Prefix {
766        self.get_body_mut().get_prefix_mut(length)
767    }
768
769    /// Get a prefix of this request's body as a string containing up to the given number of bytes.
770    ///
771    /// See [`Body::get_prefix_str_mut()`] for details.
772    ///
773    /// # Panics
774    ///
775    /// If the prefix contains invalid UTF-8 bytes, this function will panic. The exception to this
776    /// is if the bytes are invalid because a multi-byte codepoint is cut off by the requested
777    /// prefix length. In this case, the invalid bytes are left off the end of the prefix.
778    ///
779    /// To explicitly handle the possibility of invalid UTF-8 bytes, use
780    /// [`try_get_body_prefix_str_mut()`][`Self::try_get_body_prefix_str_mut()`], which returns an
781    /// error on failure rather than panicking.
782    pub fn get_body_prefix_str_mut(&mut self, length: usize) -> body::PrefixString {
783        self.get_body_mut().get_prefix_str_mut(length)
784    }
785
786    /// Try to get a prefix of the body as a string containing up to the given number of bytes.
787    ///
788    /// See [`Body::try_get_prefix_str_mut()`] for details.
789    pub fn try_get_body_prefix_str_mut(
790        &mut self,
791        length: usize,
792    ) -> Result<body::PrefixString, std::str::Utf8Error> {
793        self.get_body_mut().try_get_prefix_str_mut(length)
794    }
795
796    /// Set the given value as the request's body.
797    #[doc = include_str!("../../docs/snippets/body-argument.md")]
798    #[doc = include_str!("../../docs/snippets/discards-body.md")]
799    pub fn set_body(&mut self, body: impl Into<Body>) {
800        self.body = Some(body.into());
801    }
802
803    /// Take and return the body from this request.
804    ///
805    /// After calling this method, this request will no longer have a body.
806    ///
807    #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
808    pub fn take_body(&mut self) -> Body {
809        self.body.take().unwrap_or_else(|| Body::new())
810    }
811
812    /// Take and return the body from this request if it has one, otherwise return `None`.
813    ///
814    /// After calling this method, this request will no longer have a body.
815    pub fn try_take_body(&mut self) -> Option<Body> {
816        self.body.take()
817    }
818
819    fn take_body_handle(&mut self) -> BodyHandle {
820        self.take_body().into_handle()
821    }
822
823    /// Append another [`Body`] to the body of this request without reading or writing any body
824    /// contents.
825    ///
826    /// If this request does not have a body, the appended body is set as the request's body.
827    ///
828    #[doc = include_str!("../../docs/snippets/body-append-constant-time.md")]
829    ///
830    /// This method should be used when combining bodies that have not necessarily been read yet,
831    /// such as the body of the client. To append contents that are already in memory as strings or
832    /// bytes, you should instead use [`get_body_mut()`][`Self::get_body_mut()`] to write the
833    /// contents to the end of the body.
834    ///
835    /// # Examples
836    ///
837    /// ```no_run
838    /// # use fastly::Request;
839    /// let mut req = Request::post("https://example.com").with_body("hello! client says: ");
840    /// req.append_body(Request::from_client().into_body());
841    /// req.send("example_backend").unwrap();
842    /// ```
843    pub fn append_body(&mut self, other: Body) {
844        if let Some(ref mut body) = &mut self.body {
845            body.append(other);
846        } else {
847            self.body = Some(other);
848        }
849    }
850
851    /// Consume the request and return its body as a byte vector.
852    ///
853    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
854    ///
855    /// # Examples
856    ///
857    /// ```no_run
858    /// # use fastly::Request;
859    /// let req = Request::post("https://example.com").with_body(b"hello, world!".to_vec());
860    /// let bytes = req.into_body_bytes();
861    /// assert_eq!(&bytes, b"hello, world!");
862    pub fn into_body_bytes(mut self) -> Vec<u8> {
863        self.take_body_bytes()
864    }
865
866    /// Consume the request and return its body as a string.
867    ///
868    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
869    ///
870    /// # Panics
871    ///
872    #[doc = include_str!("../../docs/snippets/panics-reqresp-intobody-utf8.md")]
873    ///
874    /// # Examples
875    ///
876    /// ```no_run
877    /// # use fastly::Request;
878    /// let req = Request::post("https://example.com").with_body("hello, world!");
879    /// let string = req.into_body_str();
880    /// assert_eq!(&string, "hello, world!");
881    /// ```
882    pub fn into_body_str(mut self) -> String {
883        self.take_body_str()
884    }
885
886    /// Consume the request and return its body as a string, including invalid characters.
887    ///
888    #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
889    ///
890    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
891    ///
892    /// # Examples
893    ///
894    /// ```no_run
895    /// # use fastly::Request;
896    /// let mut req = Request::post("https://example.com");
897    /// req.set_body_octet_stream(b"\xF0\x90\x80 hello, world!");
898    /// let string = req.into_body_str_lossy();
899    /// assert_eq!(&string, "� hello, world!");
900    /// ```
901    pub fn into_body_str_lossy(mut self) -> String {
902        self.take_body_str_lossy()
903    }
904
905    /// Consume the request and return its body.
906    ///
907    #[doc = include_str!("../../docs/snippets/creates-empty-body.md")]
908    pub fn into_body(self) -> Body {
909        self.body.unwrap_or_else(|| Body::new())
910    }
911
912    /// Consume the request and return its body if it has one, otherwise return `None`.
913    pub fn try_into_body(self) -> Option<Body> {
914        self.body
915    }
916
917    /// Builder-style equivalent of [`set_body_text_plain()`][`Self::set_body_text_plain()`].
918    pub fn with_body_text_plain(mut self, body: &str) -> Self {
919        self.set_body_text_plain(body);
920        self
921    }
922
923    /// Set the given string as the request's body with content type `text/plain; charset=UTF-8`.
924    ///
925    #[doc = include_str!("../../docs/snippets/discards-body.md")]
926    #[doc = include_str!("../../docs/snippets/sets-text-plain.md")]
927    ///
928    /// # Examples
929    ///
930    /// ```no_run
931    /// # use fastly::Request;
932    /// let mut req = Request::post("https://example.com");
933    /// req.set_body_text_plain("hello, world!");
934    /// assert_eq!(req.get_content_type(), Some(fastly::mime::TEXT_PLAIN_UTF_8));
935    /// assert_eq!(&req.into_body_str(), "hello, world!");
936    /// ```
937    pub fn set_body_text_plain(&mut self, body: &str) {
938        self.body = Some(Body::from(body));
939        self.set_content_type(mime::TEXT_PLAIN_UTF_8);
940    }
941
942    /// Builder-style equivalent of [`set_body_text_html()`][`Self::set_body_text_html()`].
943    pub fn with_body_text_html(mut self, body: &str) -> Self {
944        self.set_body_text_html(body);
945        self
946    }
947
948    /// Set the given string as the request's body with content type `text/html; charset=UTF-8`.
949    ///
950    #[doc = include_str!("../../docs/snippets/discards-body.md")]
951    #[doc = include_str!("../../docs/snippets/sets-text-html.md")]
952    ///
953    /// # Examples
954    ///
955    /// ```no_run
956    /// # use fastly::Request;
957    /// let mut req = Request::post("https://example.com");
958    /// req.set_body_text_html("<p>hello, world!</p>");
959    /// assert_eq!(req.get_content_type(), Some(fastly::mime::TEXT_HTML_UTF_8));
960    /// assert_eq!(&req.into_body_str(), "<p>hello, world!</p>");
961    /// ```
962    pub fn set_body_text_html(&mut self, body: &str) {
963        self.body = Some(Body::from(body));
964        self.set_content_type(mime::TEXT_HTML_UTF_8);
965    }
966
967    /// Take and return the body from this request as a string.
968    ///
969    /// After calling this method, this request will no longer have a body.
970    ///
971    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
972    ///
973    /// # Panics
974    ///
975    #[doc = include_str!("../../docs/snippets/panics-reqresp-takebody-utf8.md")]
976    ///
977    /// # Examples
978    ///
979    /// ```no_run
980    /// # use fastly::Request;
981    /// let mut req = Request::post("https://example.com").with_body("hello, world!");
982    /// let string = req.take_body_str();
983    /// assert!(req.try_take_body().is_none());
984    /// assert_eq!(&string, "hello, world!");
985    /// ```
986    pub fn take_body_str(&mut self) -> String {
987        if let Some(body) = self.try_take_body() {
988            body.into_string()
989        } else {
990            String::new()
991        }
992    }
993
994    /// Take and return the body from this request as a string, including invalid characters.
995    ///
996    #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
997    ///
998    /// After calling this method, this request will no longer have a body.
999    ///
1000    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
1001    ///
1002    /// # Examples
1003    ///
1004    /// ```no_run
1005    /// # use fastly::Request;
1006    /// let mut req = Request::post("https://example.com");
1007    /// req.set_body_octet_stream(b"\xF0\x90\x80 hello, world!");
1008    /// let string = req.take_body_str_lossy();
1009    /// assert!(req.try_take_body().is_none());
1010    /// assert_eq!(&string, "� hello, world!");
1011    /// ```
1012    pub fn take_body_str_lossy(&mut self) -> String {
1013        if let Some(body) = self.try_take_body() {
1014            String::from_utf8_lossy(&body.into_bytes()).to_string()
1015        } else {
1016            String::new()
1017        }
1018    }
1019
1020    /// Return a [`Lines`][`std::io::Lines`] iterator that reads the request body a line at a time.
1021    ///
1022    /// # Examples
1023    ///
1024    /// ```no_run
1025    /// # use fastly::{Body, Request};
1026    /// use std::io::Write;
1027    ///
1028    /// fn remove_es(req: &mut Request) {
1029    ///     let mut no_es = Body::new();
1030    ///     for line in req.read_body_lines() {
1031    ///         writeln!(no_es, "{}", line.unwrap().replace("e", "")).unwrap();
1032    ///     }
1033    ///     req.set_body(no_es);
1034    /// }
1035    /// ```
1036    pub fn read_body_lines(&mut self) -> std::io::Lines<&mut Body> {
1037        self.get_body_mut().lines()
1038    }
1039
1040    /// Builder-style equivalent of [`set_body_octet_stream()`][`Self::set_body_octet_stream()`].
1041    pub fn with_body_octet_stream(mut self, body: &[u8]) -> Self {
1042        self.set_body_octet_stream(body);
1043        self
1044    }
1045
1046    /// Set the given bytes as the request's body.
1047    ///
1048    #[doc = include_str!("../../docs/snippets/discards-body.md")]
1049    #[doc = include_str!("../../docs/snippets/sets-app-octet-stream.md")]
1050    ///
1051    /// # Examples
1052    ///
1053    /// ```no_run
1054    /// # use fastly::Request;
1055    /// let mut req = Request::post("https://example.com");
1056    /// req.set_body_octet_stream(b"hello, world!");
1057    /// assert_eq!(req.get_content_type(), Some(fastly::mime::APPLICATION_OCTET_STREAM));
1058    /// assert_eq!(&req.into_body_bytes(), b"hello, world!");
1059    /// ```
1060    pub fn set_body_octet_stream(&mut self, body: &[u8]) {
1061        self.body = Some(Body::from(body));
1062        self.set_content_type(mime::APPLICATION_OCTET_STREAM);
1063    }
1064
1065    /// Take and return the body from this request as a string.
1066    ///
1067    /// After calling this method, this request will no longer have a body.
1068    ///
1069    #[doc = include_str!("../../docs/snippets/buffers-body-reqresp.md")]
1070    ///
1071    /// # Examples
1072    ///
1073    /// ```no_run
1074    /// # use fastly::Request;
1075    /// let mut req = Request::post("https://example.com").with_body(b"hello, world!".to_vec());
1076    /// let bytes = req.take_body_bytes();
1077    /// assert!(req.try_take_body().is_none());
1078    /// assert_eq!(&bytes, b"hello, world!");
1079    /// ```
1080    pub fn take_body_bytes(&mut self) -> Vec<u8> {
1081        if let Some(body) = self.try_take_body() {
1082            body.into_bytes()
1083        } else {
1084            Vec::new()
1085        }
1086    }
1087
1088    /// Return an iterator that reads the request body in chunks of at most the given number of
1089    /// bytes.
1090    ///
1091    /// If `chunk_size` does not evenly divide the length of the body, then the last chunk will not
1092    /// have length `chunk_size`.
1093    ///
1094    /// # Examples
1095    ///
1096    /// ```no_run
1097    /// # use fastly::{Body, Request};
1098    /// use std::io::Write;
1099    /// fn remove_0s(req: &mut Request) {
1100    ///     let mut no_0s = Body::new();
1101    ///     for chunk in req.read_body_chunks(4096) {
1102    ///         let mut chunk = chunk.unwrap();
1103    ///         chunk.retain(|b| *b != 0);
1104    ///         no_0s.write_all(&chunk).unwrap();
1105    ///     }
1106    ///     req.set_body(no_0s);
1107    /// }
1108    /// ```
1109    pub fn read_body_chunks<'a>(
1110        &'a mut self,
1111        chunk_size: usize,
1112    ) -> impl Iterator<Item = Result<Vec<u8>, std::io::Error>> + 'a {
1113        self.get_body_mut().read_chunks(chunk_size)
1114    }
1115
1116    /// Builder-style equivalent of [`set_body_json()`][Self::set_body_json()`].
1117    pub fn with_body_json(mut self, value: &impl Serialize) -> Result<Self, serde_json::Error> {
1118        self.set_body_json(value)?;
1119        Ok(self)
1120    }
1121
1122    /// Convert the given value to JSON and set that JSON as the request's body.
1123    ///
1124    /// The given value must implement [`serde::Serialize`]. You can either implement that trait for
1125    /// your own custom type, or use [`serde_json::Value`] to create untyped JSON values. See
1126    /// [`serde_json`] for details.
1127    ///
1128    #[doc = include_str!("../../docs/snippets/discards-body.md")]
1129    ///
1130    /// # Content type
1131    ///
1132    /// This method sets the content type to `application/json`.
1133    ///
1134    /// # Errors
1135    ///
1136    /// This method returns [`serde_json::Error`] if serialization fails.
1137    ///
1138    /// # Examples
1139    ///
1140    /// Using a type that derives [`serde::Serialize`]:
1141    ///
1142    /// ```no_run
1143    /// # use fastly::Request;
1144    /// #[derive(serde::Serialize)]
1145    /// struct MyData {
1146    ///     name: String,
1147    ///     count: u64,
1148    /// }
1149    /// let my_data = MyData { name: "Computers".to_string(), count: 1024 };
1150    /// let mut req = Request::post("https://example.com");
1151    /// req.set_body_json(&my_data).unwrap();
1152    /// assert_eq!(req.get_content_type(), Some(fastly::mime::APPLICATION_JSON));
1153    /// assert_eq!(&req.into_body_str(), r#"{"name":"Computers","count":1024}"#);
1154    /// ```
1155    ///
1156    /// Using untyped JSON and the [`serde_json::json`] macro:
1157    ///
1158    /// ```no_run
1159    /// # use fastly::Request;
1160    /// let my_data = serde_json::json!({
1161    ///     "name": "Computers",
1162    ///     "count": 1024,
1163    /// });
1164    /// let mut req = Request::post("https://example.com");
1165    /// req.set_body_json(&my_data).unwrap();
1166    /// assert_eq!(req.get_content_type(), Some(fastly::mime::APPLICATION_JSON));
1167    /// assert_eq!(&req.into_body_str(), r#"{"count":1024,"name":"Computers"}"#);
1168    /// ```
1169    pub fn set_body_json(&mut self, value: &impl Serialize) -> Result<(), serde_json::Error> {
1170        self.body = Some(Body::new());
1171        serde_json::to_writer(self.get_body_mut(), value)?;
1172        self.set_content_type(mime::APPLICATION_JSON);
1173        Ok(())
1174    }
1175
1176    /// Take the request body and attempt to parse it as a JSON value.
1177    ///
1178    /// The return type must implement [`serde::Deserialize`] without any non-static lifetimes. You
1179    /// can either implement that trait for your own custom type, or use [`serde_json::Value`] to
1180    /// deserialize untyped JSON values. See [`serde_json`] for details.
1181    ///
1182    /// After calling this method, this request will no longer have a body.
1183    ///
1184    /// # Errors
1185    ///
1186    /// This method returns [`serde_json::Error`] if deserialization fails.
1187    ///
1188    /// # Examples
1189    ///
1190    /// Using a type that derives [`serde::de::DeserializeOwned`]:
1191    ///
1192    /// ```no_run
1193    /// # use fastly::Request;
1194    /// #[derive(serde::Deserialize)]
1195    /// struct MyData {
1196    ///     name: String,
1197    ///     count: u64,
1198    /// }
1199    /// let mut req = Request::post("https://example.com")
1200    ///     .with_body(r#"{"name":"Computers","count":1024}"#);
1201    /// let my_data = req.take_body_json::<MyData>().unwrap();
1202    /// assert_eq!(&my_data.name, "Computers");
1203    /// assert_eq!(my_data.count, 1024);
1204    /// ```
1205    ///
1206    /// Using untyped JSON with [`serde_json::Value`]:
1207    ///
1208    /// ```no_run
1209    /// # use fastly::Request;
1210    /// let my_data = serde_json::json!({
1211    ///     "name": "Computers",
1212    ///     "count": 1024,
1213    /// });
1214    /// let mut req = Request::post("https://example.com")
1215    ///     .with_body(r#"{"name":"Computers","count":1024}"#);
1216    /// let my_data = req.take_body_json::<serde_json::Value>().unwrap();
1217    /// assert_eq!(my_data["name"].as_str(), Some("Computers"));
1218    /// assert_eq!(my_data["count"].as_u64(), Some(1024));
1219    /// ```
1220    pub fn take_body_json<T: DeserializeOwned>(&mut self) -> Result<T, serde_json::Error> {
1221        if let Some(body) = self.try_take_body() {
1222            serde_json::from_reader(body)
1223        } else {
1224            serde_json::from_reader(std::io::empty())
1225        }
1226    }
1227
1228    /// Builder-style equivalent of [`set_body_form()`][`Self::set_body_form()`].
1229    pub fn with_body_form(
1230        mut self,
1231        value: &impl Serialize,
1232    ) -> Result<Self, serde_urlencoded::ser::Error> {
1233        self.set_body_form(value)?;
1234        Ok(self)
1235    }
1236
1237    /// Convert the given value to `application/x-www-form-urlencoded` format and set that data as
1238    /// the request's body.
1239    ///
1240    /// The given value must implement [`serde::Serialize`]; see the trait documentation for
1241    /// details.
1242    ///
1243    #[doc = include_str!("../../docs/snippets/discards-body.md")]
1244    ///
1245    /// # Content type
1246    ///
1247    /// This method sets the content type to `application/x-www-form-urlencoded`.
1248    ///
1249    /// # Errors
1250    ///
1251    /// This method returns [`serde_urlencoded::ser::Error`] if serialization fails.
1252    ///
1253    /// # Examples
1254    ///
1255    /// ```no_run
1256    /// # use fastly::Request;
1257    /// #[derive(serde::Serialize)]
1258    /// struct MyData {
1259    ///     name: String,
1260    ///     count: u64,
1261    /// }
1262    /// let my_data = MyData { name: "Computers".to_string(), count: 1024 };
1263    /// let mut req = Request::post("https://example.com");
1264    /// req.set_body_form(&my_data).unwrap();
1265    /// assert_eq!(req.get_content_type(), Some(fastly::mime::APPLICATION_WWW_FORM_URLENCODED));
1266    /// assert_eq!(&req.into_body_str(), "name=Computers&count=1024");
1267    /// ```
1268    pub fn set_body_form(
1269        &mut self,
1270        value: &impl Serialize,
1271    ) -> Result<(), serde_urlencoded::ser::Error> {
1272        self.body = Some(Body::new());
1273        let s = serde_urlencoded::to_string(value)?;
1274        self.set_body(s);
1275        self.set_content_type(mime::APPLICATION_WWW_FORM_URLENCODED);
1276        Ok(())
1277    }
1278
1279    /// Take the request body and attempt to parse it as a `application/x-www-form-urlencoded`
1280    /// formatted string.
1281    ///
1282    #[doc = include_str!("../../docs/snippets/returns-deserializeowned.md")]
1283    ///
1284    /// After calling this method, this request will no longer have a body.
1285    ///
1286    /// # Errors
1287    ///
1288    /// This method returns [`serde_urlencoded::de::Error`] if deserialization fails.
1289    ///
1290    /// # Examples
1291    ///
1292    /// ```no_run
1293    /// # use fastly::Request;
1294    /// #[derive(serde::Deserialize)]
1295    /// struct MyData {
1296    ///     name: String,
1297    ///     count: u64,
1298    /// }
1299    /// let mut req = Request::post("https://example.com").with_body("name=Computers&count=1024");
1300    /// let my_data = req.take_body_form::<MyData>().unwrap();
1301    /// assert_eq!(&my_data.name, "Computers");
1302    /// assert_eq!(my_data.count, 1024);
1303    /// ```
1304    pub fn take_body_form<T: DeserializeOwned>(
1305        &mut self,
1306    ) -> Result<T, serde_urlencoded::de::Error> {
1307        if let Some(body) = self.try_take_body() {
1308            serde_urlencoded::from_reader(body)
1309        } else {
1310            serde_urlencoded::from_reader(std::io::empty())
1311        }
1312    }
1313
1314    /// Get the MIME type described by the request's
1315    /// [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
1316    /// header, or `None` if that header is absent or contains an invalid MIME type.
1317    ///
1318    /// # Examples
1319    ///
1320    /// ```no_run
1321    /// # use fastly::Request;
1322    /// let req = Request::post("https://example.com").with_body_text_plain("hello, world!");
1323    /// assert_eq!(req.get_content_type(), Some(fastly::mime::TEXT_PLAIN_UTF_8));
1324    /// ```
1325    pub fn get_content_type(&self) -> Option<Mime> {
1326        self.get_header_str(http::header::CONTENT_TYPE).map(|v| {
1327            v.parse()
1328                .unwrap_or_else(|_| panic!("invalid MIME type in Content-Type header: {}", v))
1329        })
1330    }
1331
1332    /// Builder-style equivalent of [`set_content_type()`][`Self::set_content_type()`].
1333    pub fn with_content_type(mut self, mime: Mime) -> Self {
1334        self.set_content_type(mime);
1335        self
1336    }
1337
1338    /// Set the MIME type described by the request's
1339    /// [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
1340    /// header.
1341    ///
1342    /// Any existing `Content-Type` header values will be overwritten.
1343    ///
1344    /// # Examples
1345    ///
1346    /// ```no_run
1347    /// # use fastly::Request;
1348    /// let mut req = Request::post("https://example.com").with_body("hello,world!");
1349    /// req.set_content_type(fastly::mime::TEXT_CSV_UTF_8);
1350    /// ```
1351    pub fn set_content_type(&mut self, mime: Mime) {
1352        self.set_header(http::header::CONTENT_TYPE, mime.as_ref())
1353    }
1354
1355    /// Get the value of the request's
1356    /// [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
1357    /// header, if it exists.
1358    pub fn get_content_length(&self) -> Option<usize> {
1359        self.get_header(http::header::CONTENT_LENGTH)
1360            .and_then(|v| v.to_str().ok())
1361            .and_then(|v| v.parse().ok())
1362    }
1363
1364    /// Returns whether the given header name is present in the request.
1365    ///
1366    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1367    ///
1368    /// # Examples
1369    ///
1370    /// ```no_run
1371    /// # use fastly::Request;
1372    /// let req = Request::get("https://example.com").with_header("hello", "world!");
1373    /// assert!(req.contains_header("hello"));
1374    /// assert!(!req.contains_header("not-present"));
1375    /// ```
1376    pub fn contains_header(&self, name: impl ToHeaderName) -> bool {
1377        !self.lazy_handle.get_header_values(name).is_empty()
1378    }
1379
1380    /// Builder-style equivalent of [`append_header()`][`Self::append_header()`].
1381    pub fn with_header(mut self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
1382        self.append_header(name, value);
1383        self
1384    }
1385
1386    /// Builder-style equivalent of [`set_header()`][`Self::set_header()`].
1387    pub fn with_set_header(mut self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
1388        self.set_header(name, value);
1389        self
1390    }
1391
1392    /// Get the value of a header as a string, or `None` if the header is not present.
1393    ///
1394    /// If there are multiple values for the header, only one is returned, which may be any of the
1395    /// values. See [`get_header_all_str()`][`Self::get_header_all_str()`] if you need to get all of
1396    /// the values.
1397    ///
1398    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1399    ///
1400    /// # Panics
1401    ///
1402    #[doc = include_str!("../../docs/snippets/panics-reqresp-header-utf8.md")]
1403    ///
1404    /// # Examples
1405    ///
1406    /// ```no_run
1407    /// # use fastly::Request;
1408    /// let req = Request::get("https://example.com").with_header("hello", "world!");
1409    /// assert_eq!(req.get_header_str("hello"), Some("world"));
1410    /// ```
1411    pub fn get_header_str(&self, name: impl ToHeaderName) -> Option<&str> {
1412        let name = name.into_borrowable();
1413        if let Some(hdr) = self.get_header(name.as_ref()) {
1414            Some(
1415                std::str::from_utf8(hdr.as_bytes())
1416                    .unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name)),
1417            )
1418        } else {
1419            None
1420        }
1421    }
1422
1423    /// Get the value of a header as a string, including invalid characters, or `None` if the header
1424    /// is not present.
1425    ///
1426    #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1427    ///
1428    /// If there are multiple values for the header, only one is returned, which may be any of the
1429    /// values. See [`get_header_all_str_lossy()`][`Self::get_header_all_str_lossy()`] if you need
1430    /// to get all of the values.
1431    ///
1432    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1433    ///
1434    /// # Examples
1435    ///
1436    /// ```no_run
1437    /// # use fastly::Request;
1438    /// # use http::header::HeaderValue;
1439    /// # use std::borrow::Cow;
1440    /// let header_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1441    /// let req = Request::get("https://example.com").with_header("hello", header_value);
1442    /// assert_eq!(req.get_header_str_lossy("hello"), Some(Cow::from("� world")));
1443    /// ```
1444    pub fn get_header_str_lossy(&self, name: impl ToHeaderName) -> Option<Cow<'_, str>> {
1445        self.get_header(name)
1446            .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()))
1447    }
1448
1449    /// Get the value of a header, or `None` if the header is not present.
1450    ///
1451    /// If there are multiple values for the header, only one is returned, which may be any of the
1452    /// values. See [`get_header_all()`][`Self::get_header_all()`] if you need to get all of the
1453    /// values.
1454    ///
1455    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1456    ///
1457    /// # Examples
1458    ///
1459    /// Handling UTF-8 values explicitly:
1460    ///
1461    /// ```no_run
1462    /// # use fastly::Request;
1463    /// # use fastly::http::HeaderValue;
1464    /// let req = Request::get("https://example.com").with_header("hello", "world!");
1465    /// assert_eq!(req.get_header("hello"), Some(&HeaderValue::from_static("world")));
1466    /// ```
1467    ///
1468    /// Safely handling invalid UTF-8 values:
1469    ///
1470    /// ```no_run
1471    /// # use fastly::Request;
1472    /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1473    /// let req = Request::get("https://example.com").with_header("hello", invalid_utf8);
1474    /// assert_eq!(req.get_header("hello").unwrap().as_bytes(), invalid_utf8);
1475    /// ```
1476    pub fn get_header(&self, name: impl ToHeaderName) -> Option<&HeaderValue> {
1477        self.lazy_handle.get_header_values(name).first()
1478    }
1479
1480    /// Get all values of a header as strings, or an empty vector if the header is not present.
1481    ///
1482    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1483    ///
1484    /// # Panics
1485    ///
1486    #[doc = include_str!("../../docs/snippets/panics-reqresp-headers-utf8.md")]
1487    ///
1488    /// # Examples
1489    ///
1490    /// ```no_run
1491    /// # use fastly::Request;
1492    /// let req = Request::get("https://example.com")
1493    ///     .with_header("hello", "world!")
1494    ///     .with_header("hello", "universe!");
1495    /// let values = req.get_header_all_str("hello");
1496    /// assert_eq!(values.len(), 2);
1497    /// assert!(values.contains(&"world!"));
1498    /// assert!(values.contains(&"universe!"));
1499    /// ```
1500    pub fn get_header_all_str(&self, name: impl ToHeaderName) -> Vec<&str> {
1501        let name = name.into_borrowable();
1502        self.get_header_all(name.as_ref())
1503            .map(|v| {
1504                std::str::from_utf8(v.as_bytes())
1505                    .unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name))
1506            })
1507            .collect()
1508    }
1509
1510    /// Get all values of a header as strings, including invalid characters, or an empty vector if the header is not present.
1511    ///
1512    #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1513    ///
1514    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1515    ///
1516    /// # Examples
1517    ///
1518    /// ```no_run
1519    /// # use std::borrow::Cow;
1520    /// # use http::header::HeaderValue;
1521    /// # use fastly::Request;
1522    /// let world_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1523    /// let universe_value = HeaderValue::from_bytes(b"\xF0\x90\x80 universe!").unwrap();
1524    /// let req = Request::get("https://example.com")
1525    ///     .with_header("hello", world_value)
1526    ///     .with_header("hello", universe_value);
1527    /// let values = req.get_header_all_str_lossy("hello");
1528    /// assert_eq!(values.len(), 2);
1529    /// assert!(values.contains(&Cow::from("� world!")));
1530    /// assert!(values.contains(&Cow::from("� universe!")));
1531    /// ```
1532    pub fn get_header_all_str_lossy(&self, name: impl ToHeaderName) -> Vec<Cow<'_, str>> {
1533        self.get_header_all(name)
1534            .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()))
1535            .collect()
1536    }
1537
1538    /// Get an iterator of all the values of a header.
1539    ///
1540    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1541    ///
1542    /// # Examples
1543    ///
1544    /// You can turn the iterator into collection, like [`Vec`]:
1545    ///
1546    /// ```no_run
1547    /// # use fastly::Request;
1548    /// # use fastly::http::HeaderValue;
1549    /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1550    /// let req = Request::get("https://example.com")
1551    ///     .with_header("hello", "world!")
1552    ///     .with_header("hello", invalid_utf8);
1553    ///
1554    /// let values: Vec<&HeaderValue> = req.get_header_all("hello").collect();
1555    /// assert_eq!(values.len(), 2);
1556    /// assert!(values.contains(&&HeaderValue::from_static("world!")));
1557    /// assert!(values.contains(&&HeaderValue::from_bytes(invalid_utf8).unwrap()));
1558    /// ```
1559    ///
1560    /// You can use the iterator in a loop:
1561    ///
1562    /// ```no_run
1563    /// # use fastly::Request;
1564    /// let invalid_utf8 = &"🐈".as_bytes()[0..3];
1565    /// let req = Request::get("https://example.com")
1566    ///     .with_header("hello", "world!")
1567    ///     .with_header("hello", invalid_utf8);
1568    ///
1569    /// for value in req.get_header_all("hello") {
1570    ///     if let Ok(s) = std::str::from_utf8(value.as_bytes()) {
1571    ///         println!("hello, {}", s);
1572    ///     } else {
1573    ///         println!("hello, invalid UTF-8!");
1574    ///     }
1575    /// }
1576    /// ```
1577    pub fn get_header_all(&self, name: impl ToHeaderName) -> impl Iterator<Item = &HeaderValue> {
1578        self.lazy_handle.get_header_values(name).into_iter()
1579    }
1580
1581    /// Get an iterator of all the request's header names and values.
1582    ///
1583    /// # Examples
1584    ///
1585    /// You can turn the iterator into a collection, like [`Vec`]:
1586    ///
1587    /// ```no_run
1588    /// # use fastly::Request;
1589    /// # use fastly::http::header::{HeaderName, HeaderValue};
1590    /// let req = Request::get("https://example.com")
1591    ///     .with_header("hello", "world!")
1592    ///     .with_header("hello", "universe!");
1593    ///
1594    /// let headers: Vec<(&HeaderName, &HeaderValue)> = req.get_headers().collect();
1595    /// assert_eq!(headers.len(), 2);
1596    /// assert!(headers.contains(&(&HeaderName::from_static("hello"), &HeaderValue::from_static("world!"))));
1597    /// assert!(headers.contains(&(&HeaderName::from_static("hello"), &HeaderValue::from_static("universe!"))));
1598    /// ```
1599    ///
1600    /// You can use the iterator in a loop:
1601    ///
1602    /// ```no_run
1603    /// # use fastly::Request;
1604    /// let req = Request::get("https://example.com")
1605    ///     .with_header("hello", "world!")
1606    ///     .with_header("hello", "universe!");
1607    ///
1608    /// for (n, v) in req.get_headers() {
1609    ///     println!("Header -  {}: {:?}", n, v);
1610    /// }
1611    /// ```
1612    pub fn get_headers(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
1613        self.lazy_handle.iter()
1614    }
1615
1616    /// Get all of the request's header names as strings, or an empty vector if no headers are
1617    /// present.
1618    ///
1619    /// # Examples
1620    ///
1621    /// ```no_run
1622    /// # use fastly::Request;
1623    /// let req = Request::get("https://example.com")
1624    ///     .with_header("hello", "world!")
1625    ///     .with_header("goodbye", "latency!");
1626    /// let names = req.get_header_names_str();
1627    /// assert_eq!(names.len(), 2);
1628    /// assert!(names.contains(&"hello"));
1629    /// assert!(names.contains(&"goodbye"));
1630    /// ```
1631    pub fn get_header_names_str(&self) -> Vec<&str> {
1632        self.get_header_names().map(|n| n.as_str()).collect()
1633    }
1634
1635    /// Get an iterator of all the request's header names.
1636    ///
1637    /// # Examples
1638    ///
1639    /// You can turn the iterator into collection, like [`Vec`]:
1640    ///
1641    /// ```no_run
1642    /// # use fastly::Request;
1643    /// # use fastly::http::header::HeaderName;
1644    /// let req = Request::get("https://example.com")
1645    ///     .with_header("hello", "world!")
1646    ///     .with_header("goodbye", "latency!");
1647    ///
1648    /// let values: Vec<&HeaderName> = req.get_header_names().collect();
1649    /// assert_eq!(values.len(), 2);
1650    /// assert!(values.contains(&&HeaderName::from_static("hello")));
1651    /// assert!(values.contains(&&HeaderName::from_static("goodbye")));
1652    /// ```
1653    ///
1654    /// You can use the iterator in a loop:
1655    ///
1656    /// ```no_run
1657    /// # use fastly::Request;
1658    /// let req = Request::get("https://example.com")
1659    ///     .with_header("hello", "world!")
1660    ///     .with_header("goodbye", "latency!");
1661    ///
1662    /// for name in req.get_header_names() {
1663    ///     println!("saw header: {:?}", name);
1664    /// }
1665    /// ```
1666    pub fn get_header_names(&self) -> impl Iterator<Item = &HeaderName> {
1667        self.lazy_handle.get_header_names()
1668    }
1669
1670    /// Set a request header to the given value, discarding any previous values for the given
1671    /// header name.
1672    ///
1673    #[doc = include_str!("../../docs/snippets/header-name-value-argument.md")]
1674    ///
1675    /// # Examples
1676    ///
1677    /// ```no_run
1678    /// # use fastly::Request;
1679    /// let mut req = Request::get("https://example.com");
1680    ///
1681    /// req.set_header("hello", "world!");
1682    /// assert_eq!(req.get_header_str("hello"), Some("world!"));
1683    ///
1684    /// req.set_header("hello", "universe!");
1685    ///
1686    /// let values = req.get_header_all_str("hello");
1687    /// assert_eq!(values.len(), 1);
1688    /// assert!(!values.contains(&"world!"));
1689    /// assert!(values.contains(&"universe!"));
1690    /// ```
1691    pub fn set_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
1692        self.lazy_handle
1693            .set_header(name.into_owned(), value.into_owned());
1694    }
1695
1696    /// Add a request header with given value.
1697    ///
1698    /// Unlike [`set_header()`][`Self::set_header()`], this does not discard existing values for the
1699    /// same header name.
1700    ///
1701    #[doc = include_str!("../../docs/snippets/header-name-value-argument.md")]
1702    ///
1703    /// # Examples
1704    ///
1705    /// ```no_run
1706    /// # use fastly::Request;
1707    /// let mut req = Request::get("https://example.com");
1708    ///
1709    /// req.set_header("hello", "world!");
1710    /// assert_eq!(req.get_header_str("hello"), Some("world!"));
1711    ///
1712    /// req.append_header("hello", "universe!");
1713    ///
1714    /// let values = req.get_header_all_str("hello");
1715    /// assert_eq!(values.len(), 2);
1716    /// assert!(values.contains(&"world!"));
1717    /// assert!(values.contains(&"universe!"));
1718    /// ```
1719    pub fn append_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
1720        self.lazy_handle
1721            .append_header_value(name.into_borrowable().as_ref(), value);
1722    }
1723
1724    /// Remove all request headers of the given name, and return one of the removed header values
1725    /// if any were present.
1726    ///
1727    #[doc = include_str!("../../docs/snippets/removes-one-header.md")]
1728    ///
1729    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1730    ///
1731    /// # Examples
1732    ///
1733    /// ```no_run
1734    /// # use fastly::Request;
1735    /// # use fastly::http::HeaderValue;
1736    /// let mut req = Request::get("https://example.com").with_header("hello", "world!");
1737    /// assert_eq!(req.get_header_str("hello"), Some("world!"));
1738    /// assert_eq!(req.remove_header("hello"), Some(HeaderValue::from_static("world!")));
1739    /// assert!(req.remove_header("not-present").is_none());
1740    /// ```
1741    pub fn remove_header(&mut self, name: impl ToHeaderName) -> Option<HeaderValue> {
1742        self.lazy_handle
1743            .remove_header(name.into_borrowable().as_ref())
1744    }
1745
1746    /// Remove all request headers of the given name, and return one of the removed header values as
1747    /// a string if any were present.
1748    ///
1749    #[doc = include_str!("../../docs/snippets/removes-one-header.md")]
1750    ///
1751    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1752    ///
1753    /// # Panics
1754    ///
1755    #[doc = include_str!("../../docs/snippets/panics-reqresp-remove-header-utf8.md")]
1756    ///
1757    /// # Examples
1758    ///
1759    /// ```no_run
1760    /// # use fastly::Request;
1761    /// let mut req = Request::get("https://example.com").with_header("hello", "world!");
1762    /// assert_eq!(req.get_header_str("hello"), Some("world!"));
1763    /// assert_eq!(req.remove_header_str("hello"), Some("world!".to_string()));
1764    /// assert!(req.remove_header_str("not-present").is_none());
1765    /// ```
1766    pub fn remove_header_str(&mut self, name: impl ToHeaderName) -> Option<String> {
1767        let name = name.into_borrowable();
1768        if let Some(hdr) = self.remove_header(name.as_ref()) {
1769            Some(
1770                std::str::from_utf8(hdr.as_bytes())
1771                    .map(|s| s.to_owned())
1772                    .unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name)),
1773            )
1774        } else {
1775            None
1776        }
1777    }
1778
1779    /// Remove all request headers of the given name, and return one of the removed header values
1780    /// as a string, including invalid characters, if any were present.
1781    ///
1782    #[doc = include_str!("../../docs/snippets/utf8-replacement.md")]
1783    ///
1784    #[doc = include_str!("../../docs/snippets/removes-one-header.md")]
1785    ///
1786    #[doc = include_str!("../../docs/snippets/header-name-argument.md")]
1787    ///
1788    /// # Examples
1789    ///
1790    /// ```no_run
1791    /// # use fastly::Request;
1792    /// # use http::header::HeaderValue;
1793    /// # use std::borrow::Cow;
1794    /// let header_value = HeaderValue::from_bytes(b"\xF0\x90\x80 world!").unwrap();
1795    /// let mut req = Request::get("https://example.com")
1796    ///     .with_header("hello", header_value);
1797    /// assert_eq!(req.get_header_str_lossy("hello"), Some(Cow::from("� world")));
1798    /// assert_eq!(req.remove_header_str_lossy("hello"), Some(String::from("� world")));
1799    /// assert!(req.remove_header_str_lossy("not-present").is_none());
1800    /// ```
1801    pub fn remove_header_str_lossy(&mut self, name: impl ToHeaderName) -> Option<String> {
1802        self.remove_header(name)
1803            .map(|hdr| String::from_utf8_lossy(hdr.as_bytes()).into_owned())
1804    }
1805
1806    /// Builder-style equivalent of [`set_method()`][`Self::set_method()`].
1807    pub fn with_method(mut self, method: impl ToMethod) -> Self {
1808        self.set_method(method);
1809        self
1810    }
1811
1812    /// Get the request method as a string.
1813    ///
1814    /// # Examples
1815    ///
1816    /// ```no_run
1817    /// # use fastly::Request;
1818    /// let req = Request::get("https://example.com");
1819    /// assert_eq!(req.get_method_str(), "GET");
1820    pub fn get_method_str(&self) -> &str {
1821        self.get_method().as_str()
1822    }
1823
1824    /// Get the request method.
1825    ///
1826    /// # Examples
1827    ///
1828    /// ```no_run
1829    /// # use fastly::Request;
1830    /// use fastly::http::Method;
1831    /// fn log_method(req: &Request) {
1832    ///     match req.get_method() {
1833    ///         &Method::GET | &Method::HEAD => println!("method was a GET or HEAD"),
1834    ///         &Method::POST => println!("method was a POST"),
1835    ///         _ => println!("method was something else"),
1836    ///     }
1837    /// }
1838    pub fn get_method(&self) -> &Method {
1839        self.lazy_handle.get_field::<Method>()
1840    }
1841
1842    /// Set the request method.
1843    ///
1844    #[doc = include_str!("../../docs/snippets/method-argument.md")]
1845    ///
1846    /// # Examples
1847    ///
1848    /// ```no_run
1849    /// # use fastly::Request;
1850    /// use fastly::http::Method;
1851    ///
1852    /// let mut req = Request::get("https://example.com");
1853    /// req.set_method(Method::POST);
1854    /// assert_eq!(req.get_method(), &Method::POST);
1855    /// ```
1856    pub fn set_method<'a>(&mut self, method: impl ToMethod) {
1857        self.lazy_handle.put_field::<Method>(method.into_owned());
1858    }
1859
1860    /// Builder-style equivalent of [`set_url()`][`Self::set_url()`].
1861    pub fn with_url(mut self, url: impl ToUrl) -> Self {
1862        self.set_url(url);
1863        self
1864    }
1865
1866    /// Get the request URL as a string.
1867    ///
1868    /// # Examples
1869    ///
1870    /// ```no_run
1871    /// # use fastly::Request;
1872    /// let req = Request::get("https://example.com");
1873    /// assert_eq!(req.get_url_str(), "https://example.com");
1874    /// ```
1875    pub fn get_url_str(&self) -> &str {
1876        self.get_url().as_str()
1877    }
1878
1879    /// Get a shared reference to the request URL.
1880    ///
1881    /// # Examples
1882    ///
1883    /// ```no_run
1884    /// # use fastly::Request;
1885    /// let req = Request::get("https://example.com/hello#world");
1886    /// let url = req.get_url();
1887    /// assert_eq!(url.host_str(), Some("example.com"));
1888    /// assert_eq!(url.path(), "/hello");
1889    /// assert_eq!(url.fragment(), Some("world"));
1890    /// ```
1891    pub fn get_url(&self) -> &Url {
1892        self.lazy_handle.get_field::<Url>()
1893    }
1894
1895    /// Get a mutable reference to the request URL.
1896    ///
1897    /// # Examples
1898    ///
1899    /// ```no_run
1900    /// # use fastly::Request;
1901    /// let mut req = Request::get("https://example.com/");
1902    ///
1903    /// let mut url = req.get_url_mut();
1904    /// url.set_path("/hello");
1905    /// url.set_fragment(Some("world"));
1906    /// drop(url);
1907    ///
1908    /// assert_eq!(req.get_url_str(), "https://example.com/hello#world");
1909    /// ```
1910    pub fn get_url_mut(&mut self) -> impl std::ops::DerefMut<Target = Url> + '_ {
1911        self.lazy_handle.get_field_mut::<Url>()
1912    }
1913
1914    /// Set the request URL.
1915    ///
1916    #[doc = include_str!("../../docs/snippets/url-argument.md")]
1917    pub fn set_url(&mut self, url: impl ToUrl) {
1918        self.lazy_handle.put_field::<Url>(url.into_owned());
1919    }
1920
1921    /// Get the path component of the request URL.
1922    ///
1923    /// # Examples
1924    ///
1925    /// ```no_run
1926    /// # use fastly::Request;
1927    /// let req = Request::get("https://example.com/hello#world");
1928    /// assert_eq!(req.get_path(), "/hello");
1929    /// ```
1930    pub fn get_path(&self) -> &str {
1931        self.get_url().path()
1932    }
1933
1934    /// Builder-style equivalent of [`set_path()`][`Self::set_path()`].
1935    pub fn with_path(mut self, path: &str) -> Self {
1936        self.set_path(path);
1937        self
1938    }
1939
1940    /// Set the path component of the request URL.
1941    ///
1942    /// # Examples
1943    ///
1944    /// ```no_run
1945    /// # use fastly::Request;
1946    /// let mut req = Request::get("https://example.com/");
1947    /// req.set_path("/hello");
1948    /// assert_eq!(req.get_url_str(), "https://example.com/hello");
1949    /// ```
1950    pub fn set_path(&mut self, path: &str) {
1951        self.get_url_mut().set_path(path);
1952    }
1953
1954    /// Get the query component of the request URL, if it exists, as a percent-encoded ASCII string.
1955    ///
1956    /// This is a shorthand for `self.get_url().query()`; see [`Url::query()`] for details and other
1957    /// query manipulation functions.
1958    pub fn get_query_str(&self) -> Option<&str> {
1959        self.get_url().query()
1960    }
1961
1962    /// Get the value of a query parameter in the request's URL.
1963    ///
1964    /// This assumes that the query string is a `&` separated list of `parameter=value` pairs.  The
1965    /// value of the first occurrence of `parameter` is returned. No URL decoding is performed.
1966    ///
1967    /// # Examples
1968    ///
1969    /// ```no_run
1970    /// # use fastly::Request;
1971    /// let req = Request::get("https://example.com/page?foo=bar&baz=qux");
1972    /// assert_eq!(req.get_query_parameter("foo"), Some("bar"));
1973    /// assert_eq!(req.get_query_parameter("baz"), Some("qux"));
1974    /// assert_eq!(req.get_query_parameter("other"), None);
1975    /// ```
1976    pub fn get_query_parameter(&self, parameter: &str) -> Option<&str> {
1977        self.get_url().query().and_then(|qs| {
1978            qs.split('&').find_map(|part| {
1979                part.strip_prefix(parameter)
1980                    .and_then(|maybe| maybe.strip_prefix('='))
1981            })
1982        })
1983    }
1984
1985    /// Attempt to parse the query component of the request URL into the specified datatype.
1986    ///
1987    #[doc = include_str!("../../docs/snippets/returns-deserializeowned.md")]
1988    ///
1989    /// # Errors
1990    ///
1991    /// This method returns [`serde_urlencoded::de::Error`] if deserialization fails.
1992    ///
1993    /// # Examples
1994    ///
1995    /// Parsing into a vector of string pairs:
1996    ///
1997    /// ```no_run
1998    /// # use fastly::Request;
1999    /// let req = Request::get("https://example.com/foo?hello=%F0%9F%8C%90!&bar=baz");
2000    /// let pairs: Vec<(String, String)> = req.get_query().unwrap();
2001    /// assert_eq!((pairs[0].0.as_str(), pairs[0].1.as_str()), ("hello", "🌐!"));
2002    /// ```
2003    ///
2004    /// Parsing into a mapping between strings (note that duplicates are removed since
2005    /// [`HashMap`][`std::collections::HashMap`] is not a multimap):
2006    ///
2007    /// ```no_run
2008    /// # use fastly::Request;
2009    /// use std::collections::HashMap;
2010    /// let req = Request::get("https://example.com/foo?hello=%F0%9F%8C%90!&bar=baz&bar=quux");
2011    /// let map: HashMap<String, String> = req.get_query().unwrap();
2012    /// assert_eq!(map.len(), 2);
2013    /// assert_eq!(map["hello"].as_str(), "🌐!");
2014    /// ```
2015    ///
2016    /// Parsing into a custom type that derives [`serde::de::Deserialize`]:
2017    ///
2018    /// ```no_run
2019    /// # use fastly::Request;
2020    /// #[derive(serde::Deserialize)]
2021    /// struct MyData {
2022    ///     name: String,
2023    ///     count: u64,
2024    /// }
2025    /// let mut req = Request::get("https://example.com/?name=Computers&count=1024");
2026    /// let my_data = req.take_body_form::<MyData>().unwrap();
2027    /// assert_eq!(&my_data.name, "Computers");
2028    /// assert_eq!(my_data.count, 1024);
2029    /// ```
2030    pub fn get_query<T: DeserializeOwned>(&self) -> Result<T, serde_urlencoded::de::Error> {
2031        serde_urlencoded::from_str(self.get_url().query().unwrap_or(""))
2032    }
2033
2034    /// Builder-style equivalent of [`set_query_str()`][`Self::set_query_str()`].
2035    pub fn with_query_str(mut self, query: impl AsRef<str>) -> Self {
2036        self.set_query_str(query);
2037        self
2038    }
2039
2040    /// Set the query string of the request URL query component to the given string, performing
2041    /// percent-encoding if necessary.
2042    ///
2043    /// # Examples
2044    ///
2045    /// ```no_run
2046    /// # use fastly::Request;
2047    /// let mut req = Request::get("https://example.com/foo");
2048    /// req.set_query_str("hello=🌐!&bar=baz");
2049    /// assert_eq!(req.get_url_str(), "https://example.com/foo?hello=%F0%9F%8C%90!&bar=baz");
2050    /// ```
2051    pub fn set_query_str(&mut self, query: impl AsRef<str>) {
2052        self.get_url_mut().set_query(Some(query.as_ref()))
2053    }
2054
2055    /// Builder-style equivalent of [`set_query()`][`Self::set_query()`].
2056    pub fn with_query(
2057        mut self,
2058        query: &impl Serialize,
2059    ) -> Result<Self, serde_urlencoded::ser::Error> {
2060        self.set_query(query)?;
2061        Ok(self)
2062    }
2063
2064    /// Convert the given value to `application/x-www-form-urlencoded` format and set that data as
2065    /// the request URL query component.
2066    ///
2067    /// The given value must implement [`serde::Serialize`]; see the trait documentation for
2068    /// details.
2069    ///
2070    /// # Errors
2071    ///
2072    /// This method returns [`serde_urlencoded::ser::Error`] if serialization fails.
2073    ///
2074    /// # Examples
2075    ///
2076    /// ```no_run
2077    /// # use fastly::Request;
2078    /// #[derive(serde::Serialize)]
2079    /// struct MyData {
2080    ///     name: String,
2081    ///     count: u64,
2082    /// }
2083    /// let my_data = MyData { name: "Computers".to_string(), count: 1024 };
2084    /// let mut req = Request::get("https://example.com/foo");
2085    /// req.set_query(&my_data).unwrap();
2086    /// assert_eq!(req.get_url_str(), "https://example.com/foo?name=Computers&count=1024");
2087    /// ```
2088    pub fn set_query(
2089        &mut self,
2090        query: &impl Serialize,
2091    ) -> Result<(), serde_urlencoded::ser::Error> {
2092        let s = serde_urlencoded::to_string(query)?;
2093        self.get_url_mut().set_query(Some(&s));
2094        Ok(())
2095    }
2096
2097    /// Remove the query component from the request URL, if one exists.
2098    ///
2099    /// # Examples
2100    ///
2101    /// ```no_run
2102    /// # use fastly::Request;
2103    /// let mut req = Request::get("https://example.com/foo?hello=%F0%9F%8C%90!&bar=baz");
2104    /// req.remove_query();
2105    /// assert_eq!(req.get_url_str(), "https://example.com/foo");
2106    /// ```
2107    pub fn remove_query(&mut self) {
2108        self.get_url_mut().set_query(None);
2109    }
2110
2111    /// Builder-style equivalent of [`set_version()`][`Self::set_version()`].
2112    pub fn with_version(mut self, version: Version) -> Self {
2113        self.set_version(version);
2114        self
2115    }
2116
2117    /// Get the HTTP version of this request.
2118    pub fn get_version(&self) -> Version {
2119        *self.lazy_handle.get_field::<Version>()
2120    }
2121
2122    /// Set the HTTP version of this request.
2123    pub fn set_version(&mut self, version: Version) {
2124        self.lazy_handle.put_field::<Version>(version);
2125    }
2126
2127    /// Builder-style equivalent of [`set_pass()`][`Self::set_pass()`].
2128    pub fn with_pass(mut self, pass: bool) -> Self {
2129        self.set_pass(pass);
2130        self
2131    }
2132
2133    /// Set whether this request should be cached if sent to a backend.
2134    ///
2135    /// By default this is `false`, which means the backend will only be reached if a cached
2136    /// response is not available. Set this to `true` to send the request directly to the backend
2137    /// without caching.
2138    ///
2139    /// # Overrides
2140    ///
2141    /// Setting this to `true` overrides any other custom caching behaviors for this request, such
2142    /// as [`Request::set_ttl()`] or [`Request::set_surrogate_key()`].
2143    pub fn set_pass(&mut self, pass: bool) {
2144        self.metadata.cache_override.set_pass(pass);
2145    }
2146
2147    /// Builder-style equivalent of [`set_ttl()`][`Self::set_ttl()`].
2148    pub fn with_ttl(mut self, ttl: u32) -> Self {
2149        self.set_ttl(ttl);
2150        self
2151    }
2152
2153    /// Override the caching behavior of this request to use the given Time to Live (TTL), in seconds.
2154    ///
2155    /// # Overrides
2156    ///
2157    /// This overrides the behavior specified in the response headers, and sets the
2158    /// [`pass`][`Self::set_pass()`] behavior to `false`.
2159    pub fn set_ttl(&mut self, ttl: u32) {
2160        self.metadata.cache_override.set_ttl(ttl);
2161    }
2162
2163    /// Builder-style equivalent of [`set_stale_while_revalidate()`][`Self::set_stale_while_revalidate()`].
2164    pub fn with_stale_while_revalidate(mut self, swr: u32) -> Self {
2165        self.set_stale_while_revalidate(swr);
2166        self
2167    }
2168
2169    /// Override the caching behavior of this request to use the given `stale-while-revalidate`
2170    /// time, in seconds.
2171    ///
2172    /// # Overrides
2173    ///
2174    /// This overrides the behavior specified in the response headers, and sets the
2175    /// [`pass`][`Self::set_pass()`] behavior to `false`.
2176    pub fn set_stale_while_revalidate(&mut self, swr: u32) {
2177        self.metadata.cache_override.set_stale_while_revalidate(swr);
2178    }
2179
2180    /// Builder-style equivalent of [`set_pci()`][`Self::set_pci()`].
2181    pub fn with_pci(mut self, pci: bool) -> Self {
2182        self.set_pci(pci);
2183        self
2184    }
2185
2186    /// Override the caching behavior of this request to enable or disable PCI/HIPAA-compliant
2187    /// non-volatile caching.
2188    ///
2189    /// By default, this is `false`, which means the request may not be PCI/HIPAA-compliant. Set it
2190    /// to `true` to enable compliant caching.
2191    ///
2192    /// See the [Fastly PCI-Compliant Caching and Delivery
2193    /// documentation](https://docs.fastly.com/products/pci-compliant-caching-and-delivery) for
2194    /// details.
2195    ///
2196    /// # Overrides
2197    ///
2198    /// This sets the [`pass`][`Self::set_pass()`] behavior to `false`.
2199    pub fn set_pci(&mut self, pci: bool) {
2200        self.metadata.cache_override.set_pci(pci);
2201    }
2202
2203    /// Builder-style equivalent of [`set_surrogate_key()`][`Self::set_surrogate_key()`].
2204    pub fn with_surrogate_key(mut self, sk: HeaderValue) -> Self {
2205        self.set_surrogate_key(sk);
2206        self
2207    }
2208
2209    /// Override the caching behavior of this request to include the given surrogate key(s),
2210    /// provided as a header value.
2211    ///
2212    /// The header value can contain more than one surrogate key, separated by spaces.
2213    ///
2214    /// Surrogate keys must contain only printable ASCII characters (those between `0x21` and
2215    /// `0x7E`, inclusive). Any invalid keys will be ignored.
2216    ///
2217    /// See the [Fastly surrogate keys
2218    /// guide](https://docs.fastly.com/en/guides/purging-api-cache-with-surrogate-keys) for details.
2219    ///
2220    /// # Overrides
2221    ///
2222    /// This sets the [`pass`][`Self::set_pass()`] behavior to `false`, and extends (but does not
2223    /// replace) any `Surrogate-Key` response headers from the backend.
2224    pub fn set_surrogate_key(&mut self, sk: HeaderValue) {
2225        self.metadata.cache_override.set_surrogate_key(sk);
2226    }
2227
2228    /// Returns the IP address of the client making the HTTP request.
2229    ///
2230    /// Returns `None` if this is not the client request.
2231    pub fn get_client_ip_addr(&self) -> Option<IpAddr> {
2232        if !self.is_from_client() {
2233            return None;
2234        }
2235        self::handle::client_ip_addr()
2236    }
2237
2238    /// Returns the IP address on which this server received the HTTP request.
2239    ///
2240    /// Returns `None` if this is not the client request.
2241    pub fn get_server_ip_addr(&self) -> Option<IpAddr> {
2242        if !self.is_from_client() {
2243            return None;
2244        }
2245        self::handle::server_ip_addr()
2246    }
2247
2248    /// Returns the client request's header names exactly as they were originally received.
2249    ///
2250    /// This includes both the original character cases, as well as the original order of the
2251    /// received headers.
2252    ///
2253    /// Returns `None` if this is not the client request.
2254    pub fn get_original_header_names(&self) -> Option<impl Iterator<Item = String>> {
2255        if !self.is_from_client() {
2256            return None;
2257        } else {
2258            Some(
2259                self::handle::client_original_header_names_impl(INITIAL_HEADER_NAME_BUF_SIZE, None)
2260                    .map(|res| res.expect("original request header name too large")),
2261            )
2262        }
2263    }
2264
2265    /// Returns the number of headers in the client request as originally received.
2266    ///
2267    /// Returns `None` if this is not the client request.
2268    pub fn get_original_header_count(&self) -> Option<u32> {
2269        if !self.is_from_client() {
2270            return None;
2271        }
2272        Some(self::handle::client_original_header_count())
2273    }
2274
2275    /// Get the HTTP/2 fingerprint of client request if available
2276    ///
2277    /// Returns `None` if this is not the client request.
2278    #[doc(hidden)]
2279    pub fn get_client_h2_fingerprint(&self) -> Option<&str> {
2280        if !self.is_from_client() {
2281            return None;
2282        }
2283
2284        self::handle::client_h2_fingerprint()
2285    }
2286
2287    /// Get the request id of the current request if available
2288    ///
2289    /// Returns `None` if this is not the client request.
2290    #[doc(hidden)]
2291    pub fn get_client_request_id(&self) -> Option<&str> {
2292        if !self.is_from_client() {
2293            return None;
2294        }
2295
2296        self::handle::client_request_id()
2297    }
2298
2299    /// Get the fingerprint of client original request headers if available
2300    ///
2301    /// Returns `None` if this is not the client request.
2302    #[doc(hidden)]
2303    pub fn get_client_oh_fingerprint(&self) -> Option<&str> {
2304        if !self.is_from_client() {
2305            return None;
2306        }
2307
2308        self::handle::client_oh_fingerprint()
2309    }
2310
2311    /// Get the raw bytes sent by the client in the TLS ClientHello message.
2312    ///
2313    /// See [RFC 5246](https://tools.ietf.org/html/rfc5246#section-7.4.1.2) for details.
2314    ///
2315    /// Returns `None` if this is not the client request.
2316    pub fn get_tls_client_hello(&self) -> Option<&[u8]> {
2317        if !self.is_from_client() {
2318            return None;
2319        }
2320
2321        self::handle::client_tls_client_hello()
2322    }
2323
2324    /// Get the JA3 hash of the TLS ClientHello message.
2325    ///
2326    /// Returns `None` if this is not available.
2327    pub fn get_tls_ja3_md5(&self) -> Option<[u8; 16]> {
2328        if !self.is_from_client() {
2329            return None;
2330        }
2331
2332        self::handle::client_tls_ja3_md5()
2333    }
2334
2335    /// Get the JA4 hash of the TLS ClientHello message.
2336    ///
2337    /// Returns `None` if this is not available.
2338    pub fn get_tls_ja4(&self) -> Option<&str> {
2339        if !self.is_from_client() {
2340            return None;
2341        }
2342
2343        self::handle::client_tls_ja4()
2344    }
2345
2346    /// Get the raw client certificate in the mutual TLS handshake message as a
2347    /// string, panicking if it is not UTF-8.
2348    /// It is in PEM format.
2349    /// Returns `None` if this is not mTLS or available.
2350    pub fn get_tls_raw_client_certificate(&self) -> Option<&'static str> {
2351        if !self.is_from_client() {
2352            return None;
2353        }
2354
2355        self::handle::client_tls_client_raw_certificate()
2356    }
2357
2358    /// Like [`Self::get_tls_raw_client_certificate`], but supports non-UTF-8 byte sequences.
2359    pub fn get_tls_raw_client_certificate_bytes(&self) -> Option<&'static [u8]> {
2360        if !self.is_from_client() {
2361            return None;
2362        }
2363
2364        self::handle::client_tls_client_raw_certificate_bytes()
2365    }
2366
2367    /// Returns the error code defined in ClientCertVerifyResult.
2368    ///
2369    /// If your service is set up for mTLS (see [this documentation](https://docs.fastly.com/en/guides/setting-up-mutual-tls-authentication)),
2370    /// then this will return information about the client's mTLS certificate, if provided.
2371    /// If the certificate is not provided, you will receive a `ClientCertVerifyResult::CertificateMissing`
2372    /// value, if the certificate properly matches your mTLS configuration, you will receive
2373    /// a `ClientCertVerifyResult::Ok`, etc.
2374    ///
2375    /// If your service is *not* set up for mTLS, this function will always return
2376    /// `Some(ClientCertVerifyResult::Ok)`.
2377    ///
2378    /// If you have a service that may be run with some domains that are mTLS protected,
2379    /// and some that are not mTLS protected, then we advise checking for both the existence
2380    /// of a certificate (by verifying that `get_tls_raw_client_certificate` or
2381    /// `get_tls_raw_client_certificate_bytes` return `Some`) before checking the result of
2382    /// this function. Doing both makes sure that you both received a certificate and that
2383    /// it was valid, and will work for both the mTLS-protected and non-mTLS-protected
2384    /// domains.
2385    ///
2386    /// This function returns `None` in the case that this `Request` is not from the
2387    /// client, but was instead generated by other parts of the code. (In other words,
2388    /// it will return `None` if this request was neither the argument to a function marked
2389    /// `fastly::main` nor generated with `Request::from_client`.)
2390    pub fn get_tls_client_cert_verify_result(&self) -> Option<ClientCertVerifyResult> {
2391        if !self.is_from_client() {
2392            return None;
2393        }
2394        self::handle::client_tls_client_cert_verify_result()
2395    }
2396
2397    /// Get the cipher suite used to secure the client TLS connection, as a string,
2398    /// panicking if it is not UTF-8.
2399    ///
2400    /// The value returned will be consistent with the [OpenSSL
2401    /// name](https://testssl.sh/openssl-iana.mapping.html) for the cipher suite.
2402    ///
2403    /// Returns `None` if this is not the client request.
2404    ///
2405    /// # Examples
2406    ///
2407    /// ```no_run
2408    /// # use fastly::Request;
2409    /// assert_eq!(Request::from_client().get_tls_cipher_openssl_name().unwrap(), "ECDHE-RSA-AES128-GCM-SHA256");
2410    /// ```
2411    pub fn get_tls_cipher_openssl_name(&self) -> Option<&'static str> {
2412        if !self.is_from_client() {
2413            return None;
2414        }
2415
2416        self::handle::client_tls_cipher_openssl_name()
2417    }
2418
2419    /// Like [`Self::get_tls_cipher_openssl_name_bytes`], but supports non-UTF-8 byte strings.
2420    pub fn get_tls_cipher_openssl_name_bytes(&self) -> Option<&'static [u8]> {
2421        if !self.is_from_client() {
2422            return None;
2423        }
2424
2425        self::handle::client_tls_cipher_openssl_name_bytes()
2426    }
2427
2428    /// Get the TLS protocol version used to secure the client TLS connection, as a string,
2429    /// panicking if it is not UTF-8.
2430    ///
2431    /// Returns `None` if this is not the client request.
2432    ///
2433    /// # Examples
2434    ///
2435    /// ```no_run
2436    /// # use fastly::Request;
2437    /// assert_eq!(Request::from_client().get_tls_protocol().unwrap(), "TLSv1.2");
2438    /// ```
2439    pub fn get_tls_protocol(&self) -> Option<&'static str> {
2440        if !self.is_from_client() {
2441            return None;
2442        }
2443        self::handle::client_tls_protocol()
2444    }
2445
2446    /// Like [`Self::get_tls_protocol`], but supports non-UTF-8 byte strings.
2447    pub fn get_tls_protocol_bytes(&self) -> Option<&'static [u8]> {
2448        if !self.is_from_client() {
2449            return None;
2450        }
2451        self::handle::client_tls_protocol_bytes()
2452    }
2453
2454    /// Set whether a `gzip`-encoded response to this request will be automatically decompressed.
2455    ///
2456    /// If the response to this request is `gzip`-encoded, it will be presented in decompressed
2457    /// form, and the `Content-Encoding` and `Content-Length` headers will be removed.
2458    pub fn set_auto_decompress_gzip(&mut self, gzip: bool) {
2459        self.metadata
2460            .auto_decompress_response
2461            .set(ContentEncodings::GZIP, gzip);
2462    }
2463
2464    /// Builder-style equivalent of
2465    /// [`set_auto_decompress_gzip()`][`Self::set_auto_decompress_gzip()`].
2466    pub fn with_auto_decompress_gzip(mut self, gzip: bool) -> Self {
2467        self.set_auto_decompress_gzip(gzip);
2468        self
2469    }
2470
2471    /// Sets how `Content-Length` and `Transfer-Encoding` will be determined when sending this
2472    /// request.
2473    ///
2474    /// See [`FramingHeadersMode`] for details on the options.
2475    pub fn set_framing_headers_mode(&mut self, mode: FramingHeadersMode) {
2476        self.metadata.framing_headers_mode = mode;
2477    }
2478
2479    /// Builder-style equivalent of
2480    /// [`set_framing_headers_mode()`][`Self::set_framing_headers_mode()`].
2481    pub fn with_framing_headers_mode(mut self, mode: FramingHeadersMode) -> Self {
2482        self.set_framing_headers_mode(mode);
2483        self
2484    }
2485
2486    /// Create a [`Request`] from the low-level [`handle` API][`crate::handle`].
2487    pub fn from_handles(req_handle: RequestHandle, body_handle: Option<BodyHandle>) -> Self {
2488        Self {
2489            lazy_handle: LazyHandle::from_handle(req_handle)
2490                .with_field_lazy::<Version>()
2491                .with_field_lazy::<Method>()
2492                .with_field_lazy::<Url>()
2493                .finish(),
2494            body: body_handle.map(Into::into),
2495            metadata: FastlyRequestMetadata::new(),
2496        }
2497    }
2498
2499    fn take_request_handle(&mut self) -> RequestHandle {
2500        let mut req_handle = self.lazy_handle.take_handle();
2501        self.metadata.flush_to_handle(&mut req_handle);
2502        if let Some(exp_key_override) = self.get_override_cache_key() {
2503            use crate::experimental::RequestHandleCacheKey;
2504            req_handle.set_cache_key(&exp_key_override);
2505        }
2506        req_handle
2507    }
2508
2509    /// Convert a [`Request`] into the low-level [`handle` API][`crate::handle`].
2510    pub fn into_handles(mut self) -> (RequestHandle, Option<BodyHandle>) {
2511        let override_cache_key = self.get_override_cache_key();
2512        let body_handle = self.try_take_body().map(Body::into_handle);
2513        let mut req_handle = self.lazy_handle.into_handle();
2514        self.metadata.flush_to_handle(&mut req_handle);
2515
2516        if let Some(exp_key_override) = override_cache_key {
2517            use crate::experimental::RequestHandleCacheKey;
2518            req_handle.set_cache_key(&exp_key_override);
2519        }
2520
2521        (req_handle, body_handle)
2522    }
2523
2524    fn get_override_cache_key(&mut self) -> Option<CacheKey> {
2525        self.metadata
2526            .override_cache_key
2527            .take()
2528            .map(|gen| match gen {
2529                CacheKeyGen::Lazy(f) => f(self),
2530                CacheKeyGen::Set(k) => k,
2531            })
2532    }
2533
2534    /// Returns whether or not the client request had a `Fastly-Key` header which is valid for
2535    /// purging content for the service.
2536    ///
2537    /// This function ignores the current value of any `Fastly-Key` header for this request.
2538    pub fn fastly_key_is_valid(&self) -> bool {
2539        if !self.is_from_client() {
2540            return false;
2541        }
2542        self::handle::fastly_key_is_valid()
2543    }
2544
2545    /// Pass the WebSocket directly to a backend.
2546    ///
2547    /// This can only be used on services that have the WebSockets feature enabled and on requests
2548    /// that are valid WebSocket requests.
2549    ///
2550    /// The sending completes in the background. Once this method has been called, no other
2551    /// response can be sent to this request, and the application can exit without affecting the
2552    /// send.
2553    pub fn handoff_websocket(self, backend: &str) -> Result<(), SendError> {
2554        assert_single_downstream_response_is_sent(true);
2555        let cloned = self.clone_without_body();
2556        let (req_handle, _) = self.into_handles();
2557        let status = self::handle::redirect_to_websocket_proxy(req_handle, backend);
2558        if status.is_err() {
2559            Err(SendError::new(
2560                backend,
2561                cloned,
2562                SendErrorCause::status(status),
2563            ))
2564        } else {
2565            Ok(())
2566        }
2567    }
2568
2569    /// Pass the request through the Fanout GRIP proxy and on to a backend.
2570    ///
2571    /// This can only be used on services that have the Fanout feature enabled.
2572    ///
2573    /// The sending completes in the background. Once this method has been called, no other
2574    /// response can be sent to this request, and the application can exit without affecting the
2575    /// send.
2576    pub fn handoff_fanout(self, backend: &str) -> Result<(), SendError> {
2577        assert_single_downstream_response_is_sent(true);
2578        let cloned = self.clone_without_body();
2579        let (req_handle, _) = self.into_handles();
2580        let status = self::handle::redirect_to_grip_proxy(req_handle, backend);
2581        if status.is_err() {
2582            Err(SendError::new(
2583                backend,
2584                cloned,
2585                SendErrorCause::status(status),
2586            ))
2587        } else {
2588            Ok(())
2589        }
2590    }
2591
2592    /// Send this request on behalf of another service.
2593    ///
2594    /// Running it on behalf of another service means that the cache store used for this request
2595    /// will be of that service, not the currently running one.
2596    #[doc = include_str!("../../docs/snippets/privileged_behalf.md")]
2597    pub fn on_behalf_of<S: AsRef<str>>(self, service: S) -> Result<Self, FastlyStatus> {
2598        let (mut req_handle, body_handle) = self.into_handles();
2599        req_handle.on_behalf_of(service.as_ref())?;
2600        Ok(Self::from_handles(req_handle, body_handle))
2601    }
2602
2603    pub(crate) fn with_metadata(mut self, metadata: FastlyRequestMetadata) -> Self {
2604        self.metadata = metadata;
2605        self
2606    }
2607
2608    /// Set the cache key to be used when attempting to satisfy this request from a cached response.
2609    pub fn set_cache_key(&mut self, key: impl Into<Vec<u8>>) {
2610        self.metadata.override_cache_key = Some(CacheKeyGen::Set(key.into().into()));
2611    }
2612
2613    /// Builder-style equivalent of [`set_cache_key()`](Self::set_cache_key()).
2614    pub fn with_cache_key(mut self, key: impl Into<Vec<u8>>) -> Self {
2615        self.set_cache_key(key);
2616        self
2617    }
2618
2619    /// Gets whether the request is potentially cacheable.
2620    pub fn is_cacheable(&mut self) -> bool {
2621        self.lazy_handle.get_handle().is_cacheable()
2622    }
2623}
2624
2625impl Into<http::Request<Body>> for Request {
2626    fn into(self) -> http::Request<Body> {
2627        let mut req = http::Request::new(self.body.unwrap_or_else(|| Body::new()));
2628        req.extensions_mut().insert(self.metadata);
2629        *req.method_mut() = self.lazy_handle.get_field::<Method>().clone();
2630        *req.uri_mut() = String::from(self.lazy_handle.get_field::<Url>().as_str())
2631            .parse()
2632            .expect("Url to Uri conversion shouldn't fail, but did");
2633        *req.version_mut() = *self.lazy_handle.get_field::<Version>();
2634        *req.headers_mut() = self.lazy_handle.into();
2635        req
2636    }
2637}
2638
2639impl From<http::Request<Body>> for Request {
2640    fn from(from: http::Request<Body>) -> Self {
2641        let (mut parts, body) = from.into_parts();
2642        let metadata: FastlyRequestMetadata = parts
2643            .extensions
2644            .remove()
2645            .unwrap_or_else(FastlyRequestMetadata::new);
2646        Request {
2647            lazy_handle: LazyHandle::detached()
2648                .with_headers(parts.headers)
2649                .with_field(parts.version)
2650                .with_field(parts.method)
2651                .with_field(
2652                    Url::parse(&parts.uri.to_string())
2653                        .expect("Uri to Url conversion shouldn't fail, but did"),
2654                )
2655                .finish(),
2656            body: Some(body),
2657            metadata,
2658        }
2659    }
2660}
2661
2662impl From<RequestHandle> for Request {
2663    fn from(handle: RequestHandle) -> Self {
2664        Self::from_handles(handle, None)
2665    }
2666}
2667
2668/// The reason that a request sent to a backend failed.
2669#[non_exhaustive]
2670#[derive(Debug, Error)]
2671pub enum SendErrorCause {
2672    /// The system encountered a timeout when trying to find an IP address for the backend
2673    /// hostname.
2674    #[error("DNS timeout")]
2675    DnsTimeout,
2676    /// The system encountered a DNS error when trying to find an IP address for the backend
2677    /// hostname. The fields $dns_error_rcode and $dns_error_info_code may be set in the
2678    /// $send_error_detail.
2679    // TODO ACF 2023-08-18: figure out what to do with the fields. probably would be best to seal
2680    // the details of `SendErrorCause` and make folks use accessor methods or `Display`
2681    #[error("DNS error (rcode={rcode:?}, info_code={info_code:?})")]
2682    DnsError {
2683        /// The DNS rcode, if available.
2684        rcode: Option<u16>,
2685        /// The DNS info-code, if available.
2686        info_code: Option<u16>,
2687    },
2688    /// The system cannot determine which backend to use, or the specified backend was invalid.
2689    #[error("Destination not found")]
2690    DestinationNotFound,
2691    /// The system considers the backend to be unavailable; e.g., recent attempts to communicate
2692    /// with it may have failed, or a health check may indicate that it is down.
2693    #[error("Destination unavailable")]
2694    DestinationUnavailable,
2695    /// The system cannot find a route to the next-hop IP address.
2696    #[error("Destination IP unroutable")]
2697    DestinationIpUnroutable,
2698    /// The system's connection to the backend was refused.
2699    #[error("Connection refused")]
2700    ConnectionRefused,
2701    /// The system's connection to the backend was closed before a complete response was
2702    /// received.
2703    #[error("Connection terminated")]
2704    ConnectionTerminated,
2705    /// The system's attempt to open a connection to the backend timed out.
2706    #[error("Connection timeout")]
2707    ConnectionTimeout,
2708    /// The system is configured to limit the number of connections it has to the backend, and
2709    /// that limit has been exceeded.
2710    #[error("Connection limit reached")]
2711    ConnectionLimitReached,
2712    /// The system encountered a TLS error when communicating with the backend, either during the
2713    /// handshake or afterwards.
2714    #[error("TLS protocol error")]
2715    TlsProtocolError,
2716    /// The system encountered an error when verifying the certificate presented by the backend.
2717    #[error("TLS certificate error")]
2718    TlsCertificateError,
2719    /// The system received a TLS alert from the backend. The field $tls_alert_id may be set in the
2720    /// $send_error_detail.
2721    #[error("TLS alert received (alert_id={alert_id:?})")]
2722    TlsAlertReceived {
2723        /// The [TLS alert
2724        /// value](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-6),
2725        /// if available.
2726        alert_id: Option<u8>,
2727    },
2728    /// The system encountered an error with the backend TLS configuration.
2729    #[error("TLS configuration error")]
2730    TlsConfigurationError,
2731    /// The system received an incomplete response to the request from the backend.
2732    #[error("Incomplete HTTP response")]
2733    HttpIncompleteResponse,
2734    /// The system received a response to the request whose header section was considered too
2735    /// large.
2736    #[error("HTTP response header section too large")]
2737    HttpResponseHeaderSectionTooLarge,
2738    /// The system received a response to the request whose body was considered too large.
2739    #[error("HTTP response body too large")]
2740    HttpResponseBodyTooLarge,
2741    /// The system reached a configured time limit waiting for the complete response.
2742    #[error("HTTP response timeout")]
2743    HttpResponseTimeout,
2744    /// The system received a response to the request whose status code or reason phrase was invalid.
2745    #[error("HTTP response status invalid")]
2746    HttpResponseStatusInvalid,
2747    /// The process of negotiating an upgrade of the HTTP version between the system and backend
2748    /// failed.
2749    #[error("HTTP upgrade failed")]
2750    HttpUpgradeFailed,
2751    /// The system encountered an HTTP protocol error when communicating with the backend. This
2752    /// error will only be used when a more specific one is not defined.
2753    #[error("HTTP protocol error")]
2754    HttpProtocolError,
2755    /// An invalid cache key was provided for the request.
2756    #[error("HTTP request cache key invalid")]
2757    HttpRequestCacheKeyInvalid,
2758    /// An invalid URI was provided for the request.
2759    #[error("HTTP request URI invalid")]
2760    HttpRequestUriInvalid,
2761    /// A caching limit was reached.
2762    #[error("HTTP caching limit exceeded")]
2763    HttpCacheLimitExceeded,
2764    /// A caching limit was reached.
2765    #[error("HTTP caching API is not enabled; please contact support for help")]
2766    HttpCacheApiUnsupported,
2767    /// An I/O error was encountered.
2768    #[error("I/O error: {0}")]
2769    IoError(#[from] std::io::Error),
2770    /// The system encountered an unexpected internal error.
2771    #[error("Internal error (status={0:?})")]
2772    InternalError(Option<FastlyStatus>),
2773    /// A custom error occurred while processing the request.
2774    #[error("Custom HTTP send error: {0}")]
2775    Custom(#[source] anyhow::Error),
2776}
2777
2778impl SendErrorCause {
2779    pub(crate) fn status(cause: fastly_shared::FastlyStatus) -> Self {
2780        match cause {
2781            fastly_shared::FastlyStatus::HTTPINVALID => SendErrorCause::HttpProtocolError,
2782            fastly_shared::FastlyStatus::HTTPINCOMPLETE => SendErrorCause::HttpIncompleteResponse,
2783            fastly_shared::FastlyStatus::HTTPHEADTOOLARGE => {
2784                SendErrorCause::HttpResponseHeaderSectionTooLarge
2785            }
2786            fastly_shared::FastlyStatus::HTTPINVALIDSTATUS => {
2787                SendErrorCause::HttpResponseStatusInvalid
2788            }
2789            other => SendErrorCause::InternalError(Some(other)),
2790        }
2791    }
2792
2793    /// Return `Err(SendErrorCause)` based on the provided `SendErrorDetail` ABI struct and optional
2794    /// `FastlyStatus`, or `Ok(())` if no error occurred.
2795    ///
2796    /// This is useful for `?`ing out of a function after calling an ABI function that returns both
2797    /// a `SendErrorDetail` and a `FastlyStatus`, allowing control flow to continue only if the call
2798    /// was successful.
2799    ///
2800    /// If `detail.tag` is `Ok`, or if `Some(FastlyStatus::OK)` is provided, the result is `Ok(())`.
2801    ///
2802    /// If no `FastlyStatus` is provided, the result is determined entirely from the
2803    /// `SendErrorDetail`. If both are provided, but the `SendErrorDetail` is uninitialized, the
2804    /// behavior falls back to the legacy `FastlyStatus`-determined behavior.
2805    pub(crate) fn detail_and_status(
2806        detail: SendErrorDetail,
2807        status: Option<FastlyStatus>,
2808    ) -> Result<(), Self> {
2809        use fastly_sys::fastly_http_req::{SendErrorDetailMask as Mask, SendErrorDetailTag as Tag};
2810        // if status is present and is OK, we're done
2811        if status.as_ref().map(FastlyStatus::is_ok).unwrap_or(false) {
2812            return Ok(());
2813        }
2814        match detail.tag {
2815            Tag::Uninitialized => {
2816                if let Some(status) = status {
2817                    // fall back to the legacy `FastlyStatus` logic if the details weren't populated
2818                    Err(SendErrorCause::status(status))
2819                } else {
2820                    // otherwise fall back to the generic internal error
2821                    Err(SendErrorCause::InternalError(None))
2822                }
2823            }
2824            Tag::Ok => Ok(()),
2825            Tag::DnsTimeout => Err(SendErrorCause::DnsTimeout),
2826            Tag::DnsError => {
2827                let rcode = if detail.mask.contains(Mask::DNS_ERROR_RCODE) {
2828                    Some(detail.dns_error_rcode)
2829                } else {
2830                    None
2831                };
2832                let info_code = if detail.mask.contains(Mask::DNS_ERROR_INFO_CODE) {
2833                    Some(detail.dns_error_info_code)
2834                } else {
2835                    None
2836                };
2837                Err(Self::DnsError { rcode, info_code })
2838            }
2839            Tag::DestinationNotFound => Err(SendErrorCause::DestinationNotFound),
2840            Tag::DestinationUnavailable => Err(SendErrorCause::DestinationUnavailable),
2841            Tag::DestinationIpUnroutable => Err(SendErrorCause::DestinationIpUnroutable),
2842            Tag::ConnectionRefused => Err(SendErrorCause::ConnectionRefused),
2843            Tag::ConnectionTerminated => Err(SendErrorCause::ConnectionTerminated),
2844            Tag::ConnectionTimeout => Err(SendErrorCause::ConnectionTimeout),
2845            Tag::ConnectionLimitReached => Err(SendErrorCause::ConnectionLimitReached),
2846            Tag::TlsProtocolError => Err(SendErrorCause::TlsProtocolError),
2847            Tag::TlsCertificateError => Err(SendErrorCause::TlsCertificateError),
2848            Tag::TlsAlertReceived => {
2849                let alert_id = if detail.mask.contains(Mask::TLS_ALERT_ID) {
2850                    Some(detail.tls_alert_id)
2851                } else {
2852                    None
2853                };
2854                Err(Self::TlsAlertReceived { alert_id })
2855            }
2856            Tag::TlsConfigurationError => Err(SendErrorCause::TlsConfigurationError),
2857            Tag::HttpIncompleteResponse => Err(SendErrorCause::HttpIncompleteResponse),
2858            Tag::HttpResponseHeaderSectionTooLarge => {
2859                Err(SendErrorCause::HttpResponseHeaderSectionTooLarge)
2860            }
2861            Tag::HttpResponseBodyTooLarge => Err(SendErrorCause::HttpResponseBodyTooLarge),
2862            Tag::HttpResponseTimeout => Err(SendErrorCause::HttpResponseTimeout),
2863            Tag::HttpResponseStatusInvalid => Err(SendErrorCause::HttpResponseStatusInvalid),
2864            Tag::HttpUpgradeFailed => Err(SendErrorCause::HttpUpgradeFailed),
2865            Tag::HttpProtocolError => Err(SendErrorCause::HttpProtocolError),
2866            Tag::HttpRequestCacheKeyInvalid => Err(SendErrorCause::HttpRequestCacheKeyInvalid),
2867            Tag::HttpRequestUriInvalid => Err(SendErrorCause::HttpRequestUriInvalid),
2868            Tag::InternalError => Err(SendErrorCause::InternalError(status)),
2869        }
2870    }
2871}
2872
2873impl From<HttpCacheError> for SendErrorCause {
2874    fn from(err: HttpCacheError) -> Self {
2875        match err {
2876            HttpCacheError::LimitExceeded => Self::HttpCacheLimitExceeded,
2877            HttpCacheError::InvalidOperation => Self::InternalError(Some(FastlyStatus::INVAL)),
2878            HttpCacheError::Unsupported => Self::InternalError(Some(FastlyStatus::UNSUPPORTED)),
2879            HttpCacheError::Other(fs) => Self::InternalError(Some(fs)),
2880        }
2881    }
2882}
2883
2884impl From<SendError> for SendErrorCause {
2885    fn from(err: SendError) -> Self {
2886        err.error
2887    }
2888}
2889
2890/// An error that occurred while sending a request.
2891///
2892/// While the body of a request is always consumed when sent, you can recover the headers and other
2893/// request metadata of the request that failed using `SendError::into_sent_req()`.
2894///
2895/// use [`SendError::root_cause()`] to inspect details about what caused the error.
2896#[derive(Debug, Error)]
2897#[error("error sending request: {error} to backend {backend}")]
2898pub struct SendError {
2899    backend: String,
2900    // TODO 2024-07-31: this now forces request headers to be copied an extra
2901    // time, while it used to be ~free. Once async fn support is added and
2902    // pending request select is no longer present, we should be able to drop
2903    // this forced clone and let users clone if needed.
2904    //
2905    // We use an `http::Request` here because we need the value to be `Send` and
2906    // `Sync` for `SendError` to be a valid error type.
2907    sent_req: http::Request<Body>,
2908    #[source]
2909    error: SendErrorCause,
2910}
2911
2912impl SendError {
2913    pub(crate) fn new(
2914        backend: impl Into<String>,
2915        sent_req: Request,
2916        error: SendErrorCause,
2917    ) -> Self {
2918        SendError {
2919            backend: backend.into(),
2920            sent_req: sent_req.into(),
2921            error: error.into(),
2922        }
2923    }
2924
2925    /// Get the name of the backend that returned this error.
2926    pub fn backend_name(&self) -> &str {
2927        &self.backend
2928    }
2929
2930    /// Get the underlying cause of this `SendError`.
2931    ///
2932    /// This is the same cause that would be returned by `err.source().downcast_ref::<SendErrorCause>()`, but more direct.
2933    pub fn root_cause(&self) -> &SendErrorCause {
2934        &self.error
2935    }
2936
2937    /// Convert the error back into the request that was originally sent.
2938    ///
2939    /// Since the original request's body is consumed by sending it, the body in the returned
2940    /// request is empty. To add a new body to the request, use [`Request::with_body()`], for example:
2941    ///
2942    /// ```no_run
2943    /// # use fastly::{Body, Error, Request};
2944    /// # fn f(bereq: Request) -> Result<(), Error> {
2945    /// if let Err(e) = bereq.send("my_backend") {
2946    ///     let new_body = Body::from("something new");
2947    ///     let new_req = e.into_sent_req().with_body(new_body);
2948    ///     new_req.send("my_other_backend")?;
2949    /// }
2950    /// # Ok(())
2951    /// # }
2952    /// ```
2953    pub fn into_sent_req(self) -> Request {
2954        self.sent_req.into()
2955    }
2956}
2957
2958/// Check whether a request looks suitable for sending to a backend.
2959///
2960/// Note that this is *not* meant to be a filter for things that could cause security issues, it is
2961/// only meant to catch errors before the hostcalls do in order to yield friendlier error messages.
2962fn validate_request(req: &Request) -> Result<(), SendErrorCause> {
2963    let scheme_ok = req.get_url().scheme().eq_ignore_ascii_case("http")
2964        || req.get_url().scheme().eq_ignore_ascii_case("https");
2965    if scheme_ok && req.get_url().has_authority() {
2966        Ok(())
2967    } else {
2968        Err(SendErrorCause::HttpRequestUriInvalid)
2969    }
2970}
2971
2972/// A `PendingRequest` paired with an open cache transaction.
2973///
2974/// Represents an in-progress backend request that will be used to complete the
2975/// cache transaction.
2976#[derive(Debug)]
2977struct PendingBackendRequestForCaching {
2978    cache_handle: HttpCacheHandle,
2979    pending_req_handle: PendingRequestHandle,
2980    after_send: Option<AfterSend>,
2981    cache_override: CacheOverride,
2982}
2983
2984impl PendingBackendRequestForCaching {
2985    /// Synchronously complete the backend request, producing a
2986    /// `CandidateResponse` that has executed any after-send hooks.
2987    fn into_candidate(self) -> Result<CandidateResponse, SendErrorCause> {
2988        let (resp_handle, resp_body_handle) = self.pending_req_handle.wait()?;
2989        let mut candidate = CandidateResponse::new(
2990            self.cache_handle,
2991            &self.cache_override,
2992            resp_handle,
2993            resp_body_handle,
2994        )?;
2995        if let Some(f) = &self.after_send {
2996            (f.after_send)(&mut candidate)?;
2997        }
2998        Ok(candidate)
2999    }
3000
3001    /// Prepares this pending request to be used for backend revalidation;
3002    /// completion will automatically occur upon drop.
3003    fn into_background_revalidation(self) -> BackgroundRevalidation {
3004        BackgroundRevalidation {
3005            pending: Some(self),
3006        }
3007    }
3008}
3009
3010#[derive(Debug)]
3011pub(crate) struct BackgroundRevalidation {
3012    pending: Option<PendingBackendRequestForCaching>,
3013}
3014
3015impl Drop for BackgroundRevalidation {
3016    fn drop(&mut self) {
3017        // if a background revalidation fails, we just drop the error on the floor
3018        self.pending
3019            .take()
3020            .and_then(|p| p.into_candidate().ok())
3021            .and_then(|c| c.apply_in_background().ok());
3022    }
3023}