servo: Merge #13058 - Response API (from malisas:malisa-responseAPI); r=Manishearth,jdm
authorMalisa Smith <malisa.tsmith@gmail.com>
Thu, 08 Sep 2016 18:58:05 -0500
changeset 339664 54be0df52ec3a17d9ff2d85f911dc003b9b8b4b0
parent 339663 1ee8d667cbfa97f6b5f22563224be558306d1052
child 339665 89bfec936a22ef359e7b65a2deae11ff8f16d7c0
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth, jdm
servo: Merge #13058 - Response API (from malisas:malisa-responseAPI); r=Manishearth,jdm <!-- Please describe your changes on the following line: --> This PR adds the [dom::Response](https://fetch.spec.whatwg.org/#response-class) implementation and addresses #11896. The relevant passing tests` expectations have been updated. In order to allow non-UTF-8-encoded status messages, `net_traits::response::Response`'s `raw_status` field has been changed from type [`Option<RawStatus>`](https://doc.servo.org/hyper/http/struct.RawStatus.html) to type `Option<(u16, Vec<u8>)>`. As a result, a few other files which rely on the `raw_status` field were affected and updated. TODOs: - The `body` and `trailer` methods. Relies on implementation of `ReadableStream` and `Promise`s. - Similarly, replace the dummy constructor `_body: Option<USVString>` argument with `body: ResponseBodyInit`. - Currently, whenever `r's response's header list` or `r's Headers object` are mentioned, I always modify the `headers_reflector` field (of type dom::Headers, or `r's Headers object`) and not the corresponding hyper::Headers list in the net_traits::Response field. A completely accurate interpretation of the spec might consider making both of these lists the same thing via a reference. [Discussion](https://github.com/whatwg/fetch/issues/358) was [had](https://github.com/servo/servo/pull/12884). --- <!-- 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. --> Source-Repo: https://github.com/servo/servo Source-Revision: 5a5a76cc5db830d2e622d4e0924837383b64dfa2
servo/components/devtools/Cargo.toml
servo/components/devtools/actors/network_event.rs
servo/components/devtools/lib.rs
servo/components/devtools_traits/lib.rs
servo/components/net/about_loader.rs
servo/components/net/blob_loader.rs
servo/components/net/fetch/methods.rs
servo/components/net/http_loader.rs
servo/components/net_traits/lib.rs
servo/components/net_traits/response.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/headers.rs
servo/components/script/dom/htmllinkelement.rs
servo/components/script/dom/htmlmediaelement.rs
servo/components/script/dom/htmlscriptelement.rs
servo/components/script/dom/mod.rs
servo/components/script/dom/response.rs
servo/components/script/dom/webidls/Response.webidl
servo/components/script/dom/xmlhttprequest.rs
servo/components/servo/Cargo.lock
servo/ports/cef/Cargo.lock
servo/tests/unit/net/fetch.rs
servo/tests/unit/net/http_loader.rs
--- a/servo/components/devtools/Cargo.toml
+++ b/servo/components/devtools/Cargo.toml
@@ -6,16 +6,17 @@ license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "devtools"
 path = "lib.rs"
 
 [dependencies]
 devtools_traits = {path = "../devtools_traits"}
+encoding = "0.2"
 hyper = "0.9.9"
 hyper_serde = "0.1.4"
 ipc-channel = "0.5"
 log = "0.3.5"
 msg = {path = "../msg"}
 plugins = {path = "../plugins"}
 serde = "0.8"
 serde_json = "0.8"
--- a/servo/components/devtools/actors/network_event.rs
+++ b/servo/components/devtools/actors/network_event.rs
@@ -6,22 +6,25 @@
 //! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js).
 //! Handles interaction with the remote web console on network events (HTTP requests, responses) in Servo.
 
 extern crate hyper;
 
 use actor::{Actor, ActorMessageStatus, ActorRegistry};
 use devtools_traits::HttpRequest as DevtoolsHttpRequest;
 use devtools_traits::HttpResponse as DevtoolsHttpResponse;
+use encoding::all::UTF_8;
+use encoding::types::{DecoderTrap, Encoding};
 use hyper::header::Headers;
 use hyper::header::{ContentType, Cookie};
 use hyper::http::RawStatus;
 use hyper::method::Method;
 use protocol::JsonPacketStream;
 use serde_json::Value;
+use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::net::TcpStream;
 use time;
 use time::Tm;
 
 struct HttpRequest {
     url: String,
     method: Method,
@@ -355,17 +358,20 @@ impl NetworkEventActor {
         self.request.timeStamp = request.timeStamp;
         self.request.connect_time = request.connect_time;
         self.request.send_time = request.send_time;
         self.is_xhr = request.is_xhr;
     }
 
     pub fn add_response(&mut self, response: DevtoolsHttpResponse) {
         self.response.headers = response.headers.clone();
-        self.response.status = response.status.clone();
+        self.response.status = response.status.as_ref().map(|&(s, ref st)| {
+            let status_text = UTF_8.decode(st, DecoderTrap::Replace).unwrap();
+            RawStatus(s, Cow::from(status_text))
+        });
         self.response.body = response.body.clone();
      }
 
     pub fn event_actor(&self) -> EventActor {
         // TODO: Send the correct values for startedDateTime, isXHR, private
         EventActor {
             actor: self.name(),
             url: self.request.url.clone(),
--- a/servo/components/devtools/lib.rs
+++ b/servo/components/devtools/lib.rs
@@ -16,16 +16,17 @@
 #![feature(plugin)]
 #![plugin(serde_macros)]
 #![plugin(plugins)]
 
 #![allow(non_snake_case)]
 #![deny(unsafe_code)]
 
 extern crate devtools_traits;
+extern crate encoding;
 extern crate hyper;
 extern crate ipc_channel;
 #[macro_use]
 extern crate log;
 extern crate msg;
 extern crate serde;
 extern crate serde_json;
 extern crate time;
--- a/servo/components/devtools_traits/lib.rs
+++ b/servo/components/devtools_traits/lib.rs
@@ -22,17 +22,16 @@ extern crate heapsize;
 extern crate hyper;
 extern crate ipc_channel;
 extern crate msg;
 extern crate serde;
 extern crate time;
 extern crate url;
 
 use hyper::header::Headers;
-use hyper::http::RawStatus;
 use hyper::method::Method;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::PipelineId;
 use std::net::TcpStream;
 use time::Duration;
 use time::Tm;
 use url::Url;
 
@@ -299,17 +298,17 @@ pub struct HttpRequest {
     pub connect_time: u64,
     pub send_time: u64,
     pub is_xhr: bool,
 }
 
 #[derive(Debug, PartialEq)]
 pub struct HttpResponse {
     pub headers: Option<Headers>,
-    pub status: Option<RawStatus>,
+    pub status: Option<(u16, Vec<u8>)>,
     pub body: Option<Vec<u8>>,
     pub pipeline_id: PipelineId,
 }
 
 pub enum NetworkEvent {
     HttpRequest(HttpRequest),
     HttpResponse(HttpResponse),
 }
--- a/servo/components/net/about_loader.rs
+++ b/servo/components/net/about_loader.rs
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 file_loader;
 use hyper::header::ContentType;
-use hyper::http::RawStatus;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper_serde::Serde;
 use mime_classifier::MimeClassifier;
 use net_traits::ProgressMsg::Done;
 use net_traits::response::HttpsState;
 use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError};
 use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt};
 use std::io;
@@ -33,17 +32,17 @@ pub fn factory(mut load_data: LoadData,
     let res = match url.path() {
         "blank" => {
             let metadata = Metadata {
                 final_url: load_data.url,
                 content_type:
                     Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])))),
                 charset: Some("utf-8".to_owned()),
                 headers: None,
-                status: Some(Serde(RawStatus(200, "OK".into()))),
+                status: Some((200, b"OK".to_vec())),
                 https_state: HttpsState::None,
                 referrer: None,
             };
             if let Ok(chan) = start_sending_sniffed_opt(start_chan,
                                                         metadata,
                                                         classifier,
                                                         &[],
                                                         load_data.context) {
--- a/servo/components/net/blob_loader.rs
+++ b/servo/components/net/blob_loader.rs
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 filemanager_thread::{FileManager, UIProvider};
 use hyper::header::{DispositionType, ContentDisposition, DispositionParam};
 use hyper::header::{Headers, ContentType, ContentLength, Charset};
-use hyper::http::RawStatus;
 use hyper_serde::Serde;
 use ipc_channel::ipc;
 use mime::{Mime, Attr};
 use mime_classifier::MimeClassifier;
 use net_traits::ProgressMsg::{Payload, Done};
 use net_traits::blob_url_store::parse_blob_url;
 use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress};
 use net_traits::response::HttpsState;
@@ -67,17 +66,17 @@ fn load_blob<UI: 'static + UIProvider>
                 headers.set(ContentLength(blob_buf.size as u64));
 
                 let metadata = Metadata {
                     final_url: load_data.url.clone(),
                     content_type: Some(Serde(ContentType(content_type.clone()))),
                     charset: charset.map(|c| c.as_str().to_string()),
                     headers: Some(Serde(headers)),
                     // https://w3c.github.io/FileAPI/#TwoHundredOK
-                    status: Some(Serde(RawStatus(200, "OK".into()))),
+                    status: Some((200, b"OK".to_vec())),
                     https_state: HttpsState::None,
                     referrer: None,
                 };
 
                 if let Ok(chan) =
                     start_sending_sniffed_opt(start_chan, metadata, classifier,
                                               &blob_buf.bytes, load_data.context.clone()) {
                     let _ = chan.send(Payload(blob_buf.bytes));
--- a/servo/components/net/fetch/methods.rs
+++ b/servo/components/net/fetch/methods.rs
@@ -968,18 +968,19 @@ fn http_network_fetch(request: Rc<Reques
                                            request_id.as_ref().map(Deref::deref), is_xhr);
 
     let pipeline_id = request.pipeline_id.get();
     let mut response = Response::new();
     match wrapped_response {
         Ok((res, msg)) => {
             response.url = Some(url.clone());
             response.status = Some(res.response.status);
-            response.raw_status = Some(res.response.status_raw().clone());
-            response.headers = res.response.headers.clone();
+            response.raw_status = Some((res.response.status_raw().0,
+                                        res.response.status_raw().1.as_bytes().to_vec()));
+                        response.headers = res.response.headers.clone();
 
             let res_body = response.body.clone();
 
             // We're about to spawn a thread to be waited on here
             *done_chan = Some(channel());
             let meta = response.metadata().expect("Response metadata should exist at this stage");
             let done_sender = done_chan.as_ref().map(|ch| ch.0.clone());
             let devtools_sender = devtools_chan.clone();
@@ -996,17 +997,17 @@ fn http_network_fetch(request: Rc<Reques
                             }
 
                             // --- Tell devtools that we got a response
                             // Send an HttpResponse message to devtools with the corresponding request_id
                             if let Some(pipeline_id) = pipeline_id {
                                 send_response_to_devtools(
                                     &sender, request_id.unwrap(),
                                     meta_headers.map(Serde::into_inner),
-                                    meta_status.map(Serde::into_inner),
+                                    meta_status,
                                     pipeline_id);
                             }
                         }
 
                         loop {
                             match read_block(&mut res) {
                                 Ok(ReadResult::Payload(chunk)) => {
                                     if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
--- a/servo/components/net/http_loader.rs
+++ b/servo/components/net/http_loader.rs
@@ -624,17 +624,17 @@ fn prepare_devtools_request(request_id: 
 pub fn send_request_to_devtools(msg: ChromeToDevtoolsControlMsg,
                             devtools_chan: &Sender<DevtoolsControlMsg>) {
     devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap();
 }
 
 pub fn send_response_to_devtools(devtools_chan: &Sender<DevtoolsControlMsg>,
                              request_id: String,
                              headers: Option<Headers>,
-                             status: Option<RawStatus>,
+                             status: Option<(u16, Vec<u8>)>,
                              pipeline_id: PipelineId) {
     let response = DevtoolsHttpResponse { headers: headers, status: status, body: None, pipeline_id: pipeline_id };
     let net_event_response = NetworkEvent::HttpResponse(response);
 
     let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event_response);
     let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
 }
 
@@ -1076,33 +1076,34 @@ pub fn load<A, B>(load_data: &LoadData,
         }
 
         let mut metadata: Metadata = Metadata::default(doc_url.clone());
         metadata.set_content_type(match adjusted_headers.get() {
             Some(&ContentType(ref mime)) => Some(mime),
             None => None
         });
         metadata.headers = Some(Serde(adjusted_headers));
-        metadata.status = Some(Serde(response.status_raw().clone()));
+        metadata.status = Some((response.status_raw().0,
+                                response.status_raw().1.as_bytes().to_vec()));
         metadata.https_state = if doc_url.scheme() == "https" {
             HttpsState::Modern
         } else {
             HttpsState::None
         };
         metadata.referrer = referrer_url.clone();
 
         // --- Tell devtools that we got a response
         // Send an HttpResponse message to devtools with the corresponding request_id
         // TODO: Send this message even when the load fails?
         if let Some(pipeline_id) = load_data.pipeline_id {
             if let Some(ref chan) = devtools_chan {
                 send_response_to_devtools(
                     &chan, request_id.unwrap(),
                     metadata.headers.clone().map(Serde::into_inner),
-                    metadata.status.clone().map(Serde::into_inner),
+                    metadata.status.clone(),
                     pipeline_id);
             }
         }
         if response.status().class() == StatusClass::Redirection {
             continue;
         } else {
             return StreamedResponse::from_http_response(box response, metadata);
         }
--- a/servo/components/net_traits/lib.rs
+++ b/servo/components/net_traits/lib.rs
@@ -555,19 +555,18 @@ pub struct Metadata {
 
     /// Character set.
     pub charset: Option<String>,
 
     #[ignore_heap_size_of = "Defined in hyper"]
     /// Headers
     pub headers: Option<Serde<Headers>>,
 
-    #[ignore_heap_size_of = "Defined in hyper"]
     /// HTTP Status
-    pub status: Option<Serde<RawStatus>>,
+    pub status: Option<(u16, Vec<u8>)>,
 
     /// Is successful HTTPS connection
     pub https_state: HttpsState,
 
     /// Referrer Url
     pub referrer: Option<Url>,
 }
 
@@ -575,17 +574,17 @@ impl Metadata {
     /// Metadata with defaults for everything optional.
     pub fn default(url: Url) -> Self {
         Metadata {
             final_url:    url,
             content_type: None,
             charset:      None,
             headers: None,
             // https://fetch.spec.whatwg.org/#concept-response-status-message
-            status: Some(Serde(RawStatus(200, "OK".into()))),
+            status: Some((200, b"OK".to_vec())),
             https_state: HttpsState::None,
             referrer: None,
         }
     }
 
     /// Extract the parts of a Mime that we care about.
     pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
         match self.headers {
--- a/servo/components/net_traits/response.rs
+++ b/servo/components/net_traits/response.rs
@@ -1,46 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 //! The [Response](https://fetch.spec.whatwg.org/#responses) object
 //! resulting from a [fetch operation](https://fetch.spec.whatwg.org/#concept-fetch)
 use hyper::header::{AccessControlExposeHeaders, ContentType, Headers};
-use hyper::http::RawStatus;
 use hyper::status::StatusCode;
 use hyper_serde::Serde;
 use std::ascii::AsciiExt;
 use std::cell::{Cell, RefCell};
 use std::sync::{Arc, Mutex};
 use url::Url;
 use {Metadata, NetworkError};
 
 /// [Response type](https://fetch.spec.whatwg.org/#concept-response-type)
-#[derive(Clone, PartialEq, Copy, Debug, Deserialize, Serialize)]
+#[derive(Clone, PartialEq, Copy, Debug, Deserialize, Serialize, HeapSizeOf)]
 pub enum ResponseType {
     Basic,
     CORS,
     Default,
     Error,
     Opaque,
     OpaqueRedirect
 }
 
 /// [Response termination reason](https://fetch.spec.whatwg.org/#concept-response-termination-reason)
-#[derive(Clone, Copy, Deserialize, Serialize)]
+#[derive(Clone, Copy, Deserialize, Serialize, HeapSizeOf)]
 pub enum TerminationReason {
     EndUserAbort,
     Fatal,
     Timeout
 }
 
 /// The response body can still be pushed to after fetch
 /// This provides a way to store unfinished response bodies
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
 pub enum ResponseBody {
     Empty, // XXXManishearth is this necessary, or is Done(vec![]) enough?
     Receiving(Vec<u8>),
     Done(Vec<u8>),
 }
 
 impl ResponseBody {
     pub fn is_done(&self) -> bool {
@@ -48,17 +47,17 @@ impl ResponseBody {
             ResponseBody::Done(..) => true,
             ResponseBody::Empty | ResponseBody::Receiving(..) => false
         }
     }
 }
 
 
 /// [Cache state](https://fetch.spec.whatwg.org/#concept-response-cache-state)
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, HeapSizeOf)]
 pub enum CacheState {
     None,
     Local,
     Validated,
     Partial
 }
 
 /// [Https state](https://fetch.spec.whatwg.org/#concept-response-https-state)
@@ -71,26 +70,29 @@ pub enum HttpsState {
 
 pub enum ResponseMsg {
     Chunk(Vec<u8>),
     Finished,
     Errored
 }
 
 /// A [Response](https://fetch.spec.whatwg.org/#concept-response) as defined by the Fetch spec
-#[derive(Clone)]
+#[derive(Clone, HeapSizeOf)]
 pub struct Response {
     pub response_type: ResponseType,
     pub termination_reason: Option<TerminationReason>,
     pub url: Option<Url>,
     pub url_list: RefCell<Vec<Url>>,
     /// `None` can be considered a StatusCode of `0`.
+    #[ignore_heap_size_of = "Defined in hyper"]
     pub status: Option<StatusCode>,
-    pub raw_status: Option<RawStatus>,
+    pub raw_status: Option<(u16, Vec<u8>)>,
+    #[ignore_heap_size_of = "Defined in hyper"]
     pub headers: Headers,
+    #[ignore_heap_size_of = "Mutex heap size undefined"]
     pub body: Arc<Mutex<ResponseBody>>,
     pub cache_state: CacheState,
     pub https_state: HttpsState,
     /// [Internal response](https://fetch.spec.whatwg.org/#concept-internal-response), only used if the Response
     /// is a filtered response
     pub internal_response: Option<Box<Response>>,
     /// whether or not to try to return the internal_response when asked for actual_response
     pub return_internal: Cell<bool>,
@@ -99,17 +101,17 @@ pub struct Response {
 impl Response {
     pub fn new() -> Response {
         Response {
             response_type: ResponseType::Default,
             termination_reason: None,
             url: None,
             url_list: RefCell::new(Vec::new()),
             status: Some(StatusCode::Ok),
-            raw_status: Some(RawStatus(200, "OK".into())),
+            raw_status: Some((200, b"OK".to_vec())),
             headers: Headers::new(),
             body: Arc::new(Mutex::new(ResponseBody::Empty)),
             cache_state: CacheState::None,
             https_state: HttpsState::None,
             internal_response: None,
             return_internal: Cell::new(true)
         }
     }
@@ -234,13 +236,13 @@ impl Response {
             return Err(NetworkError::Internal("Cannot extract metadata from network error".to_string()));
         }
 
         metadata.set_content_type(match self.headers.get() {
             Some(&ContentType(ref mime)) => Some(mime),
             None => None
         });
         metadata.headers = Some(Serde(self.headers.clone()));
-        metadata.status = self.raw_status.clone().map(Serde);
+        metadata.status = self.raw_status.clone();
         metadata.https_state = self.https_state;
         return Ok(metadata);
     }
 }
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -45,28 +45,30 @@ use euclid::length::Length as EuclidLeng
 use euclid::matrix2d::Matrix2D;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
 use html5ever::tree_builder::QuirksMode;
 use hyper::header::Headers;
 use hyper::method::Method;
 use hyper::mime::Mime;
+use hyper::status::StatusCode;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer};
 use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer};
 use js::jsval::JSVal;
 use js::rust::Runtime;
 use libc;
 use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeType, ReferrerPolicy};
 use net_traits::filemanager_thread::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::response::{Response, ResponseBody};
 use net_traits::storage_thread::StorageType;
 use net_traits::{Metadata, NetworkError, ResourceThreads};
 use offscreen_gl_context::GLLimits;
 use profile_traits::mem::ProfilerChan as MemProfilerChan;
 use profile_traits::time::ProfilerChan as TimeProfilerChan;
 use script_layout_interface::OpaqueStyleAndLayoutData;
 use script_layout_interface::reporter::CSSErrorReporter;
 use script_layout_interface::rpc::LayoutRPC;
@@ -334,17 +336,20 @@ 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);
 no_jsmanaged_fields!(ReferrerPolicy);
+no_jsmanaged_fields!(Response);
+no_jsmanaged_fields!(ResponseBody);
 no_jsmanaged_fields!(ResourceThreads);
+no_jsmanaged_fields!(StatusCode);
 no_jsmanaged_fields!(SystemTime);
 no_jsmanaged_fields!(RelativePos);
 no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
 no_jsmanaged_fields!(PathBuf);
 no_jsmanaged_fields!(CSSErrorReporter);
 no_jsmanaged_fields!(WebGLBufferId);
 no_jsmanaged_fields!(WebGLFramebufferId);
 no_jsmanaged_fields!(WebGLProgramId);
--- a/servo/components/script/dom/headers.rs
+++ b/servo/components/script/dom/headers.rs
@@ -204,17 +204,23 @@ impl Headers {
     }
 
     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) {
+    pub fn for_response(global: GlobalRef) -> Root<Headers> {
+        let headers_for_response = Headers::new(global);
+        headers_for_response.guard.set(Guard::Response);
+        headers_for_response
+    }
+
+    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) {
@@ -341,25 +347,25 @@ pub fn is_forbidden_header_name(name: &s
 // surrounded by whitespace anywhere". They provided a fix for the
 // field-content production, but ISSUE 1 has still not been resolved.
 // The production definitions likely need to be re-written.
 // [1] https://fetch.spec.whatwg.org/#concept-header-value
 // [2] https://tools.ietf.org/html/rfc7230#section-3.2
 // [3] https://tools.ietf.org/html/rfc7230#section-3.2.6
 // [4] https://www.rfc-editor.org/errata_search.php?rfc=7230
 fn validate_name_and_value(name: ByteString, value: ByteString)
-                           -> Result<(String, Vec<u8>), Error> {
+                           -> Fallible<(String, Vec<u8>)> {
     let valid_name = try!(validate_name(name));
     if !is_field_content(&value) {
         return Err(Error::Type("Value is not valid".to_string()));
     }
     Ok((valid_name, value.into()))
 }
 
-fn validate_name(name: ByteString) -> Result<String, Error> {
+fn validate_name(name: ByteString) -> Fallible<String> {
     if !is_field_name(&name) {
         return Err(Error::Type("Name is not valid".to_string()));
     }
     match String::from_utf8(name.into()) {
         Ok(ns) => Ok(ns),
         _ => Err(Error::Type("Non-UTF8 header name found".to_string())),
     }
 }
@@ -439,22 +445,22 @@ fn is_htab(x: u8) -> bool {
 }
 
 // https://tools.ietf.org/html/rfc7230#section-3.2
 fn is_field_vchar(x: u8) -> bool {
     is_vchar(x) || is_obs_text(x)
 }
 
 // https://tools.ietf.org/html/rfc5234#appendix-B.1
-fn is_vchar(x: u8) -> bool {
+pub fn is_vchar(x: u8) -> bool {
     match x {
         0x21...0x7E => true,
         _ => false,
     }
 }
 
 // http://tools.ietf.org/html/rfc7230#section-3.2.6
-fn is_obs_text(x: u8) -> bool {
+pub fn is_obs_text(x: u8) -> bool {
     match x {
         0x80...0xFF => true,
         _ => false,
     }
 }
--- a/servo/components/script/dom/htmllinkelement.rs
+++ b/servo/components/script/dom/htmllinkelement.rs
@@ -17,17 +17,16 @@ use dom::domtokenlist::DOMTokenList;
 use dom::element::{AttributeMutation, Element, ElementCreator};
 use dom::eventtarget::EventTarget;
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, document_from_node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
 use encoding::EncodingRef;
 use encoding::all::UTF_8;
 use hyper::header::ContentType;
-use hyper::http::RawStatus;
 use hyper::mime::{Mime, TopLevel, SubLevel};
 use hyper_serde::Serde;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
 use network_listener::{NetworkListener, PreInvoke};
 use script_layout_interface::message::Msg;
 use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
@@ -330,17 +329,17 @@ impl AsyncResponseListener for Styleshee
 
             let win = window_from_node(elem);
             win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap();
 
             *elem.stylesheet.borrow_mut() = Some(sheet);
             document.invalidate_stylesheets();
 
             // FIXME: Revisit once consensus is reached at: https://github.com/whatwg/html/issues/1142
-            successful = metadata.status.map_or(false, |Serde(RawStatus(code, _))| code == 200);
+            successful = metadata.status.map_or(false, |(code, _)| code == 200);
         }
 
         if elem.parser_inserted.get() {
             document.decrement_script_blocking_stylesheet_count();
         }
 
         document.finish_load(LoadType::Stylesheet(self.url.clone()));
 
--- a/servo/components/script/dom/htmlmediaelement.rs
+++ b/servo/components/script/dom/htmlmediaelement.rs
@@ -19,17 +19,16 @@ use dom::bindings::str::DOMString;
 use dom::document::Document;
 use dom::element::{Element, AttributeMutation};
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::htmlelement::HTMLElement;
 use dom::htmlsourceelement::HTMLSourceElement;
 use dom::mediaerror::MediaError;
 use dom::node::{window_from_node, document_from_node, Node, UnbindContext};
 use dom::virtualmethods::VirtualMethods;
-use hyper_serde::Serde;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
 use network_listener::{NetworkListener, PreInvoke};
 use script_thread::{Runnable, ScriptThread};
 use std::cell::Cell;
 use std::sync::{Arc, Mutex};
 use string_cache::Atom;
@@ -61,17 +60,17 @@ impl AsyncResponseListener for HTMLMedia
     fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
         self.metadata = metadata.ok();
 
         // => "If the media data cannot be fetched at all..."
         let is_failure = self.metadata
                              .as_ref()
                              .and_then(|m| m.status
                                             .as_ref()
-                                            .map(|&Serde(ref s)| s.0 < 200 || s.0 >= 300))
+                                            .map(|&(s, _)| s < 200 || s >= 300))
                              .unwrap_or(false);
         if is_failure {
             // Ensure that the element doesn't receive any further notifications
             // of the aborted fetch. The dedicated failure steps will be executed
             // when response_complete runs.
             self.ignore_response = true;
         }
     }
--- a/servo/components/script/dom/htmlscriptelement.rs
+++ b/servo/components/script/dom/htmlscriptelement.rs
@@ -21,18 +21,16 @@ use dom::event::{Event, EventBubbles, Ev
 use dom::htmlelement::HTMLElement;
 use dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
 use dom::node::{document_from_node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
 use dom::window::ScriptHelpers;
 use encoding::label::encoding_from_whatwg_label;
 use encoding::types::{DecoderTrap, EncodingRef};
 use html5ever::tree_builder::NextParserState;
-use hyper::http::RawStatus;
-use hyper_serde::Serde;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use js::jsval::UndefinedValue;
 use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
 use network_listener::{NetworkListener, PreInvoke};
 use std::ascii::AsciiExt;
 use std::cell::Cell;
 use std::sync::{Arc, Mutex};
@@ -154,17 +152,17 @@ struct ScriptContext {
 }
 
 impl AsyncResponseListener for ScriptContext {
     fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
         self.metadata = metadata.ok();
 
         let status_code = self.metadata.as_ref().and_then(|m| {
             match m.status {
-                Some(Serde(RawStatus(c, _))) => Some(c),
+                Some((c, _)) => Some(c),
                 _ => None,
             }
         }).unwrap_or(0);
 
         self.status = match status_code {
             0 => Err(NetworkError::Internal("No http status code received".to_owned())),
             200...299 => Ok(()), // HTTP ok status codes
             _ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code)))
--- a/servo/components/script/dom/mod.rs
+++ b/servo/components/script/dom/mod.rs
@@ -367,16 +367,17 @@ pub mod performancetiming;
 pub mod plugin;
 pub mod pluginarray;
 pub mod popstateevent;
 pub mod processinginstruction;
 pub mod progressevent;
 pub mod radionodelist;
 pub mod range;
 pub mod request;
+pub mod response;
 pub mod screen;
 pub mod serviceworker;
 pub mod serviceworkercontainer;
 pub mod serviceworkerglobalscope;
 pub mod serviceworkerregistration;
 pub mod servohtmlparser;
 pub mod servoxmlparser;
 pub mod storage;
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/response.rs
@@ -0,0 +1,290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 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::error::{Error, Fallible};
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JS, MutNullableHeap, Root};
+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 hyper::status::StatusCode;
+use net_traits::response::{ResponseBody as NetTraitsResponseBody};
+use std::str::FromStr;
+use url::Position;
+use url::Url;
+
+#[dom_struct]
+pub struct Response {
+    reflector_: Reflector,
+    headers_reflector: MutNullableHeap<JS<Headers>>,
+    mime_type: DOMRefCell<Vec<u8>>,
+    body_used: Cell<bool>,
+    /// `None` can be considered a StatusCode of `0`.
+    #[ignore_heap_size_of = "Defined in hyper"]
+    status: DOMRefCell<Option<StatusCode>>,
+    raw_status: DOMRefCell<Option<(u16, Vec<u8>)>>,
+    response_type: DOMRefCell<DOMResponseType>,
+    url: DOMRefCell<Option<Url>>,
+    url_list: DOMRefCell<Vec<Url>>,
+    // For now use the existing NetTraitsResponseBody enum, until body
+    // is implemented.
+    body: DOMRefCell<NetTraitsResponseBody>,
+}
+
+impl Response {
+    pub fn new_inherited() -> Response {
+        Response {
+            reflector_: Reflector::new(),
+            headers_reflector: Default::default(),
+            mime_type: DOMRefCell::new("".to_string().into_bytes()),
+            body_used: Cell::new(false),
+            status: DOMRefCell::new(Some(StatusCode::Ok)),
+            raw_status: DOMRefCell::new(Some((200, b"OK".to_vec()))),
+            response_type: DOMRefCell::new(DOMResponseType::Default),
+            url: DOMRefCell::new(None),
+            url_list: DOMRefCell::new(vec![]),
+            body: DOMRefCell::new(NetTraitsResponseBody::Empty),
+        }
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response
+    pub fn new(global: GlobalRef) -> Root<Response> {
+        reflect_dom_object(box Response::new_inherited(), global, ResponseBinding::Wrap)
+    }
+
+    pub fn Constructor(global: GlobalRef, _body: Option<USVString>, init: &ResponseBinding::ResponseInit)
+                       -> Fallible<Root<Response>> {
+        // Step 1
+        if init.status < 200 || init.status > 599 {
+            return Err(Error::Range(
+                format!("init's status member should be in the range 200 to 599, inclusive, but is {}"
+                        , init.status)));
+        }
+
+        // Step 2
+        if !is_valid_status_text(&init.statusText) {
+            return Err(Error::Type("init's statusText member does not match the reason-phrase token production"
+                                   .to_string()));
+        }
+
+        // Step 3
+        let r = Response::new(global);
+
+        // Step 4
+        *r.status.borrow_mut() = Some(StatusCode::from_u16(init.status));
+
+        // Step 5
+        *r.raw_status.borrow_mut() = Some((init.status, init.statusText.clone().into()));
+
+        // 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
+            try!(r.Headers().fill(Some(headers_member.clone())));
+        }
+
+        // Step 7
+        if let Some(_) = _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.
+
+            // 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));
+                }
+            };
+        }
+
+        // Step 8
+        *r.mime_type.borrow_mut() = r.Headers().extract_mime_type();
+
+        // Step 9
+        // TODO: `entry settings object` is not implemented in Servo yet.
+
+        // Step 10
+        // TODO: Write this step once Promises are merged in
+
+        // Step 11
+        Ok(r)
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-error
+    pub fn Error(global: GlobalRef) -> Root<Response> {
+        let r = Response::new(global);
+        *r.response_type.borrow_mut() = DOMResponseType::Error;
+        r.Headers().set_guard(Guard::Immutable);
+        *r.raw_status.borrow_mut() = Some((0, b"".to_vec()));
+        r
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-redirect
+    pub fn Redirect(global: GlobalRef, url: USVString, status: u16) -> Fallible<Root<Response>> {
+        // Step 1
+        // TODO: `entry settings object` is not implemented in Servo yet.
+        let base_url = global.get_url();
+        let parsed_url = base_url.join(&url.0);
+
+        // Step 2
+        let url = match parsed_url {
+            Ok(url) => url,
+            Err(_) => return Err(Error::Type("Url could not be parsed".to_string())),
+        };
+
+        // Step 3
+        if !is_redirect_status(status) {
+            return Err(Error::Range("status is not a redirect status".to_string()));
+        }
+
+        // Step 4
+        // see Step 4 continued
+        let r = Response::new(global);
+
+        // Step 5
+        *r.status.borrow_mut() = Some(StatusCode::from_u16(status));
+        *r.raw_status.borrow_mut() = Some((status, b"".to_vec()));
+
+        // Step 6
+        let url_bytestring = ByteString::from_str(url.as_str()).unwrap_or(ByteString::new(b"".to_vec()));
+        try!(r.Headers().Set(ByteString::new(b"Location".to_vec()), url_bytestring));
+
+        // Step 4 continued
+        // Headers Guard is set to Immutable here to prevent error in Step 6
+        r.Headers().set_guard(Guard::Immutable);
+
+        // Step 7
+        Ok(r)
+    }
+}
+
+// https://fetch.spec.whatwg.org/#redirect-status
+fn is_redirect_status(status: u16) -> bool {
+    status == 301 || status == 302 || status == 303 || status == 307 || status == 308
+}
+
+// https://tools.ietf.org/html/rfc7230#section-3.1.2
+fn is_valid_status_text(status_text: &ByteString) -> bool {
+    // reason-phrase  = *( HTAB / SP / VCHAR / obs-text )
+    for byte in status_text.iter() {
+        if !(*byte == b'\t' || *byte == b' ' || is_vchar(*byte) || is_obs_text(*byte)) {
+            return false;
+        }
+    }
+    true
+}
+
+// https://fetch.spec.whatwg.org/#null-body-status
+fn is_null_body_status(status: u16) -> bool {
+    status == 101 || status == 204 || status == 205 || status == 304
+}
+
+impl ResponseMethods for Response {
+    // https://fetch.spec.whatwg.org/#dom-response-type
+    fn Type(&self) -> DOMResponseType {
+        *self.response_type.borrow()//into()
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-url
+    fn Url(&self) -> USVString {
+        USVString(String::from((*self.url.borrow()).as_ref().map(|u| serialize_without_fragment(u)).unwrap_or("")))
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-redirected
+    fn Redirected(&self) -> bool {
+        let url_list_len = self.url_list.borrow().len();
+        url_list_len > 1
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-status
+    fn Status(&self) -> u16 {
+        match *self.raw_status.borrow() {
+            Some((s, _)) => s,
+            None => 0,
+        }
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-ok
+    fn Ok(&self) -> bool {
+        match *self.status.borrow() {
+            Some(s) => {
+                let status_num = s.to_u16();
+                return status_num >= 200 && status_num <= 299;
+            }
+            None => false,
+        }
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-statustext
+    fn StatusText(&self) -> ByteString {
+        match *self.raw_status.borrow() {
+            Some((_, ref st)) => ByteString::new(st.clone()),
+            None => ByteString::new(b"OK".to_vec()),
+        }
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-headers
+    fn Headers(&self) -> Root<Headers> {
+        self.headers_reflector.or_init(|| Headers::for_response(self.global().r()))
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-response-clone
+    fn Clone(&self) -> Fallible<Root<Response>> {
+        // Step 1
+        // TODO: This step relies on body and stream, which are still unimplemented.
+
+        // Step 2
+        let new_response = Response::new(self.global().r());
+        new_response.Headers().set_guard(self.Headers().get_guard());
+
+        // https://fetch.spec.whatwg.org/#concept-response-clone
+        // Instead of storing a net_traits::Response internally, we
+        // only store the relevant fields, and only clone them here
+        *new_response.response_type.borrow_mut() = self.response_type.borrow().clone();
+        *new_response.status.borrow_mut() = self.status.borrow().clone();
+        *new_response.raw_status.borrow_mut() = self.raw_status.borrow().clone();
+        *new_response.url.borrow_mut() = self.url.borrow().clone();
+        *new_response.url_list.borrow_mut() = self.url_list.borrow().clone();
+
+        if *self.body.borrow() != NetTraitsResponseBody::Empty {
+            *new_response.body.borrow_mut() = self.body.borrow().clone();
+        }
+
+        // Step 3
+        // TODO: This step relies on promises, which are still unimplemented.
+
+        // Step 4
+        Ok(new_response)
+    }
+
+    // https://fetch.spec.whatwg.org/#dom-body-bodyused
+    fn BodyUsed(&self) -> bool {
+        self.body_used.get()
+    }
+}
+
+fn serialize_without_fragment(url: &Url) -> &str {
+    &url[..Position::AfterQuery]
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/webidls/Response.webidl
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+// 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),
+  Exposed=(Window,Worker)]
+interface Response {
+  [NewObject] static Response error();
+  [NewObject, Throws] static Response redirect(USVString url, optional unsigned short status = 302);
+
+  readonly attribute ResponseType type;
+
+  readonly attribute USVString url;
+  readonly attribute boolean redirected;
+  readonly attribute unsigned short status;
+  readonly attribute boolean ok;
+  readonly attribute ByteString statusText;
+  [SameObject] readonly attribute Headers headers;
+  // readonly attribute ReadableStream? body;
+  // [SameObject] readonly attribute Promise<Headers> trailer;
+
+  [NewObject, Throws] Response clone();
+};
+Response implements Body;
+
+dictionary ResponseInit {
+  unsigned short status = 200;
+  ByteString statusText = "OK";
+  HeadersInit headers;
+};
+
+enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" };
+
+// typedef (BodyInit or ReadableStream) ResponseBodyInit;
--- a/servo/components/script/dom/xmlhttprequest.rs
+++ b/servo/components/script/dom/xmlhttprequest.rs
@@ -31,17 +31,16 @@ use dom::progressevent::ProgressEvent;
 use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
 use dom::xmlhttprequestupload::XMLHttpRequestUpload;
 use encoding::all::UTF_8;
 use encoding::label::encoding_from_whatwg_label;
 use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
 use euclid::length::Length;
 use hyper::header::Headers;
 use hyper::header::{ContentLength, ContentType};
-use hyper::http::RawStatus;
 use hyper::method::Method;
 use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue};
 use hyper_serde::Serde;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use js::jsapi::JS_ClearPendingException;
 use js::jsapi::{JSContext, JS_ParseJSON};
 use js::jsval::{JSVal, NullValue, UndefinedValue};
@@ -86,17 +85,17 @@ struct XHRContext {
     gen_id: GenerationId,
     buf: DOMRefCell<Vec<u8>>,
     sync_status: DOMRefCell<Option<ErrorResult>>,
 }
 
 #[derive(Clone)]
 pub enum XHRProgress {
     /// Notify that headers have been received
-    HeadersReceived(GenerationId, Option<Headers>, Option<RawStatus>),
+    HeadersReceived(GenerationId, Option<Headers>, Option<(u16, Vec<u8>)>),
     /// Partial progress (after receiving headers), containing portion of the response
     Loading(GenerationId, ByteString),
     /// Loading is done
     Done(GenerationId),
     /// There was an error (only Error::Abort, Error::Timeout or Error::Network is used)
     Errored(GenerationId, Error),
 }
 
@@ -874,17 +873,17 @@ impl XMLHttpRequest {
         };
 
         *self.response_url.borrow_mut() = metadata.final_url[..Position::AfterQuery].to_owned();
 
         // XXXManishearth Clear cache entries in case of a network error
         self.process_partial_response(XHRProgress::HeadersReceived(
             gen_id,
             metadata.headers.map(Serde::into_inner),
-            metadata.status.map(Serde::into_inner)));
+            metadata.status));
         Ok(())
     }
 
     fn process_data_available(&self, gen_id: GenerationId, payload: Vec<u8>) {
         self.process_partial_response(XHRProgress::Loading(gen_id, ByteString::new(payload)));
     }
 
     fn process_response_complete(&self, gen_id: GenerationId, status: Result<(), NetworkError>)
@@ -938,19 +937,19 @@ impl XMLHttpRequest {
                     self.dispatch_upload_progress_event(atom!("load"), None);
                     return_if_fetch_was_terminated!();
                     self.dispatch_upload_progress_event(atom!("loadend"), None);
                     return_if_fetch_was_terminated!();
                 }
                 // Part of step 13, send() (processing response)
                 // XXXManishearth handle errors, if any (substep 1)
                 // Substep 2
-                status.map(|RawStatus(code, reason)| {
+                status.map(|(code, reason)| {
                     self.status.set(code);
-                    *self.status_text.borrow_mut() = ByteString::new(reason.into_owned().into_bytes());
+                    *self.status_text.borrow_mut() = ByteString::new(reason);
                 });
                 headers.as_ref().map(|h| *self.response_headers.borrow_mut() = h.clone());
 
                 // Substep 3
                 if !self.sync.get() {
                     self.change_ready_state(XMLHttpRequestState::HeadersReceived);
                 }
             },
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -488,16 +488,17 @@ dependencies = [
  "blurz 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "devtools"
 version = "0.0.1"
 dependencies = [
  "devtools_traits 0.0.1",
+ "encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "plugins 0.0.1",
  "serde 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -446,16 +446,17 @@ dependencies = [
  "blurz 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "devtools"
 version = "0.0.1"
 dependencies = [
  "devtools_traits 0.0.1",
+ "encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "plugins 0.0.1",
  "serde 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/tests/unit/net/fetch.rs
+++ b/servo/tests/unit/net/fetch.rs
@@ -8,31 +8,29 @@ use devtools_traits::HttpResponse as Dev
 use http_loader::{expect_devtools_http_request, expect_devtools_http_response};
 use hyper::LanguageTag;
 use hyper::header::{Accept, AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowOrigin};
 use hyper::header::{AccessControlAllowMethods, AccessControlMaxAge, AcceptLanguage, AcceptEncoding};
 use hyper::header::{AccessControlRequestHeaders, AccessControlRequestMethod, UserAgent, Date};
 use hyper::header::{CacheControl, ContentLanguage, ContentLength, ContentType, Expires, LastModified};
 use hyper::header::{Headers, HttpDate, Host, Referer as HyperReferer};
 use hyper::header::{Location, SetCookie, Pragma, Encoding, qitem};
-use hyper::http::RawStatus;
 use hyper::method::Method;
 use hyper::mime::{Mime, TopLevel, SubLevel};
 use hyper::server::{Handler, Listening, Server};
 use hyper::server::{Request as HyperRequest, Response as HyperResponse};
 use hyper::status::StatusCode;
 use hyper::uri::RequestUri;
 use msg::constellation_msg::{PipelineId, ReferrerPolicy};
 use net::fetch::cors_cache::CORSCache;
 use net::fetch::methods::{FetchContext, fetch, fetch_with_cors_cache};
 use net::http_loader::HttpState;
 use net_traits::FetchTaskTarget;
 use net_traits::request::{Origin, RedirectMode, Referer, Request, RequestMode};
 use net_traits::response::{CacheState, Response, ResponseBody, ResponseType};
-use std::borrow::Cow;
 use std::fs::File;
 use std::io::Read;
 use std::rc::Rc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::mpsc::{Sender, channel};
 use std::sync::{Arc, Mutex};
 use std::thread;
 use time::{self, Duration};
@@ -829,16 +827,16 @@ fn test_fetch_with_devtools() {
 
     let content = "Yay!";
     let mut response_headers = Headers::new();
     response_headers.set(ContentLength(content.len() as u64));
     devhttpresponse.headers.as_mut().unwrap().remove::<Date>();
 
     let httpresponse = DevtoolsHttpResponse {
         headers: Some(response_headers),
-        status: Some(RawStatus(200, Cow::Borrowed("OK"))),
+        status: Some((200, b"OK".to_vec())),
         body: None,
         pipeline_id: pipeline_id,
     };
 
     assert_eq!(devhttprequest, httprequest);
     assert_eq!(devhttpresponse, httpresponse);
 }
--- a/servo/tests/unit/net/http_loader.rs
+++ b/servo/tests/unit/net/http_loader.rs
@@ -13,17 +13,16 @@ use hyper::LanguageTag;
 use hyper::header::{Accept, AcceptEncoding, ContentEncoding, ContentLength, Cookie as CookieHeader};
 use hyper::header::{Authorization, AcceptLanguage, Basic};
 use hyper::header::{Encoding, Headers, Host, Location, Quality, QualityItem, qitem, Referer, SetCookie};
 use hyper::header::{StrictTransportSecurity, UserAgent};
 use hyper::http::RawStatus;
 use hyper::method::Method;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper::status::StatusCode;
-use hyper_serde::Serde;
 use msg::constellation_msg::{PipelineId, ReferrerPolicy};
 use net::cookie::Cookie;
 use net::cookie_storage::CookieStorage;
 use net::hsts::HstsEntry;
 use net::http_loader::{LoadErrorType, HttpResponse};
 use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, UIProvider, HttpState};
 use net::resource_thread::{AuthCacheEntry, CancellationListener};
 use net_traits::{CustomResponse, Metadata, LoadOrigin};
@@ -75,17 +74,17 @@ fn respond_with(body: Vec<u8>) -> MockRe
 }
 
 fn respond_with_headers(body: Vec<u8>, mut headers: Headers) -> MockResponse {
     headers.set(ContentLength(body.len() as u64));
 
     MockResponse::new(
         headers,
         StatusCode::Ok,
-        RawStatus(200, Cow::Borrowed("Ok")),
+        RawStatus(200, Cow::Borrowed("OK")),
         body
     )
 }
 
 fn read_response(reader: &mut Read) -> String {
     let mut buf = vec![0; 1024];
     match reader.read(&mut buf) {
         Ok(len) if len > 0 => {
@@ -532,17 +531,17 @@ fn test_request_and_response_data_with_n
 
     let content = "Yay!";
     let mut response_headers = Headers::new();
     response_headers.set(ContentLength(content.len() as u64));
     response_headers.set(Host { hostname: "foo.bar".to_owned(), port: None });
 
     let httpresponse = DevtoolsHttpResponse {
         headers: Some(response_headers),
-        status: Some(RawStatus(200, Cow::Borrowed("Ok"))),
+        status: Some((200, b"OK".to_vec())),
         body: None,
         pipeline_id: pipeline_id,
     };
 
     assert_eq!(devhttprequest, httprequest);
     assert_eq!(devhttpresponse, httpresponse);
 }
 
@@ -618,25 +617,25 @@ fn test_redirected_request_to_devtools()
     let _ = load(&load_data, &ui_provider, &http_state, Some(devtools_chan), &Factory,
                  DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None);
 
     let devhttprequest = expect_devtools_http_request(&devtools_port);
     let devhttpresponse = expect_devtools_http_response(&devtools_port);
 
     assert!(devhttprequest.method == Method::Post);
     assert!(devhttprequest.url == url);
-    assert!(devhttpresponse.status == Some(RawStatus(301, Cow::Borrowed("Moved Permanently"))));
+    assert!(devhttpresponse.status == Some((301, "Moved Permanently".as_bytes().to_vec())));
 
     let devhttprequest = expect_devtools_http_request(&devtools_port);
     let devhttpresponse = expect_devtools_http_response(&devtools_port);
     let url = Url::parse("http://mozilla.org").unwrap();
 
     assert!(devhttprequest.method == Method::Get);
     assert!(devhttprequest.url == url);
-    assert!(devhttpresponse.status == Some(RawStatus(200, Cow::Borrowed("Ok"))));
+    assert!(devhttpresponse.status == Some((200, b"OK".to_vec())));
 }
 
 
 
 #[test]
 fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() {
     struct Factory;
 
@@ -1581,17 +1580,17 @@ fn test_auth_ui_sets_header_on_401() {
         &load_data, &ui_provider, &http_state,
         None, &AssertAuthHeaderRequestFactory {
             expected_headers: auth_header,
             body: <[_]>::to_vec(&[])
         }, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None), None) {
         Err(e) => panic!("response contained error {:?}", e),
         Ok(response) => {
             assert_eq!(response.metadata.status,
-                       Some(Serde(RawStatus(200, Cow::Borrowed("Ok")))));
+                       Some((200, b"OK".to_vec())));
         }
     }
 }
 
 #[test]
 fn test_auth_ui_needs_www_auth() {
     let url = Url::parse("http://mozilla.com").unwrap();
     let http_state = HttpState::new();
@@ -1616,17 +1615,17 @@ fn test_auth_ui_needs_www_auth() {
 
     let response = load(&load_data, &AuthProvider, &http_state,
                         None, &Factory, DEFAULT_USER_AGENT.to_owned(),
                         &CancellationListener::new(None), None);
     match response {
         Err(e) => panic!("response contained error {:?}", e),
         Ok(response) => {
             assert_eq!(response.metadata.status,
-                       Some(Serde(RawStatus(401, Cow::Borrowed("Unauthorized")))));
+                       Some((401, "Unauthorized".as_bytes().to_vec())));
         }
     }
 }
 
 fn assert_referer_header_matches(origin_info: &LoadOrigin,
                                  request_url: &str,
                                  expected_referrer: &str) {
     let url = Url::parse(request_url).unwrap();
@@ -1942,17 +1941,17 @@ fn load_request_for_custom_response(expe
     let (metadata, body) = join_handle.join().unwrap();
     (metadata, body)
 }
 
 #[test]
 fn test_custom_response() {
     let expected_body = b"Yay!".to_vec();
     let (metadata, body) = load_request_for_custom_response(expected_body.clone());
-    assert_eq!(metadata.status, Some(Serde(RawStatus(200, Cow::Borrowed("OK")))));
+    assert_eq!(metadata.status, Some((200, b"OK".to_vec())));
     assert_eq!(body, String::from_utf8(expected_body).unwrap());
 }
 
 #[test]
 fn test_content_blocked() {
     struct Factory;
     impl HttpRequestFactory for Factory {
         type R = MockRequest;