servo: Merge #15903 - Set Origin header in http_network_or_cache_fetch (from servo:set-origin-header); r=Wafflespeanut
authorSam Giles <sam.e.giles@gmail.com>
Thu, 09 Mar 2017 22:15:11 -0800
changeset 346907 9c29dd17051ec2db36577c463449a87eb4255ea9
parent 346906 b3eb8484454007d3a44be3a1d63cc15790d4fc09
child 346908 349830b775ae00c179133c8a9256b6338507f379
push id31480
push usercbook@mozilla.com
push dateFri, 10 Mar 2017 10:37:06 +0000
treeherdermozilla-central@e18d3dd20e8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWafflespeanut
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #15903 - Set Origin header in http_network_or_cache_fetch (from servo:set-origin-header); r=Wafflespeanut Source-Repo: https://github.com/servo/servo Source-Revision: a11a3fe68bad97970568f7d785a41bd00b707e92
servo/components/net/http_loader.rs
servo/tests/unit/net/http_loader.rs
--- a/servo/components/net/http_loader.rs
+++ b/servo/components/net/http_loader.rs
@@ -17,16 +17,17 @@ use hyper::LanguageTag;
 use hyper::client::{Pool, Request as HyperRequest, Response as HyperResponse};
 use hyper::header::{AcceptEncoding, AcceptLanguage, AccessControlAllowCredentials};
 use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods};
 use hyper::header::{AccessControlRequestHeaders, AccessControlMaxAge, AccessControlRequestMethod};
 use hyper::header::{Authorization, Basic, CacheControl, CacheDirective, ContentEncoding};
 use hyper::header::{ContentLength, Encoding, Header, Headers, Host, IfMatch, IfRange};
 use hyper::header::{IfUnmodifiedSince, IfModifiedSince, IfNoneMatch, Location, Pragma, Quality};
 use hyper::header::{QualityItem, Referer, SetCookie, UserAgent, qitem};
+use hyper::header::Origin as HyperOrigin;
 use hyper::method::Method;
 use hyper::net::Fresh;
 use hyper::status::StatusCode;
 use hyper_serde::Serde;
 use log;
 use msg::constellation_msg::PipelineId;
 use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy};
 use net_traits::hosts::replace_hosts;
@@ -780,16 +781,25 @@ fn http_redirect_fetch(request: Rc<Reque
 
     // Step 12
     // TODO implement referrer policy
 
     // Step 13
     main_fetch(request, cache, cors_flag, true, target, done_chan, context)
 }
 
+fn try_immutable_origin_to_hyper_origin(url_origin: &ImmutableOrigin) -> Option<HyperOrigin> {
+    match *url_origin {
+        // TODO (servo/servo#15569) Set "Origin: null" when hyper supports it
+        ImmutableOrigin::Opaque(_) => None,
+        ImmutableOrigin::Tuple(ref scheme, ref host, ref port) =>
+            Some(HyperOrigin::new(scheme.clone(), host.to_string(), Some(port.clone())))
+    }
+}
+
 /// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch)
 fn http_network_or_cache_fetch(request: Rc<Request>,
                                authentication_fetch_flag: bool,
                                cors_flag: bool,
                                done_chan: &mut DoneChannel,
                                context: &FetchContext)
                                -> Response {
     // TODO: Implement Window enum for Request
@@ -838,20 +848,26 @@ fn http_network_or_cache_fetch(request: 
             http_request.headers.borrow_mut().set(Referer(http_request_referrer.to_string())),
         Referrer::Client =>
             // it should be impossible for referrer to be anything else during fetching
             // https://fetch.spec.whatwg.org/#concept-request-referrer
             unreachable!()
     };
 
     // Step 9
-    if cors_flag ||
-      (*http_request.method.borrow() != Method::Get && *http_request.method.borrow() != Method::Head) {
-        // TODO update this when https://github.com/hyperium/hyper/pull/691 is finished
-        // http_request.headers.borrow_mut().set_raw("origin", origin);
+    if !http_request.omit_origin_header.get() {
+        let method = http_request.method.borrow();
+        if cors_flag || (*method != Method::Get && *method != Method::Head) {
+            debug_assert!(*http_request.origin.borrow() != Origin::Client);
+            if let Origin::Origin(ref url_origin) = *http_request.origin.borrow() {
+                if let Some(hyper_origin) = try_immutable_origin_to_hyper_origin(url_origin) {
+                    http_request.headers.borrow_mut().set(hyper_origin)
+                }
+            }
+        }
     }
 
     // Step 10
     if !http_request.headers.borrow().has::<UserAgent>() {
         let user_agent = context.user_agent.clone().into_owned();
         http_request.headers.borrow_mut().set(UserAgent(user_agent));
     }
 
--- a/servo/tests/unit/net/http_loader.rs
+++ b/servo/tests/unit/net/http_loader.rs
@@ -7,37 +7,38 @@ use devtools_traits::{ChromeToDevtoolsCo
 use devtools_traits::HttpRequest as DevtoolsHttpRequest;
 use devtools_traits::HttpResponse as DevtoolsHttpResponse;
 use fetch;
 use fetch_with_context;
 use flate2::Compression;
 use flate2::write::{DeflateEncoder, GzEncoder};
 use hyper::LanguageTag;
 use hyper::header::{Accept, AcceptEncoding, ContentEncoding, ContentLength, Cookie as CookieHeader};
-use hyper::header::{AcceptLanguage, Authorization, Basic, Date};
-use hyper::header::{Encoding, Headers, Host, Location, Quality, QualityItem, SetCookie, qitem};
+use hyper::header::{AcceptLanguage, AccessControlAllowOrigin, Authorization, Basic, Date};
+use hyper::header::{Encoding, Headers, Host, Location, Origin, Quality, QualityItem, SetCookie, qitem};
 use hyper::header::{StrictTransportSecurity, UserAgent};
 use hyper::method::Method;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper::server::{Request as HyperRequest, Response as HyperResponse};
 use hyper::status::StatusCode;
 use hyper::uri::RequestUri;
 use make_server;
 use msg::constellation_msg::TEST_PIPELINE_ID;
 use net::cookie::Cookie;
 use net::cookie_storage::CookieStorage;
 use net::resource_thread::AuthCacheEntry;
 use net_traits::{CookieSource, NetworkError};
 use net_traits::hosts::replace_host_table;
-use net_traits::request::{Request, RequestInit, CredentialsMode, Destination};
+use net_traits::request::{Request, RequestInit, RequestMode, CredentialsMode, Destination};
 use net_traits::response::ResponseBody;
 use new_fetch_context;
 use servo_url::ServoUrl;
 use std::collections::HashMap;
 use std::io::{Read, Write};
+use std::str::FromStr;
 use std::sync::{Arc, Mutex, RwLock, mpsc};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::Receiver;
 
 fn read_response(reader: &mut Read) -> String {
     let mut buf = vec![0; 1024];
     match reader.read(&mut buf) {
         Ok(len) if len > 0 => {
@@ -140,18 +141,23 @@ fn test_check_default_headers_loaded_in_
         origin: url.clone(),
         pipeline_id: Some(TEST_PIPELINE_ID),
         .. RequestInit::default()
     });
     let response = fetch(request, None);
     assert!(response.status.unwrap().is_success());
 
     // Testing for method.POST
-    headers.set(ContentLength(0 as u64));
-    *expected_headers.lock().unwrap() = Some(headers.clone());
+    let mut post_headers = headers.clone();
+    post_headers.set(ContentLength(0 as u64));
+    let url_str = url.as_str();
+    // request gets header "Origin: http://example.com" but expected_headers has
+    // "Origin: http://example.com/" which do not match for equality so strip trailing '/'
+    post_headers.set(Origin::from_str(&url_str[..url_str.len()-1]).unwrap());
+    *expected_headers.lock().unwrap() = Some(post_headers);
     let request = Request::from_init(RequestInit {
         url: url.clone(),
         method: Method::Post,
         destination: Destination::Document,
         origin: url.clone(),
         pipeline_id: Some(TEST_PIPELINE_ID),
         .. RequestInit::default()
     });
@@ -1112,8 +1118,66 @@ fn test_auth_ui_needs_www_auth() {
     });
 
     let response = fetch(request, None);
 
     let _ = server.close();
 
     assert_eq!(response.status.unwrap(), StatusCode::Unauthorized);
 }
+
+#[test]
+fn test_origin_set() {
+    let origin_header = Arc::new(Mutex::new(None));
+    let origin_header_clone = origin_header.clone();
+    let handler = move |request: HyperRequest, mut resp: HyperResponse| {
+        let origin_header_clone = origin_header.clone();
+        resp.headers_mut().set(AccessControlAllowOrigin::Any);
+        match request.headers.get::<Origin>() {
+            None => assert_eq!(origin_header_clone.lock().unwrap().take(), None),
+            Some(h) => assert_eq!(*h, origin_header_clone.lock().unwrap().take().unwrap()),
+        }
+    };
+    let (mut server, url) = make_server(handler);
+
+    let mut origin = Origin::new(url.scheme(), url.host_str().unwrap(), url.port());
+    *origin_header_clone.lock().unwrap() = Some(origin.clone());
+    let request = Request::from_init(RequestInit {
+        url: url.clone(),
+        method: Method::Post,
+        body: None,
+        origin: url.clone(),
+        .. RequestInit::default()
+    });
+    let response = fetch(request, None);
+    assert!(response.status.unwrap().is_success());
+
+    let origin_url = ServoUrl::parse("http://example.com").unwrap();
+    origin = Origin::new(origin_url.scheme(), origin_url.host_str().unwrap(), origin_url.port());
+    // Test Origin header is set on Get request with CORS mode
+    let request = Request::from_init(RequestInit {
+        url: url.clone(),
+        method: Method::Get,
+        mode: RequestMode::CorsMode,
+        body: None,
+        origin: origin_url.clone(),
+        .. RequestInit::default()
+    });
+
+    *origin_header_clone.lock().unwrap() = Some(origin.clone());
+    let response = fetch(request, None);
+    assert!(response.status.unwrap().is_success());
+
+    // Test Origin header is not set on method Head
+    let request = Request::from_init(RequestInit {
+        url: url.clone(),
+        method: Method::Head,
+        body: None,
+        origin: url.clone(),
+        .. RequestInit::default()
+    });
+
+    *origin_header_clone.lock().unwrap() = None;
+    let response = fetch(request, None);
+    assert!(response.status.unwrap().is_success());
+
+    let _ = server.close();
+}