Auto merge of #29465 - 2shiori17:issue-29388, r=jdm

Implement Headers.prototype.getSetCookie

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #29388

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-03-28 04:10:37 +02:00 committed by GitHub
commit 11559eb8f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 111 additions and 213 deletions

View file

@ -13,7 +13,9 @@ use crate::dom::globalscope::GlobalScope;
use data_url::mime::Mime as DataUrlMime;
use dom_struct::dom_struct;
use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
use net_traits::request::is_cors_safelisted_request_header;
use net_traits::{
fetch::headers::get_value_from_header_list, request::is_cors_safelisted_request_header,
};
use std::cell::Cell;
use std::str::{self, FromStr};
@ -65,55 +67,60 @@ impl HeadersMethods for Headers {
fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult {
// Step 1
let value = normalize_value(value);
// 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();
// Step 3
if self.guard.get() == Guard::Immutable {
return Err(Error::Type("Guard is immutable".to_string()));
}
// Step 4
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
return Ok(());
}
// Step 5
if self.guard.get() == Guard::RequestNoCors &&
!is_cors_safelisted_request_header(&valid_name, &valid_value)
{
return Ok(());
}
// Step 6
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
return Ok(());
}
// Step 7
// FIXME: this is NOT what WHATWG says to do when appending
// another copy of an existing header. HyperHeaders
// might not expose the information we need to do it right.
let mut combined_value: Vec<u8> = vec![];
if let Some(v) = self
.header_list
.borrow()
.get(HeaderName::from_str(&valid_name).unwrap())
{
combined_value = v.as_bytes().to_vec();
combined_value.extend(b", ");
// Step 3
if self.guard.get() == Guard::RequestNoCors {
let tmp_value = if let Some(mut value) =
get_value_from_header_list(&valid_name, &self.header_list.borrow())
{
value.extend(b", ");
value.extend(valid_value.clone());
value
} else {
valid_value.clone()
};
if !is_cors_safelisted_request_header(&valid_name, &tmp_value) {
return Ok(());
}
}
combined_value.extend(valid_value.iter().cloned());
match HeaderValue::from_bytes(&combined_value) {
// Step 4
match HeaderValue::from_bytes(&valid_value) {
Ok(value) => {
self.header_list
.borrow_mut()
.insert(HeaderName::from_str(&valid_name).unwrap(), value);
.append(HeaderName::from_str(&valid_name).unwrap(), value);
},
Err(_) => {
// can't add the header, but we don't need to panic the browser over it
warn!(
"Servo thinks \"{:?}\" is a valid HTTP header value but HeaderValue doesn't.",
combined_value
valid_value
);
},
};
// Step 5
if self.guard.get() == Guard::RequestNoCors {
self.remove_privileged_no_cors_request_headers();
}
Ok(())
}
@ -148,11 +155,20 @@ impl HeadersMethods for Headers {
fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> {
// Step 1
let valid_name = validate_name(name)?;
Ok(self
.header_list
Ok(
get_value_from_header_list(&valid_name, &self.header_list.borrow())
.map(|v| ByteString::new(v)),
)
}
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
fn GetSetCookie(&self) -> Vec<ByteString> {
self.header_list
.borrow()
.get(HeaderName::from_str(&valid_name).unwrap())
.map(|v| ByteString::new(v.as_bytes().to_vec())))
.get_all("set-cookie")
.iter()
.map(|v| ByteString::new(v.as_bytes().to_vec()))
.collect()
}
// https://fetch.spec.whatwg.org/#dom-headers-has
@ -273,19 +289,31 @@ impl Headers {
extract_mime_type(&*self.header_list.borrow()).unwrap_or(vec![])
}
pub fn sort_header_list(&self) -> Vec<(String, Vec<u8>)> {
// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
pub fn sort_and_combine(&self) -> Vec<(String, Vec<u8>)> {
let borrowed_header_list = self.header_list.borrow();
let headers_iter = borrowed_header_list.iter();
let mut header_vec = vec![];
for (name, value) in headers_iter {
let name = name.as_str().to_owned();
let value = value.as_bytes().to_vec();
let name_value = (name, value);
header_vec.push(name_value);
for name in borrowed_header_list.keys() {
let name = name.as_str();
if name == "set-cookie" {
for value in borrowed_header_list.get_all(name).iter() {
header_vec.push((name.to_owned(), value.as_bytes().to_vec()));
}
} else if let Some(value) = get_value_from_header_list(name, &borrowed_header_list) {
header_vec.push((name.to_owned(), value));
}
}
header_vec.sort();
header_vec.sort_by(|a, b| a.0.cmp(&b.0));
header_vec
}
// https://fetch.spec.whatwg.org/#ref-for-privileged-no-cors-request-header-name
pub fn remove_privileged_no_cors_request_headers(&self) {
// https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name
self.header_list.borrow_mut().remove("range");
}
}
impl Iterable for Headers {
@ -293,17 +321,18 @@ impl Iterable for Headers {
type Value = ByteString;
fn get_iterable_length(&self) -> u32 {
self.header_list.borrow().iter().count() as u32
let sorted_header_vec = self.sort_and_combine();
sorted_header_vec.len() as u32
}
fn get_value_at_index(&self, n: u32) -> ByteString {
let sorted_header_vec = self.sort_header_list();
let sorted_header_vec = self.sort_and_combine();
let value = sorted_header_vec[n as usize].1.clone();
ByteString::new(value)
}
fn get_key_at_index(&self, n: u32) -> ByteString {
let sorted_header_vec = self.sort_header_list();
let sorted_header_vec = self.sort_and_combine();
let key = sorted_header_vec[n as usize].0.clone();
ByteString::new(key.into_bytes().to_vec())
}

View file

@ -15,6 +15,7 @@ interface Headers {
undefined delete(ByteString name);
[Throws]
ByteString? get(ByteString name);
sequence<ByteString> getSetCookie();
[Throws]
boolean has(ByteString name);
[Throws]