1use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::{Arc, Mutex};
7use std::{io, mem, str};
8
9use base64::Engine as _;
10use base64::engine::general_purpose;
11use content_security_policy as csp;
12use crossbeam_channel::Sender;
13use devtools_traits::DevtoolsControlMsg;
14use embedder_traits::resources::{self, Resource};
15use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt};
16use http::header::{self, HeaderMap, HeaderName, RANGE};
17use http::{HeaderValue, Method, StatusCode};
18use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
19use log::{debug, trace, warn};
20use mime::{self, Mime};
21use net_traits::fetch::headers::extract_mime_type_as_mime;
22use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
23use net_traits::http_status::HttpStatus;
24use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
25use net_traits::request::{
26 BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator,
27 InsecureRequestsPolicy, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestMode,
28 ResponseTainting, is_cors_safelisted_method, is_cors_safelisted_request_header,
29};
30use net_traits::response::{Response, ResponseBody, ResponseType};
31use net_traits::{
32 FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
33 ResourceTimeValue, ResourceTimingType, WebSocketDomAction, WebSocketNetworkEvent,
34 set_default_accept_language,
35};
36use rustls_pki_types::CertificateDer;
37use serde::{Deserialize, Serialize};
38use servo_arc::Arc as ServoArc;
39use servo_url::{Host, ImmutableOrigin, ServoUrl};
40use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender};
41
42use crate::connector::CACertificates;
43use crate::fetch::cors_cache::CorsCache;
44use crate::fetch::fetch_params::{FetchParams, PreloadResponseCandidate};
45use crate::fetch::headers::determine_nosniff;
46use crate::filemanager_thread::FileManager;
47use crate::http_loader::{
48 HttpState, determine_requests_referrer, http_fetch, send_early_httprequest_to_devtools,
49 send_response_to_devtools, set_default_accept,
50};
51use crate::protocols::{ProtocolRegistry, is_url_potentially_trustworthy};
52use crate::request_interceptor::RequestInterceptor;
53use crate::subresource_integrity::is_response_integrity_valid;
54
55const PARTIAL_RESPONSE_TO_NON_RANGE_REQUEST_ERROR: &str = "Refusing to provide partial response\
56from earlier ranged request to API that did not make a range request";
57
58pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send);
59
60#[derive(Clone, Deserialize, Serialize)]
61pub enum Data {
62 Payload(Vec<u8>),
63 Done,
64 Cancelled,
65}
66
67pub struct WebSocketChannel {
68 pub sender: IpcSender<WebSocketNetworkEvent>,
69 pub receiver: Option<IpcReceiver<WebSocketDomAction>>,
70}
71
72impl WebSocketChannel {
73 pub fn new(
74 sender: IpcSender<WebSocketNetworkEvent>,
75 receiver: Option<IpcReceiver<WebSocketDomAction>>,
76 ) -> Self {
77 Self { sender, receiver }
78 }
79}
80
81pub struct FetchContext {
82 pub state: Arc<HttpState>,
83 pub user_agent: String,
84 pub devtools_chan: Option<Arc<Mutex<Sender<DevtoolsControlMsg>>>>,
85 pub filemanager: Arc<Mutex<FileManager>>,
86 pub file_token: FileTokenCheck,
87 pub request_interceptor: Arc<Mutex<RequestInterceptor>>,
88 pub cancellation_listener: Arc<CancellationListener>,
89 pub timing: ServoArc<Mutex<ResourceFetchTiming>>,
90 pub protocols: Arc<ProtocolRegistry>,
91 pub websocket_chan: Option<Arc<Mutex<WebSocketChannel>>>,
92 pub ca_certificates: CACertificates,
93 pub ignore_certificate_errors: bool,
94}
95
96#[derive(Default)]
97pub struct CancellationListener {
98 cancelled: AtomicBool,
99}
100
101impl CancellationListener {
102 pub(crate) fn cancelled(&self) -> bool {
103 self.cancelled.load(Ordering::Relaxed)
104 }
105
106 pub(crate) fn cancel(&self) {
107 self.cancelled.store(true, Ordering::Relaxed)
108 }
109}
110pub type DoneChannel = Option<(TokioSender<Data>, TokioReceiver<Data>)>;
111
112pub async fn fetch(request: Request, target: Target<'_>, context: &FetchContext) {
114 {
117 let mut timing_guard = context.timing.lock().unwrap();
118 timing_guard.set_attribute(ResourceAttribute::FetchStart);
119 timing_guard.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
120 }
121 fetch_with_cors_cache(request, &mut CorsCache::default(), target, context).await;
122}
123
124pub async fn fetch_with_cors_cache(
128 request: Request,
129 cache: &mut CorsCache,
130 target: Target<'_>,
131 context: &FetchContext,
132) {
133 let mut fetch_params = FetchParams::new(request);
135 let request = &mut fetch_params.request;
136
137 request.populate_request_from_client();
139
140 if
149 matches!(request.current_url().scheme(), "http" | "https")
151 && matches!(request.mode, RequestMode::SameOrigin | RequestMode::CorsMode | RequestMode::NoCors)
153 && matches!(request.method, Method::GET)
155 && (!request.unsafe_request || request.headers.is_empty())
157 {
158 if let Some(client) = request.client.as_ref() {
160 assert!(request.origin == client.origin);
162 let on_preloaded_response_available = |response| {
165 fetch_params.preload_response_candidate =
166 PreloadResponseCandidate::Response(Box::new(response))
167 };
168 let found_preloaded_resource =
172 client.consume_preloaded_resource(request, on_preloaded_response_available);
173 if found_preloaded_resource &&
176 matches!(
177 fetch_params.preload_response_candidate,
178 PreloadResponseCandidate::None
179 )
180 {
181 fetch_params.preload_response_candidate = PreloadResponseCandidate::Pending;
182 }
183 }
184 }
185
186 set_default_accept(request);
188
189 set_default_accept_language(&mut request.headers);
192
193 if request.is_subresource_request() {
200 }
202
203 main_fetch(&mut fetch_params, cache, false, target, &mut None, context).await;
205
206 }
209
210pub(crate) fn convert_request_to_csp_request(request: &Request) -> Option<csp::Request> {
211 let origin = match &request.origin {
212 Origin::Client => return None,
213 Origin::Origin(origin) => origin,
214 };
215
216 let mut original_url = request.original_url();
219 if original_url.scheme() == "ws" && request.url().scheme() == "https" {
220 original_url.as_mut_url().set_scheme("wss").unwrap();
221 }
222
223 let csp_request = csp::Request {
224 url: original_url.into_url(),
225 origin: origin.clone().into_url_origin(),
226 redirect_count: request.redirect_count,
227 destination: request.destination,
228 initiator: match request.initiator {
229 Initiator::Download => csp::Initiator::Download,
230 Initiator::ImageSet => csp::Initiator::ImageSet,
231 Initiator::Manifest => csp::Initiator::Manifest,
232 Initiator::Prefetch => csp::Initiator::Prefetch,
233 _ => csp::Initiator::None,
234 },
235 nonce: request.cryptographic_nonce_metadata.clone(),
236 integrity_metadata: request.integrity_metadata.clone(),
237 parser_metadata: match request.parser_metadata {
238 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
239 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
240 ParserMetadata::Default => csp::ParserMetadata::None,
241 },
242 };
243 Some(csp_request)
244}
245
246pub fn should_request_be_blocked_by_csp(
248 csp_request: &csp::Request,
249 policy_container: &PolicyContainer,
250) -> (csp::CheckResult, Vec<csp::Violation>) {
251 policy_container
252 .csp_list
253 .as_ref()
254 .map(|c| c.should_request_be_blocked(csp_request))
255 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
256}
257
258pub fn report_violations_for_request_by_csp(
260 csp_request: &csp::Request,
261 policy_container: &PolicyContainer,
262) -> Vec<csp::Violation> {
263 policy_container
264 .csp_list
265 .as_ref()
266 .map(|c| c.report_violations_for_request(csp_request))
267 .unwrap_or_default()
268}
269
270fn should_response_be_blocked_by_csp(
271 csp_request: &csp::Request,
272 response: &Response,
273 policy_container: &PolicyContainer,
274) -> (csp::CheckResult, Vec<csp::Violation>) {
275 if response.is_network_error() {
276 return (csp::CheckResult::Allowed, Vec::new());
277 }
278 let csp_response = csp::Response {
279 url: response
280 .actual_response()
281 .url()
282 .cloned()
283 .map(|mut url| {
288 match csp_request.url.scheme() {
289 "ws" | "wss" => {
290 url.as_mut_url()
291 .set_scheme(csp_request.url.scheme())
292 .expect("failed to set URL scheme");
293 },
294 _ => {},
295 };
296 url
297 })
298 .expect("response must have a url")
299 .into_url(),
300 redirect_count: csp_request.redirect_count,
301 };
302 policy_container
303 .csp_list
304 .as_ref()
305 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
306 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
307}
308
309pub async fn main_fetch(
311 fetch_params: &mut FetchParams,
312 cache: &mut CorsCache,
313 recursive_flag: bool,
314 target: Target<'_>,
315 done_chan: &mut DoneChannel,
316 context: &FetchContext,
317) -> Response {
318 let request = &mut fetch_params.request;
320 send_early_httprequest_to_devtools(request, context);
321 let mut response = None;
323
324 if let Some(ref details) = request.crash {
326 response = Some(Response::network_error(NetworkError::Crash(
327 details.clone(),
328 )));
329 }
330
331 if request.local_urls_only &&
334 !matches!(
335 request.current_url().scheme(),
336 "about" | "blob" | "data" | "filesystem"
337 )
338 {
339 response = Some(Response::network_error(NetworkError::Internal(
340 "Non-local scheme".into(),
341 )));
342 }
343
344 let policy_container = match &request.policy_container {
346 RequestPolicyContainer::Client => unreachable!(),
347 RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
348 };
349 let csp_request = convert_request_to_csp_request(request);
350 if let Some(csp_request) = csp_request.as_ref() {
351 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
353
354 if !violations.is_empty() {
355 target.process_csp_violations(request, violations);
356 }
357 };
358
359 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
364 should_upgrade_mixed_content_request(request, &context.protocols)
365 {
366 trace!(
367 "upgrading {} targeting {:?}",
368 request.current_url(),
369 request.destination
370 );
371 if let Some(new_scheme) = match request.current_url().scheme() {
372 "http" => Some("https"),
373 "ws" => Some("wss"),
374 _ => None,
375 } {
376 request
377 .current_url_mut()
378 .as_mut_url()
379 .set_scheme(new_scheme)
380 .unwrap();
381 }
382 } else {
383 trace!(
384 "not upgrading {} targeting {:?} with {:?}",
385 request.current_url(),
386 request.destination,
387 request.insecure_requests_policy
388 );
389 }
390 if let Some(csp_request) = csp_request.as_ref() {
391 let (check_result, violations) =
395 should_request_be_blocked_by_csp(csp_request, &policy_container);
396
397 if !violations.is_empty() {
398 target.process_csp_violations(request, violations);
399 }
400
401 if check_result == csp::CheckResult::Blocked {
402 warn!("Request blocked by CSP");
403 response = Some(Response::network_error(NetworkError::Internal(
404 "Blocked by Content-Security-Policy".into(),
405 )))
406 }
407 };
408 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
409 response = Some(Response::network_error(NetworkError::Internal(
410 "Request attempted on bad port".into(),
411 )));
412 }
413 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
414 response = Some(Response::network_error(NetworkError::Internal(
415 "Blocked as mixed content".into(),
416 )));
417 }
418
419 if request.referrer_policy == ReferrerPolicy::EmptyString {
422 request.referrer_policy = policy_container.get_referrer_policy();
423 }
424
425 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
426 Referrer::NoReferrer => None,
427 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
428 request.headers.remove(header::REFERER);
429 determine_requests_referrer(
430 request.referrer_policy,
431 referrer_source,
432 request.current_url(),
433 )
434 },
435 };
436 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
437
438 context
443 .state
444 .hsts_list
445 .read()
446 .unwrap()
447 .apply_hsts_rules(request.current_url_mut());
448
449 let current_url = request.current_url();
453 let current_scheme = current_url.scheme();
454
455 context
457 .request_interceptor
458 .lock()
459 .unwrap()
460 .intercept_request(request, &mut response, context);
461
462 let mut response = match response {
463 Some(res) => res,
464 None => {
465 let same_origin = if let Origin::Origin(ref origin) = request.origin {
468 *origin == current_url.origin()
469 } else {
470 false
471 };
472
473 if let PreloadResponseCandidate::Response(response) =
475 &fetch_params.preload_response_candidate
476 {
477 *response.clone()
483 }
484 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
487 current_scheme == "data" ||
489 context.protocols.is_fetchable(current_scheme) ||
492 matches!(
494 request.mode,
495 RequestMode::Navigate | RequestMode::WebSocket { .. }
496 )
497 {
498 request.response_tainting = ResponseTainting::Basic;
500
501 scheme_fetch(fetch_params, cache, target, done_chan, context).await
503 } else if request.mode == RequestMode::SameOrigin {
504 Response::network_error(NetworkError::Internal("Cross-origin response".into()))
505 } else if request.mode == RequestMode::NoCors {
506 if request.redirect_mode != RedirectMode::Follow {
508 Response::network_error(NetworkError::Internal(
509 "NoCors requests must follow redirects".into(),
510 ))
511 } else {
512 request.response_tainting = ResponseTainting::Opaque;
514
515 scheme_fetch(fetch_params, cache, target, done_chan, context).await
517 }
518 } else if !matches!(current_scheme, "http" | "https") {
519 Response::network_error(NetworkError::Internal("Non-http scheme".into()))
520 } else if request.use_cors_preflight ||
521 (request.unsafe_request &&
522 (!is_cors_safelisted_method(&request.method) ||
523 request.headers.iter().any(|(name, value)| {
524 !is_cors_safelisted_request_header(&name, &value)
525 })))
526 {
527 request.response_tainting = ResponseTainting::CorsTainting;
529 let response = http_fetch(
531 fetch_params,
532 cache,
533 true,
534 true,
535 false,
536 target,
537 done_chan,
538 context,
539 )
540 .await;
541 if response.is_network_error() {
543 }
545 response
547 } else {
548 request.response_tainting = ResponseTainting::CorsTainting;
550 http_fetch(
552 fetch_params,
553 cache,
554 true,
555 false,
556 false,
557 target,
558 done_chan,
559 context,
560 )
561 .await
562 }
563 },
564 };
565
566 if recursive_flag {
568 return response;
569 }
570
571 let request = &mut fetch_params.request;
573
574 let mut response = if !response.is_network_error() && response.internal_response.is_none() {
576 if request.response_tainting == ResponseTainting::CorsTainting {
578 let header_names: Option<Vec<HeaderName>> = response
580 .headers
581 .typed_get::<AccessControlExposeHeaders>()
582 .map(|v| v.iter().collect());
583 match header_names {
584 Some(ref list)
586 if request.credentials_mode != CredentialsMode::Include &&
587 list.iter().any(|header| header == "*") =>
588 {
589 response.cors_exposed_header_name_list = response
590 .headers
591 .iter()
592 .map(|(name, _)| name.as_str().to_owned())
593 .collect();
594 },
595 Some(list) => {
597 response.cors_exposed_header_name_list =
598 list.iter().map(|h| h.as_str().to_owned()).collect();
599 },
600 _ => (),
601 }
602 }
603
604 let response_type = match request.response_tainting {
606 ResponseTainting::Basic => ResponseType::Basic,
607 ResponseTainting::CorsTainting => ResponseType::Cors,
608 ResponseTainting::Opaque => ResponseType::Opaque,
609 };
610 response.to_filtered(response_type)
611 } else {
612 response
613 };
614
615 let internal_error = {
616 let response_is_network_error = response.is_network_error();
618 let should_replace_with_nosniff_error = !response_is_network_error &&
619 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
620 let should_replace_with_mime_type_error = !response_is_network_error &&
621 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
622 let should_replace_with_mixed_content = !response_is_network_error &&
623 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
624 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
625 let (check_result, violations) =
626 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
627 if !violations.is_empty() {
628 target.process_csp_violations(request, violations);
629 }
630 check_result == csp::CheckResult::Blocked
631 });
632
633 let mut network_error_response = response
635 .get_network_error()
636 .cloned()
637 .map(Response::network_error);
638
639 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
643 error_response
644 } else {
645 response.actual_response_mut()
646 };
647
648 if internal_response.url_list.is_empty() {
650 internal_response.url_list.clone_from(&request.url_list)
651 }
652
653 let mut blocked_error_response;
659
660 let internal_response = if should_replace_with_nosniff_error {
661 blocked_error_response =
663 Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
664 &blocked_error_response
665 } else if should_replace_with_mime_type_error {
666 blocked_error_response =
668 Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
669 &blocked_error_response
670 } else if should_replace_with_mixed_content {
671 blocked_error_response =
672 Response::network_error(NetworkError::Internal("Blocked as mixed content".into()));
673 &blocked_error_response
674 } else if should_replace_with_csp_error {
675 blocked_error_response =
676 Response::network_error(NetworkError::Internal("Blocked due to CSP".into()));
677 &blocked_error_response
678 } else {
679 internal_response
680 };
681
682 let internal_response = if !internal_response.is_network_error() &&
688 response_type == ResponseType::Opaque &&
689 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
690 internal_response.range_requested &&
691 !request.headers.contains_key(RANGE)
692 {
693 blocked_error_response = Response::network_error(NetworkError::Internal(
695 PARTIAL_RESPONSE_TO_NON_RANGE_REQUEST_ERROR.into(),
696 ));
697 &blocked_error_response
698 } else {
699 internal_response
700 };
701
702 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
707 if not_network_error &&
708 (is_null_body_status(&internal_response.status) ||
709 matches!(request.method, Method::HEAD | Method::CONNECT))
710 {
711 let mut body = internal_response.body.lock().unwrap();
714 *body = ResponseBody::Empty;
715 }
716
717 internal_response.get_network_error().cloned()
718 };
719
720 let mut response = if let Some(error) = internal_error {
722 Response::network_error(error)
723 } else {
724 response
725 };
726
727 let mut response_loaded = false;
729 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
730 wait_for_response(request, &mut response, target, done_chan, context).await;
732 response_loaded = true;
733
734 let integrity_metadata = &request.integrity_metadata;
736 if response.termination_reason.is_none() &&
737 !is_response_integrity_valid(integrity_metadata, &response)
738 {
739 Response::network_error(NetworkError::Internal(
740 "Subresource integrity validation failed".into(),
741 ))
742 } else {
743 response
744 }
745 } else {
746 response
747 };
748
749 if request.synchronous {
751 target.process_response(request, &response);
754 if !response_loaded {
755 wait_for_response(request, &mut response, target, done_chan, context).await;
756 }
757 target.process_response_eof(request, &response);
759 return response;
760 }
761
762 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
764 target.process_request_body(request);
769 target.process_request_eof(request);
770 }
771
772 target.process_response(request, &response);
774 send_response_to_devtools(request, context, &response, None);
776
777 if !response_loaded {
779 wait_for_response(request, &mut response, target, done_chan, context).await;
780 }
781
782 target.process_response_eof(request, &response);
784 send_response_to_devtools(request, context, &response, None);
788
789 if let Ok(http_cache) = context.state.http_cache.write() {
790 http_cache.update_awaiting_consumers(request, &response);
791 }
792
793 response
796}
797
798async fn wait_for_response(
799 request: &Request,
800 response: &mut Response,
801 target: Target<'_>,
802 done_chan: &mut DoneChannel,
803 context: &FetchContext,
804) {
805 if let Some(ref mut ch) = *done_chan {
806 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
807 loop {
808 match ch.1.recv().await {
809 Some(Data::Payload(vec)) => {
810 if let Some(body) = devtools_body.as_mut() {
811 body.extend(&vec);
812 }
813 target.process_response_chunk(request, vec);
814 },
815 Some(Data::Done) => {
816 send_response_to_devtools(request, context, response, devtools_body);
817 break;
818 },
819 Some(Data::Cancelled) => {
820 response.aborted.store(true, Ordering::Release);
821 break;
822 },
823 _ => {
824 panic!("fetch worker should always send Done before terminating");
825 },
826 }
827 }
828 } else {
829 match *response.actual_response().body.lock().unwrap() {
830 ResponseBody::Done(ref vec) if !vec.is_empty() => {
831 target.process_response_chunk(request, vec.clone());
835 if context.devtools_chan.is_some() {
836 send_response_to_devtools(request, context, response, Some(vec.clone()));
839 }
840 },
841 ResponseBody::Done(_) | ResponseBody::Empty => {},
842 _ => unreachable!(),
843 }
844 }
845}
846
847pub enum RangeRequestBounds {
849 Final(RelativePos),
851 Pending(u64),
854}
855
856impl RangeRequestBounds {
857 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
858 match self {
859 RangeRequestBounds::Final(pos) => {
860 if let Some(len) = len {
861 if pos.start <= len as i64 {
862 return Ok(*pos);
863 }
864 }
865 Err("Tried to process RangeRequestBounds::Final without len")
866 },
867 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
868 if let Some(len) = len {
869 Some((len - u64::min(len, *offset)) as i64)
870 } else {
871 Some(0)
872 },
873 None,
874 )),
875 }
876 }
877}
878
879fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
880 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
881 response
882 .headers
883 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
884 *response.body.lock().unwrap() = ResponseBody::Done(vec![]);
885 response.status = HttpStatus::default();
886 response
887}
888
889fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
890 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
891 response
892 .headers
893 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
894 *response.body.lock().unwrap() =
895 ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
896 response.status = HttpStatus::default();
897 response
898}
899
900fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
902 let error = |string| Err(io::Error::other(string));
903
904 let body = match request.body.as_mut() {
905 Some(body) => body,
906 None => return error("No body found"),
907 };
908
909 let stream = body.take_stream();
910 let stream = stream.lock().unwrap();
911 let (body_chan, body_port) = ipc::channel().unwrap();
912 let _ = stream.send(BodyChunkRequest::Connect(body_chan));
913 let _ = stream.send(BodyChunkRequest::Chunk);
914 let body_bytes = match body_port.recv().ok() {
915 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
916 _ => return error("Certificate not sent in a single chunk"),
917 };
918
919 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
920 Some(split_idx) => split_idx,
921 None => return error("Could not find ampersand in data"),
922 };
923 let (secret, cert_base64) = body_bytes.split_at(split_idx);
924
925 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
926 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
927 return error("Invalid secret sent. Ignoring request");
928 }
929
930 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
931 Ok(bytes) => bytes,
932 Err(_) => return error("Could not decode certificate base64"),
933 };
934
935 context
936 .state
937 .override_manager
938 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
939 Ok(())
940}
941
942async fn scheme_fetch(
944 fetch_params: &mut FetchParams,
945 cache: &mut CorsCache,
946 target: Target<'_>,
947 done_chan: &mut DoneChannel,
948 context: &FetchContext,
949) -> Response {
950 let request = &mut fetch_params.request;
954 let url = request.current_url();
955
956 let scheme = url.scheme();
957 match scheme {
958 "about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()),
959 "about" if url.path() == "memory" => create_about_memory(url, request.timing_type()),
960
961 "chrome" if url.path() == "allowcert" => {
962 if let Err(error) = handle_allowcert_request(request, context) {
963 warn!("Could not handle allowcert request: {error}");
964 }
965 create_blank_reply(url, request.timing_type())
966 },
967
968 "http" | "https" => {
969 http_fetch(
970 fetch_params,
971 cache,
972 false,
973 false,
974 false,
975 target,
976 done_chan,
977 context,
978 )
979 .await
980 },
981
982 _ => match context.protocols.get(scheme) {
983 Some(handler) => handler.load(request, done_chan, context).await,
984 None => Response::network_error(NetworkError::Internal("Unexpected scheme".into())),
985 },
986 }
987}
988
989fn is_null_body_status(status: &HttpStatus) -> bool {
990 matches!(
991 status.try_code(),
992 Some(StatusCode::SWITCHING_PROTOCOLS) |
993 Some(StatusCode::NO_CONTENT) |
994 Some(StatusCode::RESET_CONTENT) |
995 Some(StatusCode::NOT_MODIFIED)
996 )
997}
998
999pub fn should_be_blocked_due_to_nosniff(
1001 destination: Destination,
1002 response_headers: &HeaderMap,
1003) -> bool {
1004 if !determine_nosniff(response_headers) {
1006 return false;
1007 }
1008
1009 let mime_type = extract_mime_type_as_mime(response_headers);
1012
1013 #[inline]
1015 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1016 let javascript_mime_types: [Mime; 16] = [
1017 "application/ecmascript".parse().unwrap(),
1018 "application/javascript".parse().unwrap(),
1019 "application/x-ecmascript".parse().unwrap(),
1020 "application/x-javascript".parse().unwrap(),
1021 "text/ecmascript".parse().unwrap(),
1022 "text/javascript".parse().unwrap(),
1023 "text/javascript1.0".parse().unwrap(),
1024 "text/javascript1.1".parse().unwrap(),
1025 "text/javascript1.2".parse().unwrap(),
1026 "text/javascript1.3".parse().unwrap(),
1027 "text/javascript1.4".parse().unwrap(),
1028 "text/javascript1.5".parse().unwrap(),
1029 "text/jscript".parse().unwrap(),
1030 "text/livescript".parse().unwrap(),
1031 "text/x-ecmascript".parse().unwrap(),
1032 "text/x-javascript".parse().unwrap(),
1033 ];
1034
1035 javascript_mime_types
1036 .iter()
1037 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1038 }
1039
1040 match mime_type {
1041 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1043 Some(ref mime_type) if destination == Destination::Style => {
1045 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1046 },
1047
1048 None if destination == Destination::Style || destination.is_script_like() => true,
1049 _ => false,
1051 }
1052}
1053
1054fn should_be_blocked_due_to_mime_type(
1056 destination: Destination,
1057 response_headers: &HeaderMap,
1058) -> bool {
1059 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1061 Some(mime_type) => mime_type,
1062 None => return false,
1064 };
1065
1066 destination.is_script_like() &&
1072 match mime_type.type_() {
1073 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1074 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1075 _ => false,
1076 }
1077}
1078
1079pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1081 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1086 let is_bad_port = url.port().is_some_and(is_bad_port);
1087 if is_http_scheme && is_bad_port {
1088 return true;
1089 }
1090
1091 false
1093}
1094
1095pub fn should_request_be_blocked_as_mixed_content(
1097 request: &Request,
1098 protocol_registry: &ProtocolRegistry,
1099) -> bool {
1100 if do_settings_prohibit_mixed_security_contexts(request) ==
1104 MixedSecurityProhibited::NotProhibited
1105 {
1106 return false;
1107 }
1108
1109 if is_url_potentially_trustworthy(protocol_registry, &request.url()) {
1111 return false;
1112 }
1113
1114 if request.destination == Destination::Document {
1119 return false;
1121 }
1122
1123 true
1124}
1125
1126pub fn should_response_be_blocked_as_mixed_content(
1128 request: &Request,
1129 response: &Response,
1130 protocol_registry: &ProtocolRegistry,
1131) -> bool {
1132 if do_settings_prohibit_mixed_security_contexts(request) ==
1136 MixedSecurityProhibited::NotProhibited
1137 {
1138 return false;
1139 }
1140
1141 if response
1143 .actual_response()
1144 .url()
1145 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1146 {
1147 return false;
1148 }
1149
1150 if request.destination == Destination::Document {
1155 return false;
1157 }
1158
1159 true
1160}
1161
1162fn is_bad_port(port: u16) -> bool {
1164 static BAD_PORTS: [u16; 78] = [
1165 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1166 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1167 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1168 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1169 6668, 6669, 6697, 10080,
1170 ];
1171
1172 BAD_PORTS.binary_search(&port).is_ok()
1173}
1174
1175pub fn is_form_submission_request(request: &Request) -> bool {
1177 let content_type = request.headers.typed_get::<ContentType>();
1178 content_type.is_some_and(|ct| {
1179 let mime: Mime = ct.into();
1180 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1181 })
1182}
1183
1184fn should_upgrade_request_to_potentially_trustworthy(
1186 request: &mut Request,
1187 context: &FetchContext,
1188) -> bool {
1189 fn should_upgrade_navigation_request(request: &Request) -> bool {
1190 if is_form_submission_request(request) {
1192 return true;
1193 }
1194
1195 false
1203 }
1204
1205 if request.is_navigation_request() {
1207 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1212 !request.current_url().host_str().is_some_and(|host| {
1213 !context.state.hsts_list.read().unwrap().is_host_secure(host)
1214 })
1215 {
1216 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1217 request
1218 .headers
1219 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1220 }
1221
1222 if !should_upgrade_navigation_request(request) {
1223 return false;
1224 }
1225 }
1226
1227 request.insecure_requests_policy == InsecureRequestsPolicy::Upgrade
1229}
1230
1231#[derive(Debug, PartialEq)]
1232pub enum MixedSecurityProhibited {
1233 Prohibited,
1234 NotProhibited,
1235}
1236
1237fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1239 if let Origin::Origin(ref origin) = request.origin {
1240 let is_origin_data_url_worker = matches!(
1242 *origin,
1243 ImmutableOrigin::Opaque(servo_url::OpaqueOrigin::SecureWorkerFromDataUrl(_))
1244 );
1245
1246 if origin.is_potentially_trustworthy() || is_origin_data_url_worker {
1249 return MixedSecurityProhibited::Prohibited;
1250 }
1251 }
1252
1253 if request.has_trustworthy_ancestor_origin {
1257 return MixedSecurityProhibited::Prohibited;
1258 }
1259
1260 MixedSecurityProhibited::NotProhibited
1261}
1262
1263fn should_upgrade_mixed_content_request(
1265 request: &Request,
1266 protocol_registry: &ProtocolRegistry,
1267) -> bool {
1268 let url = request.url();
1269 if is_url_potentially_trustworthy(protocol_registry, &url) {
1271 return false;
1272 }
1273
1274 match url.host() {
1276 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1277 _ => (),
1278 }
1279
1280 if do_settings_prohibit_mixed_security_contexts(request) ==
1282 MixedSecurityProhibited::NotProhibited
1283 {
1284 return false;
1285 }
1286
1287 if !matches!(
1289 request.destination,
1290 Destination::Audio | Destination::Image | Destination::Video
1291 ) {
1292 return false;
1293 }
1294
1295 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1297 return false;
1298 }
1299
1300 true
1301}