/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::ReferrerPolicy; use crate::ResourceTimingType; use content_security_policy::{self as csp, CspList}; use http::HeaderMap; use hyper::Method; use msg::constellation_msg::PipelineId; use servo_url::{ImmutableOrigin, ServoUrl}; /// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum Initiator { None, Download, ImageSet, Manifest, XSLT, } /// A request [destination](https://fetch.spec.whatwg.org/#concept-request-destination) pub use csp::Destination; /// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin) #[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum Origin { Client, Origin(ImmutableOrigin), } /// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer) #[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum Referrer { NoReferrer, /// Default referrer if nothing is specified Client, ReferrerUrl(ServoUrl), } /// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode) #[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum RequestMode { Navigate, SameOrigin, NoCors, CorsMode, WebSocket { protocols: Vec }, } /// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum CredentialsMode { Omit, CredentialsSameOrigin, Include, } /// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum CacheMode { Default, NoStore, Reload, NoCache, ForceCache, OnlyIfCached, } /// [Service-workers mode](https://fetch.spec.whatwg.org/#request-service-workers-mode) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum ServiceWorkersMode { All, None, } /// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum RedirectMode { Follow, Error, Manual, } /// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting) #[derive(Clone, Copy, MallocSizeOf, PartialEq)] pub enum ResponseTainting { Basic, CorsTainting, Opaque, } /// [Window](https://fetch.spec.whatwg.org/#concept-request-window) #[derive(Clone, Copy, MallocSizeOf, PartialEq)] pub enum Window { NoWindow, Client, // TODO: Environmental settings object } /// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous) #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum CorsSettings { Anonymous, UseCredentials, } /// [Parser Metadata](https://fetch.spec.whatwg.org/#concept-request-parser-metadata) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum ParserMetadata { Default, ParserInserted, NotParserInserted, } #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct RequestBuilder { #[serde( deserialize_with = "::hyper_serde::deserialize", serialize_with = "::hyper_serde::serialize" )] #[ignore_malloc_size_of = "Defined in hyper"] pub method: Method, pub url: ServoUrl, #[serde( deserialize_with = "::hyper_serde::deserialize", serialize_with = "::hyper_serde::serialize" )] #[ignore_malloc_size_of = "Defined in hyper"] pub headers: HeaderMap, pub unsafe_request: bool, pub body: Option>, pub service_workers_mode: ServiceWorkersMode, // TODO: client object pub destination: Destination, pub synchronous: bool, pub mode: RequestMode, pub cache_mode: CacheMode, pub use_cors_preflight: bool, pub credentials_mode: CredentialsMode, pub use_url_credentials: bool, pub origin: ImmutableOrigin, // XXXManishearth these should be part of the client object pub referrer: Option, pub referrer_policy: Option, pub pipeline_id: Option, pub redirect_mode: RedirectMode, pub integrity_metadata: String, // This is nominally a part of the client's global object. // It is copied here to avoid having to reach across the thread // boundary every time a redirect occurs. #[ignore_malloc_size_of = "Defined in rust-content-security-policy"] pub csp_list: Option, // to keep track of redirects pub url_list: Vec, pub parser_metadata: ParserMetadata, pub initiator: Initiator, } impl RequestBuilder { pub fn new(url: ServoUrl) -> RequestBuilder { RequestBuilder { method: Method::GET, url: url, headers: HeaderMap::new(), unsafe_request: false, body: None, service_workers_mode: ServiceWorkersMode::All, destination: Destination::None, synchronous: false, mode: RequestMode::NoCors, cache_mode: CacheMode::Default, use_cors_preflight: false, credentials_mode: CredentialsMode::Omit, use_url_credentials: false, origin: ImmutableOrigin::new_opaque(), referrer: None, referrer_policy: None, pipeline_id: None, redirect_mode: RedirectMode::Follow, integrity_metadata: "".to_owned(), url_list: vec![], parser_metadata: ParserMetadata::Default, initiator: Initiator::None, csp_list: None, } } pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder { self.initiator = initiator; self } pub fn method(mut self, method: Method) -> RequestBuilder { self.method = method; self } pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder { self.headers = headers; self } pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder { self.unsafe_request = unsafe_request; self } pub fn body(mut self, body: Option>) -> RequestBuilder { self.body = body; self } pub fn destination(mut self, destination: Destination) -> RequestBuilder { self.destination = destination; self } pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder { self.synchronous = synchronous; self } pub fn mode(mut self, mode: RequestMode) -> RequestBuilder { self.mode = mode; self } pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder { self.use_cors_preflight = use_cors_preflight; self } pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder { self.credentials_mode = credentials_mode; self } pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder { self.use_url_credentials = use_url_credentials; self } pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder { self.origin = origin; self } pub fn referrer(mut self, referrer: Option) -> RequestBuilder { self.referrer = referrer; self } pub fn referrer_policy(mut self, referrer_policy: Option) -> RequestBuilder { self.referrer_policy = referrer_policy; self } pub fn pipeline_id(mut self, pipeline_id: Option) -> RequestBuilder { self.pipeline_id = pipeline_id; self } pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder { self.redirect_mode = redirect_mode; self } pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder { self.integrity_metadata = integrity_metadata; self } pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder { self.parser_metadata = parser_metadata; self } pub fn build(self) -> Request { let mut request = Request::new( self.url.clone(), Some(Origin::Origin(self.origin)), self.pipeline_id, ); request.initiator = self.initiator; request.method = self.method; request.headers = self.headers; request.unsafe_request = self.unsafe_request; request.body = self.body; request.service_workers_mode = self.service_workers_mode; request.destination = self.destination; request.synchronous = self.synchronous; request.mode = self.mode; request.use_cors_preflight = self.use_cors_preflight; request.credentials_mode = self.credentials_mode; request.use_url_credentials = self.use_url_credentials; request.cache_mode = self.cache_mode; request.referrer = self.referrer.unwrap_or(Referrer::Client); request.referrer_policy = self.referrer_policy; request.redirect_mode = self.redirect_mode; let mut url_list = self.url_list; if url_list.is_empty() { url_list.push(self.url); } request.redirect_count = url_list.len() as u32 - 1; request.url_list = url_list; request.integrity_metadata = self.integrity_metadata; request.parser_metadata = self.parser_metadata; request.csp_list = self.csp_list; request } } /// A [Request](https://fetch.spec.whatwg.org/#concept-request) as defined by /// the Fetch spec. #[derive(Clone, MallocSizeOf)] pub struct Request { /// #[ignore_malloc_size_of = "Defined in hyper"] pub method: Method, /// pub local_urls_only: bool, /// pub sandboxed_storage_area_urls: bool, /// #[ignore_malloc_size_of = "Defined in hyper"] pub headers: HeaderMap, /// pub unsafe_request: bool, /// pub body: Option>, // TODO: client object pub window: Window, // TODO: target browsing context /// pub keep_alive: bool, /// pub service_workers_mode: ServiceWorkersMode, /// pub initiator: Initiator, /// pub destination: Destination, // TODO: priority object /// pub origin: Origin, /// pub referrer: Referrer, /// pub referrer_policy: Option, pub pipeline_id: Option, /// pub synchronous: bool, /// pub mode: RequestMode, /// pub use_cors_preflight: bool, /// pub credentials_mode: CredentialsMode, /// pub use_url_credentials: bool, /// pub cache_mode: CacheMode, /// pub redirect_mode: RedirectMode, /// pub integrity_metadata: String, // Use the last method on url_list to act as spec current url field, and // first method to act as spec url field /// pub url_list: Vec, /// pub redirect_count: u32, /// pub response_tainting: ResponseTainting, /// pub parser_metadata: ParserMetadata, // This is nominally a part of the client's global object. // It is copied here to avoid having to reach across the thread // boundary every time a redirect occurs. #[ignore_malloc_size_of = "Defined in rust-content-security-policy"] pub csp_list: Option, } impl Request { pub fn new(url: ServoUrl, origin: Option, pipeline_id: Option) -> Request { Request { method: Method::GET, local_urls_only: false, sandboxed_storage_area_urls: false, headers: HeaderMap::new(), unsafe_request: false, body: None, window: Window::Client, keep_alive: false, service_workers_mode: ServiceWorkersMode::All, initiator: Initiator::None, destination: Destination::None, origin: origin.unwrap_or(Origin::Client), referrer: Referrer::Client, referrer_policy: None, pipeline_id: pipeline_id, synchronous: false, mode: RequestMode::NoCors, use_cors_preflight: false, credentials_mode: CredentialsMode::Omit, use_url_credentials: false, cache_mode: CacheMode::Default, redirect_mode: RedirectMode::Follow, integrity_metadata: String::new(), url_list: vec![url], parser_metadata: ParserMetadata::Default, redirect_count: 0, response_tainting: ResponseTainting::Basic, csp_list: None, } } /// pub fn url(&self) -> ServoUrl { self.url_list.first().unwrap().clone() } /// pub fn current_url(&self) -> ServoUrl { self.url_list.last().unwrap().clone() } /// pub fn current_url_mut(&mut self) -> &mut ServoUrl { self.url_list.last_mut().unwrap() } /// pub fn is_navigation_request(&self) -> bool { self.destination == Destination::Document } /// pub fn is_subresource_request(&self) -> bool { match self.destination { Destination::Audio | Destination::Font | Destination::Image | Destination::Manifest | Destination::Script | Destination::Style | Destination::Track | Destination::Video | Destination::Xslt | Destination::None => true, _ => false, } } pub fn timing_type(&self) -> ResourceTimingType { if self.is_navigation_request() { ResourceTimingType::Navigation } else { ResourceTimingType::Resource } } } impl Referrer { pub fn to_url(&self) -> Option<&ServoUrl> { match *self { Referrer::NoReferrer | Referrer::Client => None, Referrer::ReferrerUrl(ref url) => Some(url), } } }