Rewild Your Web
web browser dweb
at main 107 lines 3.8 kB view raw
1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5//! This protocol handler loads files from the <resources_dir_path()>/protocol/resource directory, 6//! sanitizing the path to prevent path escape attacks. 7//! For security reasons, loads are only allowed if the referrer has a 'resource' or 8//! 'servo' scheme. 9 10use std::fs::File; 11use std::future::Future; 12use std::io::BufReader; 13use std::pin::Pin; 14 15use headers::{ContentType, HeaderMapExt}; 16use servo::protocol_handler::{ 17 DoneChannel, FILE_CHUNK_SIZE, FetchContext, NetworkError, ProtocolHandler, RelativePos, 18 Request, ResourceFetchTiming, Response, ResponseBody, 19}; 20use tokio::sync::mpsc::unbounded_channel; 21 22#[derive(Default)] 23pub struct ResourceProtocolHandler {} 24 25impl ResourceProtocolHandler { 26 pub fn response_for_path( 27 request: &mut Request, 28 done_chan: &mut DoneChannel, 29 context: &FetchContext, 30 path: &str, 31 ) -> Pin<Box<dyn Future<Output = Response> + Send>> { 32 if path.contains("..") || !path.starts_with("/") { 33 return Box::pin(std::future::ready(Response::network_error( 34 NetworkError::ResourceLoadError("Invalid path".to_owned()), 35 ))); 36 } 37 38 let path = if let Some(path) = path.strip_prefix("/") { 39 path 40 } else { 41 return Box::pin(std::future::ready(Response::network_error( 42 NetworkError::ResourceLoadError("Invalid path".to_owned()), 43 ))); 44 }; 45 46 let file_path = crate::resources::ancestor_dir_path("resources") 47 .join("resource_protocol") 48 .join(path); 49 50 if !file_path.exists() || file_path.is_dir() { 51 return Box::pin(std::future::ready(Response::network_error( 52 NetworkError::ResourceLoadError("Invalid path".to_owned()), 53 ))); 54 } 55 56 let response = if let Ok(file) = File::open(file_path.clone()) { 57 let mut response = Response::new( 58 request.current_url(), 59 ResourceFetchTiming::new(request.timing_type()), 60 ); 61 let reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file); 62 63 // Set Content-Type header. 64 let mime = mime_guess::from_path(file_path).first_or_octet_stream(); 65 response.headers.typed_insert(ContentType::from(mime)); 66 67 // Setup channel to receive cross-thread messages about the file fetch 68 // operation. 69 let (mut done_sender, done_receiver) = unbounded_channel(); 70 *done_chan = Some((done_sender.clone(), done_receiver)); 71 72 *response.body.lock() = ResponseBody::Receiving(vec![]); 73 74 context.filemanager.fetch_file_in_chunks( 75 &mut done_sender, 76 reader, 77 response.body.clone(), 78 context.cancellation_listener.clone(), 79 RelativePos::full_range(), 80 ); 81 82 response 83 } else { 84 Response::network_error(NetworkError::ResourceLoadError( 85 "Opening file failed".to_owned(), 86 )) 87 }; 88 89 Box::pin(std::future::ready(response)) 90 } 91} 92 93impl ProtocolHandler for ResourceProtocolHandler { 94 fn load( 95 &self, 96 request: &mut Request, 97 done_chan: &mut DoneChannel, 98 context: &FetchContext, 99 ) -> Pin<Box<dyn Future<Output = Response> + Send>> { 100 let url = request.current_url(); 101 102 // TODO: Check referrer. 103 // We unexpectedly get `NoReferrer` for all requests from the newtab page. 104 105 Self::response_for_path(request, done_chan, context, url.path()) 106 } 107}