Auto merge of #13345 - malisas:malisa-bodyAPI, r=jdm

implement Body for dom::Response and dom::Request

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

This PR adds dom::Response and dom::Request's `Text()`, `Blob()`, `Json()`, and (part of the) `FormData()` functions, part of the [Body mixin](https://fetch.spec.whatwg.org/#body). The corresponding tests have also been updated as passing.

@jeenalee also contributed to this PR, primarily the `Blob()`, `Json()`, and `FormData()` implementations.

---
<!-- 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
- [ ] These changes fix #__ (github issue number if applicable).

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

<!-- 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/13345)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-09-29 06:19:59 -05:00 committed by GitHub
commit a03a5e814a
18 changed files with 332 additions and 167 deletions

View file

@ -2,8 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use body::{BodyOperations, BodyType, consume_body};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HeadersBinding::HeadersInit;
use dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
use dom::bindings::codegen::Bindings::RequestBinding;
use dom::bindings::codegen::Bindings::RequestBinding::ReferrerPolicy;
use dom::bindings::codegen::Bindings::RequestBinding::RequestCache;
@ -21,6 +22,8 @@ use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, DOMString, USVString};
use dom::headers::{Guard, Headers};
use dom::promise::Promise;
use dom::xmlhttprequest::Extractable;
use hyper;
use msg::constellation_msg::ReferrerPolicy as MsgReferrerPolicy;
use net_traits::request::{Origin, Window};
@ -33,6 +36,9 @@ use net_traits::request::Request as NetTraitsRequest;
use net_traits::request::RequestMode as NetTraitsRequestMode;
use net_traits::request::Type as NetTraitsRequestType;
use std::cell::Cell;
use std::mem;
use std::rc::Rc;
use style::refcell::Ref;
use url::Url;
#[dom_struct]
@ -340,7 +346,7 @@ impl Request {
try!(r.Headers().fill(Some(HeadersInit::Headers(headers_copy))));
// Step 32
let input_body = if let RequestInfo::Request(ref input_request) = input {
let mut input_body = if let RequestInfo::Request(ref input_request) = input {
let input_request_request = input_request.request.borrow();
let body = input_request_request.body.borrow();
body.clone()
@ -365,6 +371,20 @@ impl Request {
// Step 34
// TODO: `ReadableStream` object is not implemented in Servo yet.
if let Some(Some(ref init_body)) = init.body {
// Step 34.2
let extracted_body_tmp = init_body.extract();
input_body = Some(extracted_body_tmp.0);
let content_type = extracted_body_tmp.1;
// Step 34.3
if let Some(contents) = content_type {
if !r.Headers().Has(ByteString::new(b"Content-Type".to_vec())).unwrap() {
try!(r.Headers().Append(ByteString::new(b"Content-Type".to_vec()),
ByteString::new(contents.as_bytes().to_vec())));
}
}
}
// Step 35
{
@ -382,6 +402,13 @@ impl Request {
// Step 38
Ok(r)
}
// https://fetch.spec.whatwg.org/#concept-body-locked
fn locked(&self) -> bool {
// TODO: ReadableStream is unimplemented. Just return false
// for now.
false
}
}
impl Request {
@ -602,6 +629,56 @@ impl RequestMethods for Request {
// Step 2
Ok(Request::clone_from(self))
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-text
fn Text(&self) -> Rc<Promise> {
consume_body(self, BodyType::Text)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-blob
fn Blob(&self) -> Rc<Promise> {
consume_body(self, BodyType::Blob)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-formdata
fn FormData(&self) -> Rc<Promise> {
consume_body(self, BodyType::FormData)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-json
fn Json(&self) -> Rc<Promise> {
consume_body(self, BodyType::Json)
}
}
impl BodyOperations for Request {
fn get_body_used(&self) -> bool {
self.BodyUsed()
}
fn is_locked(&self) -> bool {
self.locked()
}
fn take_body(&self) -> Option<Vec<u8>> {
let ref mut net_traits_req = *self.request.borrow_mut();
let body: Option<Vec<u8>> = mem::replace(&mut *net_traits_req.body.borrow_mut(), None);
match body {
Some(_) => {
self.body_used.set(true);
body
},
_ => None,
}
}
fn get_mime_type(&self) -> Ref<Vec<u8>> {
self.mime_type.borrow()
}
}
impl Into<NetTraitsRequestCache> for RequestCache {

View file

@ -2,11 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use body::{BodyOperations, BodyType, consume_body};
use core::cell::Cell;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods;
use dom::bindings::codegen::Bindings::ResponseBinding;
use dom::bindings::codegen::Bindings::ResponseBinding::{ResponseMethods, ResponseType as DOMResponseType};
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutNullableHeap, Root};
@ -14,9 +16,14 @@ use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, USVString};
use dom::headers::{Headers, Guard};
use dom::headers::{is_vchar, is_obs_text};
use dom::promise::Promise;
use dom::xmlhttprequest::Extractable;
use hyper::status::StatusCode;
use net_traits::response::{ResponseBody as NetTraitsResponseBody};
use std::mem;
use std::rc::Rc;
use std::str::FromStr;
use style::refcell::Ref;
use url::Position;
use url::Url;
@ -33,8 +40,7 @@ pub struct Response {
response_type: DOMRefCell<DOMResponseType>,
url: DOMRefCell<Option<Url>>,
url_list: DOMRefCell<Vec<Url>>,
// For now use the existing NetTraitsResponseBody enum, until body
// is implemented.
// For now use the existing NetTraitsResponseBody enum
body: DOMRefCell<NetTraitsResponseBody>,
}
@ -59,7 +65,7 @@ impl Response {
reflect_dom_object(box Response::new_inherited(), global, ResponseBinding::Wrap)
}
pub fn Constructor(global: GlobalRef, _body: Option<USVString>, init: &ResponseBinding::ResponseInit)
pub fn Constructor(global: GlobalRef, body: Option<BodyInit>, init: &ResponseBinding::ResponseInit)
-> Fallible<Root<Response>> {
// Step 1
if init.status < 200 || init.status > 599 {
@ -86,11 +92,6 @@ impl Response {
// Step 6
if let Some(ref headers_member) = init.headers {
// Step 6.1
// TODO: Figure out how/if we should make r's response's
// header list and r's Headers object the same thing. For
// now just working with r's Headers object. Also, the
// header list should already be empty so this step may be
// unnecessary.
r.Headers().empty_header_list();
// Step 6.2
@ -98,23 +99,22 @@ impl Response {
}
// Step 7
if let Some(_) = _body {
if let Some(ref body) = body {
// Step 7.1
if is_null_body_status(init.status) {
return Err(Error::Type(
"Body is non-null but init's status member is a null body status".to_string()));
};
// Step 7.2
let content_type: Option<ByteString> = None;
// Step 7.3
// TODO: Extract body and implement step 7.3.
let (extracted_body, content_type) = body.extract();
*r.body.borrow_mut() = NetTraitsResponseBody::Done(extracted_body);
// Step 7.4
if let Some(content_type_contents) = content_type {
if !r.Headers().Has(ByteString::new(b"Content-Type".to_vec())).unwrap() {
try!(r.Headers().Append(ByteString::new(b"Content-Type".to_vec()), content_type_contents));
try!(r.Headers().Append(ByteString::new(b"Content-Type".to_vec()),
ByteString::new(content_type_contents.as_bytes().to_vec())));
}
};
}
@ -178,6 +178,38 @@ impl Response {
// Step 7
Ok(r)
}
// https://fetch.spec.whatwg.org/#concept-body-locked
fn locked(&self) -> bool {
// TODO: ReadableStream is unimplemented. Just return false
// for now.
false
}
}
impl BodyOperations for Response {
fn get_body_used(&self) -> bool {
self.BodyUsed()
}
fn is_locked(&self) -> bool {
self.locked()
}
fn take_body(&self) -> Option<Vec<u8>> {
let body: NetTraitsResponseBody = mem::replace(&mut *self.body.borrow_mut(), NetTraitsResponseBody::Empty);
match body {
NetTraitsResponseBody::Done(bytes) | NetTraitsResponseBody::Receiving(bytes) => {
self.body_used.set(true);
Some(bytes)
},
_ => None,
}
}
fn get_mime_type(&self) -> Ref<Vec<u8>> {
self.mime_type.borrow()
}
}
// https://fetch.spec.whatwg.org/#redirect-status
@ -283,6 +315,30 @@ impl ResponseMethods for Response {
fn BodyUsed(&self) -> bool {
self.body_used.get()
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-text
fn Text(&self) -> Rc<Promise> {
consume_body(self, BodyType::Text)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-blob
fn Blob(&self) -> Rc<Promise> {
consume_body(self, BodyType::Blob)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-formdata
fn FormData(&self) -> Rc<Promise> {
consume_body(self, BodyType::FormData)
}
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#dom-body-json
fn Json(&self) -> Rc<Promise> {
consume_body(self, BodyType::Json)
}
}
fn serialize_without_fragment(url: &Url) -> &str {

View file

@ -10,10 +10,9 @@
interface Body {
readonly attribute boolean bodyUsed;
// Servo does not support Promise at this moment.
// [NewObject] Promise<ArrayBuffer> arrayBuffer();
// [NewObject] Promise<Blob> blob();
// [NewObject] Promise<FormData> formData();
// [NewObject] Promise<JSON> json();
// [NewObject] Promise<USVString> text();
[NewObject] Promise<Blob> blob();
[NewObject] Promise<FormData> formData();
[NewObject] Promise<any> json();
[NewObject] Promise<USVString> text();
};

View file

@ -4,8 +4,7 @@
// https://fetch.spec.whatwg.org/#response-class
// TODO: pass 'optional ResponseBodyInit? body = null' to constructor in place of USVString
[Constructor(optional USVString? body = null, optional ResponseInit init),
[Constructor(optional BodyInit? body = null, optional ResponseInit init),
Exposed=(Window,Worker)]
interface Response {
[NewObject] static Response error();

View file

@ -1364,7 +1364,7 @@ impl XHRTimeoutCallback {
}
}
trait Extractable {
pub trait Extractable {
fn extract(&self) -> (Vec<u8>, Option<DOMString>);
}
impl Extractable for BodyInit {