Skip to content
Permalink
Browse files

Auto merge of #12700 - jeenalee:jeena-requestAPI, r=jdm

Implement the Request API

<!-- Please describe your changes on the following line: -->
This PR implements the [Request API](https://fetch.spec.whatwg.org/#request-class) for the Fetch API, including its attributes and constructor, and introduces changes in relevant files.

This Request integrates `net_traits::request::Request` and `dom::headers`.

There are few related TODOs and comments:
1. `net_traits::request::Request`'s `headers` field does not point to `dom::request::Request`'s `headers_reflector`.
2. Every Constructor step that involves `Readable Stream` object is not implemented.
3. Every Constructor step that involves `entry settings object` or `environment settings object` is not implemented.
4. `./mach build -d` does not report any error, but prints a few warnings about unused variables related to (1) and (2).
5. Enum `ReferrerPolicy` generated by `RequestBinding` does not match `net_traits::request::Request`'s implementation.
6. `Promise`s in Body webidl are commented out.

---
<!-- 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 #11895 (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [X] These changes do not require tests  because tests for the Request API already exists, but this commit does not implement the interface fully.

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12700)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Aug 13, 2016
2 parents 7b1f75b + 69f4cf6 commit 78160bf3f967ad34c671fe953de578bfa0b9542b
@@ -10,7 +10,7 @@ use std::mem::swap;
use url::{Origin as UrlOrigin, Url};

/// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Initiator {
None,
Download,
@@ -20,29 +20,29 @@ pub enum Initiator {
}

/// A request [type](https://fetch.spec.whatwg.org/#concept-request-type)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Type {
None, Audio, Font, Image,
Script, Style, Track, Video
}

/// A request [destination](https://fetch.spec.whatwg.org/#concept-request-destination)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum Destination {
None, Document, Embed, Font, Image, Manifest,
Media, Object, Report, Script, ServiceWorker,
SharedWorker, Style, Worker, XSLT
}

/// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin)
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub enum Origin {
Client,
Origin(UrlOrigin)
}

/// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, HeapSizeOf)]
pub enum Referer {
NoReferer,
/// Default referer if nothing is specified
@@ -51,7 +51,7 @@ pub enum Referer {
}

/// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum RequestMode {
Navigate,
SameOrigin,
@@ -60,15 +60,15 @@ pub enum RequestMode {
}

/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum CredentialsMode {
Omit,
CredentialsSameOrigin,
Include
}

/// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum CacheMode {
Default,
NoStore,
@@ -79,23 +79,23 @@ pub enum CacheMode {
}

/// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum RedirectMode {
Follow,
Error,
Manual
}

/// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ResponseTainting {
Basic,
CORSTainting,
Opaque
}

/// [Window](https://fetch.spec.whatwg.org/#concept-request-window)
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Window {
NoWindow,
Client,
@@ -138,11 +138,13 @@ pub struct RequestInit {
}

/// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
#[derive(Clone)]
#[derive(Clone, HeapSizeOf)]
pub struct Request {
#[ignore_heap_size_of = "Defined in hyper"]
pub method: RefCell<Method>,
pub local_urls_only: bool,
pub sandboxed_storage_area_urls: bool,
#[ignore_heap_size_of = "Defined in hyper"]
pub headers: RefCell<Headers>,
pub unsafe_request: bool,
pub body: RefCell<Option<Vec<u8>>>,
@@ -60,6 +60,7 @@ use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeType, R
use net_traits::filemanager_thread::{SelectedFileId, RelativePos};
use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
use net_traits::request::Request;
use net_traits::response::HttpsState;
use net_traits::storage_thread::StorageType;
use net_traits::{Metadata, NetworkError, ResourceThreads};
@@ -325,6 +326,7 @@ no_jsmanaged_fields!(AttrIdentifier);
no_jsmanaged_fields!(AttrValue);
no_jsmanaged_fields!(ElementSnapshot);
no_jsmanaged_fields!(HttpsState);
no_jsmanaged_fields!(Request);
no_jsmanaged_fields!(SharedRt);
no_jsmanaged_fields!(TouchpadPressurePhase);
no_jsmanaged_fields!(USVString);
@@ -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,81 +37,54 @@ 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
let mut combined_value = self.header_list.borrow_mut().get_raw(&valid_name).unwrap()[0].clone();
combined_value.push(b","[0]);
let mut combined_value: Vec<u8> = vec![];
if let Some(v) = self.header_list.borrow().get_raw(&valid_name) {
combined_value = v[0].clone();
combined_value.push(b","[0]);
}
combined_value.extend(valid_value.iter().cloned());
self.header_list.borrow_mut().set_raw(valid_name, vec![combined_value]);
Ok(())
@@ -121,19 +95,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 +140,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 +162,64 @@ 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)) => {
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`,
@@ -315,20 +347,24 @@ fn is_field_name(name: &ByteString) -> bool {
// field-content = field-vchar [ 1*( SP / HTAB / field-vchar )
// field-vchar ]
fn is_field_content(value: &ByteString) -> bool {
if value.len() == 0 {
let value_len = value.len();

if value_len == 0 {
return false;
}
if !is_field_vchar(value[0]) {
return false;
}

for &ch in &value[1..value.len() - 1] {
if !is_field_vchar(ch) || !is_space(ch) || !is_htab(ch) {
return false;
if value_len > 2 {
for &ch in &value[1..value_len - 1] {
if !is_field_vchar(ch) && !is_space(ch) && !is_htab(ch) {
return false;
}
}
}

if !is_field_vchar(value[value.len() - 1]) {
if !is_field_vchar(value[value_len - 1]) {
return false;
}

@@ -368,6 +368,7 @@ pub mod processinginstruction;
pub mod progressevent;
pub mod radionodelist;
pub mod range;
pub mod request;
pub mod screen;
pub mod serviceworker;
pub mod serviceworkercontainer;

0 comments on commit 78160bf

Please sign in to comment.
You can’t perform that action at this time.