net/fetch/
headers.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use content_security_policy::Destination;
6use headers::{Error, Header, HeaderMap};
7use http::{HeaderName, HeaderValue};
8use net_traits::fetch::headers::get_decode_and_split_header_name;
9use net_traits::request::RequestMode;
10
11static SEC_FETCH_DEST: HeaderName = HeaderName::from_static("sec-fetch-dest");
12
13static SEC_FETCH_MODE: HeaderName = HeaderName::from_static("sec-fetch-mode");
14
15static SEC_FETCH_SITE: HeaderName = HeaderName::from_static("sec-fetch-site");
16
17static SEC_FETCH_USER: HeaderName = HeaderName::from_static("sec-fetch-user");
18
19/// <https://fetch.spec.whatwg.org/#determine-nosniff>
20pub fn determine_nosniff(headers: &HeaderMap) -> bool {
21    let values = get_decode_and_split_header_name("x-content-type-options", headers);
22
23    match values {
24        None => false,
25        Some(values) => !values.is_empty() && values[0].eq_ignore_ascii_case("nosniff"),
26    }
27}
28
29/// The `sec-fetch-dest` header
30pub struct SecFetchDest(pub Destination);
31
32/// The `sec-fetch-mode` header
33///
34/// This is effectively the same as a [RequestMode], except
35/// it doesn't keep track of the websocket protocols
36pub enum SecFetchMode {
37    SameOrigin,
38    Cors,
39    NoCors,
40    Navigate,
41    WebSocket,
42}
43
44/// The `sec-fetch-user` header
45pub struct SecFetchUser;
46
47/// The `sec-fetch-site` header
48#[derive(Eq, PartialEq)]
49pub enum SecFetchSite {
50    None,
51    SameOrigin,
52    SameSite,
53    CrossSite,
54}
55
56impl Header for SecFetchDest {
57    fn name() -> &'static HeaderName {
58        &SEC_FETCH_DEST
59    }
60
61    fn decode<'i, I>(_: &mut I) -> Result<Self, Error>
62    where
63        Self: Sized,
64        I: Iterator<Item = &'i HeaderValue>,
65    {
66        // TODO
67        Err(Error::invalid())
68    }
69
70    fn encode<E>(&self, values: &mut E)
71    where
72        E: Extend<HeaderValue>,
73    {
74        let value = HeaderValue::from_static(destination_as_str(self.0));
75        values.extend(std::iter::once(value));
76    }
77}
78
79impl From<Destination> for SecFetchDest {
80    fn from(value: Destination) -> Self {
81        Self(value)
82    }
83}
84
85impl Header for SecFetchMode {
86    fn name() -> &'static HeaderName {
87        &SEC_FETCH_MODE
88    }
89
90    fn decode<'i, I>(_: &mut I) -> Result<Self, Error>
91    where
92        Self: Sized,
93        I: Iterator<Item = &'i HeaderValue>,
94    {
95        // TODO
96        Err(Error::invalid())
97    }
98
99    fn encode<E>(&self, values: &mut E)
100    where
101        E: Extend<HeaderValue>,
102    {
103        let value = HeaderValue::from_static(self.as_str());
104        values.extend(std::iter::once(value));
105    }
106}
107
108impl<'a> From<&'a RequestMode> for SecFetchMode {
109    fn from(value: &'a RequestMode) -> Self {
110        match value {
111            RequestMode::SameOrigin => Self::SameOrigin,
112            RequestMode::CorsMode => Self::Cors,
113            RequestMode::NoCors => Self::NoCors,
114            RequestMode::Navigate => Self::Navigate,
115            RequestMode::WebSocket { .. } => Self::WebSocket,
116        }
117    }
118}
119
120impl Header for SecFetchSite {
121    fn name() -> &'static HeaderName {
122        &SEC_FETCH_SITE
123    }
124
125    fn decode<'i, I>(_: &mut I) -> Result<Self, Error>
126    where
127        Self: Sized,
128        I: Iterator<Item = &'i HeaderValue>,
129    {
130        // TODO
131        Err(Error::invalid())
132    }
133
134    fn encode<E>(&self, values: &mut E)
135    where
136        E: Extend<HeaderValue>,
137    {
138        let s = match self {
139            Self::None => "none",
140            Self::SameSite => "same-site",
141            Self::CrossSite => "cross-site",
142            Self::SameOrigin => "same-origin",
143        };
144        let value = HeaderValue::from_static(s);
145        values.extend(std::iter::once(value));
146    }
147}
148
149impl Header for SecFetchUser {
150    fn name() -> &'static HeaderName {
151        &SEC_FETCH_USER
152    }
153
154    fn decode<'i, I>(_: &mut I) -> Result<Self, Error>
155    where
156        Self: Sized,
157        I: Iterator<Item = &'i HeaderValue>,
158    {
159        // TODO
160        Err(Error::invalid())
161    }
162
163    fn encode<E>(&self, values: &mut E)
164    where
165        E: Extend<HeaderValue>,
166    {
167        let value = HeaderValue::from_static("?1");
168        values.extend(std::iter::once(value));
169    }
170}
171
172const fn destination_as_str(destination: Destination) -> &'static str {
173    match destination {
174        Destination::None => "empty",
175        Destination::Audio => "audio",
176        Destination::AudioWorklet => "audioworklet",
177        Destination::Document => "document",
178        Destination::Embed => "embed",
179        Destination::Font => "font",
180        Destination::Frame => "frame",
181        Destination::IFrame => "iframe",
182        Destination::Image => "image",
183        Destination::Json => "json",
184        Destination::Manifest => "manifest",
185        Destination::Object => "object",
186        Destination::PaintWorklet => "paintworklet",
187        Destination::Report => "report",
188        Destination::Script => "script",
189        Destination::ServiceWorker => "serviceworker",
190        Destination::SharedWorker => "sharedworker",
191        Destination::Style => "style",
192        Destination::Track => "track",
193        Destination::Video => "video",
194        Destination::WebIdentity => "webidentity",
195        Destination::Worker => "worker",
196        Destination::Xslt => "xslt",
197    }
198}
199
200impl SecFetchMode {
201    /// Converts to the spec representation of a [RequestMode]
202    fn as_str(&self) -> &'static str {
203        match self {
204            Self::SameOrigin => "same-origin",
205            Self::Cors => "cors",
206            Self::NoCors => "no-cors",
207            Self::Navigate => "navigate",
208            Self::WebSocket => "websocket",
209        }
210    }
211}