servo/components/net_traits/request.rs
Michael Howell b8f3e8bb2e Add simple implementation of content-security-policy on scripts / styles
This needs a lot more hooks before it'll actually be a good
implementation, but for a start it can help get some feedback on if this
is the right way to go about it.

Part of servo/servo#4577
2019-10-16 19:46:45 +00:00

471 lines
16 KiB
Rust

/* 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<String> },
}
/// 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<Vec<u8>>,
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<Referrer>,
pub referrer_policy: Option<ReferrerPolicy>,
pub pipeline_id: Option<PipelineId>,
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<CspList>,
// to keep track of redirects
pub url_list: Vec<ServoUrl>,
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<Vec<u8>>) -> 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<Referrer>) -> RequestBuilder {
self.referrer = referrer;
self
}
pub fn referrer_policy(mut self, referrer_policy: Option<ReferrerPolicy>) -> RequestBuilder {
self.referrer_policy = referrer_policy;
self
}
pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> 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 {
/// <https://fetch.spec.whatwg.org/#concept-request-method>
#[ignore_malloc_size_of = "Defined in hyper"]
pub method: Method,
/// <https://fetch.spec.whatwg.org/#local-urls-only-flag>
pub local_urls_only: bool,
/// <https://fetch.spec.whatwg.org/#sandboxed-storage-area-urls-flag>
pub sandboxed_storage_area_urls: bool,
/// <https://fetch.spec.whatwg.org/#concept-request-header-list>
#[ignore_malloc_size_of = "Defined in hyper"]
pub headers: HeaderMap,
/// <https://fetch.spec.whatwg.org/#unsafe-request-flag>
pub unsafe_request: bool,
/// <https://fetch.spec.whatwg.org/#concept-request-body>
pub body: Option<Vec<u8>>,
// TODO: client object
pub window: Window,
// TODO: target browsing context
/// <https://fetch.spec.whatwg.org/#request-keepalive-flag>
pub keep_alive: bool,
/// <https://fetch.spec.whatwg.org/#request-service-workers-mode>
pub service_workers_mode: ServiceWorkersMode,
/// <https://fetch.spec.whatwg.org/#concept-request-initiator>
pub initiator: Initiator,
/// <https://fetch.spec.whatwg.org/#concept-request-destination>
pub destination: Destination,
// TODO: priority object
/// <https://fetch.spec.whatwg.org/#concept-request-origin>
pub origin: Origin,
/// <https://fetch.spec.whatwg.org/#concept-request-referrer>
pub referrer: Referrer,
/// <https://fetch.spec.whatwg.org/#concept-request-referrer-policy>
pub referrer_policy: Option<ReferrerPolicy>,
pub pipeline_id: Option<PipelineId>,
/// <https://fetch.spec.whatwg.org/#synchronous-flag>
pub synchronous: bool,
/// <https://fetch.spec.whatwg.org/#concept-request-mode>
pub mode: RequestMode,
/// <https://fetch.spec.whatwg.org/#use-cors-preflight-flag>
pub use_cors_preflight: bool,
/// <https://fetch.spec.whatwg.org/#concept-request-credentials-mode>
pub credentials_mode: CredentialsMode,
/// <https://fetch.spec.whatwg.org/#concept-request-use-url-credentials-flag>
pub use_url_credentials: bool,
/// <https://fetch.spec.whatwg.org/#concept-request-cache-mode>
pub cache_mode: CacheMode,
/// <https://fetch.spec.whatwg.org/#concept-request-redirect-mode>
pub redirect_mode: RedirectMode,
/// <https://fetch.spec.whatwg.org/#concept-request-integrity-metadata>
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
/// <https://fetch.spec.whatwg.org/#concept-request-url-list>
pub url_list: Vec<ServoUrl>,
/// <https://fetch.spec.whatwg.org/#concept-request-redirect-count>
pub redirect_count: u32,
/// <https://fetch.spec.whatwg.org/#concept-request-response-tainting>
pub response_tainting: ResponseTainting,
/// <https://fetch.spec.whatwg.org/#concept-request-parser-metadata>
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<CspList>,
}
impl Request {
pub fn new(url: ServoUrl, origin: Option<Origin>, pipeline_id: Option<PipelineId>) -> 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,
}
}
/// <https://fetch.spec.whatwg.org/#concept-request-url>
pub fn url(&self) -> ServoUrl {
self.url_list.first().unwrap().clone()
}
/// <https://fetch.spec.whatwg.org/#concept-request-current-url>
pub fn current_url(&self) -> ServoUrl {
self.url_list.last().unwrap().clone()
}
/// <https://fetch.spec.whatwg.org/#concept-request-current-url>
pub fn current_url_mut(&mut self) -> &mut ServoUrl {
self.url_list.last_mut().unwrap()
}
/// <https://fetch.spec.whatwg.org/#navigation-request>
pub fn is_navigation_request(&self) -> bool {
self.destination == Destination::Document
}
/// <https://fetch.spec.whatwg.org/#subresource-request>
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),
}
}
}