diff options
-rw-r--r-- | components/devtools/actors/network_event.rs | 7 | ||||
-rw-r--r-- | components/devtools_traits/lib.rs | 1 | ||||
-rw-r--r-- | components/net/fetch/methods.rs | 53 | ||||
-rw-r--r-- | components/net/http_loader.rs | 18 | ||||
-rw-r--r-- | components/net/resource_thread.rs | 5 | ||||
-rw-r--r-- | components/net_traits/request.rs | 15 | ||||
-rw-r--r-- | components/script/dom/xmlhttprequest.rs | 1 | ||||
-rw-r--r-- | tests/unit/net/fetch.rs | 182 | ||||
-rw-r--r-- | tests/unit/net/http_loader.rs | 5 |
9 files changed, 213 insertions, 74 deletions
diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index 38f7d5767df..ba2793af8b7 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -43,6 +43,7 @@ pub struct NetworkEventActor { pub name: String, request: HttpRequest, response: HttpResponse, + is_xhr: bool, } #[derive(Serialize)] @@ -340,7 +341,8 @@ impl NetworkEventActor { headers: None, status: None, body: None, - } + }, + is_xhr: false, } } @@ -353,6 +355,7 @@ 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) { @@ -369,7 +372,7 @@ impl NetworkEventActor { method: format!("{}", self.request.method), startedDateTime: format!("{}", self.request.startedDateTime.rfc3339()), timeStamp: self.request.timeStamp, - isXHR: false, + isXHR: self.is_xhr, private: false, } } diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 0abbf3c1e81..2d8f5f0c197 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -298,6 +298,7 @@ pub struct HttpRequest { pub timeStamp: i64, pub connect_time: u64, pub send_time: u64, + pub is_xhr: bool, } #[derive(Debug, PartialEq)] diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index bef25ef9ba0..15aa3d28c00 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -4,10 +4,12 @@ use connector::create_http_connector; use data_loader::decode; +use devtools_traits::DevtoolsControlMsg; use fetch::cors_cache::CORSCache; use http_loader::{HttpState, set_default_accept_encoding, set_request_cookies}; use http_loader::{NetworkHttpRequestFactory, ReadResult, StreamedResponse, obtain_response, read_block}; use http_loader::{auth_from_cache, determine_request_referrer}; +use http_loader::{send_response_to_devtools, send_request_to_devtools}; use hyper::header::{Accept, AcceptLanguage, Authorization, AccessControlAllowCredentials}; use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods}; use hyper::header::{AccessControlRequestHeaders, AccessControlMaxAge, AccessControlRequestMethod, Basic}; @@ -20,7 +22,7 @@ use hyper::status::StatusCode; use mime_guess::guess_mime_type; use msg::constellation_msg::ReferrerPolicy; use net_traits::FetchTaskTarget; -use net_traits::request::{CacheMode, CredentialsMode}; +use net_traits::request::{CacheMode, CredentialsMode, Destination}; use net_traits::request::{RedirectMode, Referer, Request, RequestMode, ResponseTainting}; use net_traits::request::{Type, Origin, Window}; use net_traits::response::{HttpsState, TerminationReason}; @@ -36,6 +38,7 @@ use std::sync::mpsc::{channel, Sender, Receiver}; use unicase::UniCase; use url::{Origin as UrlOrigin, Url}; use util::thread::spawn_named; +use uuid; pub type Target = Option<Box<FetchTaskTarget + Send>>; @@ -47,6 +50,7 @@ enum Data { pub struct FetchContext { pub state: HttpState, pub user_agent: String, + pub devtools_chan: Option<Sender<DevtoolsControlMsg>>, } type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>; @@ -210,7 +214,8 @@ fn main_fetch(request: Rc<Request>, 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, context); + let response = http_fetch(request.clone(), cache, true, true, false, + target, done_chan, context); if response.is_network_error() { // TODO clear cache entries using request } @@ -888,7 +893,8 @@ fn http_network_or_cache_fetch(request: Rc<Request>, // Step 18 if response.is_none() { - response = Some(http_network_fetch(http_request.clone(), credentials_flag, done_chan)); + response = Some(http_network_fetch(http_request.clone(), credentials_flag, + done_chan, context.devtools_chan.clone())); } let response = response.unwrap(); @@ -924,7 +930,8 @@ fn http_network_or_cache_fetch(request: Rc<Request>, /// [HTTP network fetch](https://fetch.spec.whatwg.org/#http-network-fetch) fn http_network_fetch(request: Rc<Request>, _credentials_flag: bool, - done_chan: &mut DoneChannel) -> Response { + done_chan: &mut DoneChannel, + devtools_chan: Option<Sender<DevtoolsControlMsg>>) -> Response { // TODO: Implement HTTP network fetch spec // Step 1 @@ -944,14 +951,22 @@ fn http_network_fetch(request: Rc<Request>, let url = request.current_url(); let cancellation_listener = CancellationListener::new(None); + let request_id = uuid::Uuid::new_v4().simple().to_string(); + + // XHR uses the default destination; other kinds of fetches (which haven't been implemented yet) + // do not. Once we support other kinds of fetches we'll need to be more fine grained here + // since things like image fetches are classified differently by devtools + let is_xhr = request.destination == Destination::None; let wrapped_response = obtain_response(&factory, &url, &request.method.borrow(), &request.headers.borrow(), &cancellation_listener, &request.body.borrow(), &request.method.borrow(), - &None, request.redirect_count.get() + 1, &None, ""); + &request.pipeline_id.get(), request.redirect_count.get() + 1, + &devtools_chan, &request_id, is_xhr); + let pipeline_id = request.pipeline_id.get(); let mut response = Response::new(); match wrapped_response { - Ok((res, _)) => { + Ok((res, msg)) => { response.url = Some(url.clone()); response.status = Some(res.response.status); response.raw_status = Some(res.response.status_raw().clone()); @@ -963,10 +978,29 @@ fn http_network_fetch(request: Rc<Request>, *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(); + let meta_status = meta.status.clone(); + let meta_headers = meta.headers.clone(); spawn_named(format!("fetch worker thread"), move || { match StreamedResponse::from_http_response(box res, meta) { Ok(mut res) => { *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]); + + if let Some(ref sender) = devtools_sender { + if let Some(m) = msg { + send_request_to_devtools(m, &sender); + } + + // --- 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.into(), + meta_headers, meta_status, + pipeline_id); + } + } + loop { match read_block(&mut res) { Ok(ReadResult::Payload(chunk)) => { @@ -994,7 +1028,6 @@ fn http_network_fetch(request: Rc<Request>, break; } } - } } Err(_) => { @@ -1064,9 +1097,11 @@ fn http_network_fetch(request: Rc<Request>, } /// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch) -fn cors_preflight_fetch(request: Rc<Request>, cache: &mut CORSCache, context: &FetchContext) -> Response { +fn cors_preflight_fetch(request: Rc<Request>, cache: &mut CORSCache, + context: &FetchContext) -> Response { // Step 1 - let mut preflight = Request::new(request.current_url(), Some(request.origin.borrow().clone()), false); + let mut preflight = Request::new(request.current_url(), Some(request.origin.borrow().clone()), + false, request.pipeline_id.get()); *preflight.method.borrow_mut() = Method::Options; preflight.initiator = request.initiator.clone(); preflight.type_ = request.type_.clone(); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index e8d686e95a3..2f328917352 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -600,7 +600,8 @@ fn prepare_devtools_request(request_id: String, pipeline_id: PipelineId, now: Tm, connect_time: u64, - send_time: u64) -> ChromeToDevtoolsControlMsg { + send_time: u64, + is_xhr: bool) -> ChromeToDevtoolsControlMsg { let request = DevtoolsHttpRequest { url: url, method: method, @@ -611,18 +612,19 @@ fn prepare_devtools_request(request_id: String, timeStamp: now.to_timespec().sec, connect_time: connect_time, send_time: send_time, + is_xhr: is_xhr, }; let net_event = NetworkEvent::HttpRequest(request); ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event) } -fn send_request_to_devtools(msg: ChromeToDevtoolsControlMsg, +pub fn send_request_to_devtools(msg: ChromeToDevtoolsControlMsg, devtools_chan: &Sender<DevtoolsControlMsg>) { devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap(); } -fn send_response_to_devtools(devtools_chan: &Sender<DevtoolsControlMsg>, +pub fn send_response_to_devtools(devtools_chan: &Sender<DevtoolsControlMsg>, request_id: String, headers: Option<Headers>, status: Option<RawStatus>, @@ -742,7 +744,8 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>, pipeline_id: &Option<PipelineId>, iters: u32, devtools_chan: &Option<Sender<DevtoolsControlMsg>>, - request_id: &str) + request_id: &str, + is_xhr: bool) -> Result<(A::R, Option<ChromeToDevtoolsControlMsg>), LoadError> where A: HttpRequest + 'static { let null_data = None; @@ -750,6 +753,7 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>, let connection_url = replace_hosts(&url); let mut msg; + // loop trying connections in connection pool // they may have grown stale (disconnected), in which case we'll get // a ConnectionAborted error. this loop tries again with a new @@ -809,7 +813,7 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>, request_id.clone().into(), url.clone(), method.clone(), headers, request_body.clone(), pipeline_id, time::now(), - connect_end - connect_start, send_end - send_start)) + connect_end - connect_start, send_end - send_start, is_xhr)) } else { None } @@ -987,7 +991,7 @@ pub fn load<A, B>(load_data: &LoadData, let (response, msg) = try!(obtain_response(request_factory, &doc_url, &method, &request_headers, &cancel_listener, &load_data.data, &load_data.method, - &load_data.pipeline_id, iters, &devtools_chan, &request_id)); + &load_data.pipeline_id, iters, &devtools_chan, &request_id, false)); process_response_headers(&response, &doc_url, &http_state.cookie_jar, &http_state.hsts_list, &load_data); @@ -1087,7 +1091,7 @@ pub fn load<A, B>(load_data: &LoadData, if let Some(pipeline_id) = load_data.pipeline_id { if let Some(ref chan) = devtools_chan { send_response_to_devtools( - chan, request_id, + &chan, request_id, metadata.headers.clone(), metadata.status.clone(), pipeline_id); } diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index e43451f0806..b24e1bbe7bf 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -574,7 +574,7 @@ impl CoreResourceManager { return } }; - debug!("resource_thread: loading url: {}", load_data.url); + debug!("loading url: {}", load_data.url); loader.call_box((load_data, consumer, @@ -593,6 +593,7 @@ impl CoreResourceManager { blocked_content: BLOCKED_CONTENT_RULES.clone(), }; let ua = self.user_agent.clone(); + let dc = self.devtools_chan.clone(); spawn_named(format!("fetch thread for {}", init.url), move || { let request = Request::from_init(init); // XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed) @@ -600,7 +601,7 @@ impl CoreResourceManager { // todo referrer policy? // todo service worker stuff let mut target = Some(Box::new(sender) as Box<FetchTaskTarget + Send + 'static>); - let context = FetchContext { state: http_state, user_agent: ua }; + let context = FetchContext { state: http_state, user_agent: ua, devtools_chan: dc }; fetch(Rc::new(request), &mut target, context); }) } diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index 1fbcdabe868..bf7e22d2d0b 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -4,7 +4,7 @@ use hyper::header::Headers; use hyper::method::Method; -use msg::constellation_msg::ReferrerPolicy; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use std::cell::{Cell, RefCell}; use std::mem::swap; use url::{Origin as UrlOrigin, Url}; @@ -130,6 +130,7 @@ pub struct RequestInit { // XXXManishearth these should be part of the client object pub referer_url: Option<Url>, pub referrer_policy: Option<ReferrerPolicy>, + pub pipeline_id: Option<PipelineId>, } /// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec @@ -159,6 +160,7 @@ pub struct Request { /// https://fetch.spec.whatwg.org/#concept-request-referrer pub referer: RefCell<Referer>, pub referrer_policy: Cell<Option<ReferrerPolicy>>, + pub pipeline_id: Cell<Option<PipelineId>>, pub synchronous: bool, pub mode: RequestMode, pub use_cors_preflight: bool, @@ -178,7 +180,8 @@ pub struct Request { impl Request { pub fn new(url: Url, origin: Option<Origin>, - is_service_worker_global_scope: bool) -> Request { + is_service_worker_global_scope: bool, + pipeline_id: Option<PipelineId>) -> Request { Request { method: RefCell::new(Method::Get), local_urls_only: false, @@ -198,6 +201,7 @@ impl Request { same_origin_data: Cell::new(false), referer: RefCell::new(Referer::Client), referrer_policy: Cell::new(None), + pipeline_id: Cell::new(pipeline_id), synchronous: false, mode: RequestMode::NoCORS, use_cors_preflight: false, @@ -216,7 +220,7 @@ impl Request { pub fn from_init(init: RequestInit) -> Request { let mut req = Request::new(init.url, Some(Origin::Origin(init.origin.origin())), - false); + false, init.pipeline_id); *req.method.borrow_mut() = init.method; *req.headers.borrow_mut() = init.headers; req.unsafe_request = init.unsafe_request; @@ -234,6 +238,7 @@ impl Request { Referer::NoReferer }; req.referrer_policy.set(init.referrer_policy); + req.pipeline_id.set(init.pipeline_id); req } @@ -241,7 +246,8 @@ impl Request { pub fn potential_cors_request(url: Url, cors_attribute_state: Option<CORSSettings>, is_service_worker_global_scope: bool, - same_origin_fallback: bool) -> Request { + same_origin_fallback: bool, + pipeline_id: Option<PipelineId>) -> Request { Request { method: RefCell::new(Method::Get), local_urls_only: false, @@ -281,6 +287,7 @@ impl Request { url_list: RefCell::new(vec![url]), redirect_count: Cell::new(0), response_tainting: Cell::new(ResponseTainting::Basic), + pipeline_id: Cell::new(pipeline_id), done: Cell::new(false) } } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 5cef702c01a..b35ffef73c5 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -594,6 +594,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { origin: self.global().r().get_url(), referer_url: self.referrer_url.clone(), referrer_policy: self.referrer_policy.clone(), + pipeline_id: self.pipeline_id(), }; if bypass_cross_origin_check { diff --git a/tests/unit/net/fetch.rs b/tests/unit/net/fetch.rs index c59be9349d3..f2fe7500a51 100644 --- a/tests/unit/net/fetch.rs +++ b/tests/unit/net/fetch.rs @@ -2,23 +2,31 @@ * 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 hyper::header::{AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowOrigin}; -use hyper::header::{AccessControlAllowMethods, AccessControlMaxAge}; -use hyper::header::{AccessControlRequestHeaders, AccessControlRequestMethod}; -use hyper::header::{CacheControl, ContentLanguage, ContentType, Expires, LastModified}; -use hyper::header::{Headers, HttpDate, Location, SetCookie, Pragma}; +use devtools_traits::DevtoolsControlMsg; +use devtools_traits::HttpRequest as DevtoolsHttpRequest; +use devtools_traits::HttpResponse as DevtoolsHttpResponse; +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, 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; 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; @@ -31,16 +39,19 @@ use unicase::UniCase; use url::{Origin as UrlOrigin, Url}; use util::resource_files::resources_dir_path; +const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow."; + // TODO write a struct that impls Handler for storing test values struct FetchResponseCollector { sender: Sender<Response>, } -fn new_fetch_context() -> FetchContext { +fn new_fetch_context(dc: Option<Sender<DevtoolsControlMsg>>) -> FetchContext { FetchContext { state: HttpState::new(), - user_agent: "Such Browser. Very Layout. Wow.".into(), + user_agent: DEFAULT_USER_AGENT.into(), + devtools_chan: dc, } } impl FetchTaskTarget for FetchResponseCollector { @@ -54,14 +65,14 @@ impl FetchTaskTarget for FetchResponseCollector { } } -fn fetch_async(request: Request, target: Box<FetchTaskTarget + Send>) { +fn fetch_async(request: Request, target: Box<FetchTaskTarget + Send>, dc: Option<Sender<DevtoolsControlMsg>>) { thread::spawn(move || { - fetch(Rc::new(request), &mut Some(target), new_fetch_context()); + fetch(Rc::new(request), &mut Some(target), new_fetch_context(dc)); }); } -fn fetch_sync(request: Request) -> Response { - fetch(Rc::new(request), &mut None, new_fetch_context()) +fn fetch_sync(request: Request, dc: Option<Sender<DevtoolsControlMsg>>) -> Response { + fetch(Rc::new(request), &mut None, new_fetch_context(dc)) } fn make_server<H: Handler + 'static>(handler: H) -> (Listening, Url) { @@ -83,9 +94,9 @@ fn test_fetch_response_is_not_network_error() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); if fetch_response.is_network_error() { @@ -102,9 +113,9 @@ fn test_fetch_response_body_matches_const_message() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -122,9 +133,9 @@ fn test_fetch_response_body_matches_const_message() { fn test_fetch_aboutblank() { let url = Url::parse("about:blank").unwrap(); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); assert!(!fetch_response.is_network_error()); assert!(*fetch_response.body.lock().unwrap() == ResponseBody::Done(vec![])); } @@ -133,10 +144,10 @@ fn test_fetch_aboutblank() { fn test_fetch_data() { let url = Url::parse("data:text/html,<p>Servo</p>").unwrap(); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); request.same_origin_data.set(true); let expected_resp_body = "<p>Servo</p>".to_owned(); - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); assert!(!fetch_response.is_network_error()); assert_eq!(fetch_response.headers.len(), 1); @@ -162,10 +173,10 @@ fn test_fetch_file() { let url = Url::from_file_path(path.clone()).unwrap(); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); request.same_origin_data.set(true); - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); assert!(!fetch_response.is_network_error()); assert_eq!(fetch_response.headers.len(), 1); let content_type: &ContentType = fetch_response.headers.get().unwrap(); @@ -203,11 +214,11 @@ fn test_cors_preflight_fetch() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(UrlOrigin::new_opaque()); - let mut request = Request::new(url, Some(origin), false); + let mut request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; request.use_cors_preflight = true; request.mode = RequestMode::CORSMode; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -240,15 +251,17 @@ fn test_cors_preflight_cache_fetch() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(UrlOrigin::new_opaque()); - let mut request = Request::new(url.clone(), Some(origin.clone()), false); + let mut request = Request::new(url.clone(), Some(origin.clone()), false, None); *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, &mut None, new_fetch_context()); - let fetch_response1 = fetch_with_cors_cache(wrapped_request1.clone(), &mut cache, &mut None, new_fetch_context()); + let fetch_response0 = fetch_with_cors_cache(wrapped_request0.clone(), &mut cache, + &mut None, new_fetch_context(None)); + let fetch_response1 = fetch_with_cors_cache(wrapped_request1.clone(), &mut cache, + &mut None, new_fetch_context(None)); let _ = server.close(); assert!(!fetch_response0.is_network_error() && !fetch_response1.is_network_error()); @@ -289,12 +302,12 @@ fn test_cors_preflight_fetch_network_error() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(UrlOrigin::new_opaque()); - let mut request = Request::new(url, Some(origin), false); + let mut request = Request::new(url, Some(origin), false, None); *request.method.borrow_mut() = Method::Extension("CHICKEN".to_owned()); *request.referer.borrow_mut() = Referer::NoReferer; request.use_cors_preflight = true; request.mode = RequestMode::CORSMode; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(fetch_response.is_network_error()); @@ -313,9 +326,9 @@ fn test_fetch_response_is_basic_filtered() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -358,10 +371,10 @@ 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); + let mut request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; request.mode = RequestMode::CORSMode; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -390,9 +403,9 @@ 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 request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -437,10 +450,10 @@ fn test_fetch_response_is_opaque_redirect_filtered() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; request.redirect_mode.set(RedirectMode::Manual); - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); assert!(!fetch_response.is_network_error()); @@ -471,13 +484,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); + let mut request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; // Set the flag. request.local_urls_only = true; - fetch_sync(request) + fetch_sync(request, None) }; let local_url = Url::parse("about:blank").unwrap(); @@ -512,9 +525,9 @@ fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; - let fetch_response = fetch_sync(request); + let fetch_response = fetch_sync(request, None); let _ = server.close(); fetch_response } @@ -595,11 +608,11 @@ fn test_fetch_redirect_updates_method_runner(tx: Sender<bool>, status_code: Stat let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; *request.method.borrow_mut() = method; - let _ = fetch_sync(request); + let _ = fetch_sync(request, None); let _ = server.close(); } @@ -670,7 +683,7 @@ fn test_fetch_async_returns_complete_response() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; let (tx, rx) = channel(); @@ -678,7 +691,7 @@ fn test_fetch_async_returns_complete_response() { sender: tx.clone() }); - fetch_async(request, listener); + fetch_async(request, listener, None); let fetch_response = rx.recv().unwrap(); let _ = server.close(); @@ -695,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 request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; let (tx, rx) = channel(); @@ -703,7 +716,7 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() { sender: tx.clone() }); - fetch_async(request, listener); + fetch_async(request, listener, None); let fetch_response = rx.recv().unwrap(); let _ = server.close(); @@ -735,7 +748,7 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { let (mut server, url) = make_server(handler); let origin = Origin::Origin(url.origin()); - let request = Request::new(url, Some(origin), false); + let request = Request::new(url, Some(origin), false, None); *request.referer.borrow_mut() = Referer::NoReferer; request.redirect_mode.set(RedirectMode::Manual); @@ -744,10 +757,83 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { sender: tx.clone() }); - fetch_async(request, listener); + fetch_async(request, listener, None); let fetch_response = rx.recv().unwrap(); let _ = server.close(); assert_eq!(fetch_response.response_type, ResponseType::OpaqueRedirect); assert_eq!(response_is_done(&fetch_response), true); } + +#[test] +fn test_fetch_with_devtools() { + static MESSAGE: &'static [u8] = b"Yay!"; + let handler = move |_: HyperRequest, response: HyperResponse| { + response.send(MESSAGE).unwrap(); + }; + + let (mut server, url) = make_server(handler); + + let origin = Origin::Origin(url.origin()); + let pipeline_id = PipelineId::fake_root_pipeline_id(); + let request = Request::new(url.clone(), Some(origin), false, Some(pipeline_id)); + *request.referer.borrow_mut() = Referer::NoReferer; + + let (devtools_chan, devtools_port) = channel::<DevtoolsControlMsg>(); + + let _ = fetch_sync(request, Some(devtools_chan)); + let _ = server.close(); + + // notification received from devtools + let devhttprequest = expect_devtools_http_request(&devtools_port); + let mut devhttpresponse = expect_devtools_http_response(&devtools_port); + + //Creating default headers for request + let mut headers = Headers::new(); + + headers.set(AcceptEncoding(vec![ + qitem(Encoding::Gzip), + qitem(Encoding::Deflate), + qitem(Encoding::EncodingExt("br".to_owned())) + ])); + + headers.set(Host { hostname: url.host_str().unwrap().to_owned() , port: url.port().to_owned() }); + + let accept = Accept(vec![qitem(Mime(TopLevel::Star, SubLevel::Star, vec![]))]); + headers.set(accept); + + let mut en_us: LanguageTag = Default::default(); + en_us.language = Some("en".to_owned()); + en_us.region = Some("US".to_owned()); + headers.set(AcceptLanguage(vec![qitem(en_us)])); + + headers.set(UserAgent(DEFAULT_USER_AGENT.to_owned())); + + let httprequest = DevtoolsHttpRequest { + url: url, + method: Method::Get, + headers: headers, + body: None, + pipeline_id: pipeline_id, + startedDateTime: devhttprequest.startedDateTime, + timeStamp: devhttprequest.timeStamp, + connect_time: devhttprequest.connect_time, + send_time: devhttprequest.send_time, + is_xhr: true, + }; + + 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"))), + body: None, + pipeline_id: pipeline_id, + }; + + assert_eq!(devhttprequest, httprequest); + assert_eq!(devhttpresponse, httpresponse); +} diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index cddfcce2694..53f79178aab 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -339,7 +339,7 @@ impl HttpRequest for AssertMustHaveBodyRequest { } } -fn expect_devtools_http_request(devtools_port: &Receiver<DevtoolsControlMsg>) -> DevtoolsHttpRequest { +pub fn expect_devtools_http_request(devtools_port: &Receiver<DevtoolsControlMsg>) -> DevtoolsHttpRequest { match devtools_port.recv().unwrap() { DevtoolsControlMsg::FromChrome( ChromeToDevtoolsControlMsg::NetworkEvent(_, net_event)) => { @@ -355,7 +355,7 @@ fn expect_devtools_http_request(devtools_port: &Receiver<DevtoolsControlMsg>) -> } } -fn expect_devtools_http_response(devtools_port: &Receiver<DevtoolsControlMsg>) -> DevtoolsHttpResponse { +pub fn expect_devtools_http_response(devtools_port: &Receiver<DevtoolsControlMsg>) -> DevtoolsHttpResponse { match devtools_port.recv().unwrap() { DevtoolsControlMsg::FromChrome( ChromeToDevtoolsControlMsg::NetworkEvent(_, net_event_response)) => { @@ -526,6 +526,7 @@ fn test_request_and_response_data_with_network_messages() { timeStamp: devhttprequest.timeStamp, connect_time: devhttprequest.connect_time, send_time: devhttprequest.send_time, + is_xhr: false, }; let content = "Yay!"; |