servoshell/desktop/protocols/
resource.rs1use 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::resource_protocol_dir_path().join(path);
47
48 if !file_path.exists() || file_path.is_dir() {
49 return Box::pin(std::future::ready(Response::network_error(
50 NetworkError::ResourceLoadError("Invalid path".to_owned()),
51 )));
52 }
53
54 let response = if let Ok(file) = File::open(file_path.clone()) {
55 let mut response = Response::new(
56 request.current_url(),
57 ResourceFetchTiming::new(request.timing_type()),
58 );
59 let reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
60
61 let mime = mime_guess::from_path(file_path).first_or_octet_stream();
63 response.headers.typed_insert(ContentType::from(mime));
64
65 let (mut done_sender, done_receiver) = unbounded_channel();
68 *done_chan = Some((done_sender.clone(), done_receiver));
69
70 *response.body.lock() = ResponseBody::Receiving(vec![]);
71
72 context.filemanager.fetch_file_in_chunks(
73 &mut done_sender,
74 reader,
75 response.body.clone(),
76 context.cancellation_listener.clone(),
77 RelativePos::full_range(),
78 );
79
80 response
81 } else {
82 Response::network_error(NetworkError::ResourceLoadError(
83 "Opening file failed".to_owned(),
84 ))
85 };
86
87 Box::pin(std::future::ready(response))
88 }
89}
90
91impl ProtocolHandler for ResourceProtocolHandler {
92 fn load(
93 &self,
94 request: &mut Request,
95 done_chan: &mut DoneChannel,
96 context: &FetchContext,
97 ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
98 let url = request.current_url();
99
100 Self::response_for_path(request, done_chan, context, url.path())
104 }
105}