1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::{io, mem, str};
8
9use base::generic_channel::CallbackSetter;
10use base::id::PipelineId;
11use base64::Engine as _;
12use base64::engine::general_purpose;
13use content_security_policy as csp;
14use crossbeam_channel::Sender;
15use devtools_traits::DevtoolsControlMsg;
16use embedder_traits::resources::{self, Resource};
17use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt};
18use http::header::{self, HeaderMap, HeaderName, RANGE};
19use http::{HeaderValue, Method, StatusCode};
20use ipc_channel::ipc::{self, IpcSender};
21use log::{debug, trace, warn};
22use malloc_size_of_derive::MallocSizeOf;
23use mime::{self, Mime};
24use net_traits::fetch::headers::{determine_nosniff, extract_mime_type_as_mime};
25use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
26use net_traits::http_status::HttpStatus;
27use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
28use net_traits::request::{
29 BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator,
30 InsecureRequestsPolicy, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestId,
31 RequestMode, ResponseTainting, is_cors_safelisted_method, is_cors_safelisted_request_header,
32};
33use net_traits::response::{Response, ResponseBody, ResponseType, TerminationReason};
34use net_traits::{
35 FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
36 ResourceTimeValue, ResourceTimingType, WebSocketDomAction, WebSocketNetworkEvent,
37 set_default_accept_language,
38};
39use parking_lot::Mutex;
40use rustc_hash::FxHashMap;
41use rustls_pki_types::CertificateDer;
42use serde::{Deserialize, Serialize};
43use servo_arc::Arc as ServoArc;
44use servo_url::{Host, ImmutableOrigin, ServoUrl};
45use tokio::sync::Mutex as TokioMutex;
46use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender};
47
48use crate::connector::CACertificates;
49use crate::fetch::cors_cache::CorsCache;
50use crate::fetch::fetch_params::{
51 ConsumePreloadedResources, FetchParams, SharedPreloadedResources,
52};
53use crate::filemanager_thread::FileManager;
54use crate::http_loader::{
55 HttpState, determine_requests_referrer, http_fetch, send_early_httprequest_to_devtools,
56 send_response_to_devtools, send_security_info_to_devtools, set_default_accept,
57};
58use crate::protocols::{ProtocolRegistry, is_url_potentially_trustworthy};
59use crate::request_interceptor::RequestInterceptor;
60use crate::subresource_integrity::is_response_integrity_valid;
61
62pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send);
63
64#[derive(Clone, Deserialize, Serialize)]
65pub enum Data {
66 Payload(Vec<u8>),
67 Done,
68 Cancelled,
69 Error(NetworkError),
70}
71
72pub struct WebSocketChannel {
73 pub sender: IpcSender<WebSocketNetworkEvent>,
74 pub receiver: Option<CallbackSetter<WebSocketDomAction>>,
75}
76
77impl WebSocketChannel {
78 pub fn new(
79 sender: IpcSender<WebSocketNetworkEvent>,
80 receiver: Option<CallbackSetter<WebSocketDomAction>>,
81 ) -> Self {
82 Self { sender, receiver }
83 }
84}
85
86#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
88pub struct InFlightKeepAliveRecord {
89 pub(crate) request_id: RequestId,
90 pub(crate) keep_alive_body_length: u64,
92}
93
94pub type SharedInflightKeepAliveRecords =
95 Arc<Mutex<FxHashMap<PipelineId, Vec<InFlightKeepAliveRecord>>>>;
96
97#[derive(Clone)]
98pub struct FetchContext {
99 pub state: Arc<HttpState>,
100 pub user_agent: String,
101 pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
102 pub filemanager: FileManager,
103 pub file_token: FileTokenCheck,
104 pub request_interceptor: Arc<TokioMutex<RequestInterceptor>>,
105 pub cancellation_listener: Arc<CancellationListener>,
106 pub timing: ServoArc<Mutex<ResourceFetchTiming>>,
107 pub protocols: Arc<ProtocolRegistry>,
108 pub websocket_chan: Option<Arc<Mutex<WebSocketChannel>>>,
109 pub ca_certificates: CACertificates<'static>,
110 pub ignore_certificate_errors: bool,
111 pub preloaded_resources: SharedPreloadedResources,
112 pub in_flight_keep_alive_records: SharedInflightKeepAliveRecords,
113}
114
115#[derive(Default)]
116pub struct CancellationListener {
117 cancelled: AtomicBool,
118}
119
120impl CancellationListener {
121 pub(crate) fn cancelled(&self) -> bool {
122 self.cancelled.load(Ordering::Relaxed)
123 }
124
125 pub(crate) fn cancel(&self) {
126 self.cancelled.store(true, Ordering::Relaxed)
127 }
128}
129pub type DoneChannel = Option<(TokioSender<Data>, TokioReceiver<Data>)>;
130
131pub async fn fetch(request: Request, target: Target<'_>, context: &FetchContext) -> Response {
133 {
136 let mut timing_guard = context.timing.lock();
137 timing_guard.set_attribute(ResourceAttribute::FetchStart);
138 timing_guard.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
139 }
140 fetch_with_cors_cache(request, &mut CorsCache::default(), target, context).await
141}
142
143pub async fn fetch_with_cors_cache(
147 request: Request,
148 cache: &mut CorsCache,
149 target: Target<'_>,
150 context: &FetchContext,
151) -> Response {
152 let mut fetch_params = FetchParams::new(request);
154 let request = &mut fetch_params.request;
155
156 request.populate_request_from_client();
158
159 if
168 matches!(request.current_url().scheme(), "http" | "https")
170 && matches!(request.mode, RequestMode::SameOrigin | RequestMode::CorsMode | RequestMode::NoCors)
172 && matches!(request.method, Method::GET)
174 && (!request.unsafe_request || request.headers.is_empty())
176 {
177 if let Some(client) = request.client.as_ref() {
179 assert!(request.origin == client.origin);
181 if let Some(candidate) =
189 client.consume_preloaded_resource(request, context.preloaded_resources.clone())
190 {
191 fetch_params.preload_response_candidate = candidate;
192 }
193 }
194 }
195
196 set_default_accept(request);
198
199 set_default_accept_language(&mut request.headers);
202
203 let should_track_in_flight_record = request.keep_alive && request.is_subresource_request();
212 let pipeline_id = request.pipeline_id;
213
214 if should_track_in_flight_record {
215 let record = InFlightKeepAliveRecord {
218 request_id: request.id,
219 keep_alive_body_length: request.keep_alive_body_length(),
220 };
221 let mut in_flight_records = context.in_flight_keep_alive_records.lock();
223 in_flight_records
224 .entry(pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
225 .or_default()
226 .push(record);
227 };
228 let request_id = request.id;
229
230 let response = main_fetch(&mut fetch_params, cache, false, target, &mut None, context).await;
232
233 if should_track_in_flight_record {
235 context
236 .in_flight_keep_alive_records
237 .lock()
238 .get_mut(&pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
239 .expect("Must always have initialized tracked requests before starting fetch")
240 .retain(|record| record.request_id != request_id);
241 }
242
243 response
246}
247
248pub(crate) fn convert_request_to_csp_request(request: &Request) -> Option<csp::Request> {
249 let origin = match &request.origin {
250 Origin::Client => return None,
251 Origin::Origin(origin) => origin,
252 };
253
254 let mut original_url = request.original_url();
257 if original_url.scheme() == "ws" && request.url().scheme() == "https" {
258 original_url.as_mut_url().set_scheme("wss").unwrap();
259 }
260
261 let csp_request = csp::Request {
262 url: original_url.into_url(),
263 origin: origin.clone().into_url_origin(),
264 redirect_count: request.redirect_count,
265 destination: request.destination,
266 initiator: match request.initiator {
267 Initiator::Download => csp::Initiator::Download,
268 Initiator::ImageSet => csp::Initiator::ImageSet,
269 Initiator::Manifest => csp::Initiator::Manifest,
270 Initiator::Prefetch => csp::Initiator::Prefetch,
271 _ => csp::Initiator::None,
272 },
273 nonce: request.cryptographic_nonce_metadata.clone(),
274 integrity_metadata: request.integrity_metadata.clone(),
275 parser_metadata: match request.parser_metadata {
276 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
277 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
278 ParserMetadata::Default => csp::ParserMetadata::None,
279 },
280 };
281 Some(csp_request)
282}
283
284pub fn should_request_be_blocked_by_csp(
286 csp_request: &csp::Request,
287 policy_container: &PolicyContainer,
288) -> (csp::CheckResult, Vec<csp::Violation>) {
289 policy_container
290 .csp_list
291 .as_ref()
292 .map(|c| c.should_request_be_blocked(csp_request))
293 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
294}
295
296pub fn report_violations_for_request_by_csp(
298 csp_request: &csp::Request,
299 policy_container: &PolicyContainer,
300) -> Vec<csp::Violation> {
301 policy_container
302 .csp_list
303 .as_ref()
304 .map(|c| c.report_violations_for_request(csp_request))
305 .unwrap_or_default()
306}
307
308fn should_response_be_blocked_by_csp(
309 csp_request: &csp::Request,
310 response: &Response,
311 policy_container: &PolicyContainer,
312) -> (csp::CheckResult, Vec<csp::Violation>) {
313 if response.is_network_error() {
314 return (csp::CheckResult::Allowed, Vec::new());
315 }
316 let csp_response = csp::Response {
317 url: response
318 .actual_response()
319 .url()
320 .cloned()
321 .map(|mut url| {
326 match csp_request.url.scheme() {
327 "ws" | "wss" => {
328 url.as_mut_url()
329 .set_scheme(csp_request.url.scheme())
330 .expect("failed to set URL scheme");
331 },
332 _ => {},
333 };
334 url
335 })
336 .expect("response must have a url")
337 .into_url(),
338 redirect_count: csp_request.redirect_count,
339 };
340 policy_container
341 .csp_list
342 .as_ref()
343 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
344 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
345}
346
347pub async fn main_fetch(
349 fetch_params: &mut FetchParams,
350 cache: &mut CorsCache,
351 recursive_flag: bool,
352 target: Target<'_>,
353 done_chan: &mut DoneChannel,
354 context: &FetchContext,
355) -> Response {
356 let request = &mut fetch_params.request;
358 send_early_httprequest_to_devtools(request, context);
359 let mut response = None;
361
362 if let Some(ref details) = request.crash {
364 response = Some(Response::network_error(NetworkError::Crash(
365 details.clone(),
366 )));
367 }
368
369 if request.local_urls_only &&
372 !matches!(
373 request.current_url().scheme(),
374 "about" | "blob" | "data" | "filesystem"
375 )
376 {
377 response = Some(Response::network_error(NetworkError::UnsupportedScheme));
378 }
379
380 let policy_container = match &request.policy_container {
382 RequestPolicyContainer::Client => unreachable!(),
383 RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
384 };
385 let csp_request = convert_request_to_csp_request(request);
386 if let Some(csp_request) = csp_request.as_ref() {
387 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
389
390 if !violations.is_empty() {
391 target.process_csp_violations(request, violations);
392 }
393 };
394
395 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
400 should_upgrade_mixed_content_request(request, &context.protocols)
401 {
402 trace!(
403 "upgrading {} targeting {:?}",
404 request.current_url(),
405 request.destination
406 );
407 if let Some(new_scheme) = match request.current_url().scheme() {
408 "http" => Some("https"),
409 "ws" => Some("wss"),
410 _ => None,
411 } {
412 request
413 .current_url_mut()
414 .as_mut_url()
415 .set_scheme(new_scheme)
416 .unwrap();
417 }
418 } else {
419 trace!(
420 "not upgrading {} targeting {:?} with {:?}",
421 request.current_url(),
422 request.destination,
423 request.insecure_requests_policy
424 );
425 }
426 if let Some(csp_request) = csp_request.as_ref() {
427 let (check_result, violations) =
431 should_request_be_blocked_by_csp(csp_request, &policy_container);
432
433 if !violations.is_empty() {
434 target.process_csp_violations(request, violations);
435 }
436
437 if check_result == csp::CheckResult::Blocked {
438 warn!("Request blocked by CSP");
439 response = Some(Response::network_error(NetworkError::ContentSecurityPolicy))
440 }
441 };
442 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
443 response = Some(Response::network_error(NetworkError::InvalidPort));
444 }
445 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
446 response = Some(Response::network_error(NetworkError::MixedContent));
447 }
448
449 if request.referrer_policy == ReferrerPolicy::EmptyString {
452 request.referrer_policy = policy_container.get_referrer_policy();
453 }
454
455 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
456 Referrer::NoReferrer => None,
457 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
458 request.headers.remove(header::REFERER);
459 determine_requests_referrer(
460 request.referrer_policy,
461 referrer_source,
462 request.current_url(),
463 )
464 },
465 };
466 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
467
468 context
473 .state
474 .hsts_list
475 .read()
476 .apply_hsts_rules(request.current_url_mut());
477
478 let current_url = request.current_url();
482 let current_scheme = current_url.scheme();
483
484 context
486 .request_interceptor
487 .lock()
488 .await
489 .intercept_request(request, &mut response, context)
490 .await;
491
492 let mut response = match response {
493 Some(res) => res,
494 None => {
495 let same_origin = if let Origin::Origin(ref origin) = request.origin {
498 *origin == current_url.origin()
499 } else {
500 false
501 };
502
503 if let Some((response, preload_id)) =
505 fetch_params.preload_response_candidate.response().await
506 {
507 response.get_resource_timing().lock().preloaded = true;
508 context
509 .preloaded_resources
510 .lock()
511 .unwrap()
512 .remove(&preload_id);
513 response
514 }
515 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
518 current_scheme == "data" ||
520 context.protocols.is_fetchable(current_scheme) ||
523 matches!(
525 request.mode,
526 RequestMode::Navigate | RequestMode::WebSocket { .. }
527 )
528 {
529 request.response_tainting = ResponseTainting::Basic;
531
532 scheme_fetch(fetch_params, cache, target, done_chan, context).await
534 } else if request.mode == RequestMode::SameOrigin {
535 Response::network_error(NetworkError::CrossOriginResponse)
536 } else if request.mode == RequestMode::NoCors {
537 if request.redirect_mode != RedirectMode::Follow {
539 Response::network_error(NetworkError::RedirectError)
540 } else {
541 request.response_tainting = ResponseTainting::Opaque;
543
544 scheme_fetch(fetch_params, cache, target, done_chan, context).await
546 }
547 } else if !matches!(current_scheme, "http" | "https") {
548 Response::network_error(NetworkError::UnsupportedScheme)
549 } else if request.use_cors_preflight ||
550 (request.unsafe_request &&
551 (!is_cors_safelisted_method(&request.method) ||
552 request.headers.iter().any(|(name, value)| {
553 !is_cors_safelisted_request_header(&name, &value)
554 })))
555 {
556 request.response_tainting = ResponseTainting::CorsTainting;
558 let response = http_fetch(
560 fetch_params,
561 cache,
562 true,
563 true,
564 false,
565 target,
566 done_chan,
567 context,
568 )
569 .await;
570 if response.is_network_error() {
572 }
574 response
576 } else {
577 request.response_tainting = ResponseTainting::CorsTainting;
579 http_fetch(
581 fetch_params,
582 cache,
583 true,
584 false,
585 false,
586 target,
587 done_chan,
588 context,
589 )
590 .await
591 }
592 },
593 };
594
595 if recursive_flag {
597 return response;
598 }
599
600 let request = &mut fetch_params.request;
602
603 let mut response = if !response.is_network_error() && response.internal_response.is_none() {
605 if request.response_tainting == ResponseTainting::CorsTainting {
607 let header_names: Option<Vec<HeaderName>> = response
609 .headers
610 .typed_get::<AccessControlExposeHeaders>()
611 .map(|v| v.iter().collect());
612 match header_names {
613 Some(ref list)
615 if request.credentials_mode != CredentialsMode::Include &&
616 list.iter().any(|header| header == "*") =>
617 {
618 response.cors_exposed_header_name_list = response
619 .headers
620 .iter()
621 .map(|(name, _)| name.as_str().to_owned())
622 .collect();
623 },
624 Some(list) => {
626 response.cors_exposed_header_name_list =
627 list.iter().map(|h| h.as_str().to_owned()).collect();
628 },
629 _ => (),
630 }
631 }
632
633 let response_type = match request.response_tainting {
635 ResponseTainting::Basic => ResponseType::Basic,
636 ResponseTainting::CorsTainting => ResponseType::Cors,
637 ResponseTainting::Opaque => ResponseType::Opaque,
638 };
639 response.to_filtered(response_type)
640 } else {
641 response
642 };
643
644 let internal_error = {
645 let response_is_network_error = response.is_network_error();
647 let should_replace_with_nosniff_error = !response_is_network_error &&
648 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
649 let should_replace_with_mime_type_error = !response_is_network_error &&
650 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
651 let should_replace_with_mixed_content = !response_is_network_error &&
652 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
653 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
654 let (check_result, violations) =
655 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
656 if !violations.is_empty() {
657 target.process_csp_violations(request, violations);
658 }
659 check_result == csp::CheckResult::Blocked
660 });
661
662 let mut network_error_response = response
664 .get_network_error()
665 .cloned()
666 .map(Response::network_error);
667
668 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
672 error_response
673 } else {
674 response.actual_response_mut()
675 };
676
677 if internal_response.url_list.is_empty() {
679 internal_response.url_list.clone_from(&request.url_list)
680 }
681
682 internal_response.redirect_taint = request.redirect_taint_for_request();
684
685 let mut blocked_error_response;
691
692 let internal_response = if should_replace_with_nosniff_error {
693 blocked_error_response = Response::network_error(NetworkError::Nosniff);
695 &blocked_error_response
696 } else if should_replace_with_mime_type_error {
697 blocked_error_response =
699 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
700 &blocked_error_response
701 } else if should_replace_with_mixed_content {
702 blocked_error_response = Response::network_error(NetworkError::MixedContent);
703 &blocked_error_response
704 } else if should_replace_with_csp_error {
705 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
706 &blocked_error_response
707 } else {
708 internal_response
709 };
710
711 let internal_response = if !internal_response.is_network_error() &&
717 response_type == ResponseType::Opaque &&
718 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
719 internal_response.range_requested &&
720 !request.headers.contains_key(RANGE)
721 {
722 blocked_error_response =
724 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
725 &blocked_error_response
726 } else {
727 internal_response
728 };
729
730 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
735 if not_network_error &&
736 (is_null_body_status(&internal_response.status) ||
737 matches!(request.method, Method::HEAD | Method::CONNECT))
738 {
739 let mut body = internal_response.body.lock();
742 *body = ResponseBody::Empty;
743 }
744
745 internal_response.get_network_error().cloned()
746 };
747
748 let mut response = if let Some(error) = internal_error {
750 Response::network_error(error)
751 } else {
752 response
753 };
754
755 let mut response_loaded = false;
757 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
758 wait_for_response(request, &mut response, target, done_chan, context).await;
760 response_loaded = true;
761
762 let integrity_metadata = &request.integrity_metadata;
764 if response.termination_reason.is_none() &&
765 !is_response_integrity_valid(integrity_metadata, &response)
766 {
767 Response::network_error(NetworkError::SubresourceIntegrity)
768 } else {
769 response
770 }
771 } else {
772 response
773 };
774
775 if request.synchronous {
777 target.process_response(request, &response);
780 if !response_loaded {
781 wait_for_response(request, &mut response, target, done_chan, context).await;
782 }
783 target.process_response_eof(request, &response);
785 return response;
786 }
787
788 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
790 target.process_request_body(request);
795 target.process_request_eof(request);
796 }
797
798 target.process_response(request, &response);
800 send_response_to_devtools(request, context, &response, None);
802 send_security_info_to_devtools(request, context, &response);
803
804 if !response_loaded {
806 wait_for_response(request, &mut response, target, done_chan, context).await;
807 }
808
809 target.process_response_eof(request, &response);
811 send_response_to_devtools(request, context, &response, None);
815
816 context
817 .state
818 .http_cache
819 .update_awaiting_consumers(request, &response)
820 .await;
821
822 response
825}
826
827async fn wait_for_response(
828 request: &Request,
829 response: &mut Response,
830 target: Target<'_>,
831 done_chan: &mut DoneChannel,
832 context: &FetchContext,
833) {
834 if let Some(ref mut ch) = *done_chan {
835 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
836 loop {
837 match ch.1.recv().await {
838 Some(Data::Payload(vec)) => {
839 if let Some(body) = devtools_body.as_mut() {
840 body.extend(&vec);
841 }
842 target.process_response_chunk(request, vec);
843 },
844 Some(Data::Error(network_error)) => {
845 if network_error == NetworkError::DecompressionError {
846 response.termination_reason = Some(TerminationReason::Fatal);
847 }
848 response.set_network_error(network_error);
849
850 break;
851 },
852 Some(Data::Done) => {
853 send_response_to_devtools(request, context, response, devtools_body);
854 break;
855 },
856 Some(Data::Cancelled) => {
857 response.aborted.store(true, Ordering::Release);
858 break;
859 },
860 _ => {
861 panic!("fetch worker should always send Done before terminating");
862 },
863 }
864 }
865 } else {
866 match *response.actual_response().body.lock() {
867 ResponseBody::Done(ref vec) if !vec.is_empty() => {
868 target.process_response_chunk(request, vec.clone());
872 if context.devtools_chan.is_some() {
873 send_response_to_devtools(request, context, response, Some(vec.clone()));
876 }
877 },
878 ResponseBody::Done(_) | ResponseBody::Empty => {},
879 _ => unreachable!(),
880 }
881 }
882}
883
884pub enum RangeRequestBounds {
886 Final(RelativePos),
888 Pending(u64),
891}
892
893impl RangeRequestBounds {
894 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
895 match self {
896 RangeRequestBounds::Final(pos) => {
897 if let Some(len) = len {
898 if pos.start <= len as i64 {
899 return Ok(*pos);
900 }
901 }
902 Err("Tried to process RangeRequestBounds::Final without len")
903 },
904 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
905 if let Some(len) = len {
906 Some((len - u64::min(len, *offset)) as i64)
907 } else {
908 Some(0)
909 },
910 None,
911 )),
912 }
913 }
914}
915
916fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
917 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
918 response
919 .headers
920 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
921 *response.body.lock() = ResponseBody::Done(vec![]);
922 response.status = HttpStatus::default();
923 response
924}
925
926fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
927 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
928 response
929 .headers
930 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
931 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
932 response.status = HttpStatus::default();
933 response
934}
935
936fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
938 let error = |string| Err(io::Error::other(string));
939
940 let body = match request.body.as_mut() {
941 Some(body) => body,
942 None => return error("No body found"),
943 };
944
945 let stream = body.take_stream();
946 let stream = stream.lock();
947 let (body_chan, body_port) = ipc::channel().unwrap();
948 let _ = stream.send(BodyChunkRequest::Connect(body_chan));
949 let _ = stream.send(BodyChunkRequest::Chunk);
950 let body_bytes = match body_port.recv().ok() {
951 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
952 _ => return error("Certificate not sent in a single chunk"),
953 };
954
955 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
956 Some(split_idx) => split_idx,
957 None => return error("Could not find ampersand in data"),
958 };
959 let (secret, cert_base64) = body_bytes.split_at(split_idx);
960
961 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
962 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
963 return error("Invalid secret sent. Ignoring request");
964 }
965
966 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
967 Ok(bytes) => bytes,
968 Err(_) => return error("Could not decode certificate base64"),
969 };
970
971 context
972 .state
973 .override_manager
974 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
975 Ok(())
976}
977
978async fn scheme_fetch(
980 fetch_params: &mut FetchParams,
981 cache: &mut CorsCache,
982 target: Target<'_>,
983 done_chan: &mut DoneChannel,
984 context: &FetchContext,
985) -> Response {
986 let request = &mut fetch_params.request;
990 let url = request.current_url();
991
992 let scheme = url.scheme();
993 match scheme {
994 "about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()),
995 "about" if url.path() == "memory" => create_about_memory(url, request.timing_type()),
996
997 "chrome" if url.path() == "allowcert" => {
998 if let Err(error) = handle_allowcert_request(request, context) {
999 warn!("Could not handle allowcert request: {error}");
1000 }
1001 create_blank_reply(url, request.timing_type())
1002 },
1003
1004 "http" | "https" => {
1005 http_fetch(
1006 fetch_params,
1007 cache,
1008 false,
1009 false,
1010 false,
1011 target,
1012 done_chan,
1013 context,
1014 )
1015 .await
1016 },
1017
1018 _ => match context.protocols.get(scheme) {
1019 Some(handler) => handler.load(request, done_chan, context).await,
1020 None => Response::network_error(NetworkError::UnsupportedScheme),
1021 },
1022 }
1023}
1024
1025fn is_null_body_status(status: &HttpStatus) -> bool {
1026 matches!(
1027 status.try_code(),
1028 Some(StatusCode::SWITCHING_PROTOCOLS) |
1029 Some(StatusCode::NO_CONTENT) |
1030 Some(StatusCode::RESET_CONTENT) |
1031 Some(StatusCode::NOT_MODIFIED)
1032 )
1033}
1034
1035pub fn should_be_blocked_due_to_nosniff(
1037 destination: Destination,
1038 response_headers: &HeaderMap,
1039) -> bool {
1040 if !determine_nosniff(response_headers) {
1042 return false;
1043 }
1044
1045 let mime_type = extract_mime_type_as_mime(response_headers);
1048
1049 #[inline]
1051 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1052 let javascript_mime_types: [Mime; 16] = [
1053 "application/ecmascript".parse().unwrap(),
1054 "application/javascript".parse().unwrap(),
1055 "application/x-ecmascript".parse().unwrap(),
1056 "application/x-javascript".parse().unwrap(),
1057 "text/ecmascript".parse().unwrap(),
1058 "text/javascript".parse().unwrap(),
1059 "text/javascript1.0".parse().unwrap(),
1060 "text/javascript1.1".parse().unwrap(),
1061 "text/javascript1.2".parse().unwrap(),
1062 "text/javascript1.3".parse().unwrap(),
1063 "text/javascript1.4".parse().unwrap(),
1064 "text/javascript1.5".parse().unwrap(),
1065 "text/jscript".parse().unwrap(),
1066 "text/livescript".parse().unwrap(),
1067 "text/x-ecmascript".parse().unwrap(),
1068 "text/x-javascript".parse().unwrap(),
1069 ];
1070
1071 javascript_mime_types
1072 .iter()
1073 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1074 }
1075
1076 match mime_type {
1077 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1079 Some(ref mime_type) if destination == Destination::Style => {
1081 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1082 },
1083
1084 None if destination == Destination::Style || destination.is_script_like() => true,
1085 _ => false,
1087 }
1088}
1089
1090fn should_be_blocked_due_to_mime_type(
1092 destination: Destination,
1093 response_headers: &HeaderMap,
1094) -> bool {
1095 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1097 Some(mime_type) => mime_type,
1098 None => return false,
1100 };
1101
1102 destination.is_script_like() &&
1108 match mime_type.type_() {
1109 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1110 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1111 _ => false,
1112 }
1113}
1114
1115pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1117 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1122 let is_bad_port = url.port().is_some_and(is_bad_port);
1123 if is_http_scheme && is_bad_port {
1124 return true;
1125 }
1126
1127 false
1129}
1130
1131pub fn should_request_be_blocked_as_mixed_content(
1133 request: &Request,
1134 protocol_registry: &ProtocolRegistry,
1135) -> bool {
1136 if do_settings_prohibit_mixed_security_contexts(request) ==
1140 MixedSecurityProhibited::NotProhibited
1141 {
1142 return false;
1143 }
1144
1145 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1147 return false;
1148 }
1149
1150 if request.destination == Destination::Document {
1155 return false;
1157 }
1158
1159 true
1160}
1161
1162pub fn should_response_be_blocked_as_mixed_content(
1164 request: &Request,
1165 response: &Response,
1166 protocol_registry: &ProtocolRegistry,
1167) -> bool {
1168 if do_settings_prohibit_mixed_security_contexts(request) ==
1172 MixedSecurityProhibited::NotProhibited
1173 {
1174 return false;
1175 }
1176
1177 if response
1179 .actual_response()
1180 .url()
1181 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1182 {
1183 return false;
1184 }
1185
1186 if request.destination == Destination::Document {
1191 return false;
1193 }
1194
1195 true
1196}
1197
1198fn is_bad_port(port: u16) -> bool {
1200 static BAD_PORTS: [u16; 78] = [
1201 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1202 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1203 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1204 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1205 6668, 6669, 6697, 10080,
1206 ];
1207
1208 BAD_PORTS.binary_search(&port).is_ok()
1209}
1210
1211pub fn is_form_submission_request(request: &Request) -> bool {
1213 let content_type = request.headers.typed_get::<ContentType>();
1214 content_type.is_some_and(|ct| {
1215 let mime: Mime = ct.into();
1216 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1217 })
1218}
1219
1220fn should_upgrade_request_to_potentially_trustworthy(
1222 request: &mut Request,
1223 context: &FetchContext,
1224) -> bool {
1225 fn should_upgrade_navigation_request(request: &Request) -> bool {
1226 if is_form_submission_request(request) {
1228 return true;
1229 }
1230
1231 if request
1234 .client
1235 .as_ref()
1236 .is_some_and(|client| client.is_nested_browsing_context)
1237 {
1238 return true;
1239 }
1240
1241 false
1246 }
1247
1248 if request.is_navigation_request() {
1250 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1255 request
1256 .current_url()
1257 .host_str()
1258 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1259 {
1260 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1261 request
1262 .headers
1263 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1264 }
1265
1266 if !should_upgrade_navigation_request(request) {
1267 return false;
1268 }
1269 }
1270
1271 request
1275 .client
1276 .as_ref()
1277 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1278}
1279
1280#[derive(Debug, PartialEq)]
1281pub enum MixedSecurityProhibited {
1282 Prohibited,
1283 NotProhibited,
1284}
1285
1286fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1288 if let Origin::Origin(ref origin) = request.origin {
1289 let is_origin_data_url_worker = matches!(
1291 *origin,
1292 ImmutableOrigin::Opaque(servo_url::OpaqueOrigin::SecureWorkerFromDataUrl(_))
1293 );
1294
1295 if origin.is_potentially_trustworthy() || is_origin_data_url_worker {
1298 return MixedSecurityProhibited::Prohibited;
1299 }
1300 }
1301
1302 if request.has_trustworthy_ancestor_origin {
1306 return MixedSecurityProhibited::Prohibited;
1307 }
1308
1309 MixedSecurityProhibited::NotProhibited
1310}
1311
1312fn should_upgrade_mixed_content_request(
1314 request: &Request,
1315 protocol_registry: &ProtocolRegistry,
1316) -> bool {
1317 let url = request.url();
1318 if is_url_potentially_trustworthy(protocol_registry, &url) {
1320 return false;
1321 }
1322
1323 match url.host() {
1325 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1326 _ => (),
1327 }
1328
1329 if do_settings_prohibit_mixed_security_contexts(request) ==
1331 MixedSecurityProhibited::NotProhibited
1332 {
1333 return false;
1334 }
1335
1336 if !matches!(
1338 request.destination,
1339 Destination::Audio | Destination::Image | Destination::Video
1340 ) {
1341 return false;
1342 }
1343
1344 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1346 return false;
1347 }
1348
1349 true
1350}