diff options
author | webbeef <me@webbeef.org> | 2024-08-21 21:11:16 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-22 04:11:16 +0000 |
commit | 663a92a5df39f5daef091624b6e29c228dcecbc3 (patch) | |
tree | dbcf67d033768d91ed585623c816410f0267d34d /components/net | |
parent | 562d32c0519d58052cea681a696546fd4818bd3a (diff) | |
download | servo-663a92a5df39f5daef091624b6e29c228dcecbc3.tar.gz servo-663a92a5df39f5daef091624b6e29c228dcecbc3.zip |
make protocol handlers registrable (#33104)
Signed-off-by: webbeef <me@webbeef.org>
Diffstat (limited to 'components/net')
-rw-r--r-- | components/net/fetch/methods.rs | 213 | ||||
-rw-r--r-- | components/net/lib.rs | 2 | ||||
-rw-r--r-- | components/net/protocols/blob.rs | 91 | ||||
-rw-r--r-- | components/net/protocols/data.rs (renamed from components/net/data_loader.rs) | 46 | ||||
-rw-r--r-- | components/net/protocols/file.rs | 109 | ||||
-rw-r--r-- | components/net/protocols/mod.rs | 119 | ||||
-rw-r--r-- | components/net/resource_thread.rs | 62 | ||||
-rw-r--r-- | components/net/tests/fetch.rs | 4 | ||||
-rw-r--r-- | components/net/tests/main.rs | 2 | ||||
-rw-r--r-- | components/net/tests/resource_thread.rs | 2 |
10 files changed, 431 insertions, 219 deletions
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 369644a4c7c..947afbf7060 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -3,25 +3,21 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; -use std::fs::File; -use std::io::{self, BufReader, Seek, SeekFrom}; -use std::ops::Bound; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; -use std::{mem, str}; +use std::{io, mem, str}; use base64::engine::general_purpose; use base64::Engine as _; use content_security_policy as csp; use crossbeam_channel::Sender; use devtools_traits::DevtoolsControlMsg; -use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt, Range}; +use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt}; use http::header::{self, HeaderMap, HeaderName}; use http::{Method, StatusCode}; use ipc_channel::ipc::{self, IpcReceiver}; -use log::{debug, warn}; +use log::warn; use mime::{self, Mime}; -use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreError}; use net_traits::filemanager_thread::{FileTokenCheck, RelativePos}; use net_traits::request::{ is_cors_safelisted_method, is_cors_safelisted_request_header, BodyChunkRequest, @@ -37,19 +33,16 @@ use rustls::Certificate; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; -use tokio::sync::mpsc::{ - unbounded_channel, UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender, -}; +use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender}; -use crate::data_loader::decode; use crate::fetch::cors_cache::CorsCache; use crate::fetch::headers::determine_nosniff; -use crate::filemanager_thread::{FileManager, FILE_CHUNK_SIZE}; +use crate::filemanager_thread::FileManager; use crate::http_loader::{ determine_requests_referrer, http_fetch, set_default_accept, set_default_accept_language, HttpState, }; -use crate::local_directory_listing; +use crate::protocols::ProtocolRegistry; use crate::subresource_integrity::is_response_integrity_valid; pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send); @@ -69,6 +62,7 @@ pub struct FetchContext { pub file_token: FileTokenCheck, pub cancellation_listener: Arc<Mutex<CancellationListener>>, pub timing: ServoArc<Mutex<ResourceFetchTiming>>, + pub protocols: Arc<ProtocolRegistry>, } pub struct CancellationListener { @@ -597,42 +591,6 @@ impl RangeRequestBounds { } } -/// Get the range bounds if the `Range` header is present. -fn get_range_request_bounds(range: Option<Range>) -> RangeRequestBounds { - if let Some(ref range) = range { - let (start, end) = match range - .iter() - .collect::<Vec<(Bound<u64>, Bound<u64>)>>() - .first() - { - Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None), - Some(&(Bound::Included(start), Bound::Included(end))) => { - // `end` should be less or equal to `start`. - (start, Some(i64::max(start as i64, end as i64))) - }, - Some(&(Bound::Unbounded, Bound::Included(offset))) => { - return RangeRequestBounds::Pending(offset); - }, - _ => (0, None), - }; - RangeRequestBounds::Final(RelativePos::from_opts(Some(start as i64), end)) - } else { - RangeRequestBounds::Final(RelativePos::from_opts(Some(0), None)) - } -} - -fn partial_content(response: &mut Response) { - let reason = "Partial Content".to_owned(); - response.status = Some((StatusCode::PARTIAL_CONTENT, reason.clone())); - response.raw_status = Some((StatusCode::PARTIAL_CONTENT.as_u16(), reason.into())); -} - -fn range_not_satisfiable_error(response: &mut Response) { - let reason = "Range Not Satisfiable".to_owned(); - response.status = Some((StatusCode::RANGE_NOT_SATISFIABLE, reason.clone())); - response.raw_status = Some((StatusCode::RANGE_NOT_SATISFIABLE.as_u16(), reason.into())); -} - fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response { let mut response = Response::new(url, ResourceFetchTiming::new(timing_type)); response @@ -696,7 +654,8 @@ async fn scheme_fetch( ) -> Response { let url = request.current_url(); - match url.scheme() { + let scheme = url.scheme(); + match scheme { "about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()), "chrome" if url.path() == "allowcert" => { @@ -713,158 +672,10 @@ async fn scheme_fetch( .await }, - "data" => match decode(&url) { - Ok((mime, bytes)) => { - let mut response = - Response::new(url, ResourceFetchTiming::new(request.timing_type())); - *response.body.lock().unwrap() = ResponseBody::Done(bytes); - response.headers.typed_insert(ContentType::from(mime)); - response.status = Some((StatusCode::OK, "OK".to_string())); - response.raw_status = Some((StatusCode::OK.as_u16(), b"OK".to_vec())); - response - }, - Err(_) => { - Response::network_error(NetworkError::Internal("Decoding data URL failed".into())) - }, + _ => match context.protocols.get(scheme) { + Some(handler) => handler.load(request, done_chan, context).await, + None => Response::network_error(NetworkError::Internal("Unexpected scheme".into())), }, - - "file" => { - if request.method != Method::GET { - return Response::network_error(NetworkError::Internal( - "Unexpected method for file".into(), - )); - } - if let Ok(file_path) = url.to_file_path() { - if file_path.is_dir() { - return local_directory_listing::fetch(request, url, file_path); - } - - if let Ok(file) = File::open(file_path.clone()) { - // Get range bounds (if any) and try to seek to the requested offset. - // If seeking fails, bail out with a NetworkError. - let file_size = match file.metadata() { - Ok(metadata) => Some(metadata.len()), - Err(_) => None, - }; - - let mut response = - Response::new(url, ResourceFetchTiming::new(request.timing_type())); - - let range_header = request.headers.typed_get::<Range>(); - let is_range_request = range_header.is_some(); - let Ok(range) = get_range_request_bounds(range_header).get_final(file_size) - else { - range_not_satisfiable_error(&mut response); - return response; - }; - let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file); - if reader.seek(SeekFrom::Start(range.start as u64)).is_err() { - return Response::network_error(NetworkError::Internal( - "Unexpected method for file".into(), - )); - } - - // Set response status to 206 if Range header is present. - // At this point we should have already validated the header. - if is_range_request { - partial_content(&mut response); - } - - // Set Content-Type header. - let mime = mime_guess::from_path(file_path).first_or_octet_stream(); - response.headers.typed_insert(ContentType::from(mime)); - - // Setup channel to receive cross-thread messages about the file fetch - // operation. - let (mut done_sender, done_receiver) = unbounded_channel(); - *done_chan = Some((done_sender.clone(), done_receiver)); - - *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); - - context.filemanager.lock().unwrap().fetch_file_in_chunks( - &mut done_sender, - reader, - response.body.clone(), - context.cancellation_listener.clone(), - range, - ); - - response - } else { - Response::network_error(NetworkError::Internal("Opening file failed".into())) - } - } else { - Response::network_error(NetworkError::Internal( - "Constructing file path failed".into(), - )) - } - }, - - "blob" => { - debug!("Loading blob {}", url.as_str()); - // Step 2. - if request.method != Method::GET { - return Response::network_error(NetworkError::Internal( - "Unexpected method for blob".into(), - )); - } - - let range_header = request.headers.typed_get::<Range>(); - let is_range_request = range_header.is_some(); - // We will get a final version of this range once we have - // the length of the data backing the blob. - let range = get_range_request_bounds(range_header); - - let (id, origin) = match parse_blob_url(&url) { - Ok((id, origin)) => (id, origin), - Err(error) => { - return Response::network_error(NetworkError::Internal(format!( - "Invalid blob URL ({error})" - ))); - }, - }; - - let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); - response.status = Some((StatusCode::OK, "OK".to_string())); - response.raw_status = Some((StatusCode::OK.as_u16(), b"OK".to_vec())); - - if is_range_request { - partial_content(&mut response); - } - - let (mut done_sender, done_receiver) = unbounded_channel(); - *done_chan = Some((done_sender.clone(), done_receiver)); - *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); - - if let Err(err) = context.filemanager.lock().unwrap().fetch_file( - &mut done_sender, - context.cancellation_listener.clone(), - id, - &context.file_token, - origin, - &mut response, - range, - ) { - let _ = done_sender.send(Data::Done); - let err = match err { - BlobURLStoreError::InvalidRange => { - range_not_satisfiable_error(&mut response); - return response; - }, - _ => format!("{:?}", err), - }; - return Response::network_error(NetworkError::Internal(err)); - }; - - response - }, - - "ftp" => { - debug!("ftp is not implemented"); - Response::network_error(NetworkError::Internal("Unexpected scheme".into())) - }, - - _ => Response::network_error(NetworkError::Internal("Unexpected scheme".into())), } } diff --git a/components/net/lib.rs b/components/net/lib.rs index cff77a022a0..98a70391c8c 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -8,7 +8,6 @@ pub mod async_runtime; pub mod connector; pub mod cookie; pub mod cookie_storage; -mod data_loader; mod decoder; pub mod filemanager_thread; mod hosts; @@ -18,6 +17,7 @@ pub mod http_loader; pub mod image_cache; pub mod local_directory_listing; pub mod mime_classifier; +pub mod protocols; pub mod resource_thread; mod storage_thread; pub mod subresource_integrity; diff --git a/components/net/protocols/blob.rs b/components/net/protocols/blob.rs new file mode 100644 index 00000000000..c26f95bf8da --- /dev/null +++ b/components/net/protocols/blob.rs @@ -0,0 +1,91 @@ +/* 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::future::{ready, Future}; +use std::pin::Pin; + +use headers::{HeaderMapExt, Range}; +use http::{Method, StatusCode}; +use log::debug; +use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreError}; +use net_traits::request::Request; +use net_traits::response::{Response, ResponseBody}; +use net_traits::{NetworkError, ResourceFetchTiming}; +use tokio::sync::mpsc::unbounded_channel; + +use crate::fetch::methods::{Data, DoneChannel, FetchContext}; +use crate::protocols::{ + get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler, +}; + +#[derive(Default)] +pub struct BlobProtocolHander {} + +impl ProtocolHandler for BlobProtocolHander { + fn load( + &self, + request: &mut Request, + done_chan: &mut DoneChannel, + context: &FetchContext, + ) -> Pin<Box<dyn Future<Output = Response> + Send>> { + let url = request.current_url(); + debug!("Loading blob {}", url.as_str()); + + // Step 2. + if request.method != Method::GET { + return Box::pin(ready(Response::network_error(NetworkError::Internal( + "Unexpected method for blob".into(), + )))); + } + + let range_header = request.headers.typed_get::<Range>(); + let is_range_request = range_header.is_some(); + // We will get a final version of this range once we have + // the length of the data backing the blob. + let range = get_range_request_bounds(range_header); + + let (id, origin) = match parse_blob_url(&url) { + Ok((id, origin)) => (id, origin), + Err(error) => { + return Box::pin(ready(Response::network_error(NetworkError::Internal( + format!("Invalid blob URL ({error})"), + )))); + }, + }; + + let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); + response.status = Some((StatusCode::OK, "OK".to_string())); + response.raw_status = Some((StatusCode::OK.as_u16(), b"OK".to_vec())); + + if is_range_request { + partial_content(&mut response); + } + + let (mut done_sender, done_receiver) = unbounded_channel(); + *done_chan = Some((done_sender.clone(), done_receiver)); + *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); + + if let Err(err) = context.filemanager.lock().unwrap().fetch_file( + &mut done_sender, + context.cancellation_listener.clone(), + id, + &context.file_token, + origin, + &mut response, + range, + ) { + let _ = done_sender.send(Data::Done); + let err = match err { + BlobURLStoreError::InvalidRange => { + range_not_satisfiable_error(&mut response); + return Box::pin(ready(response)); + }, + _ => format!("{:?}", err), + }; + return Box::pin(ready(Response::network_error(NetworkError::Internal(err)))); + }; + + Box::pin(ready(response)) + } +} diff --git a/components/net/data_loader.rs b/components/net/protocols/data.rs index 596ca69cb39..3835525d100 100644 --- a/components/net/data_loader.rs +++ b/components/net/protocols/data.rs @@ -2,20 +2,34 @@ * 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::future::Future; +use std::pin::Pin; + use data_url::forgiving_base64; +use headers::{ContentType, HeaderMapExt}; +use http::StatusCode; use mime::Mime; +use net_traits::request::Request; +use net_traits::response::{Response, ResponseBody}; +use net_traits::{NetworkError, ResourceFetchTiming}; use percent_encoding::percent_decode; use servo_url::ServoUrl; use url::Position; -pub enum DecodeError { +use crate::fetch::methods::{DoneChannel, FetchContext}; +use crate::protocols::ProtocolHandler; + +#[derive(Default)] +pub struct DataProtocolHander {} + +enum DecodeError { InvalidDataUri, NonBase64DataUri, } -pub type DecodeData = (Mime, Vec<u8>); +type DecodeData = (Mime, Vec<u8>); -pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> { +fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> { // data_url could do all of this work for us, // except that it currently (Nov 2019) parses mime types into a // different Mime class than other code expects @@ -55,3 +69,29 @@ pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> { } Ok((content_type, bytes)) } + +impl ProtocolHandler for DataProtocolHander { + fn load( + &self, + request: &mut Request, + _done_chan: &mut DoneChannel, + _context: &FetchContext, + ) -> Pin<Box<dyn Future<Output = Response> + Send>> { + let url = request.current_url(); + let response = match decode(&url) { + Ok((mime, bytes)) => { + let mut response = + Response::new(url, ResourceFetchTiming::new(request.timing_type())); + *response.body.lock().unwrap() = ResponseBody::Done(bytes); + response.headers.typed_insert(ContentType::from(mime)); + response.status = Some((StatusCode::OK, "OK".to_string())); + response.raw_status = Some((StatusCode::OK.as_u16(), b"OK".to_vec())); + response + }, + Err(_) => { + Response::network_error(NetworkError::Internal("Decoding data URL failed".into())) + }, + }; + Box::pin(std::future::ready(response)) + } +} diff --git a/components/net/protocols/file.rs b/components/net/protocols/file.rs new file mode 100644 index 00000000000..65e134e4635 --- /dev/null +++ b/components/net/protocols/file.rs @@ -0,0 +1,109 @@ +/* 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::fs::File; +use std::future::{ready, Future}; +use std::io::{BufReader, Seek, SeekFrom}; +use std::pin::Pin; + +use headers::{ContentType, HeaderMapExt, Range}; +use http::Method; +use net_traits::request::Request; +use net_traits::response::{Response, ResponseBody}; +use net_traits::{NetworkError, ResourceFetchTiming}; +use tokio::sync::mpsc::unbounded_channel; + +use crate::fetch::methods::{DoneChannel, FetchContext}; +use crate::filemanager_thread::FILE_CHUNK_SIZE; +use crate::local_directory_listing; +use crate::protocols::{ + get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler, +}; + +#[derive(Default)] +pub struct FileProtocolHander {} + +impl ProtocolHandler for FileProtocolHander { + fn load( + &self, + request: &mut Request, + done_chan: &mut DoneChannel, + context: &FetchContext, + ) -> Pin<Box<dyn Future<Output = Response> + Send>> { + let url = request.current_url(); + + if request.method != Method::GET { + return Box::pin(ready(Response::network_error(NetworkError::Internal( + "Unexpected method for file".into(), + )))); + } + let response = if let Ok(file_path) = url.to_file_path() { + if file_path.is_dir() { + return Box::pin(ready(local_directory_listing::fetch( + request, url, file_path, + ))); + } + + if let Ok(file) = File::open(file_path.clone()) { + // Get range bounds (if any) and try to seek to the requested offset. + // If seeking fails, bail out with a NetworkError. + let file_size = match file.metadata() { + Ok(metadata) => Some(metadata.len()), + Err(_) => None, + }; + + let mut response = + Response::new(url, ResourceFetchTiming::new(request.timing_type())); + + let range_header = request.headers.typed_get::<Range>(); + let is_range_request = range_header.is_some(); + let Ok(range) = get_range_request_bounds(range_header).get_final(file_size) else { + range_not_satisfiable_error(&mut response); + return Box::pin(ready(response)); + }; + let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file); + if reader.seek(SeekFrom::Start(range.start as u64)).is_err() { + return Box::pin(ready(Response::network_error(NetworkError::Internal( + "Unexpected method for file".into(), + )))); + } + + // Set response status to 206 if Range header is present. + // At this point we should have already validated the header. + if is_range_request { + partial_content(&mut response); + } + + // Set Content-Type header. + let mime = mime_guess::from_path(file_path).first_or_octet_stream(); + response.headers.typed_insert(ContentType::from(mime)); + + // Setup channel to receive cross-thread messages about the file fetch + // operation. + let (mut done_sender, done_receiver) = unbounded_channel(); + *done_chan = Some((done_sender.clone(), done_receiver)); + + *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); + + context.filemanager.lock().unwrap().fetch_file_in_chunks( + &mut done_sender, + reader, + response.body.clone(), + context.cancellation_listener.clone(), + range, + ); + + response + } else { + Response::network_error(NetworkError::Internal("Opening file failed".into())) + } + } else { + Response::network_error(NetworkError::Internal( + "Constructing file path failed".into(), + )) + }; + + Box::pin(ready(response)) + } +} diff --git a/components/net/protocols/mod.rs b/components/net/protocols/mod.rs new file mode 100644 index 00000000000..ea64bfcd389 --- /dev/null +++ b/components/net/protocols/mod.rs @@ -0,0 +1,119 @@ +/* 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::collections::hash_map::Entry; +use std::collections::HashMap; +use std::future::Future; +use std::ops::Bound; +use std::pin::Pin; + +use headers::Range; +use http::StatusCode; +use log::error; +use net_traits::filemanager_thread::RelativePos; +use net_traits::request::Request; +use net_traits::response::Response; + +use crate::fetch::methods::{DoneChannel, FetchContext, RangeRequestBounds}; + +mod blob; +mod data; +mod file; + +use blob::BlobProtocolHander; +use data::DataProtocolHander; +use file::FileProtocolHander; + +// The set of schemes that can't be registered. +static FORBIDDEN_SCHEMES: [&str; 4] = ["http", "https", "chrome", "about"]; + +pub trait ProtocolHandler: Send + Sync { + fn load( + &self, + request: &mut Request, + done_chan: &mut DoneChannel, + context: &FetchContext, + ) -> Pin<Box<dyn Future<Output = Response> + Send>>; +} + +#[derive(Default)] +pub struct ProtocolRegistry { + pub(crate) handlers: HashMap<String, Box<dyn ProtocolHandler>>, // Maps scheme -> handler +} + +impl ProtocolRegistry { + pub fn with_internal_protocols() -> Self { + let mut registry = Self::default(); + registry.register("data", DataProtocolHander::default()); + registry.register("blob", BlobProtocolHander::default()); + registry.register("file", FileProtocolHander::default()); + registry + } + + pub fn register(&mut self, scheme: &str, handler: impl ProtocolHandler + 'static) -> bool { + if FORBIDDEN_SCHEMES.contains(&scheme) { + error!("Protocol handler for '{scheme}' is not allowed to be registered."); + return false; + } + + if let Entry::Vacant(entry) = self.handlers.entry(scheme.into()) { + entry.insert(Box::new(handler)); + true + } else { + error!("Protocol handler for '{scheme}' is already registered."); + false + } + } + + pub fn get(&self, scheme: &str) -> Option<&dyn ProtocolHandler> { + self.handlers.get(scheme).map(|e| e.as_ref()) + } + + pub fn merge(&mut self, mut other: ProtocolRegistry) { + for (scheme, handler) in other.handlers.drain() { + if FORBIDDEN_SCHEMES.contains(&scheme.as_str()) { + error!("Protocol handler for '{scheme}' is not allowed to be registered."); + continue; + } + + self.handlers.entry(scheme).or_insert(handler); + } + } +} + +pub fn range_not_satisfiable_error(response: &mut Response) { + let reason = "Range Not Satisfiable".to_owned(); + response.status = Some((StatusCode::RANGE_NOT_SATISFIABLE, reason.clone())); + response.raw_status = Some((StatusCode::RANGE_NOT_SATISFIABLE.as_u16(), reason.into())); +} + +/// Get the range bounds if the `Range` header is present. +pub fn get_range_request_bounds(range: Option<Range>) -> RangeRequestBounds { + if let Some(ref range) = range { + let (start, end) = match range + .iter() + .collect::<Vec<(Bound<u64>, Bound<u64>)>>() + .first() + { + Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None), + Some(&(Bound::Included(start), Bound::Included(end))) => { + // `end` should be less or equal to `start`. + (start, Some(i64::max(start as i64, end as i64))) + }, + Some(&(Bound::Unbounded, Bound::Included(offset))) => { + return RangeRequestBounds::Pending(offset); + }, + _ => (0, None), + }; + RangeRequestBounds::Final(RelativePos::from_opts(Some(start as i64), end)) + } else { + RangeRequestBounds::Final(RelativePos::from_opts(Some(0), None)) + } +} + +pub fn partial_content(response: &mut Response) { + let reason = "Partial Content".to_owned(); + response.status = Some((StatusCode::PARTIAL_CONTENT, reason.clone())); + response.raw_status = Some((StatusCode::PARTIAL_CONTENT.as_u16(), reason.into())); +} diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index b885b35226a..df3f5160915 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -52,6 +52,7 @@ use crate::filemanager_thread::FileManager; use crate::hsts::HstsList; use crate::http_cache::HttpCache; use crate::http_loader::{http_redirect_fetch, HttpState}; +use crate::protocols::ProtocolRegistry; use crate::storage_thread::StorageThreadFactory; use crate::websocket_loader; @@ -76,6 +77,7 @@ pub fn new_resource_threads( config_dir: Option<PathBuf>, certificate_path: Option<String>, ignore_certificate_errors: bool, + protocols: Arc<ProtocolRegistry>, ) -> (ResourceThreads, ResourceThreads) { let ca_certificates = match certificate_path { Some(path) => match load_root_cert_store_from_file(path) { @@ -97,6 +99,7 @@ pub fn new_resource_threads( config_dir.clone(), ca_certificates, ignore_certificate_errors, + protocols, ); let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new(config_dir); ( @@ -116,6 +119,7 @@ pub fn new_core_resource_thread( config_dir: Option<PathBuf>, ca_certificates: CACertificates, ignore_certificate_errors: bool, + protocols: Arc<ProtocolRegistry>, ) -> (CoreResourceThread, CoreResourceThread) { let (public_setup_chan, public_setup_port) = ipc::channel().unwrap(); let (private_setup_chan, private_setup_port) = ipc::channel().unwrap(); @@ -141,7 +145,14 @@ pub fn new_core_resource_thread( }; mem_profiler_chan.run_with_memory_reporting( - || (channel_manager.start(public_setup_port, private_setup_port, report_port)), + || { + channel_manager.start( + public_setup_port, + private_setup_port, + report_port, + protocols, + ) + }, String::from("network-cache-reporter"), report_chan, |report_chan| report_chan, @@ -215,6 +226,7 @@ impl ResourceChannelManager { public_receiver: IpcReceiver<CoreResourceMsg>, private_receiver: IpcReceiver<CoreResourceMsg>, memory_reporter: IpcReceiver<ReportsChan>, + protocols: Arc<ProtocolRegistry>, ) { let (public_http_state, private_http_state) = create_http_states( self.config_dir.as_deref(), @@ -248,7 +260,7 @@ impl ResourceChannelManager { &public_http_state }; if let Ok(msg) = data.to() { - if !self.process_msg(msg, group) { + if !self.process_msg(msg, group, Arc::clone(&protocols)) { return; } } @@ -283,13 +295,22 @@ impl ResourceChannelManager { } /// Returns false if the thread should exit. - fn process_msg(&mut self, msg: CoreResourceMsg, http_state: &Arc<HttpState>) -> bool { + fn process_msg( + &mut self, + msg: CoreResourceMsg, + http_state: &Arc<HttpState>, + protocols: Arc<ProtocolRegistry>, + ) -> bool { match msg { CoreResourceMsg::Fetch(req_init, channels) => match channels { - FetchChannels::ResponseMsg(sender, cancel_chan) => { - self.resource_manager - .fetch(req_init, None, sender, http_state, cancel_chan) - }, + FetchChannels::ResponseMsg(sender, cancel_chan) => self.resource_manager.fetch( + req_init, + None, + sender, + http_state, + cancel_chan, + protocols, + ), FetchChannels::WebSocket { event_sender, action_receiver, @@ -299,10 +320,14 @@ impl ResourceChannelManager { action_receiver, http_state, ), - FetchChannels::Prefetch => { - self.resource_manager - .fetch(req_init, None, DiscardFetch, http_state, None) - }, + FetchChannels::Prefetch => self.resource_manager.fetch( + req_init, + None, + DiscardFetch, + http_state, + None, + protocols, + ), }, CoreResourceMsg::DeleteCookies(request) => { http_state @@ -312,9 +337,16 @@ impl ResourceChannelManager { .clear_storage(&request); return true; }, - CoreResourceMsg::FetchRedirect(req_init, res_init, sender, cancel_chan) => self - .resource_manager - .fetch(req_init, Some(res_init), sender, http_state, cancel_chan), + CoreResourceMsg::FetchRedirect(req_init, res_init, sender, cancel_chan) => { + self.resource_manager.fetch( + req_init, + Some(res_init), + sender, + http_state, + cancel_chan, + protocols, + ) + }, CoreResourceMsg::SetCookieForUrl(request, cookie, source) => self .resource_manager .set_cookie_for_url(&request, cookie.into_inner().to_owned(), source, http_state), @@ -656,6 +688,7 @@ impl CoreResourceManager { mut sender: Target, http_state: &Arc<HttpState>, cancel_chan: Option<IpcReceiver<()>>, + protocols: Arc<ProtocolRegistry>, ) { let http_state = http_state.clone(); let ua = self.user_agent.clone(); @@ -704,6 +737,7 @@ impl CoreResourceManager { file_token, cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(cancel_chan))), timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(request.timing_type()))), + protocols, }; match res_init_ { diff --git a/components/net/tests/fetch.rs b/components/net/tests/fetch.rs index 62631706b15..9ffe6817d81 100644 --- a/components/net/tests/fetch.rs +++ b/components/net/tests/fetch.rs @@ -27,6 +27,7 @@ use net::fetch::cors_cache::CorsCache; use net::fetch::methods::{self, CancellationListener, FetchContext}; use net::filemanager_thread::FileManager; use net::hsts::HstsEntry; +use net::protocols::ProtocolRegistry; use net::resource_thread::CoreResourceThreadPool; use net::test::HttpState; use net_traits::filemanager_thread::FileTokenCheck; @@ -769,6 +770,7 @@ fn test_fetch_with_hsts() { timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new( ResourceTimingType::Navigation, ))), + protocols: Arc::new(ProtocolRegistry::default()), }; // The server certificate is self-signed, so we need to add an override @@ -828,6 +830,7 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() { timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new( ResourceTimingType::Navigation, ))), + protocols: Arc::new(ProtocolRegistry::default()), }; // The server certificate is self-signed, so we need to add an override @@ -885,6 +888,7 @@ fn test_fetch_self_signed() { timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new( ResourceTimingType::Navigation, ))), + protocols: Arc::new(ProtocolRegistry::default()), }; let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) diff --git a/components/net/tests/main.rs b/components/net/tests/main.rs index dc9c859b82b..ae86c749d10 100644 --- a/components/net/tests/main.rs +++ b/components/net/tests/main.rs @@ -37,6 +37,7 @@ use hyper::{Body, Request as HyperRequest, Response as HyperResponse}; use net::fetch::cors_cache::CorsCache; use net::fetch::methods::{self, CancellationListener, FetchContext}; use net::filemanager_thread::FileManager; +use net::protocols::ProtocolRegistry; use net::resource_thread::CoreResourceThreadPool; use net::test::HttpState; use net_traits::filemanager_thread::FileTokenCheck; @@ -114,6 +115,7 @@ fn new_fetch_context( timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new( ResourceTimingType::Navigation, ))), + protocols: Arc::new(ProtocolRegistry::with_internal_protocols()), } } impl FetchTaskTarget for FetchResponseCollector { diff --git a/components/net/tests/resource_thread.rs b/components/net/tests/resource_thread.rs index 3e33a117bb3..0de76fd5dd1 100644 --- a/components/net/tests/resource_thread.rs +++ b/components/net/tests/resource_thread.rs @@ -6,6 +6,7 @@ use std::net::IpAddr; use ipc_channel::ipc; use net::connector::CACertificates; +use net::protocols::ProtocolRegistry; use net::resource_thread::new_core_resource_thread; use net::test::parse_hostsfile; use net_traits::CoreResourceMsg; @@ -32,6 +33,7 @@ fn test_exit() { None, CACertificates::Default, false, /* ignore_certificate_errors */ + std::sync::Arc::new(ProtocolRegistry::default()), ); resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap(); receiver.recv().unwrap(); |