1use std::cell::Cell;
6use std::str::{self, FromStr};
7
8use dom_struct::dom_struct;
9use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
10use js::rust::HandleObject;
11use net_traits::fetch::headers::{
12 extract_mime_type, get_decode_and_split_header_value, get_value_from_header_list,
13 is_forbidden_method,
14};
15use net_traits::request::is_cors_safelisted_request_header;
16use net_traits::trim_http_whitespace;
17use script_bindings::cformat;
18
19use crate::dom::bindings::cell::DomRefCell;
20use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
21use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
22use crate::dom::bindings::iterable::Iterable;
23use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::bindings::str::{ByteString, is_token};
26use crate::dom::globalscope::GlobalScope;
27use crate::script_runtime::CanGc;
28
29#[dom_struct]
30pub(crate) struct Headers {
31 reflector_: Reflector,
32 guard: Cell<Guard>,
33 #[ignore_malloc_size_of = "Defined in hyper"]
34 #[no_trace]
35 header_list: DomRefCell<HyperHeaders>,
36}
37
38#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
40pub(crate) enum Guard {
41 Immutable,
42 Request,
43 RequestNoCors,
44 Response,
45 None,
46}
47
48impl Headers {
49 pub(crate) fn new_inherited() -> Headers {
50 Headers {
51 reflector_: Reflector::new(),
52 guard: Cell::new(Guard::None),
53 header_list: DomRefCell::new(HyperHeaders::new()),
54 }
55 }
56
57 pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Headers> {
58 Self::new_with_proto(global, None, can_gc)
59 }
60
61 fn new_with_proto(
62 global: &GlobalScope,
63 proto: Option<HandleObject>,
64 can_gc: CanGc,
65 ) -> DomRoot<Headers> {
66 reflect_dom_object_with_proto(Box::new(Headers::new_inherited()), global, proto, can_gc)
67 }
68}
69
70impl HeadersMethods<crate::DomTypeHolder> for Headers {
71 fn Constructor(
73 global: &GlobalScope,
74 proto: Option<HandleObject>,
75 can_gc: CanGc,
76 init: Option<HeadersInit>,
77 ) -> Fallible<DomRoot<Headers>> {
78 let dom_headers_new = Headers::new_with_proto(global, proto, can_gc);
79 dom_headers_new.fill(init)?;
80 Ok(dom_headers_new)
81 }
82
83 fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult {
85 let value = trim_http_whitespace(&value);
87
88 let Some((mut valid_name, valid_value)) =
90 self.validate_name_and_value(name, ByteString::new(value.into()))?
91 else {
92 return Ok(());
93 };
94
95 valid_name = valid_name.to_lowercase();
96
97 if self.guard.get() == Guard::RequestNoCors {
99 let tmp_value = if let Some(mut value) =
101 get_value_from_header_list(&valid_name, &self.header_list.borrow())
102 {
103 value.extend(b", ");
105 value.extend(valid_value.to_vec());
106 value
107 } else {
108 valid_value.to_vec()
110 };
111 if !is_cors_safelisted_request_header(&valid_name, &tmp_value) {
113 return Ok(());
114 }
115 }
116
117 match HeaderValue::from_bytes(&valid_value) {
119 Ok(value) => {
120 self.header_list
121 .borrow_mut()
122 .append(HeaderName::from_str(&valid_name).unwrap(), value);
123 },
124 Err(_) => {
125 warn!(
127 "Servo thinks \"{:?}\" is a valid HTTP header value but HeaderValue doesn't.",
128 valid_value
129 );
130 },
131 };
132
133 if self.guard.get() == Guard::RequestNoCors {
135 self.remove_privileged_no_cors_request_headers();
136 }
137
138 Ok(())
139 }
140
141 fn Delete(&self, name: ByteString) -> ErrorResult {
143 let name_and_value = self.validate_name_and_value(name, ByteString::new(vec![]))?;
145 let Some((mut valid_name, _valid_value)) = name_and_value else {
146 return Ok(());
147 };
148
149 valid_name = valid_name.to_lowercase();
150
151 if self.guard.get() == Guard::RequestNoCors &&
154 !is_cors_safelisted_request_header(&valid_name, &b"invalid".to_vec())
155 {
156 return Ok(());
157 }
158
159 self.header_list.borrow_mut().remove(valid_name);
162
163 if self.guard.get() == Guard::RequestNoCors {
165 self.remove_privileged_no_cors_request_headers();
166 }
167
168 Ok(())
169 }
170
171 fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> {
173 let valid_name = validate_name(name)?;
175
176 Ok(
178 get_value_from_header_list(&valid_name, &self.header_list.borrow())
179 .map(ByteString::new),
180 )
181 }
182
183 fn GetSetCookie(&self) -> Vec<ByteString> {
185 self.header_list
189 .borrow()
190 .get_all("set-cookie")
191 .iter()
192 .map(|v| ByteString::new(v.as_bytes().to_vec()))
193 .collect()
194 }
195
196 fn Has(&self, name: ByteString) -> Fallible<bool> {
198 let valid_name = validate_name(name)?;
200 Ok(self.header_list.borrow_mut().get(&valid_name).is_some())
202 }
203
204 fn Set(&self, name: ByteString, value: ByteString) -> Fallible<()> {
206 let value = trim_http_whitespace(&value);
208
209 let Some((mut valid_name, valid_value)) =
211 self.validate_name_and_value(name, ByteString::new(value.into()))?
212 else {
213 return Ok(());
214 };
215 valid_name = valid_name.to_lowercase();
216
217 if self.guard.get() == Guard::RequestNoCors &&
220 !is_cors_safelisted_request_header(&valid_name, &valid_value.to_vec())
221 {
222 return Ok(());
223 }
224
225 match HeaderValue::from_bytes(&valid_value) {
228 Ok(value) => {
229 self.header_list
230 .borrow_mut()
231 .insert(HeaderName::from_str(&valid_name).unwrap(), value);
232 },
233 Err(_) => {
234 warn!(
236 "Servo thinks \"{:?}\" is a valid HTTP header value but HeaderValue doesn't.",
237 valid_value
238 );
239 },
240 };
241
242 if self.guard.get() == Guard::RequestNoCors {
244 self.remove_privileged_no_cors_request_headers();
245 }
246
247 Ok(())
248 }
249}
250
251impl Headers {
252 pub(crate) fn copy_from_headers(&self, headers: DomRoot<Headers>) -> ErrorResult {
253 for (name, value) in headers.header_list.borrow().iter() {
254 self.Append(
255 ByteString::new(Vec::from(name.as_str())),
256 ByteString::new(Vec::from(value.as_bytes())),
257 )?;
258 }
259 Ok(())
260 }
261
262 pub(crate) fn fill(&self, filler: Option<HeadersInit>) -> ErrorResult {
264 match filler {
265 Some(HeadersInit::ByteStringSequenceSequence(v)) => {
266 for mut seq in v {
267 if seq.len() == 2 {
268 let val = seq.pop().unwrap();
269 let name = seq.pop().unwrap();
270 self.Append(name, val)?;
271 } else {
272 return Err(Error::Type(cformat!(
273 "Each header object must be a sequence of length 2 - found one with length {}",
274 seq.len()
275 )));
276 }
277 }
278 Ok(())
279 },
280 Some(HeadersInit::ByteStringByteStringRecord(m)) => {
281 for (key, value) in m.iter() {
282 self.Append(key.clone(), value.clone())?;
283 }
284 Ok(())
285 },
286 None => Ok(()),
287 }
288 }
289
290 pub(crate) fn for_request(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Headers> {
291 let headers_for_request = Headers::new(global, can_gc);
292 headers_for_request.guard.set(Guard::Request);
293 headers_for_request
294 }
295
296 pub(crate) fn for_response(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Headers> {
297 let headers_for_response = Headers::new(global, can_gc);
298 headers_for_response.guard.set(Guard::Response);
299 headers_for_response
300 }
301
302 pub(crate) fn set_guard(&self, new_guard: Guard) {
303 self.guard.set(new_guard)
304 }
305
306 pub(crate) fn get_guard(&self) -> Guard {
307 self.guard.get()
308 }
309
310 pub(crate) fn set_headers(&self, hyper_headers: HyperHeaders) {
311 *self.header_list.borrow_mut() = hyper_headers;
312 }
313
314 pub(crate) fn get_headers_list(&self) -> HyperHeaders {
315 self.header_list.borrow_mut().clone()
316 }
317
318 pub(crate) fn extract_mime_type(&self) -> Vec<u8> {
320 extract_mime_type(&self.header_list.borrow()).unwrap_or_default()
321 }
322
323 pub(crate) fn sort_and_combine(&self) -> Vec<(String, Vec<u8>)> {
325 let borrowed_header_list = self.header_list.borrow();
326 let mut header_vec = vec![];
327
328 for name in borrowed_header_list.keys() {
329 let name = name.as_str();
330 if name == "set-cookie" {
331 for value in borrowed_header_list.get_all(name).iter() {
332 header_vec.push((name.to_owned(), value.as_bytes().to_vec()));
333 }
334 } else if let Some(value) = get_value_from_header_list(name, &borrowed_header_list) {
335 header_vec.push((name.to_owned(), value));
336 }
337 }
338
339 header_vec.sort_by(|a, b| a.0.cmp(&b.0));
340 header_vec
341 }
342
343 pub(crate) fn remove_privileged_no_cors_request_headers(&self) {
345 self.header_list.borrow_mut().remove("range");
347 }
348
349 pub(crate) fn validate_name_and_value(
351 &self,
352 name: ByteString,
353 value: ByteString,
354 ) -> Fallible<Option<(String, ByteString)>> {
355 let valid_name = validate_name(name)?;
357 if !is_legal_header_value(&value) {
358 return Err(Error::Type(c"Header value is not valid".to_owned()));
359 }
360 if self.guard.get() == Guard::Immutable {
362 return Err(Error::Type(c"Guard is immutable".to_owned()));
363 }
364 if self.guard.get() == Guard::Request && is_forbidden_request_header(&valid_name, &value) {
366 return Ok(None);
367 }
368 if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
370 return Ok(None);
371 }
372
373 Ok(Some((valid_name, value)))
374 }
375}
376
377impl Iterable for Headers {
378 type Key = ByteString;
379 type Value = ByteString;
380
381 fn get_iterable_length(&self) -> u32 {
382 let sorted_header_vec = self.sort_and_combine();
383 sorted_header_vec.len() as u32
384 }
385
386 fn get_value_at_index(&self, n: u32) -> ByteString {
387 let sorted_header_vec = self.sort_and_combine();
388 let value = sorted_header_vec[n as usize].1.clone();
389 ByteString::new(value)
390 }
391
392 fn get_key_at_index(&self, n: u32) -> ByteString {
393 let sorted_header_vec = self.sort_and_combine();
394 let key = sorted_header_vec[n as usize].0.clone();
395 ByteString::new(key.into_bytes().to_vec())
396 }
397}
398
399pub(crate) fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
404 let forbidden_header_names = [
405 "accept-charset",
406 "accept-encoding",
407 "access-control-request-headers",
408 "access-control-request-method",
409 "connection",
410 "content-length",
411 "cookie",
412 "cookie2",
413 "date",
414 "dnt",
415 "expect",
416 "host",
417 "keep-alive",
418 "origin",
419 "referer",
420 "set-cookie",
421 "te",
422 "trailer",
423 "transfer-encoding",
424 "upgrade",
425 "via",
426 "access-control-request-private-network",
430 ];
431
432 let lowercase_name = name.to_lowercase();
435
436 if forbidden_header_names.contains(&lowercase_name.as_str()) {
437 return true;
438 }
439
440 let forbidden_header_prefixes = ["sec-", "proxy-"];
441
442 if forbidden_header_prefixes
444 .iter()
445 .any(|prefix| lowercase_name.starts_with(prefix))
446 {
447 return true;
448 }
449
450 let potentially_forbidden_header_names = [
451 "x-http-method",
452 "x-http-method-override",
453 "x-method-override",
454 ];
455
456 if potentially_forbidden_header_names
458 .iter()
459 .any(|header| *header == lowercase_name)
460 {
461 let parsed_values = get_decode_and_split_header_value(value.to_vec());
463
464 return parsed_values
467 .iter()
468 .any(|s| is_forbidden_method(s.as_bytes()));
469 }
470
471 false
473}
474
475fn is_forbidden_response_header(name: &str) -> bool {
477 let name = name.to_ascii_lowercase();
479 matches!(name.as_str(), "set-cookie" | "set-cookie2")
480}
481
482fn validate_name(name: ByteString) -> Fallible<String> {
483 if !is_field_name(&name) {
484 return Err(Error::Type(c"Name is not valid".to_owned()));
485 }
486 match String::from_utf8(name.into()) {
487 Ok(ns) => Ok(ns),
488 _ => Err(Error::Type(c"Non-UTF8 header name found".to_owned())),
489 }
490}
491
492fn is_field_name(name: &ByteString) -> bool {
494 is_token(name)
495}
496
497fn is_legal_header_value(value: &[u8]) -> bool {
506 let value_len = value.len();
507 if value_len == 0 {
508 return true;
509 }
510 match value[0] {
511 b' ' | b'\t' => return false,
512 _ => {},
513 };
514 match value[value_len - 1] {
515 b' ' | b'\t' => return false,
516 _ => {},
517 };
518 for &ch in value {
519 match ch {
520 b'\0' | b'\n' | b'\r' => return false,
521 _ => {},
522 }
523 }
524 true
525 }
539
540pub(crate) fn is_vchar(x: u8) -> bool {
542 matches!(x, 0x21..=0x7E)
543}
544
545pub(crate) fn is_obs_text(x: u8) -> bool {
547 matches!(x, 0x80..=0xFF)
548}