rama_http/layer/cors/
mod.rs

1//! Middleware which adds headers for [CORS][mdn].
2//!
3//! # Example
4//!
5//! ```
6//! use std::convert::Infallible;
7//! use bytes::Bytes;
8//!
9//! use rama_http::{Body, Request, Response, Method, header};
10//! use rama_http::layer::cors::{Any, CorsLayer};
11//! use rama_core::service::service_fn;
12//! use rama_core::{Context, Service, Layer};
13//!
14//! async fn handle(request: Request) -> Result<Response, Infallible> {
15//!     Ok(Response::new(Body::default()))
16//! }
17//!
18//! # #[tokio::main]
19//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
20//! let cors = CorsLayer::new()
21//!     // allow `GET` and `POST` when accessing the resource
22//!     .allow_methods([Method::GET, Method::POST])
23//!     // allow requests from any origin
24//!     .allow_origin(Any);
25//!
26//! let mut service = cors.layer(service_fn(handle));
27//!
28//! let request = Request::builder()
29//!     .header(header::ORIGIN, "https://example.com")
30//!     .body(Body::default())
31//!     .unwrap();
32//!
33//! let response = service
34//!     .serve(Context::default(), request)
35//!     .await?;
36//!
37//! assert_eq!(
38//!     response.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap(),
39//!     "*",
40//! );
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
46
47#![allow(clippy::enum_variant_names)]
48
49use crate::dep::http::{
50    header::{self, HeaderName},
51    HeaderMap, HeaderValue, Method, Request, Response,
52};
53use bytes::{BufMut, BytesMut};
54use rama_core::{Context, Layer, Service};
55use rama_utils::macros::define_inner_service_accessors;
56use std::{array, fmt, mem};
57
58mod allow_credentials;
59mod allow_headers;
60mod allow_methods;
61mod allow_origin;
62mod allow_private_network;
63mod expose_headers;
64mod max_age;
65mod vary;
66
67#[cfg(test)]
68mod tests;
69
70#[doc(inline)]
71pub use self::{
72    allow_credentials::AllowCredentials, allow_headers::AllowHeaders, allow_methods::AllowMethods,
73    allow_origin::AllowOrigin, allow_private_network::AllowPrivateNetwork,
74    expose_headers::ExposeHeaders, max_age::MaxAge, vary::Vary,
75};
76
77/// Layer that applies the [`Cors`] middleware which adds headers for [CORS][mdn].
78///
79/// See the [module docs](crate::layer::cors) for an example.
80///
81/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
82#[derive(Debug, Clone)]
83#[must_use]
84pub struct CorsLayer {
85    allow_credentials: AllowCredentials,
86    allow_headers: AllowHeaders,
87    allow_methods: AllowMethods,
88    allow_origin: AllowOrigin,
89    allow_private_network: AllowPrivateNetwork,
90    expose_headers: ExposeHeaders,
91    max_age: MaxAge,
92    vary: Vary,
93}
94
95#[allow(clippy::declare_interior_mutable_const)]
96const WILDCARD: HeaderValue = HeaderValue::from_static("*");
97
98impl CorsLayer {
99    /// Create a new `CorsLayer`.
100    ///
101    /// No headers are sent by default. Use the builder methods to customize
102    /// the behavior.
103    ///
104    /// You need to set at least an allowed origin for browsers to make
105    /// successful cross-origin requests to your service.
106    pub fn new() -> Self {
107        Self {
108            allow_credentials: Default::default(),
109            allow_headers: Default::default(),
110            allow_methods: Default::default(),
111            allow_origin: Default::default(),
112            allow_private_network: Default::default(),
113            expose_headers: Default::default(),
114            max_age: Default::default(),
115            vary: Default::default(),
116        }
117    }
118
119    /// A permissive configuration:
120    ///
121    /// - All request headers allowed.
122    /// - All methods allowed.
123    /// - All origins allowed.
124    /// - All headers exposed.
125    pub fn permissive() -> Self {
126        Self::new()
127            .allow_headers(Any)
128            .allow_methods(Any)
129            .allow_origin(Any)
130            .expose_headers(Any)
131    }
132
133    /// A very permissive configuration:
134    ///
135    /// - **Credentials allowed.**
136    /// - The method received in `Access-Control-Request-Method` is sent back
137    ///   as an allowed method.
138    /// - The origin of the preflight request is sent back as an allowed origin.
139    /// - The header names received in `Access-Control-Request-Headers` are sent
140    ///   back as allowed headers.
141    /// - No headers are currently exposed, but this may change in the future.
142    pub fn very_permissive() -> Self {
143        Self::new()
144            .allow_credentials(true)
145            .allow_headers(AllowHeaders::mirror_request())
146            .allow_methods(AllowMethods::mirror_request())
147            .allow_origin(AllowOrigin::mirror_request())
148    }
149
150    /// Set the [`Access-Control-Allow-Credentials`][mdn] header.
151    ///
152    /// ```
153    /// use rama_http::layer::cors::CorsLayer;
154    ///
155    /// let layer = CorsLayer::new().allow_credentials(true);
156    /// ```
157    ///
158    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
159    pub fn allow_credentials<T>(mut self, allow_credentials: T) -> Self
160    where
161        T: Into<AllowCredentials>,
162    {
163        self.allow_credentials = allow_credentials.into();
164        self
165    }
166
167    /// Set the value of the [`Access-Control-Allow-Headers`][mdn] header.
168    ///
169    /// ```
170    /// use rama_http::layer::cors::CorsLayer;
171    /// use rama_http::header::{AUTHORIZATION, ACCEPT};
172    ///
173    /// let layer = CorsLayer::new().allow_headers([AUTHORIZATION, ACCEPT]);
174    /// ```
175    ///
176    /// All headers can be allowed with
177    ///
178    /// ```
179    /// use rama_http::layer::cors::{Any, CorsLayer};
180    ///
181    /// let layer = CorsLayer::new().allow_headers(Any);
182    /// ```
183    ///
184    /// You can also use an async closure:
185    ///
186    /// ```
187    /// # #[derive(Clone)]
188    /// # struct Client;
189    /// # fn get_api_client() -> Client {
190    /// #     Client
191    /// # }
192    /// # impl Client {
193    /// #     async fn fetch_allowed_origins(&self) -> Vec<HeaderValue> {
194    /// #         vec![HeaderValue::from_static("http://example.com")]
195    /// #     }
196    /// #     async fn fetch_allowed_origins_for_path(&self, _path: String) -> Vec<HeaderValue> {
197    /// #         vec![HeaderValue::from_static("http://example.com")]
198    /// #     }
199    /// # }
200    /// use rama_http::layer::cors::{CorsLayer, AllowOrigin};
201    /// use rama_http::dep::http::{request::Parts as RequestParts, HeaderValue};
202    ///
203    /// let client = get_api_client();
204    ///
205    /// let layer = CorsLayer::new().allow_origin(AllowOrigin::async_predicate(
206    ///     |origin: HeaderValue, _request_parts: &RequestParts| async move {
207    ///         // fetch list of origins that are allowed
208    ///         let origins = client.fetch_allowed_origins().await;
209    ///         origins.contains(&origin)
210    ///     },
211    /// ));
212    ///
213    /// let client = get_api_client();
214    ///
215    /// // if using &RequestParts, make sure all the values are owned
216    /// // before passing into the future
217    /// let layer = CorsLayer::new().allow_origin(AllowOrigin::async_predicate(
218    ///     |origin: HeaderValue, parts: &RequestParts| {
219    ///         let path = parts.uri.path().to_owned();
220    ///
221    ///         async move {
222    ///             // fetch list of origins that are allowed for this path
223    ///             let origins = client.fetch_allowed_origins_for_path(path).await;
224    ///             origins.contains(&origin)
225    ///         }
226    ///     },
227    /// ));
228    /// ```
229    ///
230    /// Note that multiple calls to this method will override any previous
231    /// calls.
232    ///
233    /// Also note that `Access-Control-Allow-Headers` is required for requests that have
234    /// `Access-Control-Request-Headers`.
235    ///
236    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
237    pub fn allow_headers<T>(mut self, headers: T) -> Self
238    where
239        T: Into<AllowHeaders>,
240    {
241        self.allow_headers = headers.into();
242        self
243    }
244
245    /// Set the value of the [`Access-Control-Max-Age`][mdn] header.
246    ///
247    /// ```
248    /// use std::time::Duration;
249    /// use rama_http::layer::cors::CorsLayer;
250    ///
251    /// let layer = CorsLayer::new().max_age(Duration::from_secs(60) * 10);
252    /// ```
253    ///
254    /// By default the header will not be set which disables caching and will
255    /// require a preflight call for all requests.
256    ///
257    /// Note that each browser has a maximum internal value that takes
258    /// precedence when the Access-Control-Max-Age is greater. For more details
259    /// see [mdn].
260    ///
261    /// If you need more flexibility, you can use supply a function which can
262    /// dynamically decide the max-age based on the origin and other parts of
263    /// each preflight request:
264    ///
265    /// ```
266    /// # struct MyServerConfig { cors_max_age: Duration }
267    /// use std::time::Duration;
268    ///
269    /// use rama_http::dep::http::{request::Parts as RequestParts, HeaderValue};
270    /// use rama_http::layer::cors::{CorsLayer, MaxAge};
271    ///
272    /// let layer = CorsLayer::new().max_age(MaxAge::dynamic(
273    ///     |_origin: &HeaderValue, parts: &RequestParts| -> Duration {
274    ///         // Let's say you want to be able to reload your config at
275    ///         // runtime and have another middleware that always inserts
276    ///         // the current config into the request extensions
277    ///         let config = parts.extensions.get::<MyServerConfig>().unwrap();
278    ///         config.cors_max_age
279    ///     },
280    /// ));
281    /// ```
282    ///
283    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
284    pub fn max_age<T>(mut self, max_age: T) -> Self
285    where
286        T: Into<MaxAge>,
287    {
288        self.max_age = max_age.into();
289        self
290    }
291
292    /// Set the value of the [`Access-Control-Allow-Methods`][mdn] header.
293    ///
294    /// ```
295    /// use rama_http::layer::cors::CorsLayer;
296    /// use rama_http::Method;
297    ///
298    /// let layer = CorsLayer::new().allow_methods([Method::GET, Method::POST]);
299    /// ```
300    ///
301    /// All methods can be allowed with
302    ///
303    /// ```
304    /// use rama_http::layer::cors::{Any, CorsLayer};
305    ///
306    /// let layer = CorsLayer::new().allow_methods(Any);
307    /// ```
308    ///
309    /// Note that multiple calls to this method will override any previous
310    /// calls.
311    ///
312    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
313    pub fn allow_methods<T>(mut self, methods: T) -> Self
314    where
315        T: Into<AllowMethods>,
316    {
317        self.allow_methods = methods.into();
318        self
319    }
320
321    /// Set the value of the [`Access-Control-Allow-Origin`][mdn] header.
322    ///
323    /// ```
324    /// use rama_http::HeaderValue;
325    /// use rama_http::layer::cors::CorsLayer;
326    ///
327    /// let layer = CorsLayer::new().allow_origin(
328    ///     "http://example.com".parse::<HeaderValue>().unwrap(),
329    /// );
330    /// ```
331    ///
332    /// Multiple origins can be allowed with
333    ///
334    /// ```
335    /// use rama_http::layer::cors::CorsLayer;
336    ///
337    /// let origins = [
338    ///     "http://example.com".parse().unwrap(),
339    ///     "http://api.example.com".parse().unwrap(),
340    /// ];
341    ///
342    /// let layer = CorsLayer::new().allow_origin(origins);
343    /// ```
344    ///
345    /// All origins can be allowed with
346    ///
347    /// ```
348    /// use rama_http::layer::cors::{Any, CorsLayer};
349    ///
350    /// let layer = CorsLayer::new().allow_origin(Any);
351    /// ```
352    ///
353    /// You can also use a closure
354    ///
355    /// ```
356    /// use rama_http::layer::cors::{CorsLayer, AllowOrigin};
357    /// use rama_http::dep::http::{request::Parts as RequestParts, HeaderValue};
358    ///
359    /// let layer = CorsLayer::new().allow_origin(AllowOrigin::predicate(
360    ///     |origin: &HeaderValue, _request_parts: &RequestParts| {
361    ///         origin.as_bytes().ends_with(b".rust-lang.org")
362    ///     },
363    /// ));
364    /// ```
365    ///
366    /// Note that multiple calls to this method will override any previous
367    /// calls.
368    ///
369    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
370    pub fn allow_origin<T>(mut self, origin: T) -> Self
371    where
372        T: Into<AllowOrigin>,
373    {
374        self.allow_origin = origin.into();
375        self
376    }
377
378    /// Set the value of the [`Access-Control-Expose-Headers`][mdn] header.
379    ///
380    /// ```
381    /// use rama_http::layer::cors::CorsLayer;
382    /// use rama_http::header::CONTENT_ENCODING;
383    ///
384    /// let layer = CorsLayer::new().expose_headers([CONTENT_ENCODING]);
385    /// ```
386    ///
387    /// All headers can be allowed with
388    ///
389    /// ```
390    /// use rama_http::layer::cors::{Any, CorsLayer};
391    ///
392    /// let layer = CorsLayer::new().expose_headers(Any);
393    /// ```
394    ///
395    /// Note that multiple calls to this method will override any previous
396    /// calls.
397    ///
398    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
399    pub fn expose_headers<T>(mut self, headers: T) -> Self
400    where
401        T: Into<ExposeHeaders>,
402    {
403        self.expose_headers = headers.into();
404        self
405    }
406
407    /// Set the value of the [`Access-Control-Allow-Private-Network`][wicg] header.
408    ///
409    /// ```
410    /// use rama_http::layer::cors::CorsLayer;
411    ///
412    /// let layer = CorsLayer::new().allow_private_network(true);
413    /// ```
414    ///
415    /// [wicg]: https://wicg.github.io/private-network-access/
416    pub fn allow_private_network<T>(mut self, allow_private_network: T) -> Self
417    where
418        T: Into<AllowPrivateNetwork>,
419    {
420        self.allow_private_network = allow_private_network.into();
421        self
422    }
423
424    /// Set the value(s) of the [`Vary`][mdn] header.
425    ///
426    /// In contrast to the other headers, this one has a non-empty default of
427    /// [`preflight_request_headers()`].
428    ///
429    /// You only need to set this is you want to remove some of these defaults,
430    /// or if you use a closure for one of the other headers and want to add a
431    /// vary header accordingly.
432    ///
433    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary
434    pub fn vary<T>(mut self, headers: T) -> Self
435    where
436        T: Into<Vary>,
437    {
438        self.vary = headers.into();
439        self
440    }
441}
442
443/// Represents a wildcard value (`*`) used with some CORS headers such as
444/// [`CorsLayer::allow_methods`].
445#[derive(Debug, Clone, Copy)]
446#[must_use]
447pub struct Any;
448
449/// Represents a wildcard value (`*`) used with some CORS headers such as
450/// [`CorsLayer::allow_methods`].
451#[deprecated = "Use Any as a unit struct literal instead"]
452pub fn any() -> Any {
453    Any
454}
455
456fn separated_by_commas<I>(mut iter: I) -> Option<HeaderValue>
457where
458    I: Iterator<Item = HeaderValue>,
459{
460    match iter.next() {
461        Some(fst) => {
462            let mut result = BytesMut::from(fst.as_bytes());
463            for val in iter {
464                result.reserve(val.len() + 1);
465                result.put_u8(b',');
466                result.extend_from_slice(val.as_bytes());
467            }
468
469            Some(HeaderValue::from_maybe_shared(result.freeze()).unwrap())
470        }
471        None => None,
472    }
473}
474
475impl Default for CorsLayer {
476    fn default() -> Self {
477        Self::new()
478    }
479}
480
481impl<S> Layer<S> for CorsLayer {
482    type Service = Cors<S>;
483
484    fn layer(&self, inner: S) -> Self::Service {
485        ensure_usable_cors_rules(self);
486
487        Cors {
488            inner,
489            layer: self.clone(),
490        }
491    }
492}
493
494/// Middleware which adds headers for [CORS][mdn].
495///
496/// See the [module docs](crate::layer::cors) for an example.
497///
498/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
499pub struct Cors<S> {
500    inner: S,
501    layer: CorsLayer,
502}
503
504impl<S: fmt::Debug> fmt::Debug for Cors<S> {
505    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
506        f.debug_struct("Cors")
507            .field("inner", &self.inner)
508            .field("layer", &self.layer)
509            .finish()
510    }
511}
512
513impl<S: Clone> Clone for Cors<S> {
514    fn clone(&self) -> Self {
515        Self {
516            inner: self.inner.clone(),
517            layer: self.layer.clone(),
518        }
519    }
520}
521
522impl<S> Cors<S> {
523    /// Create a new `Cors`.
524    ///
525    /// See [`CorsLayer::new`] for more details.
526    pub fn new(inner: S) -> Self {
527        Self {
528            inner,
529            layer: CorsLayer::new(),
530        }
531    }
532
533    /// A permissive configuration.
534    ///
535    /// See [`CorsLayer::permissive`] for more details.
536    pub fn permissive(inner: S) -> Self {
537        Self {
538            inner,
539            layer: CorsLayer::permissive(),
540        }
541    }
542
543    /// A very permissive configuration.
544    ///
545    /// See [`CorsLayer::very_permissive`] for more details.
546    pub fn very_permissive(inner: S) -> Self {
547        Self {
548            inner,
549            layer: CorsLayer::very_permissive(),
550        }
551    }
552
553    define_inner_service_accessors!();
554
555    /// Set the [`Access-Control-Allow-Credentials`][mdn] header.
556    ///
557    /// See [`CorsLayer::allow_credentials`] for more details.
558    ///
559    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
560    pub fn allow_credentials<T>(self, allow_credentials: T) -> Self
561    where
562        T: Into<AllowCredentials>,
563    {
564        self.map_layer(|layer| layer.allow_credentials(allow_credentials))
565    }
566
567    /// Set the value of the [`Access-Control-Allow-Headers`][mdn] header.
568    ///
569    /// See [`CorsLayer::allow_headers`] for more details.
570    ///
571    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
572    pub fn allow_headers<T>(self, headers: T) -> Self
573    where
574        T: Into<AllowHeaders>,
575    {
576        self.map_layer(|layer| layer.allow_headers(headers))
577    }
578
579    /// Set the value of the [`Access-Control-Max-Age`][mdn] header.
580    ///
581    /// See [`CorsLayer::max_age`] for more details.
582    ///
583    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
584    pub fn max_age<T>(self, max_age: T) -> Self
585    where
586        T: Into<MaxAge>,
587    {
588        self.map_layer(|layer| layer.max_age(max_age))
589    }
590
591    /// Set the value of the [`Access-Control-Allow-Methods`][mdn] header.
592    ///
593    /// See [`CorsLayer::allow_methods`] for more details.
594    ///
595    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
596    pub fn allow_methods<T>(self, methods: T) -> Self
597    where
598        T: Into<AllowMethods>,
599    {
600        self.map_layer(|layer| layer.allow_methods(methods))
601    }
602
603    /// Set the value of the [`Access-Control-Allow-Origin`][mdn] header.
604    ///
605    /// See [`CorsLayer::allow_origin`] for more details.
606    ///
607    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
608    pub fn allow_origin<T>(self, origin: T) -> Self
609    where
610        T: Into<AllowOrigin>,
611    {
612        self.map_layer(|layer| layer.allow_origin(origin))
613    }
614
615    /// Set the value of the [`Access-Control-Expose-Headers`][mdn] header.
616    ///
617    /// See [`CorsLayer::expose_headers`] for more details.
618    ///
619    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
620    pub fn expose_headers<T>(self, headers: T) -> Self
621    where
622        T: Into<ExposeHeaders>,
623    {
624        self.map_layer(|layer| layer.expose_headers(headers))
625    }
626
627    /// Set the value of the [`Access-Control-Allow-Private-Network`][wicg] header.
628    ///
629    /// See [`CorsLayer::allow_private_network`] for more details.
630    ///
631    /// [wicg]: https://wicg.github.io/private-network-access/
632    pub fn allow_private_network<T>(self, allow_private_network: T) -> Self
633    where
634        T: Into<AllowPrivateNetwork>,
635    {
636        self.map_layer(|layer| layer.allow_private_network(allow_private_network))
637    }
638
639    fn map_layer<F>(mut self, f: F) -> Self
640    where
641        F: FnOnce(CorsLayer) -> CorsLayer,
642    {
643        self.layer = f(self.layer);
644        self
645    }
646}
647
648impl<S, State, ReqBody, ResBody> Service<State, Request<ReqBody>> for Cors<S>
649where
650    S: Service<State, Request<ReqBody>, Response = Response<ResBody>>,
651    ReqBody: Send + 'static,
652    ResBody: Default + Send + 'static,
653    State: Clone + Send + Sync + 'static,
654{
655    type Response = S::Response;
656    type Error = S::Error;
657
658    async fn serve(
659        &self,
660        ctx: Context<State>,
661        req: Request<ReqBody>,
662    ) -> Result<Self::Response, Self::Error> {
663        let (parts, body) = req.into_parts();
664        let origin = parts.headers.get(&header::ORIGIN);
665
666        let mut headers = HeaderMap::new();
667
668        // These headers are applied to both preflight and subsequent regular CORS requests:
669        // https://fetch.spec.whatwg.org/#http-responses
670        headers.extend(self.layer.allow_credentials.to_header(origin, &parts));
671        headers.extend(self.layer.allow_private_network.to_header(origin, &parts));
672        headers.extend(self.layer.vary.to_header());
673
674        let allow_origin_future = self.layer.allow_origin.to_future(origin, &parts);
675        headers.extend(allow_origin_future.await);
676
677        // Return results immediately upon preflight request
678        if parts.method == Method::OPTIONS {
679            // These headers are applied only to preflight requests
680            headers.extend(self.layer.allow_methods.to_header(&parts));
681            headers.extend(self.layer.allow_headers.to_header(&parts));
682            headers.extend(self.layer.max_age.to_header(origin, &parts));
683
684            let mut response = Response::new(ResBody::default());
685            mem::swap(response.headers_mut(), &mut headers);
686
687            Ok(response)
688        } else {
689            // This header is applied only to non-preflight requests
690            headers.extend(self.layer.expose_headers.to_header(&parts));
691
692            let req = Request::from_parts(parts, body);
693
694            let mut response: Response<ResBody> = self.inner.serve(ctx, req).await?;
695            let response_headers = response.headers_mut();
696
697            // vary header can have multiple values, don't overwrite
698            // previously-set value(s).
699            if let Some(vary) = headers.remove(header::VARY) {
700                response_headers.append(header::VARY, vary);
701            }
702            // extend will overwrite previous headers of remaining names
703            response_headers.extend(headers.drain());
704
705            Ok(response)
706        }
707    }
708}
709
710fn ensure_usable_cors_rules(layer: &CorsLayer) {
711    if layer.allow_credentials.is_true() {
712        assert!(
713            !layer.allow_headers.is_wildcard(),
714            "Invalid CORS configuration: Cannot combine `Access-Control-Allow-Credentials: true` \
715             with `Access-Control-Allow-Headers: *`"
716        );
717
718        assert!(
719            !layer.allow_methods.is_wildcard(),
720            "Invalid CORS configuration: Cannot combine `Access-Control-Allow-Credentials: true` \
721             with `Access-Control-Allow-Methods: *`"
722        );
723
724        assert!(
725            !layer.allow_origin.is_wildcard(),
726            "Invalid CORS configuration: Cannot combine `Access-Control-Allow-Credentials: true` \
727             with `Access-Control-Allow-Origin: *`"
728        );
729
730        assert!(
731            !layer.expose_headers.is_wildcard(),
732            "Invalid CORS configuration: Cannot combine `Access-Control-Allow-Credentials: true` \
733             with `Access-Control-Expose-Headers: *`"
734        );
735    }
736}
737
738/// Returns an iterator over the three request headers that may be involved in a CORS preflight request.
739///
740/// This is the default set of header names returned in the `vary` header
741pub fn preflight_request_headers() -> impl Iterator<Item = HeaderName> {
742    #[allow(deprecated)] // Can be changed when MSRV >= 1.53
743    array::IntoIter::new([
744        header::ORIGIN,
745        header::ACCESS_CONTROL_REQUEST_METHOD,
746        header::ACCESS_CONTROL_REQUEST_HEADERS,
747    ])
748}