1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use brotli::Decompressor;
6 use connector::{Connector, create_http_connector};
7 use cookie;
8 use cookie_storage::CookieStorage;
9 use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
10 use devtools_traits::{HttpResponse as DevtoolsHttpResponse, NetworkEvent};
11 use fetch::cors_cache::CorsCache;
12 use fetch::methods::{Data, DoneChannel, FetchContext, Target};
13 use fetch::methods::{is_cors_safelisted_request_header, is_cors_safelisted_method, main_fetch};
14 use flate2::read::{DeflateDecoder, GzDecoder};
15 use hsts::HstsList;
16 use http_cache::HttpCache;
17 use hyper::Error as HttpError;
18 use hyper::LanguageTag;
19 use hyper::client::{Pool, Request as HyperRequest, Response as HyperResponse};
20 use hyper::header::{Accept, AccessControlAllowCredentials, AccessControlAllowHeaders};
21 use hyper::header::{AccessControlAllowMethods, AccessControlAllowOrigin};
22 use hyper::header::{AccessControlMaxAge, AccessControlRequestHeaders};
23 use hyper::header::{AccessControlRequestMethod, AcceptEncoding, AcceptLanguage};
24 use hyper::header::{Authorization, Basic, CacheControl, CacheDirective};
25 use hyper::header::{ContentEncoding, ContentLength, Encoding, Header, Headers};
26 use hyper::header::{Host, HttpDate, Origin as HyperOrigin, IfMatch, IfRange};
27 use hyper::header::{IfUnmodifiedSince, IfModifiedSince, IfNoneMatch, Location};
28 use hyper::header::{Pragma, Quality, QualityItem, Referer, SetCookie};
29 use hyper::header::{UserAgent, q, qitem};
30 use hyper::method::Method;
31 use hyper::status::StatusCode;
32 use hyper_openssl::OpensslClient;
33 use hyper_serde::Serde;
34 use log;
35 use msg::constellation_msg::PipelineId;
36 use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy};
37 use net_traits::request::{CacheMode, CredentialsMode, Destination, Origin};
38 use net_traits::request::{RedirectMode, Referrer, Request, RequestMode};
39 use net_traits::request::{ResponseTainting, ServiceWorkersMode};
40 use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
41 use resource_thread::AuthCache;
42 use servo_url::{ImmutableOrigin, ServoUrl};
43 use std::collections::HashSet;
44 use std::error::Error;
45 use std::io::{self, Read, Write};
46 use std::iter::FromIterator;
47 use std::mem;
48 use std::ops::Deref;
49 use std::str::FromStr;
50 use std::sync::RwLock;
51 use std::sync::mpsc::{channel, Sender};
52 use std::thread;
53 use time;
54 use time::Tm;
55 use unicase::UniCase;
56 use uuid;
57 
read_block<R: Read>(reader: &mut R) -> Result<Data, ()>58 fn read_block<R: Read>(reader: &mut R) -> Result<Data, ()> {
59     let mut buf = vec![0; 32768];
60 
61     match reader.read(&mut buf) {
62         Ok(len) if len > 0 => {
63             buf.truncate(len);
64             Ok(Data::Payload(buf))
65         }
66         Ok(_) => Ok(Data::Done),
67         Err(_) => Err(()),
68     }
69 }
70 
71 pub struct HttpState {
72     pub hsts_list: RwLock<HstsList>,
73     pub cookie_jar: RwLock<CookieStorage>,
74     pub http_cache: RwLock<HttpCache>,
75     pub auth_cache: RwLock<AuthCache>,
76     pub ssl_client: OpensslClient,
77     pub connector: Pool<Connector>,
78 }
79 
80 impl HttpState {
new(ssl_client: OpensslClient) -> HttpState81     pub fn new(ssl_client: OpensslClient) -> HttpState {
82         HttpState {
83             hsts_list: RwLock::new(HstsList::new()),
84             cookie_jar: RwLock::new(CookieStorage::new(150)),
85             auth_cache: RwLock::new(AuthCache::new()),
86             http_cache: RwLock::new(HttpCache::new()),
87             ssl_client: ssl_client.clone(),
88             connector: create_http_connector(ssl_client),
89         }
90     }
91 }
92 
precise_time_ms() -> u6493 fn precise_time_ms() -> u64 {
94     time::precise_time_ns() / (1000 * 1000)
95 }
96 
97 // Step 3 of https://fetch.spec.whatwg.org/#concept-fetch.
set_default_accept(destination: Destination, headers: &mut Headers)98 pub fn set_default_accept(destination: Destination, headers: &mut Headers) {
99     if headers.has::<Accept>() {
100         return;
101     }
102     let value = match destination {
103         // Step 3.2.
104         Destination::Document => {
105             vec![
106                 qitem(mime!(Text / Html)),
107                 qitem(mime!(Application / ("xhtml+xml"))),
108                 QualityItem::new(mime!(Application / Xml), q(0.9)),
109                 QualityItem::new(mime!(_ / _), q(0.8)),
110             ]
111         },
112         // Step 3.3.
113         Destination::Image => {
114             vec![
115                 qitem(mime!(Image / Png)),
116                 qitem(mime!(Image / ("svg+xml") )),
117                 QualityItem::new(mime!(Image / _), q(0.8)),
118                 QualityItem::new(mime!(_ / _), q(0.5)),
119             ]
120         },
121         // Step 3.3.
122         Destination::Style => {
123             vec![
124                 qitem(mime!(Text / Css)),
125                 QualityItem::new(mime!(_ / _), q(0.1))
126             ]
127         },
128         // Step 3.1.
129         _ => {
130             vec![qitem(mime!(_ / _))]
131         },
132     };
133 
134     // Step 3.4.
135     headers.set(Accept(value));
136 }
137 
set_default_accept_encoding(headers: &mut Headers)138 fn set_default_accept_encoding(headers: &mut Headers) {
139     if headers.has::<AcceptEncoding>() {
140         return
141     }
142 
143     headers.set(AcceptEncoding(vec![
144         qitem(Encoding::Gzip),
145         qitem(Encoding::Deflate),
146         qitem(Encoding::EncodingExt("br".to_owned()))
147     ]));
148 }
149 
set_default_accept_language(headers: &mut Headers)150 pub fn set_default_accept_language(headers: &mut Headers) {
151     if headers.has::<AcceptLanguage>() {
152         return;
153     }
154 
155     let mut en_us: LanguageTag = Default::default();
156     en_us.language = Some("en".to_owned());
157     en_us.region = Some("US".to_owned());
158     let mut en: LanguageTag = Default::default();
159     en.language = Some("en".to_owned());
160     headers.set(AcceptLanguage(vec![
161         qitem(en_us),
162         QualityItem::new(en, Quality(500)),
163     ]));
164 }
165 
166 /// <https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-state-no-referrer-when-downgrade>
no_referrer_when_downgrade_header(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl>167 fn no_referrer_when_downgrade_header(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl> {
168     if referrer_url.scheme() == "https" && url.scheme() != "https" {
169         return None;
170     }
171     return strip_url(referrer_url, false);
172 }
173 
174 /// <https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin>
strict_origin(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl>175 fn strict_origin(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl> {
176     if referrer_url.scheme() == "https" && url.scheme() != "https" {
177         return None;
178     }
179     strip_url(referrer_url, true)
180 }
181 
182 /// <https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin>
strict_origin_when_cross_origin(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl>183 fn strict_origin_when_cross_origin(referrer_url: ServoUrl, url: ServoUrl) -> Option<ServoUrl> {
184     if referrer_url.scheme() == "https" && url.scheme() != "https" {
185         return None;
186     }
187     let cross_origin = referrer_url.origin() != url.origin();
188     strip_url(referrer_url, cross_origin)
189 }
190 
191 /// <https://w3c.github.io/webappsec-referrer-policy/#strip-url>
strip_url(mut referrer_url: ServoUrl, origin_only: bool) -> Option<ServoUrl>192 fn strip_url(mut referrer_url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
193     if referrer_url.scheme() == "https" || referrer_url.scheme() == "http" {
194         {
195             let referrer = referrer_url.as_mut_url();
196             referrer.set_username("").unwrap();
197             referrer.set_password(None).unwrap();
198             referrer.set_fragment(None);
199             if origin_only {
200                 referrer.set_path("");
201                 referrer.set_query(None);
202             }
203         }
204         return Some(referrer_url);
205     }
206     return None;
207 }
208 
209 /// <https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer>
210 /// Steps 4-6.
determine_request_referrer(headers: &mut Headers, referrer_policy: ReferrerPolicy, referrer_source: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl>211 pub fn determine_request_referrer(headers: &mut Headers,
212                                   referrer_policy: ReferrerPolicy,
213                                   referrer_source: ServoUrl,
214                                   current_url: ServoUrl)
215                                   -> Option<ServoUrl> {
216     assert!(!headers.has::<Referer>());
217     // FIXME(#14505): this does not seem to be the correct way of checking for
218     //                same-origin requests.
219     let cross_origin = referrer_source.origin() != current_url.origin();
220     // FIXME(#14506): some of these cases are expected to consider whether the
221     //                request's client is "TLS-protected", whatever that means.
222     match referrer_policy {
223         ReferrerPolicy::NoReferrer => None,
224         ReferrerPolicy::Origin => strip_url(referrer_source, true),
225         ReferrerPolicy::SameOrigin => if cross_origin { None } else { strip_url(referrer_source, false) },
226         ReferrerPolicy::UnsafeUrl => strip_url(referrer_source, false),
227         ReferrerPolicy::OriginWhenCrossOrigin => strip_url(referrer_source, cross_origin),
228         ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
229         ReferrerPolicy::StrictOriginWhenCrossOrigin => strict_origin_when_cross_origin(referrer_source, current_url),
230         ReferrerPolicy::NoReferrerWhenDowngrade => no_referrer_when_downgrade_header(referrer_source, current_url),
231     }
232 }
233 
set_request_cookies(url: &ServoUrl, headers: &mut Headers, cookie_jar: &RwLock<CookieStorage>)234 pub fn set_request_cookies(url: &ServoUrl, headers: &mut Headers, cookie_jar: &RwLock<CookieStorage>) {
235     let mut cookie_jar = cookie_jar.write().unwrap();
236     if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
237         let mut v = Vec::new();
238         v.push(cookie_list.into_bytes());
239         headers.set_raw("Cookie".to_owned(), v);
240     }
241 }
242 
set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: String)243 fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>,
244                       request: &ServoUrl,
245                       cookie_val: String) {
246     let mut cookie_jar = cookie_jar.write().unwrap();
247     let source = CookieSource::HTTP;
248     let header = Header::parse_header(&[cookie_val.into_bytes()]);
249 
250     if let Ok(SetCookie(cookies)) = header {
251         for cookie in cookies {
252             if let Some(cookie) = cookie::Cookie::from_cookie_string(cookie, request, source) {
253                 cookie_jar.push(cookie, request, source);
254             }
255         }
256     }
257 }
258 
set_cookies_from_headers(url: &ServoUrl, headers: &Headers, cookie_jar: &RwLock<CookieStorage>)259 fn set_cookies_from_headers(url: &ServoUrl, headers: &Headers, cookie_jar: &RwLock<CookieStorage>) {
260     if let Some(cookies) = headers.get_raw("set-cookie") {
261         for cookie in cookies.iter() {
262             if let Ok(cookie_value) = String::from_utf8(cookie.clone()) {
263                 set_cookie_for_url(&cookie_jar,
264                                    &url,
265                                    cookie_value);
266             }
267         }
268     }
269 }
270 
271 struct StreamedResponse {
272     decoder: Decoder,
273 }
274 
275 
276 impl Read for StreamedResponse {
277     #[inline]
read(&mut self, buf: &mut [u8]) -> io::Result<usize>278     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
279         match self.decoder {
280             Decoder::Gzip(ref mut d) => d.read(buf),
281             Decoder::Deflate(ref mut d) => d.read(buf),
282             Decoder::Brotli(ref mut d) => d.read(buf),
283             Decoder::Plain(ref mut d) => d.read(buf)
284         }
285     }
286 }
287 
288 impl StreamedResponse {
from_http_response(response: HyperResponse) -> io::Result<StreamedResponse>289     fn from_http_response(response: HyperResponse) -> io::Result<StreamedResponse> {
290         let decoder = {
291             if let Some(ref encoding) = response.headers.get::<ContentEncoding>().cloned() {
292                 if encoding.contains(&Encoding::Gzip) {
293                     Decoder::Gzip(GzDecoder::new(response))
294                 }
295                 else if encoding.contains(&Encoding::Deflate) {
296                     Decoder::Deflate(DeflateDecoder::new(response))
297                 }
298                 else if encoding.contains(&Encoding::EncodingExt("br".to_owned())) {
299                     Decoder::Brotli(Decompressor::new(response, 1024))
300                 } else {
301                     Decoder::Plain(response)
302                 }
303             } else {
304                 Decoder::Plain(response)
305             }
306         };
307         Ok(StreamedResponse { decoder: decoder })
308     }
309 }
310 
311 enum Decoder {
312     Gzip(GzDecoder<HyperResponse>),
313     Deflate(DeflateDecoder<HyperResponse>),
314     Brotli(Decompressor<HyperResponse>),
315     Plain(HyperResponse)
316 }
317 
prepare_devtools_request(request_id: String, url: ServoUrl, method: Method, headers: Headers, body: Option<Vec<u8>>, pipeline_id: PipelineId, now: Tm, connect_time: u64, send_time: u64, is_xhr: bool) -> ChromeToDevtoolsControlMsg318 fn prepare_devtools_request(request_id: String,
319                             url: ServoUrl,
320                             method: Method,
321                             headers: Headers,
322                             body: Option<Vec<u8>>,
323                             pipeline_id: PipelineId,
324                             now: Tm,
325                             connect_time: u64,
326                             send_time: u64,
327                             is_xhr: bool) -> ChromeToDevtoolsControlMsg {
328     let request = DevtoolsHttpRequest {
329         url: url,
330         method: method,
331         headers: headers,
332         body: body,
333         pipeline_id: pipeline_id,
334         startedDateTime: now,
335         timeStamp: now.to_timespec().sec,
336         connect_time: connect_time,
337         send_time: send_time,
338         is_xhr: is_xhr,
339     };
340     let net_event = NetworkEvent::HttpRequest(request);
341 
342     ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
343 }
344 
send_request_to_devtools(msg: ChromeToDevtoolsControlMsg, devtools_chan: &Sender<DevtoolsControlMsg>)345 fn send_request_to_devtools(msg: ChromeToDevtoolsControlMsg,
346                             devtools_chan: &Sender<DevtoolsControlMsg>) {
347     devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap();
348 }
349 
send_response_to_devtools(devtools_chan: &Sender<DevtoolsControlMsg>, request_id: String, headers: Option<Headers>, status: Option<(u16, Vec<u8>)>, pipeline_id: PipelineId)350 fn send_response_to_devtools(devtools_chan: &Sender<DevtoolsControlMsg>,
351                              request_id: String,
352                              headers: Option<Headers>,
353                              status: Option<(u16, Vec<u8>)>,
354                              pipeline_id: PipelineId) {
355     let response = DevtoolsHttpResponse { headers: headers, status: status, body: None, pipeline_id: pipeline_id };
356     let net_event_response = NetworkEvent::HttpResponse(response);
357 
358     let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event_response);
359     let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
360 }
361 
auth_from_cache(auth_cache: &RwLock<AuthCache>, origin: &ImmutableOrigin) -> Option<Basic>362 fn auth_from_cache(auth_cache: &RwLock<AuthCache>, origin: &ImmutableOrigin) -> Option<Basic> {
363     if let Some(ref auth_entry) = auth_cache.read().unwrap().entries.get(&origin.ascii_serialization()) {
364         let user_name = auth_entry.user_name.clone();
365         let password  = Some(auth_entry.password.clone());
366         Some(Basic { username: user_name, password: password })
367     } else {
368         None
369     }
370 }
371 
obtain_response(connector: &Pool<Connector>, url: &ServoUrl, method: &Method, request_headers: &Headers, data: &Option<Vec<u8>>, load_data_method: &Method, pipeline_id: &Option<PipelineId>, iters: u32, request_id: Option<&str>, is_xhr: bool) -> Result<(HyperResponse, Option<ChromeToDevtoolsControlMsg>), NetworkError>372 fn obtain_response(connector: &Pool<Connector>,
373                    url: &ServoUrl,
374                    method: &Method,
375                    request_headers: &Headers,
376                    data: &Option<Vec<u8>>,
377                    load_data_method: &Method,
378                    pipeline_id: &Option<PipelineId>,
379                    iters: u32,
380                    request_id: Option<&str>,
381                    is_xhr: bool)
382                    -> Result<(HyperResponse, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
383     let null_data = None;
384 
385     // loop trying connections in connection pool
386     // they may have grown stale (disconnected), in which case we'll get
387     // a ConnectionAborted error. this loop tries again with a new
388     // connection.
389     loop {
390         let mut headers = request_headers.clone();
391 
392         // Avoid automatically sending request body if a redirect has occurred.
393         //
394         // TODO - This is the wrong behaviour according to the RFC. However, I'm not
395         // sure how much "correctness" vs. real-world is important in this case.
396         //
397         // https://tools.ietf.org/html/rfc7231#section-6.4
398         let is_redirected_request = iters != 1;
399         let request_body;
400         match data {
401             &Some(ref d) if !is_redirected_request => {
402                 headers.set(ContentLength(d.len() as u64));
403                 request_body = data;
404             }
405             _ => {
406                 if *load_data_method != Method::Get && *load_data_method != Method::Head {
407                     headers.set(ContentLength(0))
408                 }
409                 request_body = &null_data;
410             }
411         }
412 
413         if log_enabled!(log::LogLevel::Info) {
414             info!("{} {}", method, url);
415             for header in headers.iter() {
416                 info!(" - {}", header);
417             }
418             info!("{:?}", data);
419         }
420 
421         let connect_start = precise_time_ms();
422 
423         let request = HyperRequest::with_connector(method.clone(),
424                                                    url.clone().into_url(),
425                                                    &*connector);
426         let mut request = match request {
427             Ok(request) => request,
428             Err(e) => return Err(NetworkError::from_hyper_error(&url, e)),
429         };
430         *request.headers_mut() = headers.clone();
431 
432         let connect_end = precise_time_ms();
433 
434         let send_start = precise_time_ms();
435 
436         let mut request_writer = match request.start() {
437             Ok(streaming) => streaming,
438             Err(e) => return Err(NetworkError::Internal(e.description().to_owned())),
439         };
440 
441         if let Some(ref data) = *request_body {
442             if let Err(e) = request_writer.write_all(&data) {
443                 return Err(NetworkError::Internal(e.description().to_owned()))
444             }
445         }
446 
447         let response = match request_writer.send() {
448             Ok(w) => w,
449             Err(HttpError::Io(ref io_error))
450                 if io_error.kind() == io::ErrorKind::ConnectionAborted ||
451                    io_error.kind() == io::ErrorKind::ConnectionReset => {
452                 debug!("connection aborted ({:?}), possibly stale, trying new connection", io_error.description());
453                 continue;
454             },
455             Err(e) => return Err(NetworkError::Internal(e.description().to_owned())),
456         };
457 
458         let send_end = precise_time_ms();
459 
460         let msg = if let Some(request_id) = request_id {
461             if let Some(pipeline_id) = *pipeline_id {
462                 Some(prepare_devtools_request(
463                     request_id.into(),
464                     url.clone(), method.clone(), headers,
465                     request_body.clone(), pipeline_id, time::now(),
466                     connect_end - connect_start, send_end - send_start, is_xhr))
467             } else {
468                 debug!("Not notifying devtools (no pipeline_id)");
469                 None
470             }
471         } else {
472             debug!("Not notifying devtools (no request_id)");
473             None
474         };
475         return Ok((response, msg));
476     }
477 }
478 
479 /// [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch)
http_fetch(request: &mut Request, cache: &mut CorsCache, cors_flag: bool, cors_preflight_flag: bool, authentication_fetch_flag: bool, target: Target, done_chan: &mut DoneChannel, context: &FetchContext) -> Response480 pub fn http_fetch(request: &mut Request,
481                   cache: &mut CorsCache,
482                   cors_flag: bool,
483                   cors_preflight_flag: bool,
484                   authentication_fetch_flag: bool,
485                   target: Target,
486                   done_chan: &mut DoneChannel,
487                   context: &FetchContext)
488                   -> Response {
489     // This is a new async fetch, reset the channel we are waiting on
490     *done_chan = None;
491     // Step 1
492     let mut response: Option<Response> = None;
493 
494     // Step 2
495     // nothing to do, since actual_response is a function on response
496 
497     // Step 3
498     if request.service_workers_mode != ServiceWorkersMode::None {
499         // Substep 1
500         if request.service_workers_mode == ServiceWorkersMode::All {
501             // TODO (handle fetch unimplemented)
502         }
503 
504         // Substep 2
505         if response.is_none() && request.is_subresource_request() && match request.origin {
506             Origin::Origin(ref origin) => *origin == request.url().origin(),
507             _ => false,
508         } {
509             // TODO (handle foreign fetch unimplemented)
510         }
511 
512         // Substep 3
513         if let Some(ref res) = response {
514             // Subsubstep 1
515             // TODO: transmit body for request
516 
517             // Subsubstep 2
518             // nothing to do, since actual_response is a function on response
519 
520             // Subsubstep 3
521             if (res.response_type == ResponseType::Opaque &&
522                 request.mode != RequestMode::NoCors) ||
523                (res.response_type == ResponseType::OpaqueRedirect &&
524                 request.redirect_mode != RedirectMode::Manual) ||
525                (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
526                res.is_network_error() {
527                 return Response::network_error(NetworkError::Internal("Request failed".into()));
528             }
529 
530             // Subsubstep 4
531             // TODO: set response's CSP list on actual_response
532         }
533     }
534 
535     // Step 4
536     if response.is_none() {
537         // Substep 1
538         if cors_preflight_flag {
539             let method_cache_match = cache.match_method(&*request,
540                                                         request.method.clone());
541 
542             let method_mismatch = !method_cache_match && (!is_cors_safelisted_method(&request.method) ||
543                                                           request.use_cors_preflight);
544             let header_mismatch = request.headers.iter().any(|view|
545                 !cache.match_header(&*request, view.name()) && !is_cors_safelisted_request_header(&view)
546             );
547 
548             // Sub-substep 1
549             if method_mismatch || header_mismatch {
550                 let preflight_result = cors_preflight_fetch(&request, cache, context);
551                 // Sub-substep 2
552                 if let Some(e) = preflight_result.get_network_error() {
553                     return Response::network_error(e.clone());
554                 }
555             }
556         }
557 
558         // Substep 2
559         if request.redirect_mode == RedirectMode::Follow {
560             request.service_workers_mode = ServiceWorkersMode::Foreign;
561         }
562 
563         // Substep 3
564         let mut fetch_result = http_network_or_cache_fetch(
565             request, authentication_fetch_flag, cors_flag, done_chan, context);
566 
567         // Substep 4
568         if cors_flag && cors_check(&request, &fetch_result).is_err() {
569             return Response::network_error(NetworkError::Internal("CORS check failed".into()));
570         }
571 
572         fetch_result.return_internal = false;
573         response = Some(fetch_result);
574     }
575 
576     // response is guaranteed to be something by now
577     let mut response = response.unwrap();
578 
579     // Step 5
580     if response.actual_response().status.map_or(false, is_redirect_status) {
581         // Substep 1.
582         if response.actual_response().status.map_or(true, |s| s != StatusCode::SeeOther) {
583             // TODO: send RST_STREAM frame
584         }
585 
586         // Substep 2-3.
587         let location = response.actual_response().headers.get::<Location>().map(
588             |l| ServoUrl::parse_with_base(response.actual_response().url(), l)
589                 .map_err(|err| err.description().into()));
590 
591         // Substep 4.
592         response.actual_response_mut().location_url = location;
593 
594         // Substep 5.
595         response = match request.redirect_mode {
596             RedirectMode::Error => Response::network_error(NetworkError::Internal("Redirect mode error".into())),
597             RedirectMode::Manual => {
598                 response.to_filtered(ResponseType::OpaqueRedirect)
599             },
600             RedirectMode::Follow => {
601                 // set back to default
602                 response.return_internal = true;
603                 http_redirect_fetch(request, cache, response,
604                                     cors_flag, target, done_chan, context)
605             }
606         };
607     }
608     // set back to default
609     response.return_internal = true;
610     // Step 6
611     response
612 }
613 
614 /// [HTTP redirect fetch](https://fetch.spec.whatwg.org#http-redirect-fetch)
http_redirect_fetch(request: &mut Request, cache: &mut CorsCache, response: Response, cors_flag: bool, target: Target, done_chan: &mut DoneChannel, context: &FetchContext) -> Response615 pub fn http_redirect_fetch(request: &mut Request,
616                            cache: &mut CorsCache,
617                            response: Response,
618                            cors_flag: bool,
619                            target: Target,
620                            done_chan: &mut DoneChannel,
621                            context: &FetchContext)
622                            -> Response {
623     // Step 1
624     assert!(response.return_internal);
625 
626     let location_url = response.actual_response().location_url.clone();
627     let location_url = match location_url {
628         // Step 2
629         None => return response,
630         // Step 3
631         Some(Err(err)) =>
632             return Response::network_error(
633                 NetworkError::Internal("Location URL parse failure: ".to_owned() + &err)),
634         // Step 4
635         Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") =>
636             return Response::network_error(NetworkError::Internal("Location URL not an HTTP(S) scheme".into())),
637         Some(Ok(url)) => url,
638     };
639 
640     // Step 5
641     if request.redirect_count >= 20 {
642         return Response::network_error(NetworkError::Internal("Too many redirects".into()));
643     }
644 
645     // Step 6
646     request.redirect_count += 1;
647 
648     // Step 7
649     let same_origin = match request.origin {
650         Origin::Origin(ref origin) => *origin == location_url.origin(),
651         Origin::Client => panic!("Request origin should not be client for {}", request.current_url()),
652     };
653     let has_credentials = has_credentials(&location_url);
654 
655     if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
656         return Response::network_error(NetworkError::Internal("Cross-origin credentials check failed".into()));
657     }
658 
659     // Step 8
660     if cors_flag && has_credentials {
661         return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
662     }
663 
664     // Step 9
665     if response.actual_response().status.map_or(true, |s| s != StatusCode::SeeOther) &&
666        request.body.as_ref().map_or(false, |b| b.is_empty()) {
667         return Response::network_error(NetworkError::Internal("Request body is not done".into()));
668     }
669 
670     // Step 10
671     if cors_flag && location_url.origin() != request.current_url().origin() {
672         request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
673     }
674 
675     // Step 11
676     if response.actual_response().status.map_or(false, |code|
677         ((code == StatusCode::MovedPermanently || code == StatusCode::Found) && request.method == Method::Post) ||
678         code == StatusCode::SeeOther) {
679         request.method = Method::Get;
680         request.body = None;
681     }
682 
683     // Step 12
684     if let Some(_) = request.body {
685         // TODO: extract request's body's source
686     }
687 
688     // Step 13
689     request.url_list.push(location_url);
690 
691     // Step 14
692     // TODO implement referrer policy
693 
694     // Step 15
695     let recursive_flag = request.redirect_mode != RedirectMode::Manual;
696 
697     main_fetch(request, cache, cors_flag, recursive_flag, target, done_chan, context)
698 }
699 
try_immutable_origin_to_hyper_origin(url_origin: &ImmutableOrigin) -> Option<HyperOrigin>700 fn try_immutable_origin_to_hyper_origin(url_origin: &ImmutableOrigin) -> Option<HyperOrigin> {
701     match *url_origin {
702         // TODO (servo/servo#15569) Set "Origin: null" when hyper supports it
703         ImmutableOrigin::Opaque(_) => None,
704         ImmutableOrigin::Tuple(ref scheme, ref host, ref port) =>
705             Some(HyperOrigin::new(scheme.clone(), host.to_string(), Some(port.clone())))
706     }
707 }
708 
709 /// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch)
http_network_or_cache_fetch(request: &mut Request, authentication_fetch_flag: bool, cors_flag: bool, done_chan: &mut DoneChannel, context: &FetchContext) -> Response710 fn http_network_or_cache_fetch(request: &mut Request,
711                                authentication_fetch_flag: bool,
712                                cors_flag: bool,
713                                done_chan: &mut DoneChannel,
714                                context: &FetchContext)
715                                -> Response {
716     // TODO: Implement Window enum for Request
717     let request_has_no_window = true;
718 
719     // Step 2
720     let mut http_request;
721     let http_request = if request_has_no_window &&
722         request.redirect_mode == RedirectMode::Error {
723         request
724     } else {
725         // Step 3
726         // TODO Implement body source
727         http_request = request.clone();
728         &mut http_request
729     };
730 
731     // Step 4
732     let credentials_flag = match http_request.credentials_mode {
733         CredentialsMode::Include => true,
734         CredentialsMode::CredentialsSameOrigin if http_request.response_tainting == ResponseTainting::Basic
735             => true,
736         _ => false
737     };
738 
739     let content_length_value = match http_request.body {
740         None =>
741             match http_request.method {
742                 // Step 6
743                 Method::Post | Method::Put =>
744                     Some(0),
745                 // Step 5
746                 _ => None
747             },
748         // Step 7
749         Some(ref http_request_body) => Some(http_request_body.len() as u64)
750     };
751 
752     // Step 8
753     if let Some(content_length_value) = content_length_value {
754         http_request.headers.set(ContentLength(content_length_value));
755         if http_request.keep_alive {
756             // Step 9 TODO: needs request's client object
757         }
758     }
759 
760 
761     // Step 10
762     match http_request.referrer {
763         Referrer::NoReferrer => (),
764         Referrer::ReferrerUrl(ref http_request_referrer) =>
765             http_request.headers.set(Referer(http_request_referrer.to_string())),
766         Referrer::Client =>
767             // it should be impossible for referrer to be anything else during fetching
768             // https://fetch.spec.whatwg.org/#concept-request-referrer
769             unreachable!()
770     };
771 
772     // Step 11
773     if cors_flag || (http_request.method != Method::Get && http_request.method != Method::Head) {
774         debug_assert_ne!(http_request.origin, Origin::Client);
775         if let Origin::Origin(ref url_origin) = http_request.origin {
776             if let Some(hyper_origin) = try_immutable_origin_to_hyper_origin(url_origin) {
777                 http_request.headers.set(hyper_origin)
778             }
779         }
780     }
781 
782     // Step 12
783     if !http_request.headers.has::<UserAgent>() {
784         let user_agent = context.user_agent.clone().into_owned();
785         http_request.headers.set(UserAgent(user_agent));
786     }
787 
788     match http_request.cache_mode {
789         // Step 13
790         CacheMode::Default if is_no_store_cache(&http_request.headers) => {
791             http_request.cache_mode = CacheMode::NoStore;
792         },
793 
794         // Step 14
795         CacheMode::NoCache if !http_request.headers.has::<CacheControl>() => {
796             http_request.headers.set(CacheControl(vec![CacheDirective::MaxAge(0)]));
797         },
798 
799         // Step 15
800         CacheMode::Reload | CacheMode::NoStore => {
801             // Substep 1
802             if !http_request.headers.has::<Pragma>() {
803                 http_request.headers.set(Pragma::NoCache);
804             }
805 
806             // Substep 2
807             if !http_request.headers.has::<CacheControl>() {
808                 http_request.headers.set(CacheControl(vec![CacheDirective::NoCache]));
809             }
810         },
811 
812         _ => {}
813     }
814 
815     // Step 16
816     let current_url = http_request.current_url();
817     let host = Host {
818         hostname: current_url.host_str().unwrap().to_owned(),
819         port: current_url.port()
820     };
821     http_request.headers.set(host);
822     // unlike http_loader, we should not set the accept header
823     // here, according to the fetch spec
824     set_default_accept_encoding(&mut http_request.headers);
825 
826     // Step 17
827     // TODO some of this step can't be implemented yet
828     if credentials_flag {
829         // Substep 1
830         // TODO http://mxr.mozilla.org/servo/source/components/net/http_loader.rs#504
831         // XXXManishearth http_loader has block_cookies: support content blocking here too
832         set_request_cookies(&current_url,
833                             &mut http_request.headers,
834                             &context.state.cookie_jar);
835         // Substep 2
836         if !http_request.headers.has::<Authorization<String>>() {
837             // Substep 3
838             let mut authorization_value = None;
839 
840             // Substep 4
841             if let Some(basic) = auth_from_cache(&context.state.auth_cache, &current_url.origin()) {
842                 if !http_request.use_url_credentials || !has_credentials(&current_url) {
843                     authorization_value = Some(basic);
844                 }
845             }
846 
847             // Substep 5
848             if authentication_fetch_flag && authorization_value.is_none() {
849                 if has_credentials(&current_url) {
850                     authorization_value = Some(Basic {
851                         username: current_url.username().to_owned(),
852                         password: current_url.password().map(str::to_owned)
853                     })
854                 }
855             }
856 
857             // Substep 6
858             if let Some(basic) = authorization_value {
859                 http_request.headers.set(Authorization(basic));
860             }
861         }
862     }
863 
864     // Step 18
865     // TODO If there’s a proxy-authentication entry, use it as appropriate.
866 
867     // Step 19
868     let mut response: Option<Response> = None;
869 
870     // Step 20
871     let mut revalidating_flag = false;
872 
873     // Step 21
874     if let Ok(http_cache) = context.state.http_cache.read() {
875         if let Some(response_from_cache) = http_cache.construct_response(&http_request, done_chan) {
876             let response_headers = response_from_cache.response.headers.clone();
877             // Substep 1, 2, 3, 4
878             let (cached_response, needs_revalidation) = match (http_request.cache_mode, &http_request.mode) {
879                 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
880                 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => (Some(response_from_cache.response), false),
881                 (CacheMode::OnlyIfCached, _) | (CacheMode::NoStore, _) | (CacheMode::Reload, _) => (None, false),
882                 (_, _) => (Some(response_from_cache.response), response_from_cache.needs_validation)
883             };
884             if needs_revalidation {
885                 revalidating_flag = true;
886                 // Substep 5
887                 // TODO: find out why the typed header getter return None from the headers of cached responses.
888                 if let Some(date_slice) = response_headers.get_raw("Last-Modified") {
889                     let date_string = String::from_utf8_lossy(&date_slice[0]);
890                     if let Ok(http_date) = HttpDate::from_str(&date_string) {
891                         http_request.headers.set(IfModifiedSince(http_date));
892                     }
893                 }
894                 if let Some(entity_tag) =
895                     response_headers.get_raw("ETag") {
896                     http_request.headers.set_raw("If-None-Match", entity_tag.to_vec());
897 
898                 }
899             } else {
900                 // Substep 6
901                 response = cached_response;
902             }
903         }
904     }
905 
906     if let Some(ref ch) = *done_chan {
907         // The cache constructed a response with a body of ResponseBody::Receiving.
908         // We wait for the response in the cache to "finish",
909         // with a body of either Done or Cancelled.
910         loop {
911             match ch.1.recv()
912                     .expect("HTTP cache should always send Done or Cancelled") {
913                 Data::Payload(_) => {},
914                 Data::Done => break, // Return the full response as if it was initially cached as such.
915                 Data::Cancelled => {
916                     // The response was cancelled while the fetch was ongoing.
917                     // Set response to None, which will trigger a network fetch below.
918                     response = None;
919                     break;
920                 }
921             }
922         }
923     }
924     // Set done_chan back to None, it's cache-related usefulness ends here.
925     *done_chan = None;
926 
927     // Step 22
928     if response.is_none() {
929         // Substep 1
930         if http_request.cache_mode == CacheMode::OnlyIfCached {
931             return Response::network_error(
932                 NetworkError::Internal("Couldn't find response in cache".into()))
933         }
934     }
935     // More Step 22
936     if response.is_none() {
937         // Substep 2
938         let forward_response = http_network_fetch(http_request, credentials_flag,
939                                                   done_chan, context);
940         // Substep 3
941         if let Some((200...399, _)) = forward_response.raw_status {
942             if !http_request.method.safe() {
943                 if let Ok(mut http_cache) = context.state.http_cache.write() {
944                     http_cache.invalidate(&http_request, &forward_response);
945                 }
946             }
947         }
948         // Substep 4
949         if revalidating_flag && forward_response.status.map_or(false, |s| s == StatusCode::NotModified) {
950             if let Ok(mut http_cache) = context.state.http_cache.write() {
951                 response = http_cache.refresh(&http_request, forward_response.clone(), done_chan);
952             }
953         }
954 
955         // Substep 5
956         if response.is_none() {
957             if http_request.cache_mode != CacheMode::NoStore {
958                 // Subsubstep 2, doing it first to avoid a clone of forward_response.
959                 if let Ok(mut http_cache) = context.state.http_cache.write() {
960                     http_cache.store(&http_request, &forward_response);
961                 }
962             }
963             // Subsubstep 1
964             response = Some(forward_response);
965         }
966     }
967 
968     let mut response = response.unwrap();
969 
970     // Step 23
971     // FIXME: Figure out what to do with request window objects
972     if let (Some(StatusCode::Unauthorized), false, true) = (response.status, cors_flag, credentials_flag) {
973         // Substep 1
974         // TODO: Spec says requires testing on multiple WWW-Authenticate headers
975 
976         // Substep 2
977         if http_request.body.is_some() {
978             // TODO Implement body source
979         }
980 
981         // Substep 3
982         if !http_request.use_url_credentials || authentication_fetch_flag {
983             // FIXME: Prompt the user for username and password from the window
984 
985             // Wrong, but will have to do until we are able to prompt the user
986             // otherwise this creates an infinite loop
987             // We basically pretend that the user declined to enter credentials
988             return response;
989         }
990 
991         // Substep 4
992         response = http_network_or_cache_fetch(http_request,
993                                                true /* authentication flag */,
994                                                cors_flag, done_chan, context);
995     }
996 
997     // Step 24
998     if let Some(StatusCode::ProxyAuthenticationRequired) = response.status {
999         // Step 1
1000         if request_has_no_window {
1001             return Response::network_error(NetworkError::Internal("Can't find Window object".into()));
1002         }
1003 
1004         // Step 2
1005         // TODO: Spec says requires testing on Proxy-Authenticate headers
1006 
1007         // Step 3
1008         // FIXME: Prompt the user for proxy authentication credentials
1009 
1010         // Wrong, but will have to do until we are able to prompt the user
1011         // otherwise this creates an infinite loop
1012         // We basically pretend that the user declined to enter credentials
1013         return response;
1014 
1015         // Step 4
1016         // return http_network_or_cache_fetch(request, authentication_fetch_flag,
1017         //                                    cors_flag, done_chan, context);
1018     }
1019 
1020     // Step 25
1021     if authentication_fetch_flag {
1022         // TODO Create the authentication entry for request and the given realm
1023     }
1024 
1025     // Step 26
1026     response
1027 }
1028 
1029 /// [HTTP network fetch](https://fetch.spec.whatwg.org/#http-network-fetch)
http_network_fetch(request: &Request, credentials_flag: bool, done_chan: &mut DoneChannel, context: &FetchContext) -> Response1030 fn http_network_fetch(request: &Request,
1031                       credentials_flag: bool,
1032                       done_chan: &mut DoneChannel,
1033                       context: &FetchContext)
1034                       -> Response {
1035     // Step 1
1036     // nothing to do here, since credentials_flag is already a boolean
1037 
1038     // Step 2
1039     // TODO be able to create connection using current url's origin and credentials
1040 
1041     // Step 3
1042     // TODO be able to tell if the connection is a failure
1043 
1044     // Step 4
1045     // TODO: check whether the connection is HTTP/2
1046 
1047     // Step 5
1048     let url = request.current_url();
1049 
1050     let request_id = context.devtools_chan.as_ref().map(|_| {
1051         uuid::Uuid::new_v4().simple().to_string()
1052     });
1053 
1054     // XHR uses the default destination; other kinds of fetches (which haven't been implemented yet)
1055     // do not. Once we support other kinds of fetches we'll need to be more fine grained here
1056     // since things like image fetches are classified differently by devtools
1057     let is_xhr = request.destination == Destination::None;
1058     let wrapped_response = obtain_response(&context.state.connector,
1059                                            &url,
1060                                            &request.method,
1061                                            &request.headers,
1062                                            &request.body, &request.method,
1063                                            &request.pipeline_id, request.redirect_count + 1,
1064                                            request_id.as_ref().map(Deref::deref), is_xhr);
1065 
1066     let pipeline_id = request.pipeline_id;
1067     let (res, msg) = match wrapped_response {
1068         Ok(wrapped_response) => wrapped_response,
1069         Err(error) => return Response::network_error(error),
1070     };
1071 
1072     if log_enabled!(log::LogLevel::Info) {
1073         info!("response for {}", url);
1074         for header in res.headers.iter() {
1075             info!(" - {}", header);
1076         }
1077     }
1078 
1079     let mut response = Response::new(url.clone());
1080     response.status = Some(res.status);
1081     response.raw_status = Some((res.status_raw().0,
1082                                 res.status_raw().1.as_bytes().to_vec()));
1083     response.headers = res.headers.clone();
1084     response.referrer = request.referrer.to_url().cloned();
1085     response.referrer_policy = request.referrer_policy.clone();
1086 
1087     let res_body = response.body.clone();
1088 
1089     // We're about to spawn a thread to be waited on here
1090     let (done_sender, done_receiver) = channel();
1091     *done_chan = Some((done_sender.clone(), done_receiver));
1092     let meta = match response.metadata().expect("Response metadata should exist at this stage") {
1093         FetchMetadata::Unfiltered(m) => m,
1094         FetchMetadata::Filtered { unsafe_, .. } => unsafe_
1095     };
1096     let devtools_sender = context.devtools_chan.clone();
1097     let meta_status = meta.status.clone();
1098     let meta_headers = meta.headers.clone();
1099     let cancellation_listener = context.cancellation_listener.clone();
1100     if cancellation_listener.lock().unwrap().cancelled() {
1101         return Response::network_error(NetworkError::Internal("Fetch aborted".into()))
1102     }
1103     thread::Builder::new().name(format!("fetch worker thread")).spawn(move || {
1104         match StreamedResponse::from_http_response(res) {
1105             Ok(mut res) => {
1106                 *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]);
1107 
1108                 if let Some(ref sender) = devtools_sender {
1109                     if let Some(m) = msg {
1110                         send_request_to_devtools(m, &sender);
1111                     }
1112 
1113                     // --- Tell devtools that we got a response
1114                     // Send an HttpResponse message to devtools with the corresponding request_id
1115                     if let Some(pipeline_id) = pipeline_id {
1116                         send_response_to_devtools(
1117                             &sender, request_id.unwrap(),
1118                             meta_headers.map(Serde::into_inner),
1119                             meta_status,
1120                             pipeline_id);
1121                     }
1122                 }
1123 
1124                 loop {
1125                     if cancellation_listener.lock().unwrap().cancelled() {
1126                         *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
1127                         let _ = done_sender.send(Data::Cancelled);
1128                         return;
1129                     }
1130                     match read_block(&mut res) {
1131                         Ok(Data::Payload(chunk)) => {
1132                             if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
1133                                 body.extend_from_slice(&chunk);
1134                                 let _ = done_sender.send(Data::Payload(chunk));
1135                             }
1136                         },
1137                         Ok(Data::Done) | Err(_) => {
1138                             let mut body = res_body.lock().unwrap();
1139                             let completed_body = match *body {
1140                                 ResponseBody::Receiving(ref mut body) => {
1141                                     mem::replace(body, vec![])
1142                                 },
1143                                 _ => vec![],
1144                             };
1145                             *body = ResponseBody::Done(completed_body);
1146                             let _ = done_sender.send(Data::Done);
1147                             break;
1148                         }
1149                         Ok(Data::Cancelled) => unreachable!() // read_block doesn't return Data::Cancelled
1150                     }
1151                 }
1152             }
1153             Err(_) => {
1154                 // XXXManishearth we should propagate this error somehow
1155                 *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
1156                 let _ = done_sender.send(Data::Done);
1157             }
1158         }
1159     }).expect("Thread spawning failed");
1160 
1161         // TODO these substeps aren't possible yet
1162         // Substep 1
1163 
1164         // Substep 2
1165 
1166     // TODO Determine if response was retrieved over HTTPS
1167     // TODO Servo needs to decide what ciphers are to be treated as "deprecated"
1168     response.https_state = HttpsState::None;
1169 
1170     // TODO Read request
1171 
1172     // Step 6-11
1173     // (needs stream bodies)
1174 
1175     // Step 12
1176     // TODO when https://bugzilla.mozilla.org/show_bug.cgi?id=1030660
1177     // is resolved, this step will become uneccesary
1178     // TODO this step
1179     if let Some(encoding) = response.headers.get::<ContentEncoding>() {
1180         if encoding.contains(&Encoding::Gzip) {
1181         }
1182 
1183         else if encoding.contains(&Encoding::Compress) {
1184         }
1185     };
1186 
1187     // Step 13
1188     // TODO this step isn't possible yet (CSP)
1189 
1190     // Step 14
1191     if !response.is_network_error() && request.cache_mode != CacheMode::NoStore {
1192         if let Ok(mut http_cache) = context.state.http_cache.write() {
1193             http_cache.store(&request, &response);
1194         }
1195     }
1196 
1197     // TODO this step isn't possible yet
1198     // Step 15
1199     if credentials_flag {
1200         set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
1201     }
1202 
1203     // TODO these steps
1204     // Step 16
1205         // Substep 1
1206         // Substep 2
1207             // Sub-substep 1
1208             // Sub-substep 2
1209             // Sub-substep 3
1210             // Sub-substep 4
1211         // Substep 3
1212 
1213     // Step 16
1214     response
1215 }
1216 
1217 /// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch)
cors_preflight_fetch(request: &Request, cache: &mut CorsCache, context: &FetchContext) -> Response1218 fn cors_preflight_fetch(request: &Request,
1219                         cache: &mut CorsCache,
1220                         context: &FetchContext)
1221                         -> Response {
1222     // Step 1
1223     let mut preflight = Request::new(request.current_url(), Some(request.origin.clone()), request.pipeline_id);
1224     preflight.method = Method::Options;
1225     preflight.initiator = request.initiator.clone();
1226     preflight.destination = request.destination.clone();
1227     preflight.origin = request.origin.clone();
1228     preflight.referrer = request.referrer.clone();
1229     preflight.referrer_policy = request.referrer_policy;
1230 
1231     // Step 2
1232     preflight.headers.set::<AccessControlRequestMethod>(
1233         AccessControlRequestMethod(request.method.clone()));
1234 
1235     // Step 3
1236     let mut headers = request.headers
1237         .iter()
1238         .filter(|view| !is_cors_safelisted_request_header(view))
1239         .map(|view| UniCase(view.name().to_ascii_lowercase().to_owned()))
1240         .collect::<Vec<UniCase<String>>>();
1241     headers.sort();
1242 
1243     // Step 4
1244     if !headers.is_empty() {
1245         preflight.headers.set::<AccessControlRequestHeaders>(AccessControlRequestHeaders(headers));
1246     }
1247 
1248     // Step 5
1249     let response = http_network_or_cache_fetch(&mut preflight, false, false, &mut None, context);
1250 
1251     // Step 6
1252     if cors_check(&request, &response).is_ok() &&
1253        response.status.map_or(false, |status| status.is_success()) {
1254         // Substep 1, 2
1255         let mut methods = if response.headers.has::<AccessControlAllowMethods>() {
1256             match response.headers.get::<AccessControlAllowMethods>() {
1257                 Some(&AccessControlAllowMethods(ref m)) => m.clone(),
1258                 // Substep 4
1259                 None => return Response::network_error(NetworkError::Internal("CORS ACAM check failed".into()))
1260             }
1261         } else {
1262             vec![]
1263         };
1264 
1265         // Substep 3
1266         let header_names = if response.headers.has::<AccessControlAllowHeaders>() {
1267             match response.headers.get::<AccessControlAllowHeaders>() {
1268                 Some(&AccessControlAllowHeaders(ref hn)) => hn.clone(),
1269                 // Substep 4
1270                 None => return Response::network_error(NetworkError::Internal("CORS ACAH check failed".into()))
1271             }
1272         } else {
1273             vec![]
1274         };
1275 
1276         // Substep 5
1277         if (methods.iter().any(|m| m.as_ref() == "*") ||
1278             header_names.iter().any(|hn| &**hn == "*")) &&
1279            request.credentials_mode == CredentialsMode::Include {
1280             return Response::network_error(
1281                 NetworkError::Internal("CORS ACAH/ACAM and request credentials mode mismatch".into()));
1282         }
1283 
1284         // Substep 6
1285         if methods.is_empty() && request.use_cors_preflight {
1286             methods = vec![request.method.clone()];
1287         }
1288 
1289         // Substep 7
1290         debug!("CORS check: Allowed methods: {:?}, current method: {:?}",
1291                 methods, request.method);
1292         if methods.iter().all(|method| *method != request.method) &&
1293             !is_cors_safelisted_method(&request.method) &&
1294             methods.iter().all(|m| m.as_ref() != "*") {
1295             return Response::network_error(NetworkError::Internal("CORS method check failed".into()));
1296         }
1297 
1298         // Substep 8
1299         if request.headers.iter().any(
1300             |header| header.name() == "authorization" &&
1301                      header_names.iter().all(|hn| *hn != UniCase(header.name()))) {
1302             return Response::network_error(NetworkError::Internal("CORS authorization check failed".into()));
1303         }
1304 
1305         // Substep 9
1306         debug!("CORS check: Allowed headers: {:?}, current headers: {:?}", header_names, request.headers);
1307         let set: HashSet<&UniCase<String>> = HashSet::from_iter(header_names.iter());
1308         if request.headers.iter().any(
1309             |ref hv| !set.contains(&UniCase(hv.name().to_owned())) && !is_cors_safelisted_request_header(hv)) {
1310             return Response::network_error(NetworkError::Internal("CORS headers check failed".into()));
1311         }
1312 
1313         // Substep 10, 11
1314         let max_age = response.headers.get::<AccessControlMaxAge>().map(|acma| acma.0).unwrap_or(0);
1315 
1316         // Substep 12
1317         // TODO: Need to define what an imposed limit on max-age is
1318 
1319         // Substep 13 ignored, we do have a CORS cache
1320 
1321         // Substep 14, 15
1322         for method in &methods {
1323             cache.match_method_and_update(&*request, method.clone(), max_age);
1324         }
1325 
1326         // Substep 16, 17
1327         for header_name in &header_names {
1328             cache.match_header_and_update(&*request, &*header_name, max_age);
1329         }
1330 
1331         // Substep 18
1332         return response;
1333     }
1334 
1335     // Step 7
1336     Response::network_error(NetworkError::Internal("CORS check failed".into()))
1337 }
1338 
1339 /// [CORS check](https://fetch.spec.whatwg.org#concept-cors-check)
cors_check(request: &Request, response: &Response) -> Result<(), ()>1340 fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
1341     // Step 1
1342     let origin = response.headers.get::<AccessControlAllowOrigin>().cloned();
1343 
1344     // Step 2
1345     let origin = origin.ok_or(())?;
1346 
1347     // Step 3
1348     if request.credentials_mode != CredentialsMode::Include &&
1349        origin == AccessControlAllowOrigin::Any {
1350         return Ok(());
1351     }
1352 
1353     // Step 4
1354     let origin = match origin {
1355         AccessControlAllowOrigin::Value(origin) => origin,
1356         // if it's Any or Null at this point, there's nothing to do but return Err(())
1357         _ => return Err(())
1358     };
1359 
1360     match request.origin {
1361         Origin::Origin(ref o) if o.ascii_serialization() == origin.trim() => {},
1362         _ => return Err(())
1363     }
1364 
1365     // Step 5
1366     if request.credentials_mode != CredentialsMode::Include {
1367         return Ok(());
1368     }
1369 
1370     // Step 6
1371     let credentials = response.headers.get::<AccessControlAllowCredentials>().cloned();
1372 
1373     // Step 7
1374     if credentials.is_some() {
1375         return Ok(());
1376     }
1377 
1378     // Step 8
1379     Err(())
1380 }
1381 
has_credentials(url: &ServoUrl) -> bool1382 fn has_credentials(url: &ServoUrl) -> bool {
1383     !url.username().is_empty() || url.password().is_some()
1384 }
1385 
is_no_store_cache(headers: &Headers) -> bool1386 fn is_no_store_cache(headers: &Headers) -> bool {
1387     headers.has::<IfModifiedSince>() | headers.has::<IfNoneMatch>() |
1388     headers.has::<IfUnmodifiedSince>() | headers.has::<IfMatch>() |
1389     headers.has::<IfRange>()
1390 }
1391 
1392 /// <https://fetch.spec.whatwg.org/#redirect-status>
is_redirect_status(status: StatusCode) -> bool1393 pub fn is_redirect_status(status: StatusCode) -> bool {
1394     match status {
1395         StatusCode::MovedPermanently |
1396         StatusCode::Found |
1397         StatusCode::SeeOther |
1398         StatusCode::TemporaryRedirect |
1399         StatusCode::PermanentRedirect => true,
1400         _ => false,
1401     }
1402 }
1403