diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 92b9aec1b00..79469469e87 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -847,6 +847,7 @@ fn http_network_fetch(request: Rc, Ok((mut res, _)) => { response.url = Some(res.response.url.clone()); response.status = Some(res.response.status); + response.raw_status = Some(res.response.status_raw().clone()); response.headers = res.response.headers.clone(); let res_body = response.body.clone(); diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index fecbff3e077..b85532b22f1 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -11,6 +11,7 @@ use cookie; use cookie_storage::CookieStorage; use data_loader; use devtools_traits::DevtoolsControlMsg; +use fetch::methods::fetch; use file_loader; use filemanager_thread::FileManagerThreadFactory; use hsts::HstsList; @@ -23,8 +24,10 @@ use mime_classifier::{ApacheBugFlag, MIMEClassifier, NoSniffFlag}; use net_traits::LoadContext; use net_traits::ProgressMsg::Done; use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResponseAction, CoreResourceThread}; -use net_traits::{CoreResourceMsg, CookieSource, LoadConsumer, LoadData, LoadResponse, ResourceId}; -use net_traits::{NetworkError, WebSocketCommunicate, WebSocketConnectData, ResourceThreads}; +use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, LoadConsumer}; +use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId}; +use net_traits::{WebSocketCommunicate, WebSocketConnectData, ResourceThreads}; +use net_traits::request::{Referer, Request}; use profile_traits::time::ProfilerChan; use rustc_serialize::json; use rustc_serialize::{Decodable, Encodable}; @@ -36,6 +39,7 @@ use std::error::Error; use std::fs::File; use std::io::prelude::*; use std::path::Path; +use std::rc::Rc; use std::sync::mpsc::{Receiver, Sender, channel}; use std::sync::{Arc, RwLock}; use storage_thread::StorageThreadFactory; @@ -193,6 +197,8 @@ impl ResourceChannelManager { match self.from_client.recv().unwrap() { CoreResourceMsg::Load(load_data, consumer, id_sender) => self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone()), + CoreResourceMsg::Fetch(load_data, sender) => + self.resource_manager.fetch(load_data, sender), CoreResourceMsg::WebsocketConnect(connect, connect_data) => self.resource_manager.websocket_connect(connect, connect_data), CoreResourceMsg::SetCookiesForUrl(request, cookie_list, source) => @@ -480,6 +486,35 @@ impl CoreResourceManager { cancel_listener)); } + fn fetch(&self, load_data: LoadData, sender: IpcSender) { + spawn_named(format!("fetch thread for {}", load_data.url), move || { + let mut request = Request::new(load_data.url, + None, false); + // todo handle origin + // todo consider replacing LoadData with a direct mapping + // to a subset of Request + // todo set is_service_worker_global_scope + *request.method.borrow_mut() = load_data.method; + *request.headers.borrow_mut() = load_data.headers; + *request.body.borrow_mut() = load_data.data.clone(); + if let Some(cors) = load_data.cors { + request.use_cors_preflight = cors.preflight; + } + // XXXManishearth: Check origin against pipeline id + request.use_url_credentials = load_data.credentials_flag; + // todo load context / mimesniff in fetch + // todo referrer policy? + if let Some(referer) = load_data.referrer_url { + request.referer = Referer::RefererUrl(referer); + } + // todo worker stuff + + + fetch(Rc::new(request), Some(Box::new(sender))); + }) + + } + fn websocket_connect(&self, connect: WebSocketCommunicate, connect_data: WebSocketConnectData) { diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index ed17e52c876..127c85f13b0 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -112,6 +112,7 @@ pub struct LoadData { pub headers: Headers, #[ignore_heap_size_of = "Defined in hyper"] /// Headers that will apply to the initial request and any redirects + /// Unused in fetch pub preserved_headers: Headers, pub data: Option>, pub cors: Option, @@ -154,6 +155,16 @@ pub trait LoadOrigin { fn pipeline_id(&self) -> Option; } +#[derive(Deserialize, Serialize)] +pub enum FetchResponseMsg { + // todo: should have fields for transmitted/total bytes + ProcessRequestBody, + ProcessRequestEOF, + // todo: send more info about the response (or perhaps the entire Response) + ProcessResponse(Result), + ProcessResponseEOF(Result, NetworkError>), +} + pub trait FetchTaskTarget { /// https://fetch.spec.whatwg.org/#process-request-body /// @@ -176,6 +187,36 @@ pub trait FetchTaskTarget { fn process_response_eof(&mut self, response: &response::Response); } +impl FetchTaskTarget for IpcSender { + fn process_request_body(&mut self, _: &request::Request) { + let _ = self.send(FetchResponseMsg::ProcessRequestBody); + } + + fn process_request_eof(&mut self, _: &request::Request) { + let _ = self.send(FetchResponseMsg::ProcessRequestEOF); + } + + fn process_response(&mut self, response: &response::Response) { + let _ = self.send(FetchResponseMsg::ProcessResponse(response.metadata())); + } + + fn process_response_eof(&mut self, response: &response::Response) { + if response.is_network_error() { + // todo: finer grained errors + let _ = self.send(FetchResponseMsg::ProcessResponse(Err(NetworkError::Internal("Network error".into())))); + } + if let Ok(ref guard) = response.body.lock() { + if let response::ResponseBody::Done(ref vec) = **guard { + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(vec.clone()))); + return; + } + } + + // If something goes wrong, log it instead of crashing the resource thread + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(NetworkError::Internal("Incomplete body".into())))); + } +} + /// A listener for asynchronous network events. Cancelling the underlying request is unsupported. pub trait AsyncResponseListener { /// The response headers for a request have been received. @@ -348,6 +389,7 @@ pub struct WebSocketConnectData { pub enum CoreResourceMsg { /// Request the data associated with a particular URL Load(LoadData, LoadConsumer, Option>), + Fetch(LoadData, IpcSender), /// Try to make a websocket connection to a URL. WebsocketConnect(WebSocketCommunicate, WebSocketConnectData), /// Store a set of cookies for a given originating URL diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index be7a09e4b25..0aa5417b3ba 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -43,6 +43,7 @@ pub enum Origin { #[derive(Clone, PartialEq)] pub enum Referer { NoReferer, + /// Default referer if nothing is specified Client, RefererUrl(Url) } diff --git a/components/net_traits/response.rs b/components/net_traits/response.rs index 84826fed2b1..6db2228b4fe 100644 --- a/components/net_traits/response.rs +++ b/components/net_traits/response.rs @@ -4,15 +4,17 @@ //! 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, Headers}; +use hyper::header::{AccessControlExposeHeaders, ContentType, Headers}; +use hyper::http::RawStatus; use hyper::status::StatusCode; +use {Metadata, NetworkError}; use std::ascii::AsciiExt; use std::cell::{Cell, RefCell}; use std::sync::{Arc, Mutex}; use url::Url; /// [Response type](https://fetch.spec.whatwg.org/#concept-response-type) -#[derive(Clone, PartialEq, Copy, Debug)] +#[derive(Clone, PartialEq, Copy, Debug, Deserialize, Serialize)] pub enum ResponseType { Basic, CORS, @@ -23,7 +25,7 @@ pub enum ResponseType { } /// [Response termination reason](https://fetch.spec.whatwg.org/#concept-response-termination-reason) -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Deserialize, Serialize)] pub enum TerminationReason { EndUserAbort, Fatal, @@ -50,7 +52,7 @@ impl ResponseBody { /// [Cache state](https://fetch.spec.whatwg.org/#concept-response-cache-state) -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub enum CacheState { None, Local, @@ -81,6 +83,7 @@ pub struct Response { pub url_list: RefCell>, /// `None` can be considered a StatusCode of `0`. pub status: Option, + pub raw_status: Option, pub headers: Headers, pub body: Arc>, pub cache_state: CacheState, @@ -100,6 +103,7 @@ impl Response { url: None, url_list: RefCell::new(Vec::new()), status: Some(StatusCode::Ok), + raw_status: Some(RawStatus(200, "OK".into())), headers: Headers::new(), body: Arc::new(Mutex::new(ResponseBody::Empty)), cache_state: CacheState::None, @@ -116,6 +120,7 @@ impl Response { url: None, url_list: RefCell::new(vec![]), status: None, + raw_status: None, headers: Headers::new(), body: Arc::new(Mutex::new(ResponseBody::Empty)), cache_state: CacheState::None, @@ -216,4 +221,21 @@ impl Response { response } + + pub fn metadata(&self) -> Result { + let mut metadata = if let Some(ref url) = self.url { + Metadata::default(url.clone()) + } else { + return Err(NetworkError::Internal("No url found".to_string())); + }; + + metadata.set_content_type(match self.headers.get() { + Some(&ContentType(ref mime)) => Some(mime), + None => None + }); + metadata.headers = Some(self.headers.clone()); + metadata.status = self.raw_status.clone(); + metadata.https_state = self.https_state; + return Ok(metadata); + } }