script: Refactor dom/headers to match spec better (#36943)

This includes removing an implementation of normalize for `ByteString`,
because it is effectively duplicated in net under
`trim_http_whitespace`. This is part of an attempt to cleanup and
centralize code for header parsing and manipulation.

Testing: Covered by existing WPT tests

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
This commit is contained in:
Sebastian C 2025-05-21 14:07:32 -05:00 committed by GitHub
parent aaacd61800
commit cdf5fdd2b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 121 additions and 231 deletions

View file

@ -13,6 +13,7 @@ use net_traits::fetch::headers::{
is_forbidden_method, is_forbidden_method,
}; };
use net_traits::request::is_cors_safelisted_request_header; use net_traits::request::is_cors_safelisted_request_header;
use net_traits::trim_http_whitespace;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods}; use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
@ -33,7 +34,7 @@ pub(crate) struct Headers {
header_list: DomRefCell<HyperHeaders>, header_list: DomRefCell<HyperHeaders>,
} }
// https://fetch.spec.whatwg.org/#concept-headers-guard /// <https://fetch.spec.whatwg.org/#concept-headers-guard>
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) enum Guard { pub(crate) enum Guard {
Immutable, Immutable,
@ -66,7 +67,7 @@ impl Headers {
} }
impl HeadersMethods<crate::DomTypeHolder> for Headers { impl HeadersMethods<crate::DomTypeHolder> for Headers {
// https://fetch.spec.whatwg.org/#dom-headers /// <https://fetch.spec.whatwg.org/#dom-headers>
fn Constructor( fn Constructor(
global: &GlobalScope, global: &GlobalScope,
proto: Option<HandleObject>, proto: Option<HandleObject>,
@ -78,47 +79,41 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
Ok(dom_headers_new) Ok(dom_headers_new)
} }
// https://fetch.spec.whatwg.org/#concept-headers-append /// <https://fetch.spec.whatwg.org/#concept-headers-append>
fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult { fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult {
// Step 1 // 1. Normalize value.
let value = normalize_value(value); let value = trim_http_whitespace(&value);
// Step 2 // 2. If validating (name, value) for headers returns false, then return.
// https://fetch.spec.whatwg.org/#headers-validate let Some((mut valid_name, valid_value)) =
let (mut valid_name, valid_value) = validate_name_and_value(name, value)?; self.validate_name_and_value(name, ByteString::new(value.into()))?
else {
return Ok(());
};
valid_name = valid_name.to_lowercase(); valid_name = valid_name.to_lowercase();
if self.guard.get() == Guard::Immutable { // 3. If headerss guard is "request-no-cors":
return Err(Error::Type("Guard is immutable".to_string()));
}
if self.guard.get() == Guard::Request &&
is_forbidden_request_header(&valid_name, &valid_value)
{
return Ok(());
}
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
return Ok(());
}
// Step 3
if self.guard.get() == Guard::RequestNoCors { if self.guard.get() == Guard::RequestNoCors {
// 3.1. Let temporaryValue be the result of getting name from headerss header list.
let tmp_value = if let Some(mut value) = let tmp_value = if let Some(mut value) =
get_value_from_header_list(&valid_name, &self.header_list.borrow()) get_value_from_header_list(&valid_name, &self.header_list.borrow())
{ {
// 3.3. Otherwise, set temporaryValue to temporaryValue, followed by 0x2C 0x20, followed by value.
value.extend(b", "); value.extend(b", ");
value.extend(valid_value.clone()); value.extend(valid_value.to_vec());
value value
} else { } else {
valid_value.clone() // 3.2. If temporaryValue is null, then set temporaryValue to value.
valid_value.to_vec()
}; };
// 3.4. If (name, temporaryValue) is not a no-CORS-safelisted request-header, then return.
if !is_cors_safelisted_request_header(&valid_name, &tmp_value) { if !is_cors_safelisted_request_header(&valid_name, &tmp_value) {
return Ok(()); return Ok(());
} }
} }
// Step 4 // 4. Append (name, value) to headerss header list.
match HeaderValue::from_bytes(&valid_value) { match HeaderValue::from_bytes(&valid_value) {
Ok(value) => { Ok(value) => {
self.header_list self.header_list
@ -134,7 +129,7 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
}, },
}; };
// Step 5 // 5. If headerss guard is "request-no-cors", then remove privileged no-CORS request-headers from headers.
if self.guard.get() == Guard::RequestNoCors { if self.guard.get() == Guard::RequestNoCors {
self.remove_privileged_no_cors_request_headers(); self.remove_privileged_no_cors_request_headers();
} }
@ -142,50 +137,53 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
Ok(()) Ok(())
} }
// https://fetch.spec.whatwg.org/#dom-headers-delete /// <https://fetch.spec.whatwg.org/#dom-headers-delete>
fn Delete(&self, name: ByteString) -> ErrorResult { fn Delete(&self, name: ByteString) -> ErrorResult {
// Step 1 // Step 1 If validating (name, ``) for this returns false, then return.
let (mut valid_name, valid_value) = validate_name_and_value(name, ByteString::new(vec![]))?; let name_and_value = self.validate_name_and_value(name, ByteString::new(vec![]))?;
let Some((mut valid_name, _valid_value)) = name_and_value else {
return Ok(());
};
valid_name = valid_name.to_lowercase(); valid_name = valid_name.to_lowercase();
// Step 2 // Step 2 If thiss guard is "request-no-cors", name is not a no-CORS-safelisted request-header name,
if self.guard.get() == Guard::Immutable { // and name is not a privileged no-CORS request-header name, then return.
return Err(Error::Type("Guard is immutable".to_string()));
}
// Step 3
if self.guard.get() == Guard::Request &&
is_forbidden_request_header(&valid_name, &valid_value)
{
return Ok(());
}
// Step 4
if self.guard.get() == Guard::RequestNoCors && if self.guard.get() == Guard::RequestNoCors &&
!is_cors_safelisted_request_header(&valid_name, &b"invalid".to_vec()) !is_cors_safelisted_request_header(&valid_name, &b"invalid".to_vec())
{ {
return Ok(()); return Ok(());
} }
// Step 5
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) { // 3. If thiss header list does not contain name, then return.
return Ok(()); // 4. Delete name from thiss header list.
self.header_list.borrow_mut().remove(valid_name);
// 5. If thiss guard is "request-no-cors", then remove privileged no-CORS request-headers from this.
if self.guard.get() == Guard::RequestNoCors {
self.remove_privileged_no_cors_request_headers();
} }
// Step 6
self.header_list.borrow_mut().remove(&valid_name);
Ok(()) Ok(())
} }
// https://fetch.spec.whatwg.org/#dom-headers-get /// <https://fetch.spec.whatwg.org/#dom-headers-get>
fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> { fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> {
// Step 1 // 1. If name is not a header name, then throw a TypeError.
let valid_name = validate_name(name)?; let valid_name = validate_name(name)?;
// 2. Return the result of getting name from thiss header list.
Ok( Ok(
get_value_from_header_list(&valid_name, &self.header_list.borrow()) get_value_from_header_list(&valid_name, &self.header_list.borrow())
.map(ByteString::new), .map(ByteString::new),
) )
} }
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie /// <https://fetch.spec.whatwg.org/#dom-headers-getsetcookie>
fn GetSetCookie(&self) -> Vec<ByteString> { fn GetSetCookie(&self) -> Vec<ByteString> {
// 1. If thiss header list does not contain `Set-Cookie`, then return « ».
// 2. Return the values of all headers in thiss header list whose name is a
// byte-case-insensitive match for `Set-Cookie`, in order.
self.header_list self.header_list
.borrow() .borrow()
.get_all("set-cookie") .get_all("set-cookie")
@ -194,42 +192,36 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
.collect() .collect()
} }
// https://fetch.spec.whatwg.org/#dom-headers-has /// <https://fetch.spec.whatwg.org/#dom-headers-has>
fn Has(&self, name: ByteString) -> Fallible<bool> { fn Has(&self, name: ByteString) -> Fallible<bool> {
// Step 1 // 1. If name is not a header name, then throw a TypeError.
let valid_name = validate_name(name)?; let valid_name = validate_name(name)?;
// Step 2 // 2. Return true if thiss header list contains name; otherwise false.
Ok(self.header_list.borrow_mut().get(&valid_name).is_some()) Ok(self.header_list.borrow_mut().get(&valid_name).is_some())
} }
// https://fetch.spec.whatwg.org/#dom-headers-set /// <https://fetch.spec.whatwg.org/#dom-headers-set>
fn Set(&self, name: ByteString, value: ByteString) -> Fallible<()> { fn Set(&self, name: ByteString, value: ByteString) -> Fallible<()> {
// Step 1 // 1. Normalize value
let value = normalize_value(value); let value = trim_http_whitespace(&value);
// Step 2
let (mut valid_name, valid_value) = validate_name_and_value(name, value)?; // 2. If validating (name, value) for this returns false, then return.
let Some((mut valid_name, valid_value)) =
self.validate_name_and_value(name, ByteString::new(value.into()))?
else {
return Ok(());
};
valid_name = valid_name.to_lowercase(); valid_name = valid_name.to_lowercase();
// Step 3
if self.guard.get() == Guard::Immutable { // 3. If thiss guard is "request-no-cors" and (name, value) is not a
return Err(Error::Type("Guard is immutable".to_string())); // no-CORS-safelisted request-header, then return.
}
// Step 4
if self.guard.get() == Guard::Request &&
is_forbidden_request_header(&valid_name, &valid_value)
{
return Ok(());
}
// Step 5
if self.guard.get() == Guard::RequestNoCors && if self.guard.get() == Guard::RequestNoCors &&
!is_cors_safelisted_request_header(&valid_name, &valid_value) !is_cors_safelisted_request_header(&valid_name, &valid_value.to_vec())
{ {
return Ok(()); return Ok(());
} }
// Step 6
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) { // 4. Set (name, value) in thiss header list.
return Ok(());
}
// Step 7
// https://fetch.spec.whatwg.org/#concept-header-list-set // https://fetch.spec.whatwg.org/#concept-header-list-set
match HeaderValue::from_bytes(&valid_value) { match HeaderValue::from_bytes(&valid_value) {
Ok(value) => { Ok(value) => {
@ -245,6 +237,12 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
); );
}, },
}; };
// 5. If thiss guard is "request-no-cors", then remove privileged no-CORS request-headers from this.
if self.guard.get() == Guard::RequestNoCors {
self.remove_privileged_no_cors_request_headers();
}
Ok(()) Ok(())
} }
} }
@ -260,7 +258,7 @@ impl Headers {
Ok(()) Ok(())
} }
// https://fetch.spec.whatwg.org/#concept-headers-fill /// <https://fetch.spec.whatwg.org/#concept-headers-fill>
pub(crate) fn fill(&self, filler: Option<HeadersInit>) -> ErrorResult { pub(crate) fn fill(&self, filler: Option<HeadersInit>) -> ErrorResult {
match filler { match filler {
Some(HeadersInit::ByteStringSequenceSequence(v)) => { Some(HeadersInit::ByteStringSequenceSequence(v)) => {
@ -316,12 +314,12 @@ impl Headers {
self.header_list.borrow_mut().clone() self.header_list.borrow_mut().clone()
} }
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type /// <https://fetch.spec.whatwg.org/#concept-header-extract-mime-type>
pub(crate) fn extract_mime_type(&self) -> Vec<u8> { pub(crate) fn extract_mime_type(&self) -> Vec<u8> {
extract_mime_type(&self.header_list.borrow()).unwrap_or_default() extract_mime_type(&self.header_list.borrow()).unwrap_or_default()
} }
// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine /// <https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine>
pub(crate) fn sort_and_combine(&self) -> Vec<(String, Vec<u8>)> { pub(crate) fn sort_and_combine(&self) -> Vec<(String, Vec<u8>)> {
let borrowed_header_list = self.header_list.borrow(); let borrowed_header_list = self.header_list.borrow();
let mut header_vec = vec![]; let mut header_vec = vec![];
@ -341,11 +339,38 @@ impl Headers {
header_vec header_vec
} }
// https://fetch.spec.whatwg.org/#ref-for-privileged-no-cors-request-header-name /// <https://fetch.spec.whatwg.org/#ref-for-privileged-no-cors-request-header-name>
pub(crate) fn remove_privileged_no_cors_request_headers(&self) { pub(crate) fn remove_privileged_no_cors_request_headers(&self) {
// https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name // <https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name>
self.header_list.borrow_mut().remove("range"); self.header_list.borrow_mut().remove("range");
} }
/// <https://fetch.spec.whatwg.org/#headers-validate>
pub(crate) fn validate_name_and_value(
&self,
name: ByteString,
value: ByteString,
) -> Fallible<Option<(String, ByteString)>> {
// 1. If name is not a header name or value is not a header value, then throw a TypeError.
let valid_name = validate_name(name)?;
if !is_legal_header_value(&value) {
return Err(Error::Type("Header value is not valid".to_string()));
}
// 2. If headerss guard is "immutable", then throw a TypeError.
if self.guard.get() == Guard::Immutable {
return Err(Error::Type("Guard is immutable".to_string()));
}
// 3. If headerss guard is "request" and (name, value) is a forbidden request-header, then return false.
if self.guard.get() == Guard::Request && is_forbidden_request_header(&valid_name, &value) {
return Ok(None);
}
// 4. If headerss guard is "response" and name is a forbidden response-header name, then return false.
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
return Ok(None);
}
Ok(Some((valid_name, value)))
}
} }
impl Iterable for Headers { impl Iterable for Headers {
@ -391,6 +416,7 @@ pub(crate) fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
"keep-alive", "keep-alive",
"origin", "origin",
"referer", "referer",
"set-cookie",
"te", "te",
"trailer", "trailer",
"transfer-encoding", "transfer-encoding",
@ -448,26 +474,11 @@ pub(crate) fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
false false
} }
// https://fetch.spec.whatwg.org/#forbidden-response-header-name /// <https://fetch.spec.whatwg.org/#forbidden-response-header-name>
fn is_forbidden_response_header(name: &str) -> bool { fn is_forbidden_response_header(name: &str) -> bool {
matches!(name, "set-cookie" | "set-cookie2") // A forbidden response-header name is a header name that is a byte-case-insensitive match for one of
} let name = name.to_ascii_lowercase();
matches!(name.as_str(), "set-cookie" | "set-cookie2")
// There is some unresolved confusion over the definition of a name and a value.
//
// As of December 2019, WHATWG has no formal grammar production for value;
// https://fetch.spec.whatg.org/#concept-header-value just says not to have
// newlines, nulls, or leading/trailing whitespace. It even allows
// octets that aren't a valid UTF-8 encoding, and WPT tests reflect this.
// The HeaderValue class does not fully reflect this, so headers
// containing bytes with values 1..31 or 127 can't be created, failing
// WPT tests but probably not affecting anything important on the real Internet.
fn validate_name_and_value(name: ByteString, value: ByteString) -> Fallible<(String, Vec<u8>)> {
let valid_name = validate_name(name)?;
if !is_legal_header_value(&value) {
return Err(Error::Type("Header value is not valid".to_string()));
}
Ok((valid_name, value.into()))
} }
fn validate_name(name: ByteString) -> Fallible<String> { fn validate_name(name: ByteString) -> Fallible<String> {
@ -480,47 +491,20 @@ fn validate_name(name: ByteString) -> Fallible<String> {
} }
} }
// Removes trailing and leading HTTP whitespace bytes. /// <http://tools.ietf.org/html/rfc7230#section-3.2>
// https://fetch.spec.whatwg.org/#concept-header-value-normalize
pub fn normalize_value(value: ByteString) -> ByteString {
match (
index_of_first_non_whitespace(&value),
index_of_last_non_whitespace(&value),
) {
(Some(begin), Some(end)) => ByteString::new(value[begin..end + 1].to_owned()),
_ => ByteString::new(vec![]),
}
}
fn is_http_whitespace(byte: u8) -> bool {
byte == b'\t' || byte == b'\n' || byte == b'\r' || byte == b' '
}
fn index_of_first_non_whitespace(value: &ByteString) -> Option<usize> {
for (index, &byte) in value.iter().enumerate() {
if !is_http_whitespace(byte) {
return Some(index);
}
}
None
}
fn index_of_last_non_whitespace(value: &ByteString) -> Option<usize> {
for (index, &byte) in value.iter().enumerate().rev() {
if !is_http_whitespace(byte) {
return Some(index);
}
}
None
}
// http://tools.ietf.org/html/rfc7230#section-3.2
fn is_field_name(name: &ByteString) -> bool { fn is_field_name(name: &ByteString) -> bool {
is_token(name) is_token(name)
} }
// https://fetch.spec.whatg.org/#concept-header-value // As of December 2019, WHATWG has no formal grammar production for value;
fn is_legal_header_value(value: &ByteString) -> bool { // https://fetch.spec.whatg.org/#concept-header-value just says not to have
// newlines, nulls, or leading/trailing whitespace. It even allows
// octets that aren't a valid UTF-8 encoding, and WPT tests reflect this.
// The HeaderValue class does not fully reflect this, so headers
// containing bytes with values 1..31 or 127 can't be created, failing
// WPT tests but probably not affecting anything important on the real Internet.
/// <https://fetch.spec.whatg.org/#concept-header-value>
fn is_legal_header_value(value: &[u8]) -> bool {
let value_len = value.len(); let value_len = value.len();
if value_len == 0 { if value_len == 0 {
return true; return true;
@ -533,7 +517,7 @@ fn is_legal_header_value(value: &ByteString) -> bool {
b' ' | b'\t' => return false, b' ' | b'\t' => return false,
_ => {}, _ => {},
}; };
for &ch in &value[..] { for &ch in value {
match ch { match ch {
b'\0' | b'\n' | b'\r' => return false, b'\0' | b'\n' | b'\r' => return false,
_ => {}, _ => {},
@ -555,12 +539,12 @@ fn is_legal_header_value(value: &ByteString) -> bool {
// } // }
} }
// https://tools.ietf.org/html/rfc5234#appendix-B.1 /// <https://tools.ietf.org/html/rfc5234#appendix-B.1>
pub(crate) fn is_vchar(x: u8) -> bool { pub(crate) fn is_vchar(x: u8) -> bool {
matches!(x, 0x21..=0x7E) matches!(x, 0x21..=0x7E)
} }
// http://tools.ietf.org/html/rfc7230#section-3.2.6 /// <http://tools.ietf.org/html/rfc7230#section-3.2.6>
pub(crate) fn is_obs_text(x: u8) -> bool { pub(crate) fn is_obs_text(x: u8) -> bool {
matches!(x, 0x80..=0xFF) matches!(x, 0x80..=0xFF)
} }

View file

@ -7,7 +7,6 @@
pub use crate::dom::bindings::refcounted::TrustedPromise; pub use crate::dom::bindings::refcounted::TrustedPromise;
//pub use crate::dom::bindings::root::Dom; //pub use crate::dom::bindings::root::Dom;
pub use crate::dom::bindings::str::{ByteString, DOMString}; pub use crate::dom::bindings::str::{ByteString, DOMString};
pub use crate::dom::headers::normalize_value;
//pub use crate::dom::node::Node; //pub use crate::dom::node::Node;
pub mod area { pub mod area {

View file

@ -1,75 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use script::test::{ByteString, normalize_value};
#[test]
fn test_normalize_empty_bytestring() {
// empty ByteString test
let empty_bytestring = ByteString::new(vec![]);
let actual = normalize_value(empty_bytestring);
let expected = ByteString::new(vec![]);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_all_whitespace_bytestring() {
// All whitespace test. A horizontal tab, a line feed, a carriage return , and a space
let all_whitespace_bytestring = ByteString::new(vec![b'\t', b'\n', b'\r', b' ']);
let actual = normalize_value(all_whitespace_bytestring);
let expected = ByteString::new(vec![]);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_non_empty_no_whitespace_bytestring() {
// Non-empty, no whitespace ByteString test
let no_whitespace_bytestring = ByteString::new(vec![b'S', b'!']);
let actual = normalize_value(no_whitespace_bytestring);
let expected = ByteString::new(vec![b'S', b'!']);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_non_empty_leading_whitespace_bytestring() {
// Non-empty, leading whitespace, no trailing whitespace ByteString test
let leading_whitespace_bytestring =
ByteString::new(vec![b'\t', b'\n', b' ', b'\r', b'S', b'!']);
let actual = normalize_value(leading_whitespace_bytestring);
let expected = ByteString::new(vec![b'S', b'!']);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_non_empty_no_leading_whitespace_trailing_whitespace_bytestring() {
// Non-empty, no leading whitespace, but with trailing whitespace ByteString test
let trailing_whitespace_bytestring =
ByteString::new(vec![b'S', b'!', b'\t', b'\n', b' ', b'\r']);
let actual = normalize_value(trailing_whitespace_bytestring);
let expected = ByteString::new(vec![b'S', b'!']);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_non_empty_leading_and_trailing_whitespace_bytestring() {
// Non-empty, leading whitespace, and trailing whitespace ByteString test
let whitespace_sandwich_bytestring = ByteString::new(vec![
b'\t', b'\n', b' ', b'\r', b'S', b'!', b'\t', b'\n', b' ', b'\r',
]);
let actual = normalize_value(whitespace_sandwich_bytestring);
let expected = ByteString::new(vec![b'S', b'!']);
assert_eq!(actual, expected);
}
#[test]
fn test_normalize_non_empty_leading_trailing_and_internal_whitespace_bytestring() {
// Non-empty, leading whitespace, trailing whitespace,
// and internal whitespace ByteString test
let whitespace_bigmac_bytestring = ByteString::new(vec![
b'\t', b'\n', b' ', b'\r', b'S', b'\t', b'\n', b' ', b'\r', b'!', b'\t', b'\n', b' ', b'\r',
]);
let actual = normalize_value(whitespace_bigmac_bytestring);
let expected = ByteString::new(vec![b'S', b'\t', b'\n', b' ', b'\r', b'!']);
assert_eq!(actual, expected);
}

View file

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#[cfg(test)]
mod headers;
#[cfg(test)] #[cfg(test)]
mod htmlareaelement; mod htmlareaelement;
#[cfg(test)] #[cfg(test)]

View file

@ -1,19 +1,3 @@
[request-headers.any.html]
[Adding invalid request header "Set-Cookie: KO"]
expected: FAIL
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
expected: FAIL
[request-headers.any.worker.html]
[Adding invalid request header "Set-Cookie: KO"]
expected: FAIL
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
expected: FAIL
[request-headers.any.serviceworker.html] [request-headers.any.serviceworker.html]
expected: ERROR expected: ERROR