1use std::collections::hash_map::Entry;
6use std::future;
7use std::future::Future;
8use std::ops::Bound;
9use std::pin::Pin;
10
11use headers::Range;
12use http::StatusCode;
13use log::error;
14use net_traits::filemanager_thread::RelativePos;
15use net_traits::request::Request;
16use net_traits::response::Response;
17use net_traits::{DiscardFetch, NetworkError};
18use rustc_hash::FxHashMap;
19use servo_url::ServoUrl;
20use url::Position;
21
22use crate::fetch::methods::{DoneChannel, FetchContext, RangeRequestBounds, fetch};
23
24mod blob;
25mod data;
26mod file;
27
28use blob::BlobProtocolHander;
29use data::DataProtocolHander;
30use file::FileProtocolHander;
31
32type FutureResponse = Pin<Box<dyn Future<Output = Response> + Send>>;
33
34static FORBIDDEN_SCHEMES: [&str; 4] = ["http", "https", "chrome", "about"];
36
37pub trait ProtocolHandler: Send + Sync {
38 fn privileged_paths(&self) -> &'static [&'static str] {
42 &[]
43 }
44
45 fn load(
51 &self,
52 request: &mut Request,
53 done_chan: &mut DoneChannel,
54 context: &FetchContext,
55 ) -> FutureResponse;
56
57 fn is_fetchable(&self) -> bool {
61 false
62 }
63
64 fn is_secure(&self) -> bool {
70 false
71 }
72}
73
74#[derive(Default)]
75pub struct ProtocolRegistry {
76 pub(crate) handlers: FxHashMap<String, Box<dyn ProtocolHandler>>, }
78
79#[derive(Clone, Copy, Debug)]
80pub enum ProtocolRegisterError {
81 ForbiddenScheme,
82 SchemeAlreadyRegistered,
83}
84
85impl ProtocolRegistry {
86 pub fn with_internal_protocols() -> Self {
87 let mut registry = Self::default();
88 registry
91 .register("data", DataProtocolHander::default())
92 .expect("Infallible");
93 registry
94 .register("blob", BlobProtocolHander::default())
95 .expect("Infallible");
96 registry
97 .register("file", FileProtocolHander::default())
98 .expect("Infallible");
99 registry
100 }
101
102 pub fn register(
104 &mut self,
105 scheme: &str,
106 handler: impl ProtocolHandler + 'static,
107 ) -> Result<(), ProtocolRegisterError> {
108 if FORBIDDEN_SCHEMES.contains(&scheme) {
109 error!("Protocol handler for '{scheme}' is not allowed to be registered.");
110 return Err(ProtocolRegisterError::ForbiddenScheme);
111 }
112
113 if let Entry::Vacant(entry) = self.handlers.entry(scheme.into()) {
114 entry.insert(Box::new(handler));
115 Ok(())
116 } else {
117 error!("Protocol handler for '{scheme}' is already registered.");
118 Err(ProtocolRegisterError::SchemeAlreadyRegistered)
119 }
120 }
121
122 pub fn register_page_content_handler(
123 &mut self,
124 scheme: String,
125 url: String,
126 ) -> Result<(), ProtocolRegisterError> {
127 self.register(
128 &scheme.clone(),
129 WebPageContentProtocolHandler { url, scheme },
130 )
131 }
132
133 pub fn get(&self, scheme: &str) -> Option<&dyn ProtocolHandler> {
134 self.handlers.get(scheme).map(|e| e.as_ref())
135 }
136
137 pub fn merge(&mut self, mut other: ProtocolRegistry) {
138 for (scheme, handler) in other.handlers.drain() {
139 if FORBIDDEN_SCHEMES.contains(&scheme.as_str()) {
140 error!("Protocol handler for '{scheme}' is not allowed to be registered.");
141 continue;
142 }
143
144 self.handlers.entry(scheme).or_insert(handler);
145 }
146 }
147
148 pub fn is_fetchable(&self, scheme: &str) -> bool {
149 self.handlers
150 .get(scheme)
151 .is_some_and(|handler| handler.is_fetchable())
152 }
153
154 pub fn is_secure(&self, scheme: &str) -> bool {
155 self.handlers
156 .get(scheme)
157 .is_some_and(|handler| handler.is_secure())
158 }
159
160 pub fn privileged_urls(&self) -> Vec<ServoUrl> {
161 self.handlers
162 .iter()
163 .flat_map(|(scheme, handler)| {
164 let paths = handler.privileged_paths();
165 paths
166 .iter()
167 .filter_map(move |path| ServoUrl::parse(&format!("{scheme}:{path}")).ok())
168 })
169 .collect()
170 }
171}
172
173struct WebPageContentProtocolHandler {
174 url: String,
175 scheme: String,
176}
177
178impl ProtocolHandler for WebPageContentProtocolHandler {
179 fn load(
181 &self,
182 request: &mut Request,
183 _done_chan: &mut DoneChannel,
184 context: &FetchContext,
185 ) -> FutureResponse {
186 let mut url = request.current_url();
187 assert!(url.scheme() == self.scheme);
189 let _ = url.set_username("");
196
197 let _ = url.set_password(None);
204
205 let encoded_url = &url[Position::AfterScheme..][1..];
210 let handler_url_string = self.url.replacen("%s", encoded_url, 1);
213 let Ok(result_url) = ServoUrl::parse(&handler_url_string) else {
215 return Box::pin(future::ready(Response::network_error(
216 NetworkError::Internal("Failed to parse substituted protocol handler url".into()),
217 )));
218 };
219 assert!(matches!(result_url.scheme(), "http" | "https"));
221 request.url_list.push(result_url);
223 let request2 = request.clone();
224 let context2 = context.clone();
225 Box::pin(async move { fetch(request2, &mut DiscardFetch, &context2).await })
226 }
227}
228
229pub fn is_url_potentially_trustworthy(
231 protocol_registry: &ProtocolRegistry,
232 url: &ServoUrl,
233) -> bool {
234 url.is_potentially_trustworthy() || protocol_registry.is_secure(url.scheme())
235}
236
237pub fn range_not_satisfiable_error(response: &mut Response) {
238 response.status = StatusCode::RANGE_NOT_SATISFIABLE.into();
239}
240
241pub fn get_range_request_bounds(range: Option<Range>, len: u64) -> RangeRequestBounds {
243 if let Some(ref range) = range {
244 let (start, end) = match range.satisfiable_ranges(len).next() {
245 Some((Bound::Included(start), Bound::Unbounded)) => (start, None),
246 Some((Bound::Included(start), Bound::Included(end))) => {
247 (start, Some(i64::max(start as i64, end as i64)))
249 },
250 Some((Bound::Unbounded, Bound::Included(offset))) => {
251 return RangeRequestBounds::Pending(offset);
252 },
253 _ => (0, None),
254 };
255 RangeRequestBounds::Final(RelativePos::from_opts(Some(start as i64), end))
256 } else {
257 RangeRequestBounds::Final(RelativePos::from_opts(Some(0), None))
258 }
259}
260
261pub fn partial_content(response: &mut Response) {
262 response.status = StatusCode::PARTIAL_CONTENT.into();
263}