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 net::fetch::methods::{DoneChannel, FetchContext};
17use net::filemanager_thread::FILE_CHUNK_SIZE;
18use net::protocols::ProtocolHandler;
19use net_traits::ResourceFetchTiming;
20use net_traits::filemanager_thread::RelativePos;
21use net_traits::request::Request;
22use net_traits::response::{Response, ResponseBody};
23use tokio::sync::mpsc::unbounded_channel;
24
25#[derive(Default)]
26pub struct ResourceProtocolHandler {}
27
28impl ResourceProtocolHandler {
29 pub fn response_for_path(
30 request: &mut Request,
31 done_chan: &mut DoneChannel,
32 context: &FetchContext,
33 path: &str,
34 ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
35 if path.contains("..") || !path.starts_with("/") {
36 return Box::pin(std::future::ready(Response::network_internal_error(
37 "Invalid path",
38 )));
39 }
40
41 let path = if let Some(path) = path.strip_prefix("/") {
42 path
43 } else {
44 return Box::pin(std::future::ready(Response::network_internal_error(
45 "Invalid path",
46 )));
47 };
48
49 let file_path = crate::resources::resources_dir_path()
50 .join("resource_protocol")
51 .join(path);
52
53 if !file_path.exists() || file_path.is_dir() {
54 return Box::pin(std::future::ready(Response::network_internal_error(
55 "Invalid path",
56 )));
57 }
58
59 let response = if let Ok(file) = File::open(file_path.clone()) {
60 let mut response = Response::new(
61 request.current_url(),
62 ResourceFetchTiming::new(request.timing_type()),
63 );
64 let reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
65
66 let mime = mime_guess::from_path(file_path).first_or_octet_stream();
68 response.headers.typed_insert(ContentType::from(mime));
69
70 let (mut done_sender, done_receiver) = unbounded_channel();
73 *done_chan = Some((done_sender.clone(), done_receiver));
74
75 *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]);
76
77 context.filemanager.lock().unwrap().fetch_file_in_chunks(
78 &mut done_sender,
79 reader,
80 response.body.clone(),
81 context.cancellation_listener.clone(),
82 RelativePos::full_range(),
83 );
84
85 response
86 } else {
87 Response::network_internal_error("Opening file failed")
88 };
89
90 Box::pin(std::future::ready(response))
91 }
92}
93
94impl ProtocolHandler for ResourceProtocolHandler {
95 fn load(
96 &self,
97 request: &mut Request,
98 done_chan: &mut DoneChannel,
99 context: &FetchContext,
100 ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
101 let url = request.current_url();
102
103 Self::response_for_path(request, done_chan, context, url.path())
107 }
108}