mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Implement the Request API for the Fetch API.
This commit adds new files related to implementing the [Request API](https://fetch.spec.whatwg.org/#request-class). This commit also changes the expected web platform tests results. It also modifies the following files: components/net_traits/request.rs HeapSizeOf is implemented in net_traits/request so that dom::request can be used as a wrapper around net_traits::request::Request. components/script/dom/headers.rs Several methods are added to Headers so that request can access and modify some of the headers fields.
This commit is contained in:
parent
b7facf41cb
commit
fabe2b8f7e
13 changed files with 1070 additions and 359 deletions
|
@ -12,18 +12,19 @@ use dom::bindings::js::Root;
|
|||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use dom::bindings::str::{ByteString, is_token};
|
||||
use hyper::header::Headers as HyperHeaders;
|
||||
use std::cell::Cell;
|
||||
use std::result::Result;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct Headers {
|
||||
reflector_: Reflector,
|
||||
guard: Guard,
|
||||
guard: Cell<Guard>,
|
||||
#[ignore_heap_size_of = "Defined in hyper"]
|
||||
header_list: DOMRefCell<HyperHeaders>
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-headers-guard
|
||||
#[derive(JSTraceable, HeapSizeOf, PartialEq)]
|
||||
#[derive(Copy, Clone, JSTraceable, HeapSizeOf, PartialEq)]
|
||||
pub enum Guard {
|
||||
Immutable,
|
||||
Request,
|
||||
|
@ -36,76 +37,46 @@ impl Headers {
|
|||
pub fn new_inherited() -> Headers {
|
||||
Headers {
|
||||
reflector_: Reflector::new(),
|
||||
guard: Guard::None,
|
||||
guard: Cell::new(Guard::None),
|
||||
header_list: DOMRefCell::new(HyperHeaders::new()),
|
||||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-headers-fill
|
||||
pub fn new(global: GlobalRef, init: Option<HeadersBinding::HeadersInit>)
|
||||
-> Fallible<Root<Headers>> {
|
||||
let dom_headers_new = reflect_dom_object(box Headers::new_inherited(), global, HeadersBinding::Wrap);
|
||||
match init {
|
||||
// Step 1
|
||||
Some(HeadersOrByteStringSequenceSequence::Headers(h)) => {
|
||||
// header_list_copy has type hyper::header::Headers
|
||||
let header_list_copy = h.header_list.clone();
|
||||
for header in header_list_copy.borrow().iter() {
|
||||
try!(dom_headers_new.Append(
|
||||
ByteString::new(Vec::from(header.name())),
|
||||
ByteString::new(Vec::from(header.value_string().into_bytes()))
|
||||
));
|
||||
}
|
||||
Ok(dom_headers_new)
|
||||
},
|
||||
// Step 2
|
||||
Some(HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(v)) => {
|
||||
for mut seq in v {
|
||||
if seq.len() == 2 {
|
||||
let val = seq.pop().unwrap();
|
||||
let name = seq.pop().unwrap();
|
||||
try!(dom_headers_new.Append(name, val));
|
||||
} else {
|
||||
return Err(Error::Type(
|
||||
format!("Each header object must be a sequence of length 2 - found one with length {}",
|
||||
seq.len())));
|
||||
}
|
||||
}
|
||||
Ok(dom_headers_new)
|
||||
},
|
||||
// Step 3 TODO constructor for when init is an open-ended dictionary
|
||||
None => Ok(dom_headers_new),
|
||||
}
|
||||
pub fn new(global: GlobalRef) -> Root<Headers> {
|
||||
reflect_dom_object(box Headers::new_inherited(), global, HeadersBinding::Wrap)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-headers
|
||||
pub fn Constructor(global: GlobalRef, init: Option<HeadersBinding::HeadersInit>)
|
||||
-> Fallible<Root<Headers>> {
|
||||
Headers::new(global, init)
|
||||
let dom_headers_new = Headers::new(global);
|
||||
try!(dom_headers_new.fill(init));
|
||||
Ok(dom_headers_new)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeadersMethods for Headers {
|
||||
// https://fetch.spec.whatwg.org/#concept-headers-append
|
||||
fn Append(&self, name: ByteString, value: ByteString) -> Result<(), Error> {
|
||||
fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult {
|
||||
// Step 1
|
||||
let value = normalize_value(value);
|
||||
// Step 2
|
||||
let (mut valid_name, valid_value) = try!(validate_name_and_value(name, value));
|
||||
valid_name = valid_name.to_lowercase();
|
||||
// Step 3
|
||||
if self.guard == Guard::Immutable {
|
||||
if self.guard.get() == Guard::Immutable {
|
||||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
// Step 4
|
||||
if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 5
|
||||
if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 6
|
||||
if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 7
|
||||
|
@ -121,19 +92,19 @@ impl HeadersMethods for Headers {
|
|||
// Step 1
|
||||
let valid_name = try!(validate_name(name));
|
||||
// Step 2
|
||||
if self.guard == Guard::Immutable {
|
||||
if self.guard.get() == Guard::Immutable {
|
||||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
// Step 3
|
||||
if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 4
|
||||
if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 5
|
||||
if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 6
|
||||
|
@ -166,19 +137,19 @@ impl HeadersMethods for Headers {
|
|||
let (mut valid_name, valid_value) = try!(validate_name_and_value(name, value));
|
||||
valid_name = valid_name.to_lowercase();
|
||||
// Step 3
|
||||
if self.guard == Guard::Immutable {
|
||||
if self.guard.get() == Guard::Immutable {
|
||||
return Err(Error::Type("Guard is immutable".to_string()));
|
||||
}
|
||||
// Step 4
|
||||
if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 5
|
||||
if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 6
|
||||
if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
|
||||
return Ok(());
|
||||
}
|
||||
// Step 7
|
||||
|
@ -188,6 +159,66 @@ impl HeadersMethods for Headers {
|
|||
}
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
// https://fetch.spec.whatwg.org/#concept-headers-fill
|
||||
pub fn fill(&self, filler: Option<HeadersBinding::HeadersInit>) -> ErrorResult {
|
||||
match filler {
|
||||
// Step 1
|
||||
Some(HeadersOrByteStringSequenceSequence::Headers(h)) => {
|
||||
// header_list_copy has type hyper::header::Headers
|
||||
let header_list_copy = h.header_list.clone();
|
||||
for header in h.header_list.borrow().iter() {
|
||||
try!(self.Append(
|
||||
ByteString::new(Vec::from(header.name())),
|
||||
ByteString::new(Vec::from(header.value_string().into_bytes()))
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
// Step 2
|
||||
Some(HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(v)) => {
|
||||
for mut seq in v {
|
||||
if seq.len() == 2 {
|
||||
let val = seq.pop().unwrap();
|
||||
let name = seq.pop().unwrap();
|
||||
try!(self.Append(name, val));
|
||||
} else {
|
||||
return Err(Error::Type(
|
||||
format!("Each header object must be a sequence of length 2 - found one with length {}",
|
||||
seq.len())));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
// Step 3 TODO constructor for when init is an open-ended dictionary
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_request(global: GlobalRef) -> Root<Headers> {
|
||||
let headers_for_request = Headers::new(global);
|
||||
headers_for_request.guard.set(Guard::Request);
|
||||
headers_for_request
|
||||
}
|
||||
|
||||
pub fn set_guard(&self, new_guard: Guard) {
|
||||
self.guard.set(new_guard)
|
||||
}
|
||||
|
||||
pub fn get_guard(&self) -> Guard {
|
||||
self.guard.get()
|
||||
}
|
||||
|
||||
pub fn empty_header_list(&self) {
|
||||
*self.header_list.borrow_mut() = HyperHeaders::new();
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
|
||||
pub fn extract_mime_type(&self) -> Vec<u8> {
|
||||
self.header_list.borrow().get_raw("content-type").map_or(vec![], |v| v[0].clone())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// "Content-Type" once parsed, the value should be
|
||||
// `application/x-www-form-urlencoded`, `multipart/form-data`,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue