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