Skip to content

Commit

Permalink
Partial implementation of Main Fetch step, including appropiate updat…
Browse files Browse the repository at this point in the history
…es to tests
  • Loading branch information
nikkisquared committed Jan 29, 2016
1 parent 842ec7c commit db8cc6e
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 65 deletions.
12 changes: 5 additions & 7 deletions components/net/fetch/cors_cache.rs
Expand Up @@ -14,7 +14,7 @@ use std::ascii::AsciiExt;
use std::sync::mpsc::{Sender, Receiver, channel};
use time;
use time::{now, Timespec};
use url::Url;
use url::{Origin, Url};

/// Union type for CORS cache entries
///
Expand Down Expand Up @@ -44,7 +44,7 @@ impl HeaderOrMethod {
/// An entry in the CORS cache
#[derive(Clone)]
pub struct CORSCacheEntry {
pub origin: Url,
pub origin: Origin,
pub url: Url,
pub max_age: u32,
pub credentials: bool,
Expand All @@ -53,7 +53,7 @@ pub struct CORSCacheEntry {
}

impl CORSCacheEntry {
fn new(origin: Url, url: Url, max_age: u32, credentials: bool,
fn new(origin: Origin, url: Url, max_age: u32, credentials: bool,
header_or_method: HeaderOrMethod) -> CORSCacheEntry {
CORSCacheEntry {
origin: origin,
Expand All @@ -68,7 +68,7 @@ impl CORSCacheEntry {

/// Properties of Request required to cache match.
pub struct CacheRequestDetails {
pub origin: Url,
pub origin: Origin,
pub destination: Url,
pub credentials: bool
}
Expand Down Expand Up @@ -109,9 +109,7 @@ pub trait CORSCache {
pub struct BasicCORSCache(Vec<CORSCacheEntry>);

fn match_headers(cors_cache: &CORSCacheEntry, cors_req: &CacheRequestDetails) -> bool {
cors_cache.origin.scheme == cors_req.origin.scheme &&
cors_cache.origin.host() == cors_req.origin.host() &&
cors_cache.origin.port() == cors_req.origin.port() &&
cors_cache.origin == cors_req.origin &&
cors_cache.url == cors_req.destination &&
cors_cache.credentials == cors_req.credentials
}
Expand Down
213 changes: 193 additions & 20 deletions components/net/fetch/methods.rs
Expand Up @@ -22,6 +22,7 @@ use net_traits::response::{Response, ResponseBody, ResponseType};
use net_traits::{AsyncFetchListener, Metadata};
use resource_thread::CancellationListener;
use std::ascii::AsciiExt;
use std::cell::RefCell;
use std::io::Read;
use std::rc::Rc;
use std::str::FromStr;
Expand Down Expand Up @@ -90,14 +91,177 @@ pub fn fetch(request: Rc<Request>, cors_flag: bool) -> Response {
// TODO: Figure out what a Priority object is
// Step 3
// Step 4
main_fetch(request, cors_flag)
main_fetch(request, cors_flag, false)
}

/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
fn main_fetch(request: Rc<Request>, _cors_flag: bool) -> Response {
fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Response {
// TODO: Implement main fetch spec
let response = basic_fetch(request);
response

// Step 1
let mut response = None;

// Step 2
if request.local_urls_only {
match &*request.current_url().scheme {
"about" | "blob" | "data" | "filesystem" => response = Some(Response::network_error()),
_ => { }
};
}

// Step 3
// TODO be able to execute report CSP

// Step 4
// TODO this step, based off of http_loader.rs

// Step 5
// TODO this step

// Step 6
if request.referer != Referer::NoReferer {
// TODO be able to invoke "determine request's referer"
}

// Step 7
// TODO this step

// Step 8
if !request.synchronous && !recursive_flag {
// TODO run the remaining steps in parallel
}

// Step 9
let mut response = if response.is_none() {

let current_url = request.current_url();
let origin_match = request.origin == current_url.origin();

if (!cors_flag && origin_match) ||
(current_url.scheme == "data" && request.same_origin_data.get()) ||
current_url.scheme == "about" ||
request.mode == RequestMode::Navigate {

basic_fetch(request.clone())

} 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())

} else if current_url.scheme != "http" && current_url.scheme != "https" {
Response::network_error()

} else if request.use_cors_preflight ||
(request.unsafe_request &&
(!is_simple_method(&request.method.borrow()) ||
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(), BasicCORSCache::new(), true, true, false);
if Response::is_network_error(&response) {
// TODO clear cache entries using request
}
response

} else {
request.response_tainting.set(ResponseTainting::CORSTainting);
http_fetch(request.clone(), BasicCORSCache::new(), true, false, false)
}
} else {
response.unwrap()
};

// Step 10
if recursive_flag {
return response;
}

// Step 11
// no need to check if response is a network error, since the type would not be `Default`
let mut response = if response.response_type == ResponseType::Default {
let old_response = Rc::new(response);
let response_type = match request.response_tainting.get() {
ResponseTainting::Basic => ResponseType::Basic,
ResponseTainting::CORSTainting => ResponseType::CORS,
ResponseTainting::Opaque => ResponseType::Opaque,
};
Response::to_filtered(old_response, response_type)
} else {
response
};

// Step 12
let mut internal_response = if Response::is_network_error(&response) {
Rc::new(Response::network_error())
} else {
response.internal_response.clone().unwrap()
};

// Step 13
// TODO this step

// Step 14
if !Response::is_network_error(&response) && (is_null_body_status(&internal_response.status) ||
match *request.method.borrow() {
Method::Head | Method::Connect => true,
_ => false })
{
// when the Fetch implementation does asynchronous retrieval of the body,
// we will need to make sure nothing tries to write to the body at this point
*internal_response.body.borrow_mut() = ResponseBody::Empty;
}

// Step 15
// TODO be able to compare response integrity against request integrity metadata
// if !Response::is_network_error(&response) {

// // Substep 1
// // TODO wait for response

// // Substep 2
// if response.termination_reason.is_none() {
// response = Response::network_error();
// internal_response = Response::network_error();
// }
// }

// Step 16
if request.synchronous {
// TODO wait for internal_response
return response;
}

// Step 17
if request.body.is_some() && match &*request.current_url().scheme {
"http" | "https" => true,
_ => false }
{
// TODO queue a fetch task on request to process end-of-file
}

// Step 18
// TODO this step

match *internal_response.body.borrow() {
// Step 20
ResponseBody::Empty => {
// Substep 1
// Substep 2
},

// Step 19
_ => {
// Substep 1
// Substep 2
}
};

// TODO remove this line when asynchronous fetches are supported
return response;
}

/// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch)
Expand Down Expand Up @@ -180,7 +344,7 @@ fn http_fetch(request: Rc<Request>,
if (res.response_type == ResponseType::Opaque &&
request.mode != RequestMode::NoCORS) ||
(res.response_type == ResponseType::OpaqueRedirect &&
request.redirect_mode != RedirectMode::Manual) ||
request.redirect_mode.get() != RedirectMode::Manual) ||
res.response_type == ResponseType::Error {
return Response::network_error();
}
Expand All @@ -205,9 +369,7 @@ fn http_fetch(request: Rc<Request>,
let mut method_mismatch = false;
let mut header_mismatch = false;

// FIXME: Once Url::Origin is available, rewrite origin to
// take an Origin instead of a Url
let origin = request.origin.clone().unwrap_or(Url::parse("").unwrap());
let origin = request.origin.clone();
let url = request.current_url();
let credentials = request.credentials_mode == CredentialsMode::Include;
let method_cache_match = cache.match_method(CacheRequestDetails {
Expand All @@ -217,7 +379,7 @@ fn http_fetch(request: Rc<Request>,
}, request.method.borrow().clone());

method_mismatch = !method_cache_match && (!is_simple_method(&request.method.borrow()) ||
request.mode == RequestMode::ForcedPreflightMode);
request.use_cors_preflight);
header_mismatch = request.headers.borrow().iter().any(|view|
!cache.match_header(CacheRequestDetails {
origin: origin.clone(),
Expand All @@ -226,12 +388,13 @@ fn http_fetch(request: Rc<Request>,
}, view.name()) && !is_simple_header(&view)
);

// Sub-substep 1
if method_mismatch || header_mismatch {
let preflight_result = preflight_fetch(request.clone());
// Sub-substep 2
if preflight_result.response_type == ResponseType::Error {
return Response::network_error();
}
response = Some(Rc::new(preflight_result));
}
}

Expand All @@ -242,7 +405,7 @@ fn http_fetch(request: Rc<Request>,
let credentials = match request.credentials_mode {
CredentialsMode::Include => true,
CredentialsMode::CredentialsSameOrigin if (!cors_flag ||
request.response_tainting == ResponseTainting::Opaque)
request.response_tainting.get() == ResponseTainting::Opaque)
=> true,
_ => false
};
Expand Down Expand Up @@ -271,7 +434,7 @@ fn http_fetch(request: Rc<Request>,
StatusCode::TemporaryRedirect | StatusCode::PermanentRedirect => {

// Step 1
if request.redirect_mode == RedirectMode::Error {
if request.redirect_mode.get() == RedirectMode::Error {
return Response::network_error();
}

Expand Down Expand Up @@ -308,11 +471,11 @@ fn http_fetch(request: Rc<Request>,
// Step 9
request.same_origin_data.set(false);

match request.redirect_mode {
match request.redirect_mode.get() {

// Step 10
RedirectMode::Manual => {
response = Rc::new(Response::to_filtered(actual_response, ResponseType::Opaque));
response = Rc::new(Response::to_filtered(actual_response, ResponseType::OpaqueRedirect));
}

// Step 11
Expand Down Expand Up @@ -350,7 +513,7 @@ fn http_fetch(request: Rc<Request>,
request.url_list.borrow_mut().push(location_url);

// Substep 6
return main_fetch(request.clone(), cors_flag);
return main_fetch(request.clone(), cors_flag, true);
}
RedirectMode::Error => { panic!("RedirectMode is Error after step 8") }
}
Expand Down Expand Up @@ -418,7 +581,7 @@ fn http_network_or_cache_fetch(request: Rc<Request>,

// Step 1
let http_request = if request_has_no_window &&
request.redirect_mode != RedirectMode::Follow {
request.redirect_mode.get() != RedirectMode::Follow {
request.clone()
} else {
Rc::new((*request).clone())
Expand Down Expand Up @@ -457,9 +620,7 @@ fn http_network_or_cache_fetch(request: Rc<Request>,
// Step 7
if http_request.omit_origin_header == false {
// TODO update this when https://github.com/hyperium/hyper/pull/691 is finished
if let Some(ref _origin) = http_request.origin {
// http_request.headers.borrow_mut().set_raw("origin", origin);
}
// http_request.headers.borrow_mut().set_raw("origin", origin);
}

// Step 8
Expand Down Expand Up @@ -628,7 +789,7 @@ fn http_network_fetch(request: Rc<Request>,

let mut body = vec![];
res.response.read_to_end(&mut body);
response.body = ResponseBody::Done(body);
*response.body.borrow_mut() = ResponseBody::Done(body);
},
Err(e) =>
response.termination_reason = Some(TerminationReason::Fatal)
Expand Down Expand Up @@ -677,6 +838,7 @@ fn http_network_fetch(request: Rc<Request>,
// Substep 3
// Substep 4

// TODO these steps
// Step 10
// Substep 1
// Substep 2
Expand Down Expand Up @@ -761,3 +923,14 @@ fn response_needs_revalidation(response: &Response) -> bool {
// // TODO this function

// }

fn is_null_body_status(status: &Option<StatusCode>) -> bool {
match *status {
Some(status) => match status {
StatusCode::SwitchingProtocols | StatusCode::NoContent |
StatusCode::ResetContent | StatusCode::NotModified => true,
_ => false
},
_ => false
}
}

0 comments on commit db8cc6e

Please sign in to comment.