mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Improve spec conformance around request header validation (#33418)
* fix: improve spec conformance around request header validation Signed-off-by: Shane Handley <shanehandley@fastmail.com> * account for additional test passes Signed-off-by: Shane Handley <shanehandley@fastmail.com> * fix: remove redundant .to_vec call Signed-off-by: Shane Handley <shanehandley@fastmail.com> --------- Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
parent
6071b4a961
commit
6a3cdc47ec
8 changed files with 263 additions and 482 deletions
|
@ -2,162 +2,15 @@
|
|||
* 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 std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
use headers::HeaderMap;
|
||||
use net_traits::fetch::headers::get_value_from_header_list;
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#http-tab-or-space>
|
||||
const HTTP_TAB_OR_SPACE: &[char] = &['\u{0009}', '\u{0020}'];
|
||||
use net_traits::fetch::headers::get_decode_and_split_header_name;
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#determine-nosniff>
|
||||
pub fn determine_nosniff(headers: &HeaderMap) -> bool {
|
||||
let values = get_header_value_as_list("x-content-type-options", headers);
|
||||
let values = get_decode_and_split_header_name("x-content-type-options", headers);
|
||||
|
||||
match values {
|
||||
None => false,
|
||||
Some(values) => !values.is_empty() && values[0].eq_ignore_ascii_case("nosniff"),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split>
|
||||
fn get_header_value_as_list(name: &str, headers: &HeaderMap) -> Option<Vec<String>> {
|
||||
fn char_is_not_quote_or_comma(c: char) -> bool {
|
||||
c != '\u{0022}' && c != '\u{002C}'
|
||||
}
|
||||
|
||||
// Step 1
|
||||
let initial_value = get_value_from_header_list(name, headers);
|
||||
|
||||
if let Some(input) = initial_value {
|
||||
// https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
|
||||
// Step 1
|
||||
let input = input.into_iter().map(char::from).collect::<String>();
|
||||
|
||||
// Step 2
|
||||
let mut position = input.chars().peekable();
|
||||
|
||||
// Step 3
|
||||
let mut values: Vec<String> = vec![];
|
||||
|
||||
// Step 4
|
||||
let mut value = String::new();
|
||||
|
||||
// Step 5
|
||||
while position.peek().is_some() {
|
||||
// Step 5.1
|
||||
value += &*collect_sequence(&mut position, char_is_not_quote_or_comma);
|
||||
|
||||
// Step 5.2
|
||||
if let Some(&ch) = position.peek() {
|
||||
if ch == '\u{0022}' {
|
||||
// Step 5.2.1.1
|
||||
value += &*collect_http_quoted_string(&mut position, false);
|
||||
|
||||
// Step 5.2.1.2
|
||||
if position.peek().is_some() {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// ch == '\u{002C}'
|
||||
|
||||
// Step 5.2.2.2
|
||||
position.next();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5.3
|
||||
value = value.trim_matches(HTTP_TAB_OR_SPACE).to_string();
|
||||
|
||||
// Step 5.4
|
||||
values.push(value);
|
||||
|
||||
// Step 5.5
|
||||
value = String::new();
|
||||
}
|
||||
|
||||
return Some(values);
|
||||
}
|
||||
|
||||
// Step 2
|
||||
None
|
||||
}
|
||||
|
||||
/// <https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points>
|
||||
fn collect_sequence<F>(position: &mut Peekable<Chars>, condition: F) -> String
|
||||
where
|
||||
F: Fn(char) -> bool,
|
||||
{
|
||||
// Step 1
|
||||
let mut result = String::new();
|
||||
|
||||
// Step 2
|
||||
while let Some(&ch) = position.peek() {
|
||||
if !condition(ch) {
|
||||
break;
|
||||
}
|
||||
result.push(ch);
|
||||
position.next();
|
||||
}
|
||||
|
||||
// Step 3
|
||||
result
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#collect-an-http-quoted-string>
|
||||
fn collect_http_quoted_string(position: &mut Peekable<Chars>, extract_value: bool) -> String {
|
||||
fn char_is_not_quote_or_backslash(c: char) -> bool {
|
||||
c != '\u{0022}' && c != '\u{005C}'
|
||||
}
|
||||
|
||||
// Step 2
|
||||
// We will store the 'extracted value' or the raw value
|
||||
let mut value = String::new();
|
||||
|
||||
// Step 3, 4
|
||||
let should_be_quote = position.next();
|
||||
if let Some(ch) = should_be_quote {
|
||||
if !extract_value {
|
||||
value.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5
|
||||
loop {
|
||||
// Step 5.1
|
||||
value += &*collect_sequence(position, char_is_not_quote_or_backslash);
|
||||
|
||||
// Step 5.2
|
||||
if position.peek().is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 5.3, 5.4
|
||||
let quote_or_backslash = position.next().unwrap();
|
||||
if !extract_value {
|
||||
value.push(quote_or_backslash);
|
||||
}
|
||||
|
||||
if quote_or_backslash == '\u{005C}' {
|
||||
if let Some(ch) = position.next() {
|
||||
value.push(ch);
|
||||
} else {
|
||||
// Step 5.5.1
|
||||
if extract_value {
|
||||
value.push('\u{005C}');
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Step 5.6.1
|
||||
// assert quote_or_backslash is a quote
|
||||
|
||||
// Step 5.6.2
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6, 7
|
||||
value
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ use data_url::mime::Mime as DataUrlMime;
|
|||
use dom_struct::dom_struct;
|
||||
use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
|
||||
use js::rust::HandleObject;
|
||||
use net_traits::fetch::headers::get_value_from_header_list;
|
||||
use net_traits::fetch::headers::{
|
||||
get_decode_and_split_header_value, get_value_from_header_list, is_forbidden_method,
|
||||
};
|
||||
use net_traits::request::is_cors_safelisted_request_header;
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
|
@ -85,12 +87,15 @@ impl HeadersMethods for Headers {
|
|||
// Step 2
|
||||
// https://fetch.spec.whatwg.org/#headers-validate
|
||||
let (mut valid_name, valid_value) = validate_name_and_value(name, value)?;
|
||||
|
||||
valid_name = valid_name.to_lowercase();
|
||||
|
||||
if self.guard.get() == Guard::Immutable {
|
||||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
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) {
|
||||
|
@ -141,13 +146,18 @@ impl HeadersMethods for Headers {
|
|||
// https://fetch.spec.whatwg.org/#dom-headers-delete
|
||||
fn Delete(&self, name: ByteString) -> ErrorResult {
|
||||
// Step 1
|
||||
let valid_name = validate_name(name)?;
|
||||
let (mut valid_name, valid_value) = validate_name_and_value(name, ByteString::new(vec![]))?;
|
||||
|
||||
valid_name = valid_name.to_lowercase();
|
||||
|
||||
// Step 2
|
||||
if self.guard.get() == Guard::Immutable {
|
||||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
// Step 3
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
if self.guard.get() == Guard::Request &&
|
||||
is_forbidden_request_header(&valid_name, &valid_value)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
// Step 4
|
||||
|
@ -205,7 +215,9 @@ impl HeadersMethods for Headers {
|
|||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
// Step 4
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
if self.guard.get() == Guard::Request &&
|
||||
is_forbidden_request_header(&valid_name, &valid_value)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
// Step 5
|
||||
|
@ -358,14 +370,12 @@ impl Iterable for Headers {
|
|||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
|
||||
fn is_forbidden_response_header(name: &str) -> bool {
|
||||
matches!(name, "set-cookie" | "set-cookie2")
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-header-name
|
||||
pub fn is_forbidden_header_name(name: &str) -> bool {
|
||||
let disallowed_headers = [
|
||||
/// This function will internally convert `name` to lowercase for matching, so explicitly converting
|
||||
/// before calling is not necessary
|
||||
///
|
||||
/// <https://fetch.spec.whatwg.org/#forbidden-request-header>
|
||||
pub fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
|
||||
let forbidden_header_names = [
|
||||
"accept-charset",
|
||||
"accept-encoding",
|
||||
"access-control-request-headers",
|
||||
|
@ -386,14 +396,61 @@ pub fn is_forbidden_header_name(name: &str) -> bool {
|
|||
"transfer-encoding",
|
||||
"upgrade",
|
||||
"via",
|
||||
// This list is defined in the fetch spec, however the draft spec for private-network-access
|
||||
// proposes this additional forbidden name, which is currently included in WPT tests. See:
|
||||
// https://wicg.github.io/private-network-access/#forbidden-header-names
|
||||
"access-control-request-private-network",
|
||||
];
|
||||
|
||||
let disallowed_header_prefixes = ["sec-", "proxy-"];
|
||||
// Step 1: If name is a byte-case-insensitive match for one of (forbidden_header_names), return
|
||||
// true
|
||||
let lowercase_name = name.to_lowercase();
|
||||
|
||||
disallowed_headers.iter().any(|header| *header == name) ||
|
||||
disallowed_header_prefixes
|
||||
if forbidden_header_names
|
||||
.iter()
|
||||
.any(|header| *header == lowercase_name.as_str())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
let forbidden_header_prefixes = ["sec-", "proxy-"];
|
||||
|
||||
// Step 2: If name when byte-lowercased starts with `proxy-` or `sec-`, then return true.
|
||||
if forbidden_header_prefixes
|
||||
.iter()
|
||||
.any(|prefix| lowercase_name.starts_with(prefix))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
let potentially_forbidden_header_names = [
|
||||
"x-http-method",
|
||||
"x-http-method-override",
|
||||
"x-method-override",
|
||||
];
|
||||
|
||||
// Step 3: If name is a byte-case-insensitive match for one of (potentially_forbidden_header_names)
|
||||
if potentially_forbidden_header_names
|
||||
.iter()
|
||||
.any(|header| *header == lowercase_name)
|
||||
{
|
||||
// Step 3.1: Let parsedValues be the result of getting, decoding, and splitting value.
|
||||
let parsed_values = get_decode_and_split_header_value(value.to_vec());
|
||||
|
||||
// Step 3.2: For each method of parsedValues: if the isomorphic encoding of method is a
|
||||
// forbidden method, then return true.
|
||||
return parsed_values
|
||||
.iter()
|
||||
.any(|prefix| name.starts_with(prefix))
|
||||
.any(|s| is_forbidden_method(s.as_bytes()));
|
||||
}
|
||||
|
||||
// Step 4: Return false.
|
||||
false
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
|
||||
fn is_forbidden_response_header(name: &str) -> bool {
|
||||
matches!(name, "set-cookie" | "set-cookie2")
|
||||
}
|
||||
|
||||
// There is some unresolved confusion over the definition of a name and a value.
|
||||
|
|
|
@ -13,6 +13,7 @@ use http::method::InvalidMethod;
|
|||
use http::Method as HttpMethod;
|
||||
use js::jsapi::JSObject;
|
||||
use js::rust::HandleObject;
|
||||
use net_traits::fetch::headers::is_forbidden_method;
|
||||
use net_traits::request::{
|
||||
CacheMode as NetTraitsRequestCache, CredentialsMode as NetTraitsRequestCredentials,
|
||||
Destination as NetTraitsRequestDestination, Origin, RedirectMode as NetTraitsRequestRedirect,
|
||||
|
@ -503,14 +504,6 @@ fn is_method(m: &ByteString) -> bool {
|
|||
m.as_str().is_some()
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-method
|
||||
fn is_forbidden_method(m: &ByteString) -> bool {
|
||||
matches!(
|
||||
m.to_lower().as_str(),
|
||||
Some("connect") | Some("trace") | Some("track")
|
||||
)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#cors-safelisted-method
|
||||
fn is_cors_safelisted_method(m: &HttpMethod) -> bool {
|
||||
m == HttpMethod::GET || m == HttpMethod::HEAD || m == HttpMethod::POST
|
||||
|
|
|
@ -59,7 +59,7 @@ use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLD
|
|||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::headers::{extract_mime_type, is_forbidden_header_name};
|
||||
use crate::dom::headers::{extract_mime_type, is_forbidden_request_header};
|
||||
use crate::dom::node::Node;
|
||||
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||
use crate::dom::progressevent::ProgressEvent;
|
||||
|
@ -460,41 +460,37 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
|
||||
/// <https://xhr.spec.whatwg.org/#the-setrequestheader()-method>
|
||||
fn SetRequestHeader(&self, name: ByteString, value: ByteString) -> ErrorResult {
|
||||
// Step 1, 2
|
||||
// Step 1: If this’s state is not opened, then throw an "InvalidStateError" DOMException.
|
||||
// Step 2: If this’s send() flag is set, then throw an "InvalidStateError" DOMException.
|
||||
if self.ready_state.get() != XMLHttpRequestState::Opened || self.send_flag.get() {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
|
||||
// Step 3
|
||||
// Step 3: Normalize value.
|
||||
let value = trim_http_whitespace(&value);
|
||||
|
||||
// Step 4
|
||||
// Step 4: If name is not a header name or value is not a header value, then throw a
|
||||
// "SyntaxError" DOMException.
|
||||
if !is_token(&name) || !is_field_value(value) {
|
||||
return Err(Error::Syntax);
|
||||
}
|
||||
let name_lower = name.to_lower();
|
||||
let name_str = match name_lower.as_str() {
|
||||
Some(s) => {
|
||||
// Step 5
|
||||
// Disallowed headers and header prefixes:
|
||||
// https://fetch.spec.whatwg.org/#forbidden-header-name
|
||||
if is_forbidden_header_name(s) {
|
||||
return Ok(());
|
||||
} else {
|
||||
s
|
||||
}
|
||||
},
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
let name_str = name.as_str().ok_or(Error::Syntax)?;
|
||||
|
||||
// Step 5: If (name, value) is a forbidden request-header, then return.
|
||||
if is_forbidden_request_header(name_str, &value) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!(
|
||||
"SetRequestHeader: name={:?}, value={:?}",
|
||||
name.as_str(),
|
||||
name_str,
|
||||
str::from_utf8(value).ok()
|
||||
);
|
||||
let mut headers = self.request_headers.borrow_mut();
|
||||
|
||||
// Step 6
|
||||
// Step 6: Combine (name, value) in this’s author request headers.
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-combine
|
||||
let value = match headers.get(name_str).map(HeaderValue::as_bytes) {
|
||||
Some(raw) => {
|
||||
let mut buf = raw.to_vec();
|
||||
|
|
|
@ -2,17 +2,185 @@
|
|||
* 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 std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
use headers::HeaderMap;
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#http-tab-or-space>
|
||||
const HTTP_TAB_OR_SPACE: &[char] = &['\u{0009}', '\u{0020}'];
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-header-list-get>
|
||||
pub fn get_value_from_header_list(name: &str, headers: &HeaderMap) -> Option<Vec<u8>> {
|
||||
let values = headers.get_all(name).iter().map(|val| val.as_bytes());
|
||||
|
||||
// Step 1
|
||||
// Step 1: If list does not contain name, then return null.
|
||||
if values.size_hint() == (0, Some(0)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Step 2
|
||||
// Step 2: Return the values of all headers in list whose name is a byte-case-insensitive match
|
||||
// for name, separated from each other by 0x2C 0x20, in order.
|
||||
Some(values.collect::<Vec<&[u8]>>().join(&[0x2C, 0x20][..]))
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#forbidden-method>
|
||||
pub fn is_forbidden_method(method: &[u8]) -> bool {
|
||||
matches!(
|
||||
method.to_ascii_lowercase().as_slice(),
|
||||
b"connect" | b"trace" | b"track"
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split>
|
||||
pub fn get_decode_and_split_header_name(name: &str, headers: &HeaderMap) -> Option<Vec<String>> {
|
||||
// Step 1: Let value be the result of getting name from list.
|
||||
// Step 2: If value is null, then return null.
|
||||
// Step 3: Return the result of getting, decoding, and splitting value.
|
||||
get_value_from_header_list(name, headers).map(|value| get_decode_and_split_header_value(value))
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#header-value-get-decode-and-split>
|
||||
pub fn get_decode_and_split_header_value(value: Vec<u8>) -> Vec<String> {
|
||||
fn char_is_not_quote_or_comma(c: char) -> bool {
|
||||
c != '\u{0022}' && c != '\u{002C}'
|
||||
}
|
||||
|
||||
// Step 1: Let input be the result of isomorphic decoding value.
|
||||
let input = value.into_iter().map(char::from).collect::<String>();
|
||||
|
||||
// Step 2: Let position be a position variable for input, initially pointing at the start of
|
||||
// input.
|
||||
let mut position = input.chars().peekable();
|
||||
|
||||
// Step 3: Let values be a list of strings, initially « ».
|
||||
let mut values: Vec<String> = vec![];
|
||||
|
||||
// Step 4: Let temporaryValue be the empty string.
|
||||
let mut temporary_value = String::new();
|
||||
|
||||
// Step 5: While true:
|
||||
while position.peek().is_some() {
|
||||
// Step 5.1: Append the result of collecting a sequence of code points that are not U+0022
|
||||
// (") or U+002C (,) from input, given position, to temporaryValue.
|
||||
temporary_value += &*collect_sequence(&mut position, char_is_not_quote_or_comma);
|
||||
|
||||
// Step 5.2: If position is not past the end of input and the code point at position within
|
||||
// input is U+0022 ("):
|
||||
if let Some(&ch) = position.peek() {
|
||||
if ch == '\u{0022}' {
|
||||
// Step 5.2.1: Append the result of collecting an HTTP quoted string from input,
|
||||
// given position, to temporaryValue.
|
||||
temporary_value += &*collect_http_quoted_string(&mut position, false);
|
||||
|
||||
// Step 5.2.2: If position is not past the end of input, then continue.
|
||||
if position.peek().is_some() {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Step 5.2.2: If position is not past the end of input, then continue.
|
||||
position.next();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5.3: Remove all HTTP tab or space from the start and end of temporaryValue.
|
||||
temporary_value = temporary_value.trim_matches(HTTP_TAB_OR_SPACE).to_string();
|
||||
|
||||
// Step 5.4: Append temporaryValue to values.
|
||||
values.push(temporary_value);
|
||||
|
||||
// Step 5.5: Set temporaryValue to the empty string.
|
||||
temporary_value = String::new();
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points>
|
||||
fn collect_sequence<F>(position: &mut Peekable<Chars>, condition: F) -> String
|
||||
where
|
||||
F: Fn(char) -> bool,
|
||||
{
|
||||
// Step 1: Let result be the empty string.
|
||||
let mut result = String::new();
|
||||
|
||||
// Step 2: While position doesn’t point past the end of input and the code point at position
|
||||
// within input meets the condition condition:
|
||||
while let Some(&ch) = position.peek() {
|
||||
if !condition(ch) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 2.1: Append that code point to the end of result.
|
||||
result.push(ch);
|
||||
|
||||
// Step 2.2: Advance position by 1.
|
||||
position.next();
|
||||
}
|
||||
|
||||
// Step 3: Return result.
|
||||
result
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#collect-an-http-quoted-string>
|
||||
fn collect_http_quoted_string(position: &mut Peekable<Chars>, extract_value: bool) -> String {
|
||||
fn char_is_not_quote_or_backslash(c: char) -> bool {
|
||||
c != '\u{0022}' && c != '\u{005C}'
|
||||
}
|
||||
|
||||
// Step 2: let value be the empty string
|
||||
// We will store the 'extracted value' or the raw value
|
||||
let mut value = String::new();
|
||||
|
||||
// Step 3, 4
|
||||
let should_be_quote = position.next();
|
||||
if let Some(ch) = should_be_quote {
|
||||
if !extract_value {
|
||||
value.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: While true:
|
||||
loop {
|
||||
// Step 5.1: Append the result of collecting a sequence of code points that are not U+0022
|
||||
// (") or U+005C (\) from input, given position, to value.
|
||||
value += &*collect_sequence(position, char_is_not_quote_or_backslash);
|
||||
|
||||
// Step 5.2: If position is past the end of input, then break.
|
||||
if position.peek().is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 5.3: Let quoteOrBackslash be the code point at position within input.
|
||||
// Step 5.4: Advance position by 1.
|
||||
let quote_or_backslash = position.next().unwrap();
|
||||
|
||||
if !extract_value {
|
||||
value.push(quote_or_backslash);
|
||||
}
|
||||
|
||||
// Step 5.5: If quoteOrBackslash is U+005C (\), then:
|
||||
if quote_or_backslash == '\u{005C}' {
|
||||
if let Some(ch) = position.next() {
|
||||
// Step 5.5.2: Append the code point at position within input to value.
|
||||
value.push(ch);
|
||||
} else {
|
||||
// Step 5.5.1: If position is past the end of input, then append U+005C (\) to value and break.
|
||||
if extract_value {
|
||||
value.push('\u{005C}');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Step 5.6.1: Assert quote_or_backslash is a quote
|
||||
assert_eq!(quote_or_backslash, '\u{0022}');
|
||||
|
||||
// Step 5.6.2: break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6, 7
|
||||
value
|
||||
}
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
[request-private-network-headers.tentative.any.worker.html]
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-private-network-headers.tentative.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
||||
[request-private-network-headers.tentative.any.html]
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-private-network-headers.tentative.any.serviceworker.html]
|
||||
expected: ERROR
|
||||
|
|
|
@ -1,267 +1,5 @@
|
|||
[request-forbidden-headers.any.worker.html]
|
||||
[header x-http-method-override is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-forbidden-headers.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
||||
[request-forbidden-headers.any.html]
|
||||
[header x-http-method-override is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value TRACE]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value TRACK]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value CONNECT]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value trace]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value track]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value connect]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method-override is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header x-http-method is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header x-method-override is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-HTTP-METHOD is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[header X-METHOD-OVERRIDE is forbidden to use value trace,]
|
||||
expected: FAIL
|
||||
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-forbidden-headers.any.serviceworker.html]
|
||||
expected: ERROR
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
[request-private-network-headers.tentative.any.worker.html]
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-private-network-headers.tentative.any.serviceworker.html]
|
||||
expected: ERROR
|
||||
|
||||
[request-private-network-headers.tentative.any.html]
|
||||
[Access-Control-Request-Private-Network is a forbidden request header]
|
||||
expected: FAIL
|
||||
|
||||
[Adding invalid request header "Access-Control-Request-Private-Network: KO"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[request-private-network-headers.tentative.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue