diff options
Diffstat (limited to 'components/net_traits')
-rw-r--r-- | components/net_traits/Cargo.toml | 42 | ||||
-rw-r--r-- | components/net_traits/blob_url_store.rs | 75 | ||||
-rw-r--r-- | components/net_traits/fetch/headers.rs | 18 | ||||
-rw-r--r-- | components/net_traits/filemanager_thread.rs | 190 | ||||
-rw-r--r-- | components/net_traits/image/base.rs | 121 | ||||
-rw-r--r-- | components/net_traits/image_cache.rs | 156 | ||||
-rw-r--r-- | components/net_traits/lib.rs | 859 | ||||
-rw-r--r-- | components/net_traits/pub_domains.rs | 159 | ||||
-rw-r--r-- | components/net_traits/quality.rs | 87 | ||||
-rw-r--r-- | components/net_traits/request.rs | 749 | ||||
-rw-r--r-- | components/net_traits/response.rs | 364 | ||||
-rw-r--r-- | components/net_traits/storage_thread.rs | 48 | ||||
-rw-r--r-- | components/net_traits/tests/image.rs | 28 | ||||
-rw-r--r-- | components/net_traits/tests/lib.rs | 212 | ||||
-rw-r--r-- | components/net_traits/tests/pub_domains.rs | 122 | ||||
-rw-r--r-- | components/net_traits/tests/whitespace.rs | 25 |
16 files changed, 0 insertions, 3255 deletions
diff --git a/components/net_traits/Cargo.toml b/components/net_traits/Cargo.toml deleted file mode 100644 index 13005f40790..00000000000 --- a/components/net_traits/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "net_traits" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -edition = "2018" -publish = false - -[lib] -name = "net_traits" -path = "lib.rs" -test = false -doctest = false - -[dependencies] -content-security-policy = { workspace = true } -cookie = { workspace = true } -embedder_traits = { path = "../embedder_traits" } -headers = { workspace = true } -http = { workspace = true } -hyper = { workspace = true } -hyper_serde = { workspace = true } -image = { workspace = true } -ipc-channel = { workspace = true } -lazy_static = { workspace = true } -log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { workspace = true } -mime = { workspace = true } -msg = { path = "../msg" } -num-traits = { workspace = true } -percent-encoding = { workspace = true } -pixels = { path = "../pixels" } -rustls = { workspace = true } -serde = { workspace = true } -servo_arc = { path = "../servo_arc" } -servo_rand = { path = "../rand" } -servo_url = { path = "../url" } -time = { workspace = true } -url = { workspace = true } -uuid = { workspace = true } -webrender_api = { git = "https://github.com/servo/webrender" } diff --git a/components/net_traits/blob_url_store.rs b/components/net_traits/blob_url_store.rs deleted file mode 100644 index ab853b702c9..00000000000 --- a/components/net_traits/blob_url_store.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* 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 std::str::FromStr; - -use serde::{Deserialize, Serialize}; -use servo_url::ServoUrl; -use url::Url; -use uuid::Uuid; - -use crate::filemanager_thread::FileOrigin; - -/// Errors returned to Blob URL Store request -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub enum BlobURLStoreError { - /// Invalid File UUID - InvalidFileID, - /// Invalid URL origin - InvalidOrigin, - /// Invalid entry content - InvalidEntry, - /// Invalid range - InvalidRange, - /// External error, from like file system, I/O etc. - External(String), -} - -/// Standalone blob buffer object -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BlobBuf { - pub filename: Option<String>, - /// MIME type string - pub type_string: String, - /// Size of content in bytes - pub size: u64, - /// Content of blob - pub bytes: Vec<u8>, -} - -/// Parse URL as Blob URL scheme's definition -/// -/// <https://w3c.github.io/FileAPI/#DefinitionOfScheme> -pub fn parse_blob_url(url: &ServoUrl) -> Result<(Uuid, FileOrigin), ()> { - let url_inner = Url::parse(url.path()).map_err(|_| ())?; - let segs = url_inner - .path_segments() - .map(|c| c.collect::<Vec<_>>()) - .ok_or(())?; - - if url.query().is_some() || segs.len() > 1 { - return Err(()); - } - - let id = { - let id = segs.first().ok_or(())?; - Uuid::from_str(id).map_err(|_| ())? - }; - Ok((id, get_blob_origin(&ServoUrl::from_url(url_inner)))) -} - -/// Given an URL, returning the Origin that a Blob created under this -/// URL should have. -/// -/// HACK(izgzhen): Not well-specified on spec, and it is a bit a hack -/// both due to ambiguity of spec and that we have to serialization the -/// Origin here. -pub fn get_blob_origin(url: &ServoUrl) -> FileOrigin { - if url.scheme() == "file" { - // NOTE: by default this is "null" (Opaque), which is not ideal - "file://".to_string() - } else { - url.origin().ascii_serialization() - } -} diff --git a/components/net_traits/fetch/headers.rs b/components/net_traits/fetch/headers.rs deleted file mode 100644 index ae95066bcf5..00000000000 --- a/components/net_traits/fetch/headers.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* 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 headers::HeaderMap; - -/// <https://fetch.spec.whatwg.org/#concept-header-list-get> -pub fn get_value_from_header_list(name: &str, headers: &HeaderMap) -> Option<Vec<u8>> { - let values = headers.get_all(name).iter().map(|val| val.as_bytes()); - - // Step 1 - if values.size_hint() == (0, Some(0)) { - return None; - } - - // Step 2 - return Some(values.collect::<Vec<&[u8]>>().join(&[0x2C, 0x20][..])); -} diff --git a/components/net_traits/filemanager_thread.rs b/components/net_traits/filemanager_thread.rs deleted file mode 100644 index ee18e295393..00000000000 --- a/components/net_traits/filemanager_thread.rs +++ /dev/null @@ -1,190 +0,0 @@ -/* 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 std::cmp::{max, min}; -use std::ops::Range; -use std::path::PathBuf; - -use embedder_traits::FilterPattern; -use ipc_channel::ipc::IpcSender; -use malloc_size_of_derive::MallocSizeOf; -use num_traits::ToPrimitive; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::blob_url_store::{BlobBuf, BlobURLStoreError}; - -// HACK: Not really process-safe now, we should send Origin -// directly instead of this in future, blocked on #11722 -/// File manager store entry's origin -pub type FileOrigin = String; - -/// A token modulating access to a file for a blob URL. -pub enum FileTokenCheck { - /// Checking against a token not required, - /// used for accessing a file - /// that isn't linked to from a blob URL. - NotRequired, - /// Checking against token required. - Required(Uuid), - /// Request should always fail, - /// used for cases when a check is required, - /// but no token could be acquired. - ShouldFail, -} - -/// Relative slice positions of a sequence, -/// whose semantic should be consistent with (start, end) parameters in -/// <https://w3c.github.io/FileAPI/#dfn-slice> -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct RelativePos { - /// Relative to first byte if non-negative, - /// relative to one past last byte if negative, - pub start: i64, - /// Relative offset from first byte if Some(non-negative), - /// relative to one past last byte if Some(negative), - /// None if one past last byte - pub end: Option<i64>, -} - -impl RelativePos { - /// Full range from start to end - pub fn full_range() -> RelativePos { - RelativePos { - start: 0, - end: None, - } - } - - /// Instantiate optional slice position parameters - pub fn from_opts(start: Option<i64>, end: Option<i64>) -> RelativePos { - RelativePos { - start: start.unwrap_or(0), - end, - } - } - - /// Slice the inner sliced range by repositioning - pub fn slice_inner(&self, rel_pos: &RelativePos) -> RelativePos { - RelativePos { - start: self.start + rel_pos.start, - end: match (self.end, rel_pos.end) { - (Some(old_end), Some(rel_end)) => Some(old_end + rel_end), - (old, None) => old, - (None, rel) => rel, - }, - } - } - - /// Compute absolute range by giving the total size - /// <https://w3c.github.io/FileAPI/#slice-method-algo> - pub fn to_abs_range(&self, size: usize) -> Range<usize> { - let size = size as i64; - - let start = { - if self.start < 0 { - max(size + self.start, 0) - } else { - min(self.start, size) - } - }; - - let end = match self.end { - Some(rel_end) => { - if rel_end < 0 { - max(size + rel_end, 0) - } else { - min(rel_end, size) - } - }, - None => size, - }; - - let span: i64 = max(end - start, 0); - - Range { - start: start.to_usize().unwrap(), - end: (start + span).to_usize().unwrap(), - } - } -} - -/// Response to file selection request -#[derive(Debug, Deserialize, Serialize)] -pub struct SelectedFile { - pub id: Uuid, - pub filename: PathBuf, - pub modified: u64, - pub size: u64, - // https://w3c.github.io/FileAPI/#dfn-type - pub type_string: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum FileManagerThreadMsg { - /// Select a single file. Last field is pre-selected file path for testing - SelectFile( - Vec<FilterPattern>, - IpcSender<FileManagerResult<SelectedFile>>, - FileOrigin, - Option<String>, - ), - - /// Select multiple files. Last field is pre-selected file paths for testing - SelectFiles( - Vec<FilterPattern>, - IpcSender<FileManagerResult<Vec<SelectedFile>>>, - FileOrigin, - Option<Vec<String>>, - ), - - /// Read FileID-indexed file in chunks, optionally check URL validity based on boolean flag - ReadFile( - IpcSender<FileManagerResult<ReadFileProgress>>, - Uuid, - FileOrigin, - ), - - /// Add an entry as promoted memory-based blob - PromoteMemory(Uuid, BlobBuf, bool, FileOrigin), - - /// Add a sliced entry pointing to the parent FileID, and send back the associated FileID - /// as part of a valid Blob URL - AddSlicedURLEntry( - Uuid, - RelativePos, - IpcSender<Result<Uuid, BlobURLStoreError>>, - FileOrigin, - ), - - /// Decrease reference count and send back the acknowledgement - DecRef(Uuid, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>), - - /// Activate an internal FileID so it becomes valid as part of a Blob URL - ActivateBlobURL(Uuid, IpcSender<Result<(), BlobURLStoreError>>, FileOrigin), - - /// Revoke Blob URL and send back the acknowledgement - RevokeBlobURL(Uuid, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>), -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum ReadFileProgress { - Meta(BlobBuf), - Partial(Vec<u8>), - EOF, -} - -pub type FileManagerResult<T> = Result<T, FileManagerThreadError>; - -#[derive(Debug, Deserialize, Serialize)] -pub enum FileManagerThreadError { - /// The selection action is invalid due to exceptional reason - InvalidSelection, - /// The selection action is cancelled by user - UserCancelled, - /// Errors returned from file system request - FileSystemError(String), - /// Blob URL Store error - BlobURLStoreError(BlobURLStoreError), -} diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs deleted file mode 100644 index 74e2d8823dc..00000000000 --- a/components/net_traits/image/base.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* 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 std::fmt; - -use image::ImageFormat; -use ipc_channel::ipc::IpcSharedMemory; -use log::debug; -use malloc_size_of_derive::MallocSizeOf; -use pixels::PixelFormat; -use serde::{Deserialize, Serialize}; -use webrender_api::ImageKey; - -use crate::image_cache::CorsStatus; - -#[derive(Clone, Deserialize, MallocSizeOf, Serialize)] -pub struct Image { - pub width: u32, - pub height: u32, - pub format: PixelFormat, - #[ignore_malloc_size_of = "Defined in ipc-channel"] - pub bytes: IpcSharedMemory, - #[ignore_malloc_size_of = "Defined in webrender_api"] - pub id: Option<ImageKey>, - pub cors_status: CorsStatus, -} - -impl fmt::Debug for Image { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}", - self.width, self.height, self.format, self.id - ) - } -} - -#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] -pub struct ImageMetadata { - pub width: u32, - pub height: u32, -} - -// FIXME: Images must not be copied every frame. Instead we should atomically -// reference count them. - -pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<Image> { - if buffer.is_empty() { - return None; - } - - let image_fmt_result = detect_image_format(buffer); - match image_fmt_result { - Err(msg) => { - debug!("{}", msg); - None - }, - Ok(_) => match image::load_from_memory(buffer) { - Ok(image) => { - let mut rgba = image.into_rgba8(); - pixels::rgba8_byte_swap_colors_inplace(&mut *rgba); - Some(Image { - width: rgba.width(), - height: rgba.height(), - format: PixelFormat::BGRA8, - bytes: IpcSharedMemory::from_bytes(&*rgba), - id: None, - cors_status, - }) - }, - Err(e) => { - debug!("Image decoding error: {:?}", e); - None - }, - }, - } -} - -// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img -pub fn detect_image_format(buffer: &[u8]) -> Result<ImageFormat, &str> { - if is_gif(buffer) { - Ok(ImageFormat::Gif) - } else if is_jpeg(buffer) { - Ok(ImageFormat::Jpeg) - } else if is_png(buffer) { - Ok(ImageFormat::Png) - } else if is_webp(buffer) { - Ok(ImageFormat::WebP) - } else if is_bmp(buffer) { - Ok(ImageFormat::Bmp) - } else if is_ico(buffer) { - Ok(ImageFormat::Ico) - } else { - Err("Image Format Not Supported") - } -} - -fn is_gif(buffer: &[u8]) -> bool { - buffer.starts_with(b"GIF87a") || buffer.starts_with(b"GIF89a") -} - -fn is_jpeg(buffer: &[u8]) -> bool { - buffer.starts_with(&[0xff, 0xd8, 0xff]) -} - -fn is_png(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) -} - -fn is_bmp(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x42, 0x4D]) -} - -fn is_ico(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x00, 0x00, 0x01, 0x00]) -} - -fn is_webp(buffer: &[u8]) -> bool { - buffer.starts_with(b"RIFF") && buffer.len() >= 14 && &buffer[8..14] == b"WEBPVP" -} diff --git a/components/net_traits/image_cache.rs b/components/net_traits/image_cache.rs deleted file mode 100644 index 81d34964db4..00000000000 --- a/components/net_traits/image_cache.rs +++ /dev/null @@ -1,156 +0,0 @@ -/* 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 std::sync::Arc; - -use ipc_channel::ipc::IpcSender; -use log::debug; -use malloc_size_of_derive::MallocSizeOf; -use serde::{Deserialize, Serialize}; -use servo_url::{ImmutableOrigin, ServoUrl}; - -use crate::image::base::{Image, ImageMetadata}; -use crate::request::CorsSettings; -use crate::{FetchResponseMsg, WebrenderIpcSender}; - -// ====================================================================== -// Aux structs and enums. -// ====================================================================== - -/// Indicating either entire image or just metadata availability -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum ImageOrMetadataAvailable { - ImageAvailable { - #[ignore_malloc_size_of = "Arc"] - image: Arc<Image>, - url: ServoUrl, - is_placeholder: bool, - }, - MetadataAvailable(ImageMetadata), -} - -/// This is optionally passed to the image cache when requesting -/// and image, and returned to the specified event loop when the -/// image load completes. It is typically used to trigger a reflow -/// and/or repaint. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct ImageResponder { - id: PendingImageId, - sender: IpcSender<PendingImageResponse>, -} - -impl ImageResponder { - pub fn new(sender: IpcSender<PendingImageResponse>, id: PendingImageId) -> ImageResponder { - ImageResponder { - sender: sender, - id: id, - } - } - - pub fn respond(&self, response: ImageResponse) { - debug!("Notifying listener"); - // This send can fail if thread waiting for this notification has panicked. - // That's not a case that's worth warning about. - // TODO(#15501): are there cases in which we should perform cleanup? - let _ = self.sender.send(PendingImageResponse { - response: response, - id: self.id, - }); - } -} - -/// The returned image. -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum ImageResponse { - /// The requested image was loaded. - Loaded(#[ignore_malloc_size_of = "Arc"] Arc<Image>, ServoUrl), - /// The request image metadata was loaded. - MetadataLoaded(ImageMetadata), - /// The requested image failed to load, so a placeholder was loaded instead. - PlaceholderLoaded(#[ignore_malloc_size_of = "Arc"] Arc<Image>, ServoUrl), - /// Neither the requested image nor the placeholder could be loaded. - None, -} - -/// The unique id for an image that has previously been requested. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] -pub struct PendingImageId(pub u64); - -#[derive(Debug, Deserialize, Serialize)] -pub struct PendingImageResponse { - pub response: ImageResponse, - pub id: PendingImageId, -} - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum UsePlaceholder { - No, - Yes, -} - -// ====================================================================== -// ImageCache public API. -// ====================================================================== - -pub enum ImageCacheResult { - Available(ImageOrMetadataAvailable), - LoadError, - Pending(PendingImageId), - ReadyForRequest(PendingImageId), -} - -pub trait ImageCache: Sync + Send { - fn new(webrender_api: WebrenderIpcSender) -> Self - where - Self: Sized; - - /// Definitively check whether there is a cached, fully loaded image available. - fn get_image( - &self, - url: ServoUrl, - origin: ImmutableOrigin, - cors_setting: Option<CorsSettings>, - ) -> Option<Arc<Image>>; - - fn get_cached_image_status( - &self, - url: ServoUrl, - origin: ImmutableOrigin, - cors_setting: Option<CorsSettings>, - use_placeholder: UsePlaceholder, - ) -> ImageCacheResult; - - /// Add a listener for the provided pending image id, eventually called by - /// ImageCacheStore::complete_load. - /// If only metadata is available, Available(ImageOrMetadataAvailable) will - /// be returned. - /// If Available(ImageOrMetadataAvailable::Image) or LoadError is the final value, - /// the provided listener will be dropped (consumed & not added to PendingLoad). - fn track_image( - &self, - url: ServoUrl, - origin: ImmutableOrigin, - cors_setting: Option<CorsSettings>, - sender: IpcSender<PendingImageResponse>, - use_placeholder: UsePlaceholder, - ) -> ImageCacheResult; - - /// Add a new listener for the given pending image id. If the image is already present, - /// the responder will still receive the expected response. - fn add_listener(&self, id: PendingImageId, listener: ImageResponder); - - /// Inform the image cache about a response for a pending request. - fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg); -} - -/// Whether this response passed any CORS checks, and is thus safe to read from -/// in cross-origin environments. -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum CorsStatus { - /// The response is either same-origin or cross-origin but passed CORS checks. - Safe, - /// The response is cross-origin and did not pass CORS checks. It is unsafe - /// to expose pixel data to the requesting environment. - Unsafe, -} diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs deleted file mode 100644 index b8b32725a25..00000000000 --- a/components/net_traits/lib.rs +++ /dev/null @@ -1,859 +0,0 @@ -/* 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/. */ - -#![deny(unsafe_code)] - -use cookie::Cookie; -use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; -use http::{Error as HttpError, HeaderMap, StatusCode}; -use hyper::Error as HyperError; -use hyper_serde::Serde; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; -use ipc_channel::router::ROUTER; -use ipc_channel::Error as IpcError; -use lazy_static::lazy_static; -use log::warn; -use malloc_size_of::malloc_size_of_is_0; -use malloc_size_of_derive::MallocSizeOf; -use mime::Mime; -use msg::constellation_msg::HistoryStateId; -use rustls::Certificate; -use serde::{Deserialize, Serialize}; -use servo_rand::RngCore; -use servo_url::{ImmutableOrigin, ServoUrl}; -use time::precise_time_ns; -use webrender_api::{ImageData, ImageDescriptor, ImageKey}; - -use crate::filemanager_thread::FileManagerThreadMsg; -use crate::request::{Request, RequestBuilder}; -use crate::response::{HttpsState, Response, ResponseInit}; -use crate::storage_thread::StorageThreadMsg; - -pub mod blob_url_store; -pub mod filemanager_thread; -pub mod image_cache; -pub mod pub_domains; -pub mod quality; -pub mod request; -pub mod response; -pub mod storage_thread; - -/// Image handling. -/// -/// It may be surprising that this goes in the network crate as opposed to the graphics crate. -/// However, image handling is generally very integrated with the network stack (especially where -/// caching is involved) and as a result it must live in here. -pub mod image { - pub mod base; -} - -/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/) -pub mod fetch { - pub mod headers; -} - -/// A loading context, for context-specific sniffing, as defined in -/// <https://mimesniff.spec.whatwg.org/#context-specific-sniffing> -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum LoadContext { - Browsing, - Image, - AudioVideo, - Plugin, - Style, - Script, - Font, - TextTrack, - CacheManifest, -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct CustomResponse { - #[ignore_malloc_size_of = "Defined in hyper"] - #[serde( - deserialize_with = "::hyper_serde::deserialize", - serialize_with = "::hyper_serde::serialize" - )] - pub headers: HeaderMap, - #[ignore_malloc_size_of = "Defined in hyper"] - #[serde( - deserialize_with = "::hyper_serde::deserialize", - serialize_with = "::hyper_serde::serialize" - )] - pub raw_status: (StatusCode, String), - pub body: Vec<u8>, -} - -impl CustomResponse { - pub fn new( - headers: HeaderMap, - raw_status: (StatusCode, String), - body: Vec<u8>, - ) -> CustomResponse { - CustomResponse { - headers: headers, - raw_status: raw_status, - body: body, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct CustomResponseMediator { - pub response_chan: IpcSender<Option<CustomResponse>>, - pub load_url: ServoUrl, -} - -/// [Policies](https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-states) -/// for providing a referrer header for a request -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum ReferrerPolicy { - /// "no-referrer" - NoReferrer, - /// "no-referrer-when-downgrade" - NoReferrerWhenDowngrade, - /// "origin" - Origin, - /// "same-origin" - SameOrigin, - /// "origin-when-cross-origin" - OriginWhenCrossOrigin, - /// "unsafe-url" - UnsafeUrl, - /// "strict-origin" - StrictOrigin, - /// "strict-origin-when-cross-origin" - StrictOriginWhenCrossOrigin, -} - -impl ToString for ReferrerPolicy { - fn to_string(&self) -> String { - match self { - ReferrerPolicy::NoReferrer => "no-referrer", - ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade", - ReferrerPolicy::Origin => "origin", - ReferrerPolicy::SameOrigin => "same-origin", - ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin", - ReferrerPolicy::UnsafeUrl => "unsafe-url", - ReferrerPolicy::StrictOrigin => "strict-origin", - ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin", - } - .to_string() - } -} - -impl From<ReferrerPolicyHeader> for ReferrerPolicy { - fn from(policy: ReferrerPolicyHeader) -> Self { - match policy { - ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer, - ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => { - ReferrerPolicy::NoReferrerWhenDowngrade - }, - ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin, - ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin, - ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin, - ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl, - ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin, - ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => { - ReferrerPolicy::StrictOriginWhenCrossOrigin - }, - } - } -} - -impl From<ReferrerPolicy> for ReferrerPolicyHeader { - fn from(referrer_policy: ReferrerPolicy) -> Self { - match referrer_policy { - ReferrerPolicy::NoReferrer => ReferrerPolicyHeader::NO_REFERRER, - ReferrerPolicy::NoReferrerWhenDowngrade => { - ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE - }, - ReferrerPolicy::SameOrigin => ReferrerPolicyHeader::SAME_ORIGIN, - ReferrerPolicy::Origin => ReferrerPolicyHeader::ORIGIN, - ReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN, - ReferrerPolicy::UnsafeUrl => ReferrerPolicyHeader::UNSAFE_URL, - ReferrerPolicy::StrictOrigin => ReferrerPolicyHeader::STRICT_ORIGIN, - ReferrerPolicy::StrictOriginWhenCrossOrigin => { - ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN - }, - } - } -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum FetchResponseMsg { - // todo: should have fields for transmitted/total bytes - ProcessRequestBody, - ProcessRequestEOF, - // todo: send more info about the response (or perhaps the entire Response) - ProcessResponse(Result<FetchMetadata, NetworkError>), - ProcessResponseChunk(Vec<u8>), - ProcessResponseEOF(Result<ResourceFetchTiming, NetworkError>), -} - -pub trait FetchTaskTarget { - /// <https://fetch.spec.whatwg.org/#process-request-body> - /// - /// Fired when a chunk of the request body is transmitted - fn process_request_body(&mut self, request: &Request); - - /// <https://fetch.spec.whatwg.org/#process-request-end-of-file> - /// - /// Fired when the entire request finishes being transmitted - fn process_request_eof(&mut self, request: &Request); - - /// <https://fetch.spec.whatwg.org/#process-response> - /// - /// Fired when headers are received - fn process_response(&mut self, response: &Response); - - /// Fired when a chunk of response content is received - fn process_response_chunk(&mut self, chunk: Vec<u8>); - - /// <https://fetch.spec.whatwg.org/#process-response-end-of-file> - /// - /// Fired when the response is fully fetched - fn process_response_eof(&mut self, response: &Response); -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum FilteredMetadata { - Basic(Metadata), - Cors(Metadata), - Opaque, - OpaqueRedirect(ServoUrl), -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum FetchMetadata { - Unfiltered(Metadata), - Filtered { - filtered: FilteredMetadata, - unsafe_: Metadata, - }, -} - -pub trait FetchResponseListener { - fn process_request_body(&mut self); - fn process_request_eof(&mut self); - fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>); - fn process_response_chunk(&mut self, chunk: Vec<u8>); - fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>); - fn resource_timing(&self) -> &ResourceFetchTiming; - fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming; - fn submit_resource_timing(&mut self); -} - -impl FetchTaskTarget for IpcSender<FetchResponseMsg> { - fn process_request_body(&mut self, _: &Request) { - let _ = self.send(FetchResponseMsg::ProcessRequestBody); - } - - fn process_request_eof(&mut self, _: &Request) { - let _ = self.send(FetchResponseMsg::ProcessRequestEOF); - } - - fn process_response(&mut self, response: &Response) { - let _ = self.send(FetchResponseMsg::ProcessResponse(response.metadata())); - } - - fn process_response_chunk(&mut self, chunk: Vec<u8>) { - let _ = self.send(FetchResponseMsg::ProcessResponseChunk(chunk)); - } - - fn process_response_eof(&mut self, response: &Response) { - if let Some(e) = response.get_network_error() { - let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(e.clone()))); - } else { - let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(response - .get_resource_timing() - .lock() - .unwrap() - .clone()))); - } - } -} - -/// A fetch task that discards all data it's sent, -/// useful when speculatively prefetching data that we don't need right -/// now, but might need in the future. -pub struct DiscardFetch; - -impl FetchTaskTarget for DiscardFetch { - fn process_request_body(&mut self, _: &Request) {} - - fn process_request_eof(&mut self, _: &Request) {} - - fn process_response(&mut self, _: &Response) {} - - fn process_response_chunk(&mut self, _: Vec<u8>) {} - - fn process_response_eof(&mut self, _: &Response) {} -} - -pub trait Action<Listener> { - fn process(self, listener: &mut Listener); -} - -impl<T: FetchResponseListener> Action<T> for FetchResponseMsg { - /// Execute the default action on a provided listener. - fn process(self, listener: &mut T) { - match self { - FetchResponseMsg::ProcessRequestBody => listener.process_request_body(), - FetchResponseMsg::ProcessRequestEOF => listener.process_request_eof(), - FetchResponseMsg::ProcessResponse(meta) => listener.process_response(meta), - FetchResponseMsg::ProcessResponseChunk(data) => listener.process_response_chunk(data), - FetchResponseMsg::ProcessResponseEOF(data) => { - match data { - Ok(ref response_resource_timing) => { - // update listener with values from response - *listener.resource_timing_mut() = response_resource_timing.clone(); - listener.process_response_eof(Ok(response_resource_timing.clone())); - // TODO timing check https://w3c.github.io/resource-timing/#dfn-timing-allow-check - - listener.submit_resource_timing(); - }, - // TODO Resources for which the fetch was initiated, but was later aborted - // (e.g. due to a network error) MAY be included as PerformanceResourceTiming - // objects in the Performance Timeline and MUST contain initialized attribute - // values for processed substeps of the processing model. - Err(e) => listener.process_response_eof(Err(e)), - } - }, - } - } -} - -/// Handle to a resource thread -pub type CoreResourceThread = IpcSender<CoreResourceMsg>; - -pub type IpcSendResult = Result<(), IpcError>; - -/// Abstraction of the ability to send a particular type of message, -/// used by net_traits::ResourceThreads to ease the use its IpcSender sub-fields -/// XXX: If this trait will be used more in future, some auto derive might be appealing -pub trait IpcSend<T> -where - T: serde::Serialize + for<'de> serde::Deserialize<'de>, -{ - /// send message T - fn send(&self, _: T) -> IpcSendResult; - /// get underlying sender - fn sender(&self) -> IpcSender<T>; -} - -// FIXME: Originally we will construct an Arc<ResourceThread> from ResourceThread -// in script_thread to avoid some performance pitfall. Now we decide to deal with -// the "Arc" hack implicitly in future. -// See discussion: http://logs.glob.uno/?c=mozilla%23servo&s=16+May+2016&e=16+May+2016#c430412 -// See also: https://github.com/servo/servo/blob/735480/components/script/script_thread.rs#L313 -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct ResourceThreads { - core_thread: CoreResourceThread, - storage_thread: IpcSender<StorageThreadMsg>, -} - -impl ResourceThreads { - pub fn new(c: CoreResourceThread, s: IpcSender<StorageThreadMsg>) -> ResourceThreads { - ResourceThreads { - core_thread: c, - storage_thread: s, - } - } - - pub fn clear_cache(&self) { - let _ = self.core_thread.send(CoreResourceMsg::ClearCache); - } -} - -impl IpcSend<CoreResourceMsg> for ResourceThreads { - fn send(&self, msg: CoreResourceMsg) -> IpcSendResult { - self.core_thread.send(msg) - } - - fn sender(&self) -> IpcSender<CoreResourceMsg> { - self.core_thread.clone() - } -} - -impl IpcSend<StorageThreadMsg> for ResourceThreads { - fn send(&self, msg: StorageThreadMsg) -> IpcSendResult { - self.storage_thread.send(msg) - } - - fn sender(&self) -> IpcSender<StorageThreadMsg> { - self.storage_thread.clone() - } -} - -// Ignore the sub-fields -malloc_size_of_is_0!(ResourceThreads); - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub enum IncludeSubdomains { - Included, - NotIncluded, -} - -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum MessageData { - Text(String), - Binary(Vec<u8>), -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum WebSocketDomAction { - SendMessage(MessageData), - Close(Option<u16>, Option<String>), -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum WebSocketNetworkEvent { - ConnectionEstablished { protocol_in_use: Option<String> }, - MessageReceived(MessageData), - Close(Option<u16>, String), - Fail, -} - -#[derive(Debug, Deserialize, Serialize)] -/// IPC channels to communicate with the script thread about network or DOM events. -pub enum FetchChannels { - ResponseMsg( - IpcSender<FetchResponseMsg>, - /* cancel_chan */ Option<IpcReceiver<()>>, - ), - WebSocket { - event_sender: IpcSender<WebSocketNetworkEvent>, - action_receiver: IpcReceiver<WebSocketDomAction>, - }, - /// If the fetch is just being done to populate the cache, - /// not because the data is needed now. - Prefetch, -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum CoreResourceMsg { - Fetch(RequestBuilder, FetchChannels), - /// Initiate a fetch in response to processing a redirection - FetchRedirect( - RequestBuilder, - ResponseInit, - IpcSender<FetchResponseMsg>, - /* cancel_chan */ Option<IpcReceiver<()>>, - ), - /// Store a cookie for a given originating URL - SetCookieForUrl(ServoUrl, Serde<Cookie<'static>>, CookieSource), - /// Store a set of cookies for a given originating URL - SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource), - /// Retrieve the stored cookies for a given URL - GetCookiesForUrl(ServoUrl, IpcSender<Option<String>>, CookieSource), - /// Get a cookie by name for a given originating URL - GetCookiesDataForUrl( - ServoUrl, - IpcSender<Vec<Serde<Cookie<'static>>>>, - CookieSource, - ), - DeleteCookies(ServoUrl), - /// Get a history state by a given history state id - GetHistoryState(HistoryStateId, IpcSender<Option<Vec<u8>>>), - /// Set a history state for a given history state id - SetHistoryState(HistoryStateId, Vec<u8>), - /// Removes history states for the given ids - RemoveHistoryStates(Vec<HistoryStateId>), - /// Synchronization message solely for knowing the state of the ResourceChannelManager loop - Synchronize(IpcSender<()>), - /// Clear the network cache. - ClearCache, - /// Send the service worker network mediator for an origin to CoreResourceThread - NetworkMediator(IpcSender<CustomResponseMediator>, ImmutableOrigin), - /// Message forwarded to file manager's handler - ToFileManager(FileManagerThreadMsg), - /// Break the load handler loop, send a reply when done cleaning up local resources - /// and exit - Exit(IpcSender<()>), -} - -/// Instruct the resource thread to make a new request. -pub fn fetch_async<F>(request: RequestBuilder, core_resource_thread: &CoreResourceThread, f: F) -where - F: Fn(FetchResponseMsg) + Send + 'static, -{ - let (action_sender, action_receiver) = ipc::channel().unwrap(); - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| f(message.to().unwrap())), - ); - core_resource_thread - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, None), - )) - .unwrap(); -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct ResourceCorsData { - /// CORS Preflight flag - pub preflight: bool, - /// Origin of CORS Request - pub origin: ServoUrl, -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct ResourceFetchTiming { - pub domain_lookup_start: u64, - pub timing_check_passed: bool, - pub timing_type: ResourceTimingType, - /// Number of redirects until final resource (currently limited to 20) - pub redirect_count: u16, - pub request_start: u64, - pub secure_connection_start: u64, - pub response_start: u64, - pub fetch_start: u64, - pub response_end: u64, - pub redirect_start: u64, - pub redirect_end: u64, - pub connect_start: u64, - pub connect_end: u64, - pub start_time: u64, -} - -pub enum RedirectStartValue { - #[allow(dead_code)] - Zero, - FetchStart, -} - -pub enum RedirectEndValue { - Zero, - ResponseEnd, -} - -// TODO: refactor existing code to use this enum for setting time attributes -// suggest using this with all time attributes in the future -pub enum ResourceTimeValue { - Zero, - Now, - FetchStart, - RedirectStart, -} - -pub enum ResourceAttribute { - RedirectCount(u16), - DomainLookupStart, - RequestStart, - ResponseStart, - RedirectStart(RedirectStartValue), - RedirectEnd(RedirectEndValue), - FetchStart, - ConnectStart(u64), - ConnectEnd(u64), - SecureConnectionStart, - ResponseEnd, - StartTime(ResourceTimeValue), -} - -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum ResourceTimingType { - Resource, - Navigation, - Error, - None, -} - -impl ResourceFetchTiming { - pub fn new(timing_type: ResourceTimingType) -> ResourceFetchTiming { - ResourceFetchTiming { - timing_type: timing_type, - timing_check_passed: true, - domain_lookup_start: 0, - redirect_count: 0, - secure_connection_start: 0, - request_start: 0, - response_start: 0, - fetch_start: 0, - redirect_start: 0, - redirect_end: 0, - connect_start: 0, - connect_end: 0, - response_end: 0, - start_time: 0, - } - } - - // TODO currently this is being set with precise time ns when it should be time since - // time origin (as described in Performance::now) - pub fn set_attribute(&mut self, attribute: ResourceAttribute) { - let should_attribute_always_be_updated = match attribute { - ResourceAttribute::FetchStart | - ResourceAttribute::ResponseEnd | - ResourceAttribute::StartTime(_) => true, - _ => false, - }; - if !self.timing_check_passed && !should_attribute_always_be_updated { - return; - } - match attribute { - ResourceAttribute::DomainLookupStart => self.domain_lookup_start = precise_time_ns(), - ResourceAttribute::RedirectCount(count) => self.redirect_count = count, - ResourceAttribute::RequestStart => self.request_start = precise_time_ns(), - ResourceAttribute::ResponseStart => self.response_start = precise_time_ns(), - ResourceAttribute::RedirectStart(val) => match val { - RedirectStartValue::Zero => self.redirect_start = 0, - RedirectStartValue::FetchStart => { - if self.redirect_start == 0 { - self.redirect_start = self.fetch_start - } - }, - }, - ResourceAttribute::RedirectEnd(val) => match val { - RedirectEndValue::Zero => self.redirect_end = 0, - RedirectEndValue::ResponseEnd => self.redirect_end = self.response_end, - }, - ResourceAttribute::FetchStart => self.fetch_start = precise_time_ns(), - ResourceAttribute::ConnectStart(val) => self.connect_start = val, - ResourceAttribute::ConnectEnd(val) => self.connect_end = val, - ResourceAttribute::SecureConnectionStart => { - self.secure_connection_start = precise_time_ns() - }, - ResourceAttribute::ResponseEnd => self.response_end = precise_time_ns(), - ResourceAttribute::StartTime(val) => match val { - ResourceTimeValue::RedirectStart - if self.redirect_start == 0 || !self.timing_check_passed => {}, - _ => self.start_time = self.get_time_value(val), - }, - } - } - - fn get_time_value(&self, time: ResourceTimeValue) -> u64 { - match time { - ResourceTimeValue::Zero => 0, - ResourceTimeValue::Now => precise_time_ns(), - ResourceTimeValue::FetchStart => self.fetch_start, - ResourceTimeValue::RedirectStart => self.redirect_start, - } - } - - pub fn mark_timing_check_failed(&mut self) { - self.timing_check_passed = false; - self.domain_lookup_start = 0; - self.redirect_count = 0; - self.request_start = 0; - self.response_start = 0; - self.redirect_start = 0; - self.connect_start = 0; - self.connect_end = 0; - } -} - -/// Metadata about a loaded resource, such as is obtained from HTTP headers. -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct Metadata { - /// Final URL after redirects. - pub final_url: ServoUrl, - - /// Location URL from the response headers. - pub location_url: Option<Result<ServoUrl, String>>, - - #[ignore_malloc_size_of = "Defined in hyper"] - /// MIME type / subtype. - pub content_type: Option<Serde<ContentType>>, - - /// Character set. - pub charset: Option<String>, - - #[ignore_malloc_size_of = "Defined in hyper"] - /// Headers - pub headers: Option<Serde<HeaderMap>>, - - /// HTTP Status - pub status: Option<(u16, Vec<u8>)>, - - /// Is successful HTTPS connection - pub https_state: HttpsState, - - /// Referrer Url - pub referrer: Option<ServoUrl>, - - /// Referrer Policy of the Request used to obtain Response - pub referrer_policy: Option<ReferrerPolicy>, - /// Performance information for navigation events - pub timing: Option<ResourceFetchTiming>, - /// True if the request comes from a redirection - pub redirected: bool, -} - -impl Metadata { - /// Metadata with defaults for everything optional. - pub fn default(url: ServoUrl) -> Self { - Metadata { - final_url: url, - location_url: None, - content_type: None, - charset: None, - headers: None, - // https://fetch.spec.whatwg.org/#concept-response-status-message - status: Some((200, b"".to_vec())), - https_state: HttpsState::None, - referrer: None, - referrer_policy: None, - timing: None, - redirected: false, - } - } - - /// Extract the parts of a Mime that we care about. - pub fn set_content_type(&mut self, content_type: Option<&Mime>) { - if self.headers.is_none() { - self.headers = Some(Serde(HeaderMap::new())); - } - - if let Some(mime) = content_type { - self.headers - .as_mut() - .unwrap() - .typed_insert(ContentType::from(mime.clone())); - if let Some(charset) = mime.get_param(mime::CHARSET) { - self.charset = Some(charset.to_string()); - } - self.content_type = Some(Serde(ContentType::from(mime.clone()))); - } - } - - /// Set the referrer policy associated with the loaded resource. - pub fn set_referrer_policy(&mut self, referrer_policy: Option<ReferrerPolicy>) { - if self.headers.is_none() { - self.headers = Some(Serde(HeaderMap::new())); - } - - self.referrer_policy = referrer_policy; - if let Some(referrer_policy) = referrer_policy { - self.headers - .as_mut() - .unwrap() - .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into()); - } - } -} - -/// The creator of a given cookie -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub enum CookieSource { - /// An HTTP API - HTTP, - /// A non-HTTP API - NonHTTP, -} - -/// Network errors that have to be exported out of the loaders -#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] -pub enum NetworkError { - /// Could be any of the internal errors, like unsupported scheme, connection errors, etc. - Internal(String), - LoadCancelled, - /// SSL validation error, to be converted to Resource::BadCertHTML in the HTML parser. - SslValidation(String, Vec<u8>), - /// Crash error, to be converted to Resource::Crash in the HTML parser. - Crash(String), -} - -impl NetworkError { - pub fn from_hyper_error(error: &HyperError, certificate: Option<Certificate>) -> Self { - let error_string = error.to_string(); - match certificate { - Some(certificate) => NetworkError::SslValidation(error_string, certificate.0), - _ => NetworkError::Internal(error_string), - } - } - - pub fn from_http_error(error: &HttpError) -> Self { - NetworkError::Internal(error.to_string()) - } -} - -/// Normalize `slice`, as defined by -/// [the Fetch Spec](https://fetch.spec.whatwg.org/#concept-header-value-normalize). -pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] { - const HTTP_WS_BYTES: &'static [u8] = b"\x09\x0A\x0D\x20"; - - loop { - match slice.split_first() { - Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder, - _ => break, - } - } - - loop { - match slice.split_last() { - Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder, - _ => break, - } - } - - slice -} - -pub fn http_percent_encode(bytes: &[u8]) -> String { - // This encode set is used for HTTP header values and is defined at - // https://tools.ietf.org/html/rfc5987#section-3.2 - const HTTP_VALUE: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS - .add(b' ') - .add(b'"') - .add(b'%') - .add(b'\'') - .add(b'(') - .add(b')') - .add(b'*') - .add(b',') - .add(b'/') - .add(b':') - .add(b';') - .add(b'<') - .add(b'-') - .add(b'>') - .add(b'?') - .add(b'[') - .add(b'\\') - .add(b']') - .add(b'{') - .add(b'}'); - - percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string() -} - -#[derive(Deserialize, Serialize)] -pub enum NetToCompositorMsg { - AddImage(ImageKey, ImageDescriptor, ImageData), - GenerateImageKey(IpcSender<ImageKey>), -} - -#[derive(Clone, Deserialize, Serialize)] -pub struct WebrenderIpcSender(IpcSender<NetToCompositorMsg>); - -impl WebrenderIpcSender { - pub fn new(sender: IpcSender<NetToCompositorMsg>) -> Self { - Self(sender) - } - - pub fn generate_image_key(&self) -> ImageKey { - let (sender, receiver) = ipc::channel().unwrap(); - self.0 - .send(NetToCompositorMsg::GenerateImageKey(sender)) - .expect("error sending image key generation"); - receiver.recv().expect("error receiving image key result") - } - - pub fn add_image(&self, key: ImageKey, descriptor: ImageDescriptor, data: ImageData) { - if let Err(e) = self - .0 - .send(NetToCompositorMsg::AddImage(key, descriptor, data)) - { - warn!("Error sending image update: {}", e); - } - } -} - -lazy_static! { - pub static ref PRIVILEGED_SECRET: u32 = servo_rand::ServoRng::new().next_u32(); -} diff --git a/components/net_traits/pub_domains.rs b/components/net_traits/pub_domains.rs deleted file mode 100644 index 1efb8faf56d..00000000000 --- a/components/net_traits/pub_domains.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* 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/. */ - -//! Implementation of public domain matching. -//! -//! The list is a file located on the `resources` folder and loaded once on first need. -//! -//! The list can be updated with `./mach update-pub-domains` from this source: -//! <https://publicsuffix.org/list/> -//! -//! This implementation is not strictly following the specification of the list. Wildcards are not -//! restricted to appear only in the leftmost position, but the current list has no such cases so -//! we don't need to make the code more complex for it. The `mach` update command makes sure that -//! those cases are not present. - -use std::collections::HashSet; -use std::iter::FromIterator; - -use embedder_traits::resources::{self, Resource}; -use lazy_static::lazy_static; -use servo_url::{Host, ImmutableOrigin, ServoUrl}; - -#[derive(Clone, Debug)] -pub struct PubDomainRules { - rules: HashSet<String>, - wildcards: HashSet<String>, - exceptions: HashSet<String>, -} - -lazy_static! { - static ref PUB_DOMAINS: PubDomainRules = load_pub_domains(); -} - -impl<'a> FromIterator<&'a str> for PubDomainRules { - fn from_iter<T>(iter: T) -> Self - where - T: IntoIterator<Item = &'a str>, - { - let mut result = PubDomainRules::new(); - for item in iter { - if item.starts_with("!") { - result.exceptions.insert(String::from(&item[1..])); - } else if item.starts_with("*.") { - result.wildcards.insert(String::from(&item[2..])); - } else { - result.rules.insert(String::from(item)); - } - } - result - } -} - -impl PubDomainRules { - pub fn new() -> PubDomainRules { - PubDomainRules { - rules: HashSet::new(), - wildcards: HashSet::new(), - exceptions: HashSet::new(), - } - } - pub fn parse(content: &str) -> PubDomainRules { - content - .lines() - .map(str::trim) - .filter(|s| !s.is_empty()) - .filter(|s| !s.starts_with("//")) - .collect() - } - fn suffix_pair<'a>(&self, domain: &'a str) -> (&'a str, &'a str) { - let domain = domain.trim_start_matches("."); - let mut suffix = domain; - let mut prev_suffix = domain; - for (index, _) in domain.match_indices(".") { - let next_suffix = &domain[index + 1..]; - if self.exceptions.contains(suffix) { - return (next_suffix, suffix); - } else if self.wildcards.contains(next_suffix) { - return (suffix, prev_suffix); - } else if self.rules.contains(suffix) { - return (suffix, prev_suffix); - } else { - prev_suffix = suffix; - suffix = next_suffix; - } - } - return (suffix, prev_suffix); - } - pub fn public_suffix<'a>(&self, domain: &'a str) -> &'a str { - let (public, _) = self.suffix_pair(domain); - public - } - pub fn registrable_suffix<'a>(&self, domain: &'a str) -> &'a str { - let (_, registrable) = self.suffix_pair(domain); - registrable - } - pub fn is_public_suffix(&self, domain: &str) -> bool { - // Speeded-up version of - // domain != "" && - // self.public_suffix(domain) == domain. - let domain = domain.trim_start_matches("."); - match domain.find(".") { - None => !domain.is_empty(), - Some(index) => { - !self.exceptions.contains(domain) && self.wildcards.contains(&domain[index + 1..]) || - self.rules.contains(domain) - }, - } - } - pub fn is_registrable_suffix(&self, domain: &str) -> bool { - // Speeded-up version of - // self.public_suffix(domain) != domain && - // self.registrable_suffix(domain) == domain. - let domain = domain.trim_start_matches("."); - match domain.find(".") { - None => false, - Some(index) => { - self.exceptions.contains(domain) || - !self.wildcards.contains(&domain[index + 1..]) && - !self.rules.contains(domain) && - self.is_public_suffix(&domain[index + 1..]) - }, - } - } -} - -fn load_pub_domains() -> PubDomainRules { - PubDomainRules::parse(&resources::read_string(Resource::DomainList)) -} - -pub fn pub_suffix(domain: &str) -> &str { - PUB_DOMAINS.public_suffix(domain) -} - -pub fn reg_suffix(domain: &str) -> &str { - PUB_DOMAINS.registrable_suffix(domain) -} - -pub fn is_pub_domain(domain: &str) -> bool { - PUB_DOMAINS.is_public_suffix(domain) -} - -pub fn is_reg_domain(domain: &str) -> bool { - PUB_DOMAINS.is_registrable_suffix(domain) -} - -/// The registered domain name (aka eTLD+1) for a URL. -/// Returns None if the URL has no host name. -/// Returns the registered suffix for the host name if it is a domain. -/// Leaves the host name alone if it is an IP address. -pub fn reg_host(url: &ServoUrl) -> Option<Host> { - match url.origin() { - ImmutableOrigin::Tuple(_, Host::Domain(domain), _) => { - Some(Host::Domain(String::from(reg_suffix(&*domain)))) - }, - ImmutableOrigin::Tuple(_, ip, _) => Some(ip), - ImmutableOrigin::Opaque(_) => None, - } -} diff --git a/components/net_traits/quality.rs b/components/net_traits/quality.rs deleted file mode 100644 index 095cd121bad..00000000000 --- a/components/net_traits/quality.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* 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/. */ - -//TODO(eijebong): Remove this once typed headers figure out quality -// This is copy pasted from the old hyper headers to avoid hardcoding everything -// (I would probably also make some silly mistakes while migrating...) - -use std::{fmt, str}; - -use http::header::HeaderValue; -use mime::Mime; - -/// A quality value, as specified in [RFC7231]. -/// -/// Quality values are decimal numbers between 0 and 1 (inclusive) with up to 3 fractional digits of precision. -/// -/// [RFC7231]: https://tools.ietf.org/html/rfc7231#section-5.3.1 -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Quality(u16); - -impl Quality { - /// Creates a quality value from a value between 0 and 1000 inclusive. - /// - /// This is semantically divided by 1000 to produce a value between 0 and 1. - /// - /// # Panics - /// - /// Panics if the value is greater than 1000. - pub fn from_u16(quality: u16) -> Quality { - assert!(quality <= 1000); - Quality(quality) - } -} - -/// A value paired with its "quality" as defined in [RFC7231]. -/// -/// Quality items are used in content negotiation headers such as `Accept` and `Accept-Encoding`. -/// -/// [RFC7231]: https://tools.ietf.org/html/rfc7231#section-5.3 -#[derive(Clone, Debug, PartialEq)] -pub struct QualityItem<T> { - pub item: T, - pub quality: Quality, -} - -impl<T> QualityItem<T> { - /// Creates a new quality item. - pub fn new(item: T, quality: Quality) -> QualityItem<T> { - QualityItem { item, quality } - } -} - -impl<T> fmt::Display for QualityItem<T> -where - T: fmt::Display, -{ - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.item, fmt)?; - match self.quality.0 { - 1000 => Ok(()), - 0 => fmt.write_str("; q=0"), - mut x => { - fmt.write_str("; q=0.")?; - let mut digits = *b"000"; - digits[2] = (x % 10) as u8 + b'0'; - x /= 10; - digits[1] = (x % 10) as u8 + b'0'; - x /= 10; - digits[0] = (x % 10) as u8 + b'0'; - - let s = str::from_utf8(&digits[..]).unwrap(); - fmt.write_str(s.trim_end_matches('0')) - }, - } - } -} - -pub fn quality_to_value(q: Vec<QualityItem<Mime>>) -> HeaderValue { - HeaderValue::from_str( - &q.iter() - .map(|q| q.to_string()) - .collect::<Vec<String>>() - .join(", "), - ) - .unwrap() -} diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs deleted file mode 100644 index 1f9f0abf986..00000000000 --- a/components/net_traits/request.rs +++ /dev/null @@ -1,749 +0,0 @@ -/* 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 std::sync::{Arc, Mutex}; - -use content_security_policy::{self as csp, CspList}; -use http::header::{HeaderName, AUTHORIZATION}; -use http::{HeaderMap, Method}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; -use malloc_size_of_derive::MallocSizeOf; -use mime::Mime; -use msg::constellation_msg::PipelineId; -use serde::{Deserialize, Serialize}; -use servo_url::{ImmutableOrigin, ServoUrl}; - -use crate::response::HttpsState; -use crate::{ReferrerPolicy, ResourceTimingType}; - -/// 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, - /// Contains the url that "client" would be resolved to. See - /// [https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer](https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer) - /// - /// If you are unsure you should probably use - /// [`GlobalScope::get_referrer`](https://doc.servo.org/script/dom/globalscope/struct.GlobalScope.html#method.get_referrer) - Client(ServoUrl), - 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, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -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, -} - -/// <https://fetch.spec.whatwg.org/#concept-body-source> -#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum BodySource { - Null, - Object, -} - -/// Messages used to implement <https://fetch.spec.whatwg.org/#concept-request-transmit-body> -/// which are sent from script to net. -#[derive(Debug, Deserialize, Serialize)] -pub enum BodyChunkResponse { - /// A chunk of bytes. - Chunk(Vec<u8>), - /// The body is done. - Done, - /// There was an error streaming the body, - /// terminate fetch. - Error, -} - -/// Messages used to implement <https://fetch.spec.whatwg.org/#concept-request-transmit-body> -/// which are sent from net to script -/// (with the exception of Done, which is sent from script to script). -#[derive(Debug, Deserialize, Serialize)] -pub enum BodyChunkRequest { - /// Connect a fetch in `net`, with a stream of bytes from `script`. - Connect(IpcSender<BodyChunkResponse>), - /// Re-extract a new stream from the source, following a redirect. - Extract(IpcReceiver<BodyChunkRequest>), - /// Ask for another chunk. - Chunk, - /// Signal the stream is done(sent from script to script). - Done, - /// Signal the stream has errored(sent from script to script). - Error, -} - -/// The net component's view into <https://fetch.spec.whatwg.org/#bodies> -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct RequestBody { - /// Net's channel to communicate with script re this body. - #[ignore_malloc_size_of = "Channels are hard"] - chan: Arc<Mutex<IpcSender<BodyChunkRequest>>>, - /// <https://fetch.spec.whatwg.org/#concept-body-source> - source: BodySource, - /// <https://fetch.spec.whatwg.org/#concept-body-total-bytes> - total_bytes: Option<usize>, -} - -impl RequestBody { - pub fn new( - chan: IpcSender<BodyChunkRequest>, - source: BodySource, - total_bytes: Option<usize>, - ) -> Self { - RequestBody { - chan: Arc::new(Mutex::new(chan)), - source, - total_bytes, - } - } - - /// Step 12 of https://fetch.spec.whatwg.org/#concept-http-redirect-fetch - pub fn extract_source(&mut self) { - match self.source { - BodySource::Null => panic!("Null sources should never be re-directed."), - BodySource::Object => { - let (chan, port) = ipc::channel().unwrap(); - let mut selfchan = self.chan.lock().unwrap(); - let _ = selfchan.send(BodyChunkRequest::Extract(port)); - *selfchan = chan; - }, - } - } - - pub fn take_stream(&self) -> Arc<Mutex<IpcSender<BodyChunkRequest>>> { - self.chan.clone() - } - - pub fn source_is_null(&self) -> bool { - self.source == BodySource::Null - } - - pub fn len(&self) -> Option<usize> { - self.total_bytes.clone() - } -} - -#[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<RequestBody>, - 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: 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, - pub https_state: HttpsState, - pub response_tainting: ResponseTainting, - /// Servo internal: if crash details are present, trigger a crash error page with these details. - pub crash: Option<String>, -} - -impl RequestBuilder { - pub fn new(url: ServoUrl, referrer: Referrer) -> 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::CredentialsSameOrigin, - use_url_credentials: false, - origin: ImmutableOrigin::new_opaque(), - referrer: referrer, - 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, - https_state: HttpsState::None, - response_tainting: ResponseTainting::Basic, - crash: 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<RequestBody>) -> 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_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 https_state(mut self, https_state: HttpsState) -> RequestBuilder { - self.https_state = https_state; - self - } - - pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder { - self.response_tainting = response_tainting; - self - } - - pub fn crash(mut self, crash: Option<String>) -> Self { - self.crash = crash; - self - } - - pub fn build(self) -> Request { - let mut request = Request::new( - self.url.clone(), - Some(Origin::Origin(self.origin)), - self.referrer, - self.pipeline_id, - self.https_state, - ); - 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_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.response_tainting = self.response_tainting; - request.crash = self.crash; - 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<RequestBody>, - // 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>, - pub https_state: HttpsState, - /// Servo internal: if crash details are present, trigger a crash error page with these details. - pub crash: Option<String>, -} - -impl Request { - pub fn new( - url: ServoUrl, - origin: Option<Origin>, - referrer: Referrer, - pipeline_id: Option<PipelineId>, - https_state: HttpsState, - ) -> 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, - referrer_policy: None, - pipeline_id: pipeline_id, - synchronous: false, - mode: RequestMode::NoCors, - use_cors_preflight: false, - credentials_mode: CredentialsMode::CredentialsSameOrigin, - 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_state: https_state, - crash: 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 => None, - Referrer::Client(ref url) => Some(url), - Referrer::ReferrerUrl(ref url) => Some(url), - } - } -} - -// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte -// TODO: values in the control-code range are being quietly stripped out by -// HeaderMap and never reach this function to be loudly rejected! -fn is_cors_unsafe_request_header_byte(value: &u8) -> bool { - match value { - 0x00..=0x08 | - 0x10..=0x19 | - 0x22 | - 0x28 | - 0x29 | - 0x3A | - 0x3C | - 0x3E | - 0x3F | - 0x40 | - 0x5B | - 0x5C | - 0x5D | - 0x7B | - 0x7D | - 0x7F => true, - _ => false, - } -} - -// https://fetch.spec.whatwg.org/#cors-safelisted-request-header -// subclause `accept` -fn is_cors_safelisted_request_accept(value: &[u8]) -> bool { - !(value.iter().any(is_cors_unsafe_request_header_byte)) -} - -// https://fetch.spec.whatwg.org/#cors-safelisted-request-header -// subclauses `accept-language`, `content-language` -fn is_cors_safelisted_language(value: &[u8]) -> bool { - value.iter().all(|&x| match x { - 0x30..=0x39 | - 0x41..=0x5A | - 0x61..=0x7A | - 0x20 | - 0x2A | - 0x2C | - 0x2D | - 0x2E | - 0x3B | - 0x3D => true, - _ => false, - }) -} - -// https://fetch.spec.whatwg.org/#cors-safelisted-request-header -// subclause `content-type` -fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool { - // step 1 - if value.iter().any(is_cors_unsafe_request_header_byte) { - return false; - } - // step 2 - let value_string = if let Ok(s) = std::str::from_utf8(value) { - s - } else { - return false; - }; - let value_mime_result: Result<Mime, _> = value_string.parse(); - match value_mime_result { - Err(_) => false, // step 3 - Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) { - (mime::APPLICATION, mime::WWW_FORM_URLENCODED) | - (mime::MULTIPART, mime::FORM_DATA) | - (mime::TEXT, mime::PLAIN) => true, - _ => false, // step 4 - }, - } -} - -// TODO: "DPR", "Downlink", "Save-Data", "Viewport-Width", "Width": -// ... once parsed, the value should not be failure. -// https://fetch.spec.whatwg.org/#cors-safelisted-request-header -pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>( - name: &N, - value: &V, -) -> bool { - let name: &str = name.as_ref(); - let value: &[u8] = value.as_ref(); - if value.len() > 128 { - return false; - } - match name { - "accept" => is_cors_safelisted_request_accept(value), - "accept-language" | "content-language" => is_cors_safelisted_language(value), - "content-type" => is_cors_safelisted_request_content_type(value), - _ => false, - } -} - -/// <https://fetch.spec.whatwg.org/#cors-safelisted-method> -pub fn is_cors_safelisted_method(m: &Method) -> bool { - match *m { - Method::GET | Method::HEAD | Method::POST => true, - _ => false, - } -} - -/// <https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name> -pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool { - name == AUTHORIZATION -} - -/// <https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names> -pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> { - // Step 1 - let mut unsafe_names: Vec<&HeaderName> = vec![]; - // Step 2 - let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![]; - // Step 3 - let mut safelist_value_size = 0; - - // Step 4 - for (name, value) in headers.iter() { - if !is_cors_safelisted_request_header(&name, &value) { - unsafe_names.push(name); - } else { - potentillay_unsafe_names.push(name); - safelist_value_size += value.as_ref().len(); - } - } - - // Step 5 - if safelist_value_size > 1024 { - unsafe_names.extend_from_slice(&potentillay_unsafe_names); - } - - // Step 6 - return convert_header_names_to_sorted_lowercase_set(unsafe_names); -} - -/// <https://fetch.spec.whatwg.org/#ref-for-convert-header-names-to-a-sorted-lowercase-set> -pub fn convert_header_names_to_sorted_lowercase_set( - header_names: Vec<&HeaderName>, -) -> Vec<HeaderName> { - // HeaderName does not implement the needed traits to use a BTreeSet - // So create a new Vec, sort, then dedup - let mut ordered_set = header_names.to_vec(); - ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap()); - ordered_set.dedup(); - return ordered_set.into_iter().cloned().collect(); -} diff --git a/components/net_traits/response.rs b/components/net_traits/response.rs deleted file mode 100644 index 552c0057c50..00000000000 --- a/components/net_traits/response.rs +++ /dev/null @@ -1,364 +0,0 @@ -/* 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/. */ - -//! The [Response](https://fetch.spec.whatwg.org/#responses) object -//! resulting from a [fetch operation](https://fetch.spec.whatwg.org/#concept-fetch) -use std::sync::atomic::AtomicBool; -use std::sync::Mutex; - -use headers::{ContentType, HeaderMapExt}; -use http::{HeaderMap, StatusCode}; -use hyper_serde::Serde; -use malloc_size_of_derive::MallocSizeOf; -use serde::{Deserialize, Serialize}; -use servo_arc::Arc; -use servo_url::ServoUrl; - -use crate::{ - FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, - ResourceTimingType, -}; - -/// [Response type](https://fetch.spec.whatwg.org/#concept-response-type) -#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum ResponseType { - Basic, - Cors, - Default, - Error(NetworkError), - Opaque, - OpaqueRedirect, -} - -/// [Response termination reason](https://fetch.spec.whatwg.org/#concept-response-termination-reason) -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum TerminationReason { - EndUserAbort, - Fatal, - Timeout, -} - -/// The response body can still be pushed to after fetch -/// This provides a way to store unfinished response bodies -#[derive(Clone, Debug, MallocSizeOf, PartialEq)] -pub enum ResponseBody { - Empty, // XXXManishearth is this necessary, or is Done(vec![]) enough? - Receiving(Vec<u8>), - Done(Vec<u8>), -} - -impl ResponseBody { - pub fn is_done(&self) -> bool { - match *self { - ResponseBody::Done(..) => true, - ResponseBody::Empty | ResponseBody::Receiving(..) => false, - } - } -} - -/// [Cache state](https://fetch.spec.whatwg.org/#concept-response-cache-state) -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum CacheState { - None, - Local, - Validated, - Partial, -} - -/// [Https state](https://fetch.spec.whatwg.org/#concept-response-https-state) -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum HttpsState { - None, - Deprecated, - Modern, -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct ResponseInit { - 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 status_code: u16, - pub referrer: Option<ServoUrl>, - pub location_url: Option<Result<ServoUrl, String>>, -} - -/// A [Response](https://fetch.spec.whatwg.org/#concept-response) as defined by the Fetch spec -#[derive(Clone, Debug, MallocSizeOf)] -pub struct Response { - pub response_type: ResponseType, - pub termination_reason: Option<TerminationReason>, - url: Option<ServoUrl>, - pub url_list: Vec<ServoUrl>, - /// `None` can be considered a StatusCode of `0`. - #[ignore_malloc_size_of = "Defined in hyper"] - pub status: Option<(StatusCode, String)>, - pub raw_status: Option<(u16, Vec<u8>)>, - #[ignore_malloc_size_of = "Defined in hyper"] - pub headers: HeaderMap, - #[ignore_malloc_size_of = "Mutex heap size undefined"] - pub body: Arc<Mutex<ResponseBody>>, - pub cache_state: CacheState, - pub https_state: HttpsState, - pub referrer: Option<ServoUrl>, - pub referrer_policy: Option<ReferrerPolicy>, - /// [CORS-exposed header-name list](https://fetch.spec.whatwg.org/#concept-response-cors-exposed-header-name-list) - pub cors_exposed_header_name_list: Vec<String>, - /// [Location URL](https://fetch.spec.whatwg.org/#concept-response-location-url) - pub location_url: Option<Result<ServoUrl, String>>, - /// [Internal response](https://fetch.spec.whatwg.org/#concept-internal-response), only used if the Response - /// is a filtered response - pub internal_response: Option<Box<Response>>, - /// whether or not to try to return the internal_response when asked for actual_response - pub return_internal: bool, - /// https://fetch.spec.whatwg.org/#concept-response-aborted - #[ignore_malloc_size_of = "AtomicBool heap size undefined"] - pub aborted: Arc<AtomicBool>, - /// track network metrics - #[ignore_malloc_size_of = "Mutex heap size undefined"] - pub resource_timing: Arc<Mutex<ResourceFetchTiming>>, -} - -impl Response { - pub fn new(url: ServoUrl, resource_timing: ResourceFetchTiming) -> Response { - Response { - response_type: ResponseType::Default, - termination_reason: None, - url: Some(url), - url_list: vec![], - status: Some((StatusCode::OK, "".to_string())), - raw_status: Some((200, b"".to_vec())), - headers: HeaderMap::new(), - body: Arc::new(Mutex::new(ResponseBody::Empty)), - cache_state: CacheState::None, - https_state: HttpsState::None, - referrer: None, - referrer_policy: None, - cors_exposed_header_name_list: vec![], - location_url: None, - internal_response: None, - return_internal: true, - aborted: Arc::new(AtomicBool::new(false)), - resource_timing: Arc::new(Mutex::new(resource_timing)), - } - } - - pub fn from_init(init: ResponseInit, resource_timing_type: ResourceTimingType) -> Response { - let mut res = Response::new(init.url, ResourceFetchTiming::new(resource_timing_type)); - res.location_url = init.location_url; - res.headers = init.headers; - res.referrer = init.referrer; - res.status = StatusCode::from_u16(init.status_code) - .map(|s| (s, s.to_string())) - .ok(); - res - } - - pub fn network_error(e: NetworkError) -> Response { - Response { - response_type: ResponseType::Error(e), - termination_reason: None, - url: None, - url_list: vec![], - status: None, - raw_status: None, - headers: HeaderMap::new(), - body: Arc::new(Mutex::new(ResponseBody::Empty)), - cache_state: CacheState::None, - https_state: HttpsState::None, - referrer: None, - referrer_policy: None, - cors_exposed_header_name_list: vec![], - location_url: None, - internal_response: None, - return_internal: true, - aborted: Arc::new(AtomicBool::new(false)), - resource_timing: Arc::new(Mutex::new(ResourceFetchTiming::new( - ResourceTimingType::Error, - ))), - } - } - - pub fn url(&self) -> Option<&ServoUrl> { - self.url.as_ref() - } - - pub fn is_network_error(&self) -> bool { - match self.response_type { - ResponseType::Error(..) => true, - _ => false, - } - } - - pub fn get_network_error(&self) -> Option<&NetworkError> { - match self.response_type { - ResponseType::Error(ref e) => Some(e), - _ => None, - } - } - - pub fn actual_response(&self) -> &Response { - if self.return_internal && self.internal_response.is_some() { - &**self.internal_response.as_ref().unwrap() - } else { - self - } - } - - pub fn actual_response_mut(&mut self) -> &mut Response { - if self.return_internal && self.internal_response.is_some() { - &mut **self.internal_response.as_mut().unwrap() - } else { - self - } - } - - pub fn to_actual(self) -> Response { - if self.return_internal && self.internal_response.is_some() { - *self.internal_response.unwrap() - } else { - self - } - } - - pub fn get_resource_timing(&self) -> Arc<Mutex<ResourceFetchTiming>> { - Arc::clone(&self.resource_timing) - } - - /// Convert to a filtered response, of type `filter_type`. - /// Do not use with type Error or Default - #[rustfmt::skip] - pub fn to_filtered(self, filter_type: ResponseType) -> Response { - match filter_type { - ResponseType::Default | - ResponseType::Error(..) => panic!(), - _ => (), - } - - let old_response = self.to_actual(); - - if let ResponseType::Error(e) = old_response.response_type { - return Response::network_error(e); - } - - let old_headers = old_response.headers.clone(); - let exposed_headers = old_response.cors_exposed_header_name_list.clone(); - let mut response = old_response.clone(); - response.internal_response = Some(Box::new(old_response)); - response.response_type = filter_type; - - match response.response_type { - ResponseType::Default | - ResponseType::Error(..) => unreachable!(), - - ResponseType::Basic => { - let headers = old_headers.iter().filter(|(name, _)| { - match &*name.as_str().to_ascii_lowercase() { - "set-cookie" | "set-cookie2" => false, - _ => true - } - }).map(|(n, v)| (n.clone(), v.clone())).collect(); - response.headers = headers; - }, - - ResponseType::Cors => { - let headers = old_headers.iter().filter(|(name, _)| { - match &*name.as_str().to_ascii_lowercase() { - "cache-control" | "content-language" | "content-type" | - "expires" | "last-modified" | "pragma" => true, - "set-cookie" | "set-cookie2" => false, - header => { - exposed_headers.iter().any(|h| *header == h.as_str().to_ascii_lowercase()) - } - } - }).map(|(n, v)| (n.clone(), v.clone())).collect(); - response.headers = headers; - }, - - ResponseType::Opaque => { - response.url_list = vec![]; - response.url = None; - response.headers = HeaderMap::new(); - response.status = None; - response.body = Arc::new(Mutex::new(ResponseBody::Empty)); - response.cache_state = CacheState::None; - }, - - ResponseType::OpaqueRedirect => { - response.headers = HeaderMap::new(); - response.status = None; - response.body = Arc::new(Mutex::new(ResponseBody::Empty)); - response.cache_state = CacheState::None; - }, - } - - response - } - - pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> { - fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata { - let mut metadata = Metadata::default(url.clone()); - metadata.set_content_type( - response - .headers - .typed_get::<ContentType>() - .map(|v| v.into()) - .as_ref(), - ); - metadata.location_url = response.location_url.clone(); - metadata.headers = Some(Serde(response.headers.clone())); - metadata.status = response.raw_status.clone(); - metadata.https_state = response.https_state; - metadata.referrer = response.referrer.clone(); - metadata.referrer_policy = response.referrer_policy.clone(); - metadata.redirected = response.actual_response().url_list.len() > 1; - metadata - } - - if let Some(error) = self.get_network_error() { - return Err(error.clone()); - } - - let metadata = self.url.as_ref().map(|url| init_metadata(self, url)); - - if let Some(ref response) = self.internal_response { - match response.url { - Some(ref url) => { - let unsafe_metadata = init_metadata(response, url); - - match self.response_type { - ResponseType::Basic => Ok(FetchMetadata::Filtered { - filtered: FilteredMetadata::Basic(metadata.unwrap()), - unsafe_: unsafe_metadata, - }), - ResponseType::Cors => Ok(FetchMetadata::Filtered { - filtered: FilteredMetadata::Cors(metadata.unwrap()), - unsafe_: unsafe_metadata, - }), - ResponseType::Default => unreachable!(), - ResponseType::Error(ref network_err) => Err(network_err.clone()), - ResponseType::Opaque => Ok(FetchMetadata::Filtered { - filtered: FilteredMetadata::Opaque, - unsafe_: unsafe_metadata, - }), - ResponseType::OpaqueRedirect => Ok(FetchMetadata::Filtered { - filtered: FilteredMetadata::OpaqueRedirect(url.clone()), - unsafe_: unsafe_metadata, - }), - } - }, - None => Err(NetworkError::Internal( - "No url found in unsafe response".to_owned(), - )), - } - } else { - assert_eq!(self.response_type, ResponseType::Default); - Ok(FetchMetadata::Unfiltered(metadata.unwrap())) - } - } -} diff --git a/components/net_traits/storage_thread.rs b/components/net_traits/storage_thread.rs deleted file mode 100644 index 0253603016e..00000000000 --- a/components/net_traits/storage_thread.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* 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 ipc_channel::ipc::IpcSender; -use malloc_size_of_derive::MallocSizeOf; -use serde::{Deserialize, Serialize}; -use servo_url::ServoUrl; - -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum StorageType { - Session, - Local, -} - -/// Request operations on the storage data associated with a particular url -#[derive(Debug, Deserialize, Serialize)] -pub enum StorageThreadMsg { - /// gets the number of key/value pairs present in the associated storage data - Length(IpcSender<usize>, ServoUrl, StorageType), - - /// gets the name of the key at the specified index in the associated storage data - Key(IpcSender<Option<String>>, ServoUrl, StorageType, u32), - - /// Gets the available keys in the associated storage data - Keys(IpcSender<Vec<String>>, ServoUrl, StorageType), - - /// gets the value associated with the given key in the associated storage data - GetItem(IpcSender<Option<String>>, ServoUrl, StorageType, String), - - /// sets the value of the given key in the associated storage data - SetItem( - IpcSender<Result<(bool, Option<String>), ()>>, - ServoUrl, - StorageType, - String, - String, - ), - - /// removes the key/value pair for the given key in the associated storage data - RemoveItem(IpcSender<Option<String>>, ServoUrl, StorageType, String), - - /// clears the associated storage data by removing all the key/value pairs - Clear(IpcSender<bool>, ServoUrl, StorageType), - - /// send a reply when done cleaning up thread resources and then shut it down - Exit(IpcSender<()>), -} diff --git a/components/net_traits/tests/image.rs b/components/net_traits/tests/image.rs deleted file mode 100644 index a4963702b57..00000000000 --- a/components/net_traits/tests/image.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* 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 net_traits::image::base::detect_image_format; - -#[test] -fn test_supported_images() { - let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a']; - let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a']; - let jpeg = [0xff, 0xd8, 0xff]; - let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; - let webp = [ - b'R', b'I', b'F', b'F', 0x01, 0x02, 0x03, 0x04, b'W', b'E', b'B', b'P', b'V', b'P', - ]; - let bmp = [0x42, 0x4D]; - let ico = [0x00, 0x00, 0x01, 0x00]; - let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05]; - - assert!(detect_image_format(&gif1).is_ok()); - assert!(detect_image_format(&gif2).is_ok()); - assert!(detect_image_format(&jpeg).is_ok()); - assert!(detect_image_format(&png).is_ok()); - assert!(detect_image_format(&webp).is_ok()); - assert!(detect_image_format(&bmp).is_ok()); - assert!(detect_image_format(&ico).is_ok()); - assert!(detect_image_format(&junk_format).is_err()); -} diff --git a/components/net_traits/tests/lib.rs b/components/net_traits/tests/lib.rs deleted file mode 100644 index 290ca902935..00000000000 --- a/components/net_traits/tests/lib.rs +++ /dev/null @@ -1,212 +0,0 @@ -/* 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 net_traits::{ResourceAttribute, ResourceFetchTiming, ResourceTimeValue, ResourceTimingType}; - -#[test] -fn test_set_start_time_to_fetch_start_if_nonzero_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.fetch_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - assert!( - resource_timing.fetch_start > 0, - "`fetch_start` should have a positive value" - ); - - // verify that setting `start_time` to `fetch_start` succeeds - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); - assert_eq!( - resource_timing.start_time, resource_timing.fetch_start, - "`start_time` should equal `fetch_start`" - ); -} - -#[test] -fn test_set_start_time_to_fetch_start_if_zero_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.start_time = 1; - assert!( - resource_timing.start_time > 0, - "`start_time` should have a positive value" - ); - assert_eq!( - resource_timing.fetch_start, 0, - "`fetch_start` should be zero" - ); - - // verify that setting `start_time` to `fetch_start` succeeds even when `fetch_start` == zero - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); - assert_eq!( - resource_timing.start_time, resource_timing.fetch_start, - "`start_time` should equal `fetch_start`" - ); -} - -#[test] -fn test_set_start_time_to_fetch_start_if_nonzero_no_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.mark_timing_check_failed(); - resource_timing.fetch_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - assert!( - resource_timing.fetch_start > 0, - "`fetch_start` should have a positive value" - ); - - // verify that setting `start_time` to `fetch_start` succeeds even when TAO check failed - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); - assert_eq!( - resource_timing.start_time, resource_timing.fetch_start, - "`start_time` should equal `fetch_start`" - ); -} - -#[test] -fn test_set_start_time_to_fetch_start_if_zero_no_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.mark_timing_check_failed(); - resource_timing.start_time = 1; - assert!( - resource_timing.start_time > 0, - "`start_time` should have a positive value" - ); - assert_eq!( - resource_timing.fetch_start, 0, - "`fetch_start` should be zero" - ); - - // verify that setting `start_time` to `fetch_start` succeeds even when `fetch_start`==0 and no TAO - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); - assert_eq!( - resource_timing.start_time, resource_timing.fetch_start, - "`start_time` should equal `fetch_start`" - ); -} - -#[test] -fn test_set_start_time_to_redirect_start_if_nonzero_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.redirect_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - assert!( - resource_timing.redirect_start > 0, - "`redirect_start` should have a positive value" - ); - - // verify that setting `start_time` to `redirect_start` succeeds for nonzero `redirect_start`, TAO pass - resource_timing.set_attribute(ResourceAttribute::StartTime( - ResourceTimeValue::RedirectStart, - )); - assert_eq!( - resource_timing.start_time, resource_timing.redirect_start, - "`start_time` should equal `redirect_start`" - ); -} - -#[test] -fn test_not_set_start_time_to_redirect_start_if_zero_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.start_time = 1; - assert!( - resource_timing.start_time > 0, - "`start_time` should have a positive value" - ); - assert_eq!( - resource_timing.redirect_start, 0, - "`redirect_start` should be zero" - ); - - // verify that setting `start_time` to `redirect_start` fails if `redirect_start` == 0 - resource_timing.set_attribute(ResourceAttribute::StartTime( - ResourceTimeValue::RedirectStart, - )); - assert_ne!( - resource_timing.start_time, resource_timing.redirect_start, - "`start_time` should *not* equal `redirect_start`" - ); -} - -#[test] -fn test_not_set_start_time_to_redirect_start_if_nonzero_no_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.mark_timing_check_failed(); - // Note: properly-behaved redirect_start should never be nonzero once TAO check has failed - resource_timing.redirect_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - assert!( - resource_timing.redirect_start > 0, - "`redirect_start` should have a positive value" - ); - - // verify that setting `start_time` to `redirect_start` fails if TAO check fails - resource_timing.set_attribute(ResourceAttribute::StartTime( - ResourceTimeValue::RedirectStart, - )); - assert_ne!( - resource_timing.start_time, resource_timing.redirect_start, - "`start_time` should *not* equal `redirect_start`" - ); -} - -#[test] -fn test_not_set_start_time_to_redirect_start_if_zero_no_tao() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.mark_timing_check_failed(); - resource_timing.start_time = 1; - assert!( - resource_timing.start_time > 0, - "`start_time` should have a positive value" - ); - assert_eq!( - resource_timing.redirect_start, 0, - "`redirect_start` should be zero" - ); - - // verify that setting `start_time` to `redirect_start` fails if `redirect_start`==0 and no TAO - resource_timing.set_attribute(ResourceAttribute::StartTime( - ResourceTimeValue::RedirectStart, - )); - assert_ne!( - resource_timing.start_time, resource_timing.redirect_start, - "`start_time` should *not* equal `redirect_start`" - ); -} - -#[test] -fn test_set_start_time() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - - // verify setting `start_time` to current time succeeds - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::Now)); - assert!(resource_timing.start_time > 0, "failed to set `start_time`"); -} -#[test] -fn test_reset_start_time() { - let mut resource_timing: ResourceFetchTiming = - ResourceFetchTiming::new(ResourceTimingType::Resource); - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - - resource_timing.start_time = 1; - assert!( - resource_timing.start_time > 0, - "`start_time` should have a positive value" - ); - - // verify resetting `start_time` (to zero) succeeds - resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::Zero)); - assert_eq!( - resource_timing.start_time, 0, - "failed to reset `start_time`" - ); -} diff --git a/components/net_traits/tests/pub_domains.rs b/components/net_traits/tests/pub_domains.rs deleted file mode 100644 index ea58e7650e3..00000000000 --- a/components/net_traits/tests/pub_domains.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* 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 net_traits::pub_domains::{is_pub_domain, is_reg_domain, pub_suffix, reg_suffix}; - -// These tests may need to be updated if the PSL changes. - -#[test] -fn test_is_pub_domain_plain() { - assert!(is_pub_domain("com")); - assert!(is_pub_domain(".org")); - assert!(is_pub_domain("za.org")); - assert!(is_pub_domain("xn--od0alg.hk")); - assert!(is_pub_domain("xn--krdsherad-m8a.no")); -} - -#[test] -fn test_is_pub_domain_wildcard() { - assert!(is_pub_domain("hello.bd")); - assert!(is_pub_domain("world.jm")); - assert!(is_pub_domain("toto.kobe.jp")); -} - -#[test] -fn test_is_pub_domain_exception() { - assert_eq!(is_pub_domain("www.ck"), false); - assert_eq!(is_pub_domain("city.kawasaki.jp"), false); - assert_eq!(is_pub_domain("city.nagoya.jp"), false); - assert_eq!(is_pub_domain("teledata.mz"), false); -} - -#[test] -fn test_is_pub_domain_not() { - assert_eq!(is_pub_domain(""), false); - assert_eq!(is_pub_domain("."), false); - assert_eq!(is_pub_domain("..."), false); - assert_eq!(is_pub_domain(".servo.org"), false); - assert_eq!(is_pub_domain("www.mozilla.org"), false); - assert_eq!(is_pub_domain("publicsuffix.org"), false); - assert_eq!(is_pub_domain("hello.world.jm"), false); - assert_eq!(is_pub_domain("toto.toto.kobe.jp"), false); -} - -#[test] -fn test_is_pub_domain() { - assert!(!is_pub_domain("city.yokohama.jp")); - assert!(!is_pub_domain("foo.bar.baz.yokohama.jp")); - assert!(!is_pub_domain("foo.bar.city.yokohama.jp")); - assert!(!is_pub_domain("foo.bar.com")); - assert!(!is_pub_domain("foo.bar.tokyo.jp")); - assert!(!is_pub_domain("foo.bar.yokohama.jp")); - assert!(!is_pub_domain("foo.city.yokohama.jp")); - assert!(!is_pub_domain("foo.com")); - assert!(!is_pub_domain("foo.tokyo.jp")); - assert!(!is_pub_domain("yokohama.jp")); - assert!(is_pub_domain("com")); - assert!(is_pub_domain("foo.yokohama.jp")); - assert!(is_pub_domain("jp")); - assert!(is_pub_domain("tokyo.jp")); -} - -#[test] -fn test_is_reg_domain() { - assert!(!is_reg_domain("com")); - assert!(!is_reg_domain("foo.bar.baz.yokohama.jp")); - assert!(!is_reg_domain("foo.bar.com")); - assert!(!is_reg_domain("foo.bar.tokyo.jp")); - assert!(!is_reg_domain("foo.city.yokohama.jp")); - assert!(!is_reg_domain("foo.yokohama.jp")); - assert!(!is_reg_domain("jp")); - assert!(!is_reg_domain("tokyo.jp")); - assert!(is_reg_domain("city.yokohama.jp")); - assert!(is_reg_domain("foo.bar.yokohama.jp")); - assert!(is_reg_domain("foo.com")); - assert!(is_reg_domain("foo.tokyo.jp")); - assert!(is_reg_domain("yokohama.jp")); -} - -#[test] -fn test_pub_suffix() { - assert_eq!(pub_suffix("city.yokohama.jp"), "yokohama.jp"); - assert_eq!(pub_suffix("com"), "com"); - assert_eq!(pub_suffix("foo.bar.baz.yokohama.jp"), "baz.yokohama.jp"); - assert_eq!(pub_suffix("foo.bar.com"), "com"); - assert_eq!(pub_suffix("foo.bar.tokyo.jp"), "tokyo.jp"); - assert_eq!(pub_suffix("foo.bar.yokohama.jp"), "bar.yokohama.jp"); - assert_eq!(pub_suffix("foo.city.yokohama.jp"), "yokohama.jp"); - assert_eq!(pub_suffix("foo.com"), "com"); - assert_eq!(pub_suffix("foo.tokyo.jp"), "tokyo.jp"); - assert_eq!(pub_suffix("foo.yokohama.jp"), "foo.yokohama.jp"); - assert_eq!(pub_suffix("jp"), "jp"); - assert_eq!(pub_suffix("tokyo.jp"), "tokyo.jp"); - assert_eq!(pub_suffix("yokohama.jp"), "jp"); -} - -#[test] -fn test_reg_suffix() { - assert_eq!(reg_suffix("city.yokohama.jp"), "city.yokohama.jp"); - assert_eq!(reg_suffix("com"), "com"); - assert_eq!(reg_suffix("foo.bar.baz.yokohama.jp"), "bar.baz.yokohama.jp"); - assert_eq!(reg_suffix("foo.bar.com"), "bar.com"); - assert_eq!(reg_suffix("foo.bar.tokyo.jp"), "bar.tokyo.jp"); - assert_eq!(reg_suffix("foo.bar.yokohama.jp"), "foo.bar.yokohama.jp"); - assert_eq!(reg_suffix("foo.city.yokohama.jp"), "city.yokohama.jp"); - assert_eq!(reg_suffix("foo.com"), "foo.com"); - assert_eq!(reg_suffix("foo.tokyo.jp"), "foo.tokyo.jp"); - assert_eq!(reg_suffix("foo.yokohama.jp"), "foo.yokohama.jp"); - assert_eq!(reg_suffix("jp"), "jp"); - assert_eq!(reg_suffix("tokyo.jp"), "tokyo.jp"); - assert_eq!(reg_suffix("yokohama.jp"), "yokohama.jp"); -} - -#[test] -fn test_weirdness() { - // These are weird results, but AFAICT they are spec-compliant. - assert_ne!( - pub_suffix("city.yokohama.jp"), - pub_suffix(pub_suffix("city.yokohama.jp")) - ); - assert!(!is_pub_domain(pub_suffix("city.yokohama.jp"))); -} diff --git a/components/net_traits/tests/whitespace.rs b/components/net_traits/tests/whitespace.rs deleted file mode 100644 index d1e6b7a2ac8..00000000000 --- a/components/net_traits/tests/whitespace.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* 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/. */ - -#[test] -fn test_trim_http_whitespace() { - fn test_trim(in_: &[u8], out: &[u8]) { - let b = net_traits::trim_http_whitespace(in_); - assert_eq!(b, out); - } - - test_trim(b"", b""); - - test_trim(b" ", b""); - test_trim(b"a", b"a"); - test_trim(b" a", b"a"); - test_trim(b"a ", b"a"); - test_trim(b" a ", b"a"); - - test_trim(b"\t", b""); - test_trim(b"a", b"a"); - test_trim(b"\ta", b"a"); - test_trim(b"a\t", b"a"); - test_trim(b"\ta\t", b"a"); -} |