diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 79469469e87..d2bab7b16ad 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -5,6 +5,7 @@ use connector::create_http_connector; use data_loader::decode; use fetch::cors_cache::CORSCache; +use http_loader::{HttpState, set_request_cookies}; use http_loader::{NetworkHttpRequestFactory, ReadResult, obtain_response, read_block}; use hyper::header::{Accept, AcceptLanguage, Authorization, AccessControlAllowCredentials}; use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods}; @@ -38,13 +39,14 @@ pub type Target = Option>; type DoneChannel = Option<(Sender<()>, Receiver<()>)>; /// [Fetch](https://fetch.spec.whatwg.org#concept-fetch) -pub fn fetch(request: Rc, target: Target) -> Response { - fetch_with_cors_cache(request, &mut CORSCache::new(), target) +pub fn fetch(request: Rc, target: &mut Target, state: HttpState) -> Response { + fetch_with_cors_cache(request, &mut CORSCache::new(), target, state) } pub fn fetch_with_cors_cache(request: Rc, cache: &mut CORSCache, - mut target: Target) -> Response { + target: &mut Target, + state: HttpState) -> Response { // Step 1 if request.window.get() == Window::Client { // TODO: Set window to request's client object if client is a Window object @@ -105,12 +107,13 @@ pub fn fetch_with_cors_cache(request: Rc, } // Step 7 - main_fetch(request, cache, false, false, &mut target, &mut None) + main_fetch(request, cache, false, false, target, &mut None, &state) } /// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch) fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, - recursive_flag: bool, target: &mut Target, done_chan: &mut DoneChannel) -> Response { + recursive_flag: bool, target: &mut Target, done_chan: &mut DoneChannel, + state: &HttpState) -> Response { // TODO: Implement main fetch spec // Step 1 @@ -137,8 +140,10 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, // TODO this step (referer policy) // Step 8 - if request.referer != Referer::NoReferer { + if *request.referer.borrow() != Referer::NoReferer { // TODO be able to invoke "determine request's referer" + // once this is filled in be sure to update the match + // referer below to have an unreachable branch for client } // Step 9 @@ -163,14 +168,14 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, (current_url.scheme() == "file" && request.same_origin_data.get()) || current_url.scheme() == "about" || request.mode == RequestMode::Navigate { - basic_fetch(request.clone(), cache, target, done_chan) + basic_fetch(request.clone(), cache, target, done_chan, state) } else if request.mode == RequestMode::SameOrigin { Response::network_error() } else if request.mode == RequestMode::NoCORS { request.response_tainting.set(ResponseTainting::Opaque); - basic_fetch(request.clone(), cache, target, done_chan) + basic_fetch(request.clone(), cache, target, done_chan, state) } else if !matches!(current_url.scheme(), "http" | "https") { Response::network_error() @@ -181,7 +186,7 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, request.headers.borrow().iter().any(|h| !is_simple_header(&h)))) { request.response_tainting.set(ResponseTainting::CORSTainting); request.redirect_mode.set(RedirectMode::Error); - let response = http_fetch(request.clone(), cache, true, true, false, target, done_chan); + let response = http_fetch(request.clone(), cache, true, true, false, target, done_chan, state); if response.is_network_error() { // TODO clear cache entries using request } @@ -189,7 +194,7 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, } else { request.response_tainting.set(ResponseTainting::CORSTainting); - http_fetch(request.clone(), cache, true, false, false, target, done_chan) + http_fetch(request.clone(), cache, true, false, false, target, done_chan, state) } } }; @@ -253,10 +258,8 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, // Step 18 if request.synchronous { - if !response.is_network_error() { - if let Some(ref ch) = *done_chan { - let _ = ch.1.recv(); - } + if let Some(ref ch) = *done_chan { + let _ = ch.1.recv(); } return response; } @@ -273,23 +276,22 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, } } - { - // Step 20 - if let Some(ref mut target) = *target { - target.process_response(&response); - } + // Step 20 + if let Some(ref mut target) = *target { + target.process_response(&response); + } - // Step 21 - if !response.is_network_error() { - if let Some(ref ch) = *done_chan { - let _ = ch.1.recv(); - } - } + // Step 21 + if let Some(ref ch) = *done_chan { + let _ = ch.1.recv(); + } - // Step 22 - if let Some(ref mut target) = *target { - target.process_response_eof(&response); - } + // Step 22 + request.done.set(true); + + // Step 23 + if let Some(ref mut target) = *target { + target.process_response_eof(&response); } // TODO remove this line when only asynchronous fetches are used @@ -298,19 +300,22 @@ fn main_fetch(request: Rc, cache: &mut CORSCache, cors_flag: bool, /// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch) fn basic_fetch(request: Rc, cache: &mut CORSCache, - target: &mut Target, done_chan: &mut DoneChannel) -> Response { + target: &mut Target, done_chan: &mut DoneChannel, + state: &HttpState) -> Response { let url = request.current_url(); match url.scheme() { "about" if url.path() == "blank" => { let mut response = Response::new(); + // https://github.com/whatwg/fetch/issues/312 + response.url = Some(url); response.headers.set(ContentType(mime!(Text / Html; Charset = Utf8))); *response.body.lock().unwrap() = ResponseBody::Done(vec![]); response }, "http" | "https" => { - http_fetch(request.clone(), cache, false, false, false, target, done_chan) + http_fetch(request.clone(), cache, false, false, false, target, done_chan, state) }, "data" => { @@ -318,6 +323,8 @@ fn basic_fetch(request: Rc, cache: &mut CORSCache, match decode(&url) { Ok((mime, bytes)) => { let mut response = Response::new(); + // https://github.com/whatwg/fetch/issues/312 + response.url = Some(url.clone()); *response.body.lock().unwrap() = ResponseBody::Done(bytes); response.headers.set(ContentType(mime)); response @@ -339,6 +346,8 @@ fn basic_fetch(request: Rc, cache: &mut CORSCache, let mime = guess_mime_type(file_path); let mut response = Response::new(); + // https://github.com/whatwg/fetch/issues/312 + response.url = Some(url.clone()); *response.body.lock().unwrap() = ResponseBody::Done(bytes); response.headers.set(ContentType(mime)); response @@ -367,7 +376,8 @@ fn http_fetch(request: Rc, cors_preflight_flag: bool, authentication_fetch_flag: bool, target: &mut Target, - done_chan: &mut DoneChannel) -> Response { + done_chan: &mut DoneChannel, + state: &HttpState) -> Response { // This is a new async fetch, reset the channel we are waiting on *done_chan = None; @@ -423,7 +433,7 @@ fn http_fetch(request: Rc, // Sub-substep 1 if method_mismatch || header_mismatch { - let preflight_result = cors_preflight_fetch(request.clone(), cache); + let preflight_result = cors_preflight_fetch(request.clone(), cache, state); // Sub-substep 2 if preflight_result.response_type == ResponseType::Error { return Response::network_error(); @@ -444,7 +454,7 @@ fn http_fetch(request: Rc, // Substep 4 let fetch_result = http_network_or_cache_fetch(request.clone(), credentials, authentication_fetch_flag, - done_chan); + done_chan, state); // Substep 5 if cors_flag && cors_check(request.clone(), &fetch_result).is_err() { @@ -472,7 +482,7 @@ fn http_fetch(request: Rc, // set back to default response.return_internal.set(true); http_redirect_fetch(request, cache, Rc::new(response), - cors_flag, target, done_chan) + cors_flag, target, done_chan, state) } } }, @@ -495,7 +505,7 @@ fn http_fetch(request: Rc, // Step 4 return http_fetch(request, cache, cors_flag, cors_preflight_flag, - true, target, done_chan); + true, target, done_chan, state); } // Code 407 @@ -513,7 +523,7 @@ fn http_fetch(request: Rc, return http_fetch(request, cache, cors_flag, cors_preflight_flag, authentication_fetch_flag, target, - done_chan); + done_chan, state); } _ => { } @@ -536,7 +546,8 @@ fn http_redirect_fetch(request: Rc, response: Rc, cors_flag: bool, target: &mut Target, - done_chan: &mut DoneChannel) -> Response { + done_chan: &mut DoneChannel, + state: &HttpState) -> Response { // Step 1 assert_eq!(response.return_internal.get(), true); @@ -610,14 +621,15 @@ fn http_redirect_fetch(request: Rc, request.url_list.borrow_mut().push(location_url); // Step 15 - main_fetch(request, cache, cors_flag, true, target, done_chan) + main_fetch(request, cache, cors_flag, true, target, done_chan, state) } /// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch) fn http_network_or_cache_fetch(request: Rc, credentials_flag: bool, authentication_fetch_flag: bool, - done_chan: &mut DoneChannel) -> Response { + done_chan: &mut DoneChannel, + state: &HttpState) -> Response { // TODO: Implement Window enum for Request let request_has_no_window = true; @@ -648,15 +660,17 @@ fn http_network_or_cache_fetch(request: Rc, } // Step 6 - match http_request.referer { - Referer::NoReferer => + match *http_request.referer.borrow() { + // Referer::Client should not be here, but we don't set + // the referer yet, so we club it with NoReferer + Referer::NoReferer | Referer::Client => http_request.headers.borrow_mut().set(RefererHeader("".to_owned())), Referer::RefererUrl(ref http_request_referer) => http_request.headers.borrow_mut().set(RefererHeader(http_request_referer.to_string())), - Referer::Client => - // it should be impossible for referer to be anything else during fetching - // https://fetch.spec.whatwg.org/#concept-request-referrer - unreachable!() + // Referer::Client => + // // it should be impossible for referer to be anything else during fetching + // // https://fetch.spec.whatwg.org/#concept-request-referrer + // unreachable!() }; // Step 7 @@ -705,7 +719,9 @@ fn http_network_or_cache_fetch(request: Rc, if credentials_flag { // Substep 1 // TODO http://mxr.mozilla.org/servo/source/components/net/http_loader.rs#504 - + set_request_cookies(&http_request.current_url(), + &mut *http_request.headers.borrow_mut(), + &state.cookie_jar); // Substep 2 if !http_request.headers.borrow().has::>() { // Substep 3 @@ -780,7 +796,7 @@ fn http_network_or_cache_fetch(request: Rc, // Step 18 if response.is_none() { - response = Some(http_network_fetch(request.clone(), http_request.clone(), credentials_flag, done_chan)); + response = Some(http_network_fetch(http_request.clone(), http_request.clone(), credentials_flag, done_chan)); } let response = response.unwrap(); @@ -839,8 +855,8 @@ fn http_network_fetch(request: Rc, let wrapped_response = obtain_response(&factory, &url, &request.method.borrow(), &request.headers.borrow(), - &cancellation_listener, &None, &request.method.borrow(), - &None, request.redirect_count.get(), &None, ""); + &cancellation_listener, &request.body.borrow(), &request.method.borrow(), + &None, request.redirect_count.get()+1, &None, ""); let mut response = Response::new(); match wrapped_response { @@ -938,14 +954,14 @@ fn http_network_fetch(request: Rc, } /// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch) -fn cors_preflight_fetch(request: Rc, cache: &mut CORSCache) -> Response { +fn cors_preflight_fetch(request: Rc, cache: &mut CORSCache, state: &HttpState) -> Response { // Step 1 let mut preflight = Request::new(request.current_url(), Some(request.origin.borrow().clone()), false); *preflight.method.borrow_mut() = Method::Options; preflight.initiator = request.initiator.clone(); preflight.type_ = request.type_.clone(); preflight.destination = request.destination.clone(); - preflight.referer = request.referer.clone(); + *preflight.referer.borrow_mut() = request.referer.borrow().clone(); // Step 2 preflight.headers.borrow_mut().set::( @@ -966,7 +982,7 @@ fn cors_preflight_fetch(request: Rc, cache: &mut CORSCache) -> Response // Step 6 let preflight = Rc::new(preflight); - let response = http_network_or_cache_fetch(preflight.clone(), false, false, &mut None); + let response = http_network_or_cache_fetch(preflight.clone(), false, false, &mut None, state); // Step 7 if cors_check(request.clone(), &response).is_ok() && diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index f94b6ffce32..e62fc2dd415 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -453,9 +453,9 @@ fn determine_request_referrer(headers: &mut Headers, return None; } -pub fn set_request_cookies(url: Url, headers: &mut Headers, cookie_jar: &Arc>) { +pub fn set_request_cookies(url: &Url, headers: &mut Headers, cookie_jar: &Arc>) { let mut cookie_jar = cookie_jar.write().unwrap(); - if let Some(cookie_list) = cookie_jar.cookies_for_url(&url, CookieSource::HTTP) { + if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) { let mut v = Vec::new(); v.push(cookie_list.into_bytes()); headers.set_raw("Cookie".to_owned(), v); @@ -665,7 +665,7 @@ pub fn modify_request_headers(headers: &mut Headers, // https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch step 11 if load_data.credentials_flag { if !block_cookies { - set_request_cookies(url.clone(), headers, cookie_jar); + set_request_cookies(&url, headers, cookie_jar); } // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 12 diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 95d360c62a6..18857e5005c 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -24,7 +24,7 @@ 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, FetchResponseMsg, LoadConsumer}; +use net_traits::{CoreResourceMsg, CookieSource, FetchResponseMsg, FetchTaskTarget, LoadConsumer}; use net_traits::{LoadData, LoadResponse, NetworkError, ResourceId}; use net_traits::{WebSocketCommunicate, WebSocketConnectData, ResourceThreads}; use net_traits::request::{Request, RequestInit}; @@ -486,16 +486,24 @@ impl CoreResourceManager { cancel_listener)); } - fn fetch(&self, init: RequestInit, sender: IpcSender) { + fn fetch(&self, init: RequestInit, sender: IpcSender) { + let http_state = HttpState { + hsts_list: self.hsts_list.clone(), + cookie_jar: self.cookie_jar.clone(), + auth_cache: self.auth_cache.clone(), + blocked_content: BLOCKED_CONTENT_RULES.clone(), + }; spawn_named(format!("fetch thread for {}", init.url), move || { + let sync = init.synchronous; let request = Request::from_init(init); // XXXManishearth: Check origin against pipeline id // todo load context / mimesniff in fetch // todo referrer policy? - // todo worker stuff - fetch(Rc::new(request), Some(Box::new(sender))); + // todo service worker stuff + let mut target = Some(Box::new(sender) as Box); + let response = fetch(Rc::new(request), &mut target, http_state); + target.unwrap().fetch_done(&response, sync); }) - } fn websocket_connect(&self, diff --git a/components/net/websocket_loader.rs b/components/net/websocket_loader.rs index 60d6c8932f6..d748e309d20 100644 --- a/components/net/websocket_loader.rs +++ b/components/net/websocket_loader.rs @@ -43,7 +43,7 @@ fn establish_a_websocket_connection(resource_url: &Url, net_url: (Host, String, request.headers.set(WebSocketProtocol(protocols.clone())); }; - http_loader::set_request_cookies(resource_url.clone(), &mut request.headers, &cookie_jar); + http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar); let response = try!(request.send()); try!(response.validate()); diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 92251bf77d5..59656fbea9d 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -36,6 +36,8 @@ use hyper::method::Method; use hyper::mime::{Attr, Mime}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; +use request::{Request, RequestInit}; +use response::{HttpsState, Response, ResponseBody}; use std::io::Error as IOError; use std::sync::mpsc::Sender; use std::thread; @@ -162,72 +164,111 @@ pub enum FetchResponseMsg { ProcessRequestEOF, // todo: send more info about the response (or perhaps the entire Response) ProcessResponse(Result), - ProcessResponseEOF(Result, NetworkError>), + ProcessResponseEOF(Result>, NetworkError>), + FetchDone(Result<(Metadata, Option>), NetworkError>), } pub trait FetchTaskTarget { /// https://fetch.spec.whatwg.org/#process-request-body /// /// Fired when a chunk of the request body is transmitted - fn process_request_body(&mut self, request: &request::Request); + fn process_request_body(&mut self, request: &Request); /// https://fetch.spec.whatwg.org/#process-request-end-of-file /// /// Fired when the entire request finishes being transmitted - fn process_request_eof(&mut self, request: &request::Request); + fn process_request_eof(&mut self, request: &Request); /// https://fetch.spec.whatwg.org/#process-response /// /// Fired when headers are received - fn process_response(&mut self, response: &response::Response); + fn process_response(&mut self, response: &Response); /// https://fetch.spec.whatwg.org/#process-response-end-of-file /// /// Fired when the response is fully fetched - fn process_response_eof(&mut self, response: &response::Response); + fn process_response_eof(&mut self, response: &Response); + + /// Called when fetch terminates, useful for sync + fn fetch_done(&mut self, response: &Response, sync: bool); } pub trait FetchResponseListener { fn process_request_body(&mut self); fn process_request_eof(&mut self); fn process_response(&mut self, metadata: Result); - fn process_response_eof(&mut self, response: Result, NetworkError>); + fn process_response_eof(&mut self, response: Result>, NetworkError>); + + fn fetch_done(&mut self, response: Result<(Metadata, Option>), NetworkError>); } impl FetchTaskTarget for IpcSender { - fn process_request_body(&mut self, _: &request::Request) { - println!("PRqB"); + fn process_request_body(&mut self, _: &Request) { let _ = self.send(FetchResponseMsg::ProcessRequestBody); } - fn process_request_eof(&mut self, _: &request::Request) { - println!("PRqE"); + fn process_request_eof(&mut self, _: &Request) { let _ = self.send(FetchResponseMsg::ProcessRequestEOF); } - fn process_response(&mut self, response: &response::Response) { - println!("PR"); + fn process_response(&mut self, response: &Response) { let _ = self.send(FetchResponseMsg::ProcessResponse(response.metadata())); } - fn process_response_eof(&mut self, response: &response::Response) { - println!("PRE"); + fn process_response_eof(&mut self, response: &Response) { if response.is_network_error() { // todo: finer grained errors - let _ = self.send(FetchResponseMsg::ProcessResponse(Err(NetworkError::Internal("Network error".into())))); + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(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; + match **guard { + ResponseBody::Done(ref vec) => { + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(Some(vec.clone())))); + return; + } + ResponseBody::Empty => { + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(None))); + return; + } + _ => () } } // If something goes wrong, log it instead of crashing the resource thread let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(NetworkError::Internal("Incomplete body".into())))); } + + fn fetch_done(&mut self, response: &Response, sync: bool) { + if !sync { + // fetch_done is only used by sync XHR, avoid pointless data cloning + return; + } + if response.is_network_error() { + // todo: finer grained errors + let _ = self.send(FetchResponseMsg::FetchDone(Err(NetworkError::Internal("Network error".into())))); + } + if let Ok(ref guard) = response.body.lock() { + match **guard { + ResponseBody::Done(ref vec) => { + let ret = response.metadata().map(|m| (m, Some(vec.clone()))); + let _ = self.send(FetchResponseMsg::FetchDone(ret)); + return; + } + ResponseBody::Empty => { + let ret = response.metadata().map(|m| (m, None)); + let _ = self.send(FetchResponseMsg::FetchDone(ret)); + return; + } + _ => () + } + } + + // If something goes wrong, log it instead of crashing the resource thread + let _ = self.send(FetchResponseMsg::FetchDone(Err(NetworkError::Internal("Incomplete body".into())))); + } } + pub trait Action { fn process(self, listener: &mut Listener); } @@ -275,6 +316,7 @@ impl Action for FetchResponseMsg { FetchResponseMsg::ProcessRequestEOF => listener.process_request_eof(), FetchResponseMsg::ProcessResponse(meta) => listener.process_response(meta), FetchResponseMsg::ProcessResponseEOF(data) => listener.process_response_eof(data), + FetchResponseMsg::FetchDone(response) => listener.fetch_done(response), } } } @@ -416,7 +458,7 @@ pub struct WebSocketConnectData { pub enum CoreResourceMsg { /// Request the data associated with a particular URL Load(LoadData, LoadConsumer, Option>), - Fetch(request::RequestInit, IpcSender), + Fetch(RequestInit, IpcSender), /// Try to make a websocket connection to a URL. WebsocketConnect(WebSocketCommunicate, WebSocketConnectData), /// Store a set of cookies for a given originating URL @@ -555,7 +597,7 @@ pub struct Metadata { pub status: Option, /// Is successful HTTPS connection - pub https_state: response::HttpsState, + pub https_state: HttpsState, } impl Metadata { @@ -568,7 +610,7 @@ impl Metadata { headers: None, // https://fetch.spec.whatwg.org/#concept-response-status-message status: Some(RawStatus(200, "OK".into())), - https_state: response::HttpsState::None, + https_state: HttpsState::None, } } diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index 70ac600bb25..6909d7b25e7 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -151,7 +151,7 @@ pub struct Request { pub origin: RefCell, pub omit_origin_header: Cell, pub same_origin_data: Cell, - pub referer: Referer, + pub referer: RefCell, // TODO: referrer policy pub synchronous: bool, pub mode: RequestMode, @@ -190,7 +190,7 @@ impl Request { origin: RefCell::new(origin.unwrap_or(Origin::Client)), omit_origin_header: Cell::new(false), same_origin_data: Cell::new(false), - referer: Referer::Client, + referer: RefCell::new(Referer::Client), synchronous: false, mode: RequestMode::NoCORS, use_cors_preflight: false, @@ -245,7 +245,7 @@ impl Request { origin: RefCell::new(Origin::Client), omit_origin_header: Cell::new(false), same_origin_data: Cell::new(false), - referer: Referer::Client, + referer: RefCell::new(Referer::Client), synchronous: false, // Step 1-2 mode: match cors_attribute_state { diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 7239ae5a745..7425a2feff3 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -230,12 +230,14 @@ impl XMLHttpRequest { *self.sync_status.borrow_mut() = Some(rv); } } - fn process_response_eof(&mut self, response: Result, NetworkError>) { + fn process_response_eof(&mut self, response: Result>, NetworkError>) { match response { Ok(buf) => { - *self.buf.borrow_mut() = buf; - // todo move to a process_chunk - self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); + if let Some(buf) = buf { + *self.buf.borrow_mut() = buf; + // todo move to a process_chunk + self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); + } let rv = self.xhr.root().process_response_complete(self.gen_id, Ok(())); *self.sync_status.borrow_mut() = Some(rv); } @@ -244,8 +246,17 @@ impl XMLHttpRequest { *self.sync_status.borrow_mut() = Some(rv); } } - - + } + fn fetch_done(&mut self, response: Result<(Metadata, Option>), NetworkError>) { + match response { + Ok(response) => { + self.process_response(Ok(response.0)); + self.process_response_eof(Ok(response.1)); + } + Err(err) => { + self.process_response_eof(Err(err)); + } + } } } @@ -261,7 +272,6 @@ impl XMLHttpRequest { script_chan: script_chan, }; ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - println!("routing"); listener.notify_fetch(message.to().unwrap()); }); core_resource_thread.send(Fetch(init, action_sender)).unwrap(); @@ -525,6 +535,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { }; // Step 4 let extracted = data.as_ref().map(|d| d.extract()); + self.request_body_len.set(extracted.as_ref().map_or(0, |e| e.0.len())); // todo preserved headers? @@ -604,6 +615,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { debug!("request_headers = {:?}", *self.request_headers.borrow()); self.fetch_time.set(time::now().to_timespec().sec); + let rv = self.fetch(request, self.global().r()); // Step 10 if self.sync.get() { diff --git a/tests/unit/net/fetch.rs b/tests/unit/net/fetch.rs index 16dc664ffff..3eb0ff78567 100644 --- a/tests/unit/net/fetch.rs +++ b/tests/unit/net/fetch.rs @@ -15,6 +15,7 @@ use hyper::status::StatusCode; use hyper::uri::RequestUri; use net::fetch::cors_cache::CORSCache; use net::fetch::methods::{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}; @@ -44,11 +45,12 @@ impl FetchTaskTarget for FetchResponseCollector { fn process_response_eof(&mut self, response: &Response) { self.sender.send(response.clone()); } + fn fetch_done(&mut self, _: &Response, _: bool) {} } fn fetch_async(request: Request, target: Box) { thread::spawn(move || { - fetch(Rc::new(request), Some(target)); + fetch(Rc::new(request), &mut Some(target), HttpState::new()); }); } @@ -72,10 +74,10 @@ fn test_fetch_response_is_not_network_error() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); if fetch_response.is_network_error() { @@ -93,10 +95,10 @@ fn test_fetch_response_body_matches_const_message() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -115,10 +117,10 @@ fn test_fetch_aboutblank() { let url = Url::parse("about:blank").unwrap(); let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); assert!(!fetch_response.is_network_error()); assert!(*fetch_response.body.lock().unwrap() == ResponseBody::Done(vec![])); } @@ -130,7 +132,7 @@ fn test_fetch_data() { let request = Request::new(url, Some(origin), false); request.same_origin_data.set(true); let expected_resp_body = "

Servo

".to_owned(); - let fetch_response = fetch(Rc::new(request), None); + let fetch_response = fetch(Rc::new(request), &mut None, HttpState::new()); assert!(!fetch_response.is_network_error()); assert_eq!(fetch_response.headers.len(), 1); @@ -159,7 +161,7 @@ fn test_fetch_file() { let request = Request::new(url, Some(origin), false); request.same_origin_data.set(true); - let fetch_response = fetch(Rc::new(request), None); + let fetch_response = fetch(Rc::new(request), &mut None, HttpState::new()); assert!(!fetch_response.is_network_error()); assert_eq!(fetch_response.headers.len(), 1); let content_type: &ContentType = fetch_response.headers.get().unwrap(); @@ -198,12 +200,12 @@ fn test_cors_preflight_fetch() { let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.use_cors_preflight = true; request.mode = RequestMode::CORSMode; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -237,14 +239,14 @@ fn test_cors_preflight_cache_fetch() { let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url.clone(), Some(origin.clone()), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.use_cors_preflight = true; request.mode = RequestMode::CORSMode; let wrapped_request0 = Rc::new(request.clone()); let wrapped_request1 = Rc::new(request); - let fetch_response0 = fetch_with_cors_cache(wrapped_request0.clone(), &mut cache, None); - let fetch_response1 = fetch_with_cors_cache(wrapped_request1.clone(), &mut cache, None); + let fetch_response0 = fetch_with_cors_cache(wrapped_request0.clone(), &mut cache, &mut None, HttpState::new()); + let fetch_response1 = fetch_with_cors_cache(wrapped_request1.clone(), &mut cache, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response0.is_network_error() && !fetch_response1.is_network_error()); @@ -287,12 +289,12 @@ fn test_cors_preflight_fetch_network_error() { let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url, Some(origin), false); *request.method.borrow_mut() = Method::Extension("CHICKEN".to_owned()); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.use_cors_preflight = true; request.mode = RequestMode::CORSMode; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(fetch_response.is_network_error()); @@ -312,10 +314,10 @@ fn test_fetch_response_is_basic_filtered() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -359,11 +361,11 @@ fn test_fetch_response_is_cors_filtered() { // an origin mis-match will stop it from defaulting to a basic filtered response let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.mode = RequestMode::CORSMode; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -393,10 +395,10 @@ fn test_fetch_response_is_opaque_filtered() { // an origin mis-match will fall through to an Opaque filtered response let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -442,11 +444,11 @@ fn test_fetch_response_is_opaque_redirect_filtered() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.redirect_mode.set(RedirectMode::Manual); let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -478,13 +480,13 @@ fn test_fetch_with_local_urls_only() { let do_fetch = |url: Url| { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; // Set the flag. request.local_urls_only = true; let wrapped_request = Rc::new(request); - fetch(wrapped_request, None) + fetch(wrapped_request, &mut None, HttpState::new()) }; let local_url = Url::parse("about:blank").unwrap(); @@ -520,10 +522,10 @@ fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let wrapped_request = Rc::new(request); - let fetch_response = fetch(wrapped_request, None); + let fetch_response = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); fetch_response } @@ -605,11 +607,11 @@ fn test_fetch_redirect_updates_method_runner(tx: Sender, status_code: Stat let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; *request.method.borrow_mut() = method; let wrapped_request = Rc::new(request); - let _ = fetch(wrapped_request, None); + let _ = fetch(wrapped_request, &mut None, HttpState::new()); let _ = server.close(); } @@ -681,7 +683,7 @@ fn test_fetch_async_returns_complete_response() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let (tx, rx) = channel(); let listener = Box::new(FetchResponseCollector { @@ -706,7 +708,7 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() { // an origin mis-match will fall through to an Opaque filtered response let origin = Origin::Origin(UrlOrigin::new_opaque()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; let (tx, rx) = channel(); let listener = Box::new(FetchResponseCollector { @@ -746,7 +748,7 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { let origin = Origin::Origin(url.origin()); let mut request = Request::new(url, Some(origin), false); - request.referer = Referer::NoReferer; + *request.referer.borrow_mut() = Referer::NoReferer; request.redirect_mode.set(RedirectMode::Manual); let (tx, rx) = channel();