1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::{io, mem, str};
8
9use base64::Engine as _;
10use base64::engine::general_purpose;
11use bytes::Bytes;
12use content_security_policy as csp;
13use embedder_traits::resources::{self, Resource};
14use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt};
15use http::header::{self, HeaderMap, HeaderName, RANGE};
16use http::{HeaderValue, Method, StatusCode};
17use ipc_channel::ipc;
18use log::{debug, trace, warn};
19use malloc_size_of_derive::MallocSizeOf;
20use mime::{self, Mime};
21use net_traits::fetch::headers::{determine_nosniff, extract_mime_type_as_mime};
22use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
23use net_traits::http_status::HttpStatus;
24use net_traits::policy_container::PolicyContainer;
25use net_traits::request::{
26 BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator,
27 InsecureRequestsPolicy, InternalRequest, Origin, ParserMetadata, RedirectMode, Referrer,
28 Request, RequestBody, RequestId, RequestMode, ResponseTainting, is_cors_safelisted_method,
29 is_cors_safelisted_request_header,
30};
31use net_traits::response::{Response, ResponseBody, ResponseType, TerminationReason};
32use net_traits::{
33 FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
34 ResourceFetchTimingContainer, ResourceTimeValue, ResourceTimingType, WebSocketDomAction,
35 WebSocketNetworkEvent, set_default_accept_language,
36};
37use parking_lot::Mutex;
38use profile_traits::generic_callback::GenericCallback as ProfileGenericCallback;
39use rustc_hash::FxHashMap;
40use rustls_pki_types::CertificateDer;
41use serde::{Deserialize, Serialize};
42use servo_base::generic_channel::CallbackSetter;
43use servo_base::id::PipelineId;
44use servo_url::{Host, ServoUrl};
45use tokio::sync::Mutex as TokioMutex;
46use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender};
47
48use crate::connector::CACertificates;
49use crate::devtools::{
50 send_early_httprequest_to_devtools, send_response_to_devtools, send_security_info_to_devtools,
51};
52use crate::fetch::fetch_params::{
53 ConsumePreloadedResources, FetchParams, SharedPreloadedResources,
54};
55use crate::filemanager_thread::FileManager;
56use crate::http_loader::{HttpState, determine_requests_referrer, http_fetch, set_default_accept};
57use crate::protocols::{ProtocolRegistry, is_url_potentially_trustworthy};
58use crate::request_interceptor::RequestInterceptor;
59use crate::subresource_integrity::is_response_integrity_valid;
60
61pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send);
62
63#[derive(Clone, Deserialize, Serialize)]
64pub enum Data {
65 Payload(bytes::Bytes),
66 ContentLength(usize),
67 Done,
68 Cancelled,
69 Error(NetworkError),
70}
71
72pub struct WebSocketChannel {
73 pub sender: ProfileGenericCallback<WebSocketNetworkEvent>,
74 pub receiver: Option<CallbackSetter<WebSocketDomAction>>,
75}
76
77impl WebSocketChannel {
78 pub fn new(
79 sender: ProfileGenericCallback<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 #[cfg(feature = "devtools")]
102 pub devtools_chan: Option<crossbeam_channel::Sender<devtools_traits::DevtoolsControlMsg>>,
103 pub filemanager: FileManager,
104 pub file_token: FileTokenCheck,
105 pub request_interceptor: Arc<TokioMutex<RequestInterceptor>>,
106 pub cancellation_listener: Arc<CancellationListener>,
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}
129
130pub(crate) struct AutoRequestBodyStreamCloser {
134 body: Option<RequestBody>,
135}
136
137impl AutoRequestBodyStreamCloser {
138 pub(crate) fn new(body: Option<&RequestBody>) -> Self {
139 Self {
140 body: body.cloned(),
141 }
142 }
143
144 pub(crate) fn disarm(&mut self) {
145 self.body = None;
146 }
147}
148
149impl Drop for AutoRequestBodyStreamCloser {
150 fn drop(&mut self) {
151 if let Some(body) = self.body.take() {
152 body.close_stream();
153 }
154 }
155}
156
157pub(crate) fn transfers_request_body_stream_to_later_manual_redirect(
161 request: &Request,
162 response: &Response,
163) -> bool {
164 request.mode == RequestMode::Navigate &&
165 request.redirect_mode == RedirectMode::Manual &&
166 request.body.is_some() &&
167 !response.is_network_error() &&
168 response
169 .actual_response()
170 .status
171 .try_code()
172 .is_some_and(|status| status.is_redirection())
173}
174
175pub type DoneChannel = Option<(TokioSender<Data>, TokioReceiver<Data>)>;
176
177pub async fn fetch(request: Request, target: Target<'_>, context: &FetchContext) -> Response {
179 let timing_info: ResourceFetchTimingContainer =
184 ResourceFetchTiming::new(request.timing_type()).into();
185
186 timing_info.set_attributes(&[
187 ResourceAttribute::FetchStart,
188 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
189 ]);
190
191 let mut fetch_params = FetchParams::new(request, timing_info);
202 let mut request_body_stream_closer =
206 AutoRequestBodyStreamCloser::new(fetch_params.request.body.as_ref());
207 let request = &mut fetch_params.request;
208
209 request.populate_request_from_client();
211
212 if
221 matches!(request.current_url().scheme(), "http" | "https")
223 && matches!(request.mode, RequestMode::SameOrigin | RequestMode::CorsMode | RequestMode::NoCors)
225 && matches!(request.method, Method::GET)
227 && (!request.unsafe_request || request.headers.is_empty())
229 {
230 if let Some(client) = request.client.as_ref() {
232 assert!(request.origin == client.origin);
234 if let Some(candidate) =
242 client.consume_preloaded_resource(request, context.preloaded_resources.clone())
243 {
244 fetch_params.preload_response_candidate = candidate;
245 }
246 }
247 }
248
249 set_default_accept(request);
251
252 set_default_accept_language(&mut request.headers);
255
256 let should_track_in_flight_record = request.keep_alive && request.is_subresource_request();
265 let pipeline_id = request.pipeline_id;
266
267 if should_track_in_flight_record {
268 let record = InFlightKeepAliveRecord {
271 request_id: request.id,
272 keep_alive_body_length: request.keep_alive_body_length(),
273 };
274 let mut in_flight_records = context.in_flight_keep_alive_records.lock();
276 in_flight_records
277 .entry(pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
278 .or_default()
279 .push(record);
280 };
281 let request_id = request.id;
282
283 let response = main_fetch(&mut fetch_params, false, target, &mut None, context).await;
285
286 if transfers_request_body_stream_to_later_manual_redirect(&fetch_params.request, &response) {
287 request_body_stream_closer.disarm();
288 }
289
290 if should_track_in_flight_record {
292 context
293 .in_flight_keep_alive_records
294 .lock()
295 .get_mut(&pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
296 .expect("Must always have initialized tracked requests before starting fetch")
297 .retain(|record| record.request_id != request_id);
298 }
299
300 response
303}
304
305pub(crate) fn convert_request_to_csp_request(request: &Request) -> Option<csp::Request> {
306 if request.is_internal_request == InternalRequest::Yes {
307 return None;
308 }
309 let origin = match &request.origin {
310 Origin::Client => return None,
311 Origin::Origin(origin) => origin,
312 };
313
314 let csp_request = csp::Request {
315 url: request.url().into_url(),
316 current_url: request.current_url().into_url(),
317 origin: origin.clone().into_url_origin(),
318 redirect_count: request.redirect_count,
319 destination: request.destination,
320 initiator: match request.initiator {
321 Initiator::Download => csp::Initiator::Download,
322 Initiator::ImageSet => csp::Initiator::ImageSet,
323 Initiator::Manifest => csp::Initiator::Manifest,
324 Initiator::Prefetch => csp::Initiator::Prefetch,
325 _ => csp::Initiator::None,
326 },
327 nonce: request.cryptographic_nonce_metadata.clone(),
328 integrity_metadata: request.integrity_metadata.clone(),
329 parser_metadata: match request.parser_metadata {
330 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
331 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
332 ParserMetadata::Default => csp::ParserMetadata::None,
333 },
334 };
335 Some(csp_request)
336}
337
338pub fn should_request_be_blocked_by_csp(
340 csp_request: &csp::Request,
341 policy_container: &PolicyContainer,
342) -> (csp::CheckResult, Vec<csp::Violation>) {
343 policy_container
344 .csp_list
345 .as_ref()
346 .map(|c| c.should_request_be_blocked(csp_request))
347 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
348}
349
350pub fn report_violations_for_request_by_csp(
352 csp_request: &csp::Request,
353 policy_container: &PolicyContainer,
354) -> Vec<csp::Violation> {
355 policy_container
356 .csp_list
357 .as_ref()
358 .map(|c| c.report_violations_for_request(csp_request))
359 .unwrap_or_default()
360}
361
362fn should_response_be_blocked_by_csp(
363 csp_request: &csp::Request,
364 response: &Response,
365 policy_container: &PolicyContainer,
366) -> (csp::CheckResult, Vec<csp::Violation>) {
367 if response.is_network_error() {
368 return (csp::CheckResult::Allowed, Vec::new());
369 }
370 let csp_response = csp::Response {
371 url: response
372 .actual_response()
373 .url()
374 .cloned()
375 .map(|mut url| {
380 match csp_request.url.scheme() {
381 "ws" | "wss" => {
382 url.as_mut_url()
383 .set_scheme(csp_request.url.scheme())
384 .expect("failed to set URL scheme");
385 },
386 _ => {},
387 };
388 url
389 })
390 .expect("response must have a url")
391 .into_url(),
392 redirect_count: csp_request.redirect_count,
393 };
394 policy_container
395 .csp_list
396 .as_ref()
397 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
398 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
399}
400
401pub async fn main_fetch(
403 fetch_params: &mut FetchParams,
404 recursive_flag: bool,
405 target: Target<'_>,
406 done_chan: &mut DoneChannel,
407 context: &FetchContext,
408) -> Response {
409 let request = &mut fetch_params.request;
411 send_early_httprequest_to_devtools(request, context);
412 let mut response = None;
414
415 if let Some(ref details) = request.crash {
417 response = Some(Response::network_error(NetworkError::Crash(
418 details.clone(),
419 )));
420 }
421
422 if request.local_urls_only &&
425 !matches!(
426 request.current_url().scheme(),
427 "about" | "blob" | "data" | "filesystem"
428 )
429 {
430 response = Some(Response::network_error(NetworkError::UnsupportedScheme));
431 }
432
433 let csp_request = convert_request_to_csp_request(request);
436 if let Some(csp_request) = csp_request.as_ref() {
437 let violations = report_violations_for_request_by_csp(
439 csp_request,
440 request
441 .policy_container
442 .policy_container()
443 .expect("Always has a valid policycontainer"),
444 );
445
446 if !violations.is_empty() {
447 target.process_csp_violations(request, violations);
448 }
449 };
450
451 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
454 should_upgrade_mixed_content_request(request, &context.protocols)
455 {
456 trace!(
457 "upgrading {} targeting {:?}",
458 request.current_url(),
459 request.destination
460 );
461 if let Some(new_scheme) = match request.current_url().scheme() {
462 "http" => Some("https"),
463 "ws" => Some("wss"),
464 _ => None,
465 } {
466 request
467 .current_url_mut()
468 .as_mut_url()
469 .set_scheme(new_scheme)
470 .unwrap();
471 }
472 } else {
473 let insecure_requests_policy = request
474 .client
475 .as_ref()
476 .map(|client| client.insecure_requests_policy);
477 trace!(
478 "not upgrading {} targeting {:?} with {:?}",
479 request.current_url(),
480 request.destination,
481 insecure_requests_policy
482 );
483 }
484 if let Some(csp_request) = csp_request.as_ref() {
485 let (check_result, violations) = should_request_be_blocked_by_csp(
490 csp_request,
491 request
492 .policy_container
493 .policy_container()
494 .expect("Always has a valid policy container"),
495 );
496
497 if !violations.is_empty() {
498 target.process_csp_violations(request, violations);
499 }
500
501 if check_result == csp::CheckResult::Blocked {
502 warn!("Request blocked by CSP");
503 response = Some(Response::network_error(NetworkError::ContentSecurityPolicy))
504 }
505 };
506 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
507 response = Some(Response::network_error(NetworkError::InvalidPort));
508 }
509 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
510 response = Some(Response::network_error(NetworkError::MixedContent));
511 }
512
513 if request.referrer_policy == ReferrerPolicy::EmptyString {
516 request.referrer_policy = request
517 .policy_container
518 .policy_container()
519 .expect("Always has a valid policycontainer")
520 .get_referrer_policy();
521 }
522
523 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
526 Referrer::NoReferrer => None,
527 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
528 request.headers.remove(header::REFERER);
529 determine_requests_referrer(
530 request.referrer_policy,
531 referrer_source,
532 request.current_url(),
533 )
534 },
535 };
536 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
537
538 context
540 .state
541 .hsts_list
542 .read()
543 .apply_hsts_rules(request.current_url_mut());
544
545 let current_url = request.current_url();
549 let current_scheme = current_url.scheme();
550
551 context
553 .request_interceptor
554 .lock()
555 .await
556 .intercept_request(request, &mut response, &fetch_params.timing_info)
557 .await;
558
559 let mut response = match response {
560 Some(response) => response,
561 None => {
564 let same_origin = if let Origin::Origin(ref origin) = request.origin {
565 *origin == request.current_url_with_blob_claim().origin()
566 } else {
567 false
568 };
569
570 if let Some((response, preload_id)) =
572 fetch_params.preload_response_candidate.response().await
573 {
574 response.get_resource_timing().inner().preloaded = true;
575 context
576 .preloaded_resources
577 .lock()
578 .unwrap()
579 .remove(&preload_id);
580 response
581 }
582 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
585 current_scheme == "data" ||
587 context.protocols.is_fetchable(current_scheme) ||
590 matches!(
592 request.mode,
593 RequestMode::Navigate | RequestMode::WebSocket { .. }
594 )
595 {
596 request.response_tainting = ResponseTainting::Basic;
598
599 scheme_fetch(fetch_params, target, done_chan, context).await
601 } else if request.mode == RequestMode::SameOrigin {
602 Response::network_error(NetworkError::CrossOriginResponse)
603 } else if request.mode == RequestMode::NoCors {
604 if request.redirect_mode != RedirectMode::Follow {
606 Response::network_error(NetworkError::RedirectError)
607 } else {
608 request.response_tainting = ResponseTainting::Opaque;
610
611 scheme_fetch(fetch_params, target, done_chan, context).await
613 }
614 } else if !matches!(current_scheme, "http" | "https") {
615 Response::network_error(NetworkError::UnsupportedScheme)
616 } else if request.use_cors_preflight ||
617 (request.unsafe_request &&
618 (!is_cors_safelisted_method(&request.method) ||
619 request.headers.iter().any(|(name, value)| {
620 !is_cors_safelisted_request_header(&name, &value)
621 })))
622 {
623 request.response_tainting = ResponseTainting::CorsTainting;
625
626 let origin = request.origin.clone();
627 let url = request.current_url();
628
629 let cors_with_preflight_response =
632 http_fetch(fetch_params, true, true, false, target, done_chan, context).await;
633 if cors_with_preflight_response.is_network_error() {
635 context
636 .state
637 .cors_cache
638 .write()
639 .clear_entries_for_origin_and_url(&origin, &url);
640 }
641 cors_with_preflight_response
643 } else {
644 request.response_tainting = ResponseTainting::CorsTainting;
646
647 http_fetch(fetch_params, true, false, false, target, done_chan, context).await
649 }
650 },
651 };
652
653 if recursive_flag {
655 return response;
656 }
657
658 let request = &mut fetch_params.request;
660
661 if !response.is_network_error() && response.internal_response.is_none() {
663 if request.response_tainting == ResponseTainting::CorsTainting {
665 let header_names: Option<Vec<HeaderName>> = response
668 .headers
669 .typed_get::<AccessControlExposeHeaders>()
670 .map(|v| v.iter().collect());
671
672 if let Some(ref list) = header_names {
673 if request.credentials_mode != CredentialsMode::Include &&
677 list.iter().any(|header| header == "*")
678 {
679 response.cors_exposed_header_name_list = response
680 .headers
681 .iter()
682 .map(|(name, _)| name.as_str().to_owned())
683 .collect();
684 } else {
685 response.cors_exposed_header_name_list =
688 list.iter().map(|h| h.as_str().to_owned()).collect();
689 }
690 }
691 }
692
693 let response_type = match request.response_tainting {
696 ResponseTainting::Basic => ResponseType::Basic,
697 ResponseTainting::CorsTainting => ResponseType::Cors,
698 ResponseTainting::Opaque => ResponseType::Opaque,
699 };
700 response = response.to_filtered(response_type);
701 }
702
703 let internal_error = {
704 let response_is_network_error = response.is_network_error();
706 let should_replace_with_nosniff_error = !response_is_network_error &&
707 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
708 let should_replace_with_mime_type_error = !response_is_network_error &&
709 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
710 let should_replace_with_mixed_content = !response_is_network_error &&
711 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
712 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
713 let (check_result, violations) = should_response_be_blocked_by_csp(
714 &csp_request,
715 &response,
716 request
717 .policy_container
718 .policy_container()
719 .expect("Always has a valid policy container"),
720 );
721 if !violations.is_empty() {
722 target.process_csp_violations(request, violations);
723 }
724 check_result == csp::CheckResult::Blocked
725 });
726
727 let mut network_error_response = response
729 .get_network_error()
730 .cloned()
731 .map(Response::network_error);
732
733 let response_is_opaque = response.response_type == ResponseType::Opaque;
736 let internal_response = if let Some(error_response) = network_error_response.as_mut() {
737 error_response
738 } else {
739 response.actual_response_mut()
740 };
741
742 if internal_response.url_list.is_empty() {
744 internal_response.url_list = request
745 .url_list
746 .iter()
747 .map(|locked_url| locked_url.url())
748 .collect();
749 }
750
751 internal_response.redirect_taint = request.redirect_taint_for_request();
753
754 let mut blocked_error_response;
766
767 let internal_response = if should_replace_with_nosniff_error {
768 blocked_error_response = Response::network_error(NetworkError::Nosniff);
770 &blocked_error_response
771 } else if should_replace_with_mime_type_error {
772 blocked_error_response =
774 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
775 &blocked_error_response
776 } else if should_replace_with_mixed_content {
777 blocked_error_response = Response::network_error(NetworkError::MixedContent);
778 &blocked_error_response
779 } else if should_replace_with_csp_error {
780 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
781 &blocked_error_response
782 } else {
783 internal_response
784 };
785
786 let internal_response = if !internal_response.is_network_error() &&
792 response_is_opaque &&
793 internal_response.status.is_a_range_status() &&
794 internal_response.range_requested &&
795 !request.headers.contains_key(RANGE)
796 {
797 blocked_error_response =
799 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
800 &blocked_error_response
801 } else {
802 internal_response
803 };
804
805 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
810 if not_network_error &&
811 (is_null_body_status(&internal_response.status) ||
812 matches!(request.method, Method::HEAD | Method::CONNECT))
813 {
814 let mut body = internal_response.body.lock();
817 *body = ResponseBody::Empty;
818 }
819
820 internal_response.get_network_error().cloned()
821 };
822
823 if let Some(error) = internal_error {
825 response = Response::network_error(error);
826 }
827
828 let mut response_loaded = false;
830 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
831 wait_for_response(request, &mut response, target, done_chan, context).await;
833 response_loaded = true;
834
835 let integrity_metadata = &request.integrity_metadata;
837 if response.termination_reason.is_none() &&
838 !is_response_integrity_valid(integrity_metadata, &response)
839 {
840 Response::network_error(NetworkError::SubresourceIntegrity)
841 } else {
842 response
843 }
844 } else {
845 response
846 };
847
848 if request.synchronous {
850 target.process_response(request, &response);
853 if !response_loaded {
854 wait_for_response(request, &mut response, target, done_chan, context).await;
855 }
856 target.process_response_eof(request, &response);
858 return response;
859 }
860
861 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
863 target.process_request_body(request);
868 }
869
870 target.process_response(request, &response);
872 send_response_to_devtools(request, context, &response, None);
874 send_security_info_to_devtools(request, context, &response);
875
876 if !response_loaded {
878 wait_for_response(request, &mut response, target, done_chan, context).await;
879 }
880
881 target.process_response_eof(request, &response);
883 send_response_to_devtools(request, context, &response, None);
887
888 context
889 .state
890 .http_cache
891 .update_awaiting_consumers(request, &response)
892 .await;
893
894 response
897}
898
899async fn wait_for_response(
900 request: &Request,
901 response: &mut Response,
902 target: Target<'_>,
903 done_chan: &mut DoneChannel,
904 context: &FetchContext,
905) {
906 if let Some(ref mut ch) = *done_chan {
907 #[cfg(feature = "devtools")]
908 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
909 #[cfg(not(feature = "devtools"))]
910 let devtools_body = None;
911 loop {
912 match ch.1.recv().await {
913 Some(Data::ContentLength(length)) => {
914 target.process_response_length_hint(request, length);
915 },
916 Some(Data::Payload(bytes)) => {
917 #[cfg(feature = "devtools")]
918 if let Some(body) = devtools_body.as_mut() {
919 body.extend(&bytes);
920 }
921 target.process_response_chunk(request, bytes);
922 },
923 Some(Data::Error(network_error)) => {
924 if network_error == NetworkError::DecompressionError {
925 response.termination_reason = Some(TerminationReason::Fatal);
926 }
927 response.set_network_error(network_error);
928
929 break;
930 },
931 Some(Data::Done) => {
932 send_response_to_devtools(request, context, response, devtools_body);
933 break;
934 },
935 Some(Data::Cancelled) => {
936 response.aborted.store(true, Ordering::Release);
937 break;
938 },
939
940 None => {
941 panic!("fetch worker should always send Done before terminating");
942 },
943 }
944 }
945 } else {
946 match *response.actual_response().body.lock() {
947 ResponseBody::Done(ref vec) if !vec.is_empty() => {
948 target.process_response_chunk(request, Bytes::copy_from_slice(vec));
952 #[cfg(feature = "devtools")]
953 if context.devtools_chan.is_some() {
954 send_response_to_devtools(request, context, response, Some(vec.clone()));
957 }
958 },
959 ResponseBody::Done(_) | ResponseBody::Empty => {},
960 _ => unreachable!(),
961 }
962 }
963}
964
965pub enum RangeRequestBounds {
967 Final(RelativePos),
969 Pending(u64),
972}
973
974impl RangeRequestBounds {
975 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
976 match self {
977 RangeRequestBounds::Final(pos) => {
978 if let Some(len) = len &&
979 pos.start <= len as i64
980 {
981 return Ok(*pos);
982 }
983 Err("Tried to process RangeRequestBounds::Final without len")
984 },
985 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
986 if let Some(len) = len {
987 Some((len - u64::min(len, *offset)) as i64)
988 } else {
989 Some(0)
990 },
991 None,
992 )),
993 }
994 }
995}
996
997fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
998 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
999 response
1000 .headers
1001 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
1002 *response.body.lock() = ResponseBody::Done(vec![]);
1003 response.status = HttpStatus::default();
1004 response
1005}
1006
1007fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
1008 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
1009 response
1010 .headers
1011 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
1012 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
1013 response.status = HttpStatus::default();
1014 response
1015}
1016
1017fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
1019 let error = |string| Err(io::Error::other(string));
1020
1021 let body = match request.body.as_mut() {
1022 Some(body) => body,
1023 None => return error("No body found"),
1024 };
1025
1026 let stream = body.clone_stream();
1027 let mut stream = stream.lock();
1028 let (body_chan, body_port) = ipc::channel().unwrap();
1029 let Some(chunk_requester) = stream.as_mut() else {
1030 log::error!(
1031 "Could not connect to the request body stream because it has already been closed."
1032 );
1033 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1034 };
1035 chunk_requester
1036 .send(BodyChunkRequest::Connect(body_chan))
1037 .map_err(|error| {
1038 log::error!(
1039 "Could not connect to the request body stream because it has already been closed: {error}"
1040 );
1041 std::io::Error::other("Could not connect to request body stream")
1042 })?;
1043 chunk_requester
1044 .send(BodyChunkRequest::Chunk)
1045 .map_err(|error| {
1046 log::error!(
1047 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1048 );
1049 std::io::Error::other("Could not request request body chunk")
1050 })?;
1051 let body_bytes = match body_port.recv().ok() {
1052 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1053 _ => return error("Certificate not sent in a single chunk"),
1054 };
1055
1056 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1057 Some(split_idx) => split_idx,
1058 None => return error("Could not find ampersand in data"),
1059 };
1060 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1061
1062 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1063 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1064 return error("Invalid secret sent. Ignoring request");
1065 }
1066
1067 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1068 Ok(bytes) => bytes,
1069 Err(_) => return error("Could not decode certificate base64"),
1070 };
1071
1072 context
1073 .state
1074 .override_manager
1075 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1076 Ok(())
1077}
1078
1079async fn scheme_fetch(
1081 fetch_params: &mut FetchParams,
1082 target: Target<'_>,
1083 done_chan: &mut DoneChannel,
1084 context: &FetchContext,
1085) -> Response {
1086 let request = &mut fetch_params.request;
1090 let url_and_blob_lock = request.current_url_with_blob_claim();
1091
1092 let scheme = url_and_blob_lock.scheme();
1093 match scheme {
1094 "about" if url_and_blob_lock.path() == "blank" => {
1095 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1096 },
1097 "about" if url_and_blob_lock.path() == "memory" => {
1098 create_about_memory(url_and_blob_lock.url(), request.timing_type())
1099 },
1100
1101 "chrome" if url_and_blob_lock.path() == "allowcert" => {
1102 if let Err(error) = handle_allowcert_request(request, context) {
1103 warn!("Could not handle allowcert request: {error}");
1104 }
1105 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1106 },
1107
1108 "http" | "https" => {
1109 http_fetch(
1110 fetch_params,
1111 false,
1112 false,
1113 false,
1114 target,
1115 done_chan,
1116 context,
1117 )
1118 .await
1119 },
1120
1121 _ => match context.protocols.get(scheme) {
1122 Some(handler) => handler.load(request, done_chan, context).await,
1123 None => Response::network_error(NetworkError::UnsupportedScheme),
1124 },
1125 }
1126}
1127
1128fn is_null_body_status(status: &HttpStatus) -> bool {
1129 matches!(
1130 status.try_code(),
1131 Some(StatusCode::SWITCHING_PROTOCOLS) |
1132 Some(StatusCode::NO_CONTENT) |
1133 Some(StatusCode::RESET_CONTENT) |
1134 Some(StatusCode::NOT_MODIFIED)
1135 )
1136}
1137
1138pub fn should_be_blocked_due_to_nosniff(
1140 destination: Destination,
1141 response_headers: &HeaderMap,
1142) -> bool {
1143 if !determine_nosniff(response_headers) {
1145 return false;
1146 }
1147
1148 let mime_type = extract_mime_type_as_mime(response_headers);
1151
1152 #[inline]
1154 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1155 let javascript_mime_types: [Mime; 16] = [
1156 "application/ecmascript".parse().unwrap(),
1157 "application/javascript".parse().unwrap(),
1158 "application/x-ecmascript".parse().unwrap(),
1159 "application/x-javascript".parse().unwrap(),
1160 "text/ecmascript".parse().unwrap(),
1161 "text/javascript".parse().unwrap(),
1162 "text/javascript1.0".parse().unwrap(),
1163 "text/javascript1.1".parse().unwrap(),
1164 "text/javascript1.2".parse().unwrap(),
1165 "text/javascript1.3".parse().unwrap(),
1166 "text/javascript1.4".parse().unwrap(),
1167 "text/javascript1.5".parse().unwrap(),
1168 "text/jscript".parse().unwrap(),
1169 "text/livescript".parse().unwrap(),
1170 "text/x-ecmascript".parse().unwrap(),
1171 "text/x-javascript".parse().unwrap(),
1172 ];
1173
1174 javascript_mime_types
1175 .iter()
1176 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1177 }
1178
1179 match mime_type {
1180 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1182 Some(ref mime_type) if destination == Destination::Style => {
1184 mime_type.type_() != mime::TEXT || mime_type.subtype() != mime::CSS
1185 },
1186
1187 None if destination == Destination::Style || destination.is_script_like() => true,
1188 _ => false,
1190 }
1191}
1192
1193fn should_be_blocked_due_to_mime_type(
1195 destination: Destination,
1196 response_headers: &HeaderMap,
1197) -> bool {
1198 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1200 Some(mime_type) => mime_type,
1201 None => return false,
1203 };
1204
1205 destination.is_script_like() &&
1211 match mime_type.type_() {
1212 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1213 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1214 _ => false,
1215 }
1216}
1217
1218pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1220 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1225 let is_bad_port = url.port().is_some_and(is_bad_port);
1226 if is_http_scheme && is_bad_port {
1227 return true;
1228 }
1229
1230 false
1232}
1233
1234pub fn should_request_be_blocked_as_mixed_content(
1236 request: &Request,
1237 protocol_registry: &ProtocolRegistry,
1238) -> bool {
1239 if do_settings_prohibit_mixed_security_contexts(request) ==
1243 MixedSecurityProhibited::NotProhibited
1244 {
1245 return false;
1246 }
1247
1248 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1250 return false;
1251 }
1252
1253 if request.destination == Destination::Document {
1258 return false;
1260 }
1261
1262 true
1263}
1264
1265pub fn should_response_be_blocked_as_mixed_content(
1267 request: &Request,
1268 response: &Response,
1269 protocol_registry: &ProtocolRegistry,
1270) -> bool {
1271 if do_settings_prohibit_mixed_security_contexts(request) ==
1275 MixedSecurityProhibited::NotProhibited
1276 {
1277 return false;
1278 }
1279
1280 if response
1282 .actual_response()
1283 .url()
1284 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1285 {
1286 return false;
1287 }
1288
1289 if request.destination == Destination::Document {
1294 return false;
1296 }
1297
1298 true
1299}
1300
1301fn is_bad_port(port: u16) -> bool {
1303 static BAD_PORTS: [u16; 83] = [
1304 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95,
1305 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179,
1306 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601,
1307 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 6000, 6566,
1308 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080,
1309 ];
1310
1311 BAD_PORTS.binary_search(&port).is_ok()
1312}
1313
1314pub fn is_form_submission_request(request: &Request) -> bool {
1316 let content_type = request.headers.typed_get::<ContentType>();
1317 content_type.is_some_and(|ct| {
1318 let mime: Mime = ct.into();
1319 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1320 })
1321}
1322
1323fn should_upgrade_request_to_potentially_trustworthy(
1325 request: &mut Request,
1326 context: &FetchContext,
1327) -> bool {
1328 fn should_upgrade_navigation_request(request: &Request) -> bool {
1329 if is_form_submission_request(request) {
1331 return true;
1332 }
1333
1334 if request
1337 .client
1338 .as_ref()
1339 .is_some_and(|client| client.is_nested_browsing_context)
1340 {
1341 return true;
1342 }
1343
1344 false
1349 }
1350
1351 if request.is_navigation_request() {
1353 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1358 request
1359 .current_url()
1360 .host_str()
1361 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1362 {
1363 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1364 request
1365 .headers
1366 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1367 }
1368
1369 if !should_upgrade_navigation_request(request) {
1370 return false;
1371 }
1372 }
1373
1374 request
1378 .client
1379 .as_ref()
1380 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1381}
1382
1383#[derive(Debug, PartialEq)]
1384pub enum MixedSecurityProhibited {
1385 Prohibited,
1386 NotProhibited,
1387}
1388
1389fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1391 let Some(ref client) = request.client else {
1392 return MixedSecurityProhibited::NotProhibited;
1393 };
1394
1395 let Origin::Origin(ref origin) = client.origin else {
1396 unreachable!("Settings' origin is never a \"client\"");
1397 };
1398
1399 if origin.is_potentially_trustworthy() || origin.is_for_data_worker_from_secure_context() {
1403 return MixedSecurityProhibited::Prohibited;
1404 }
1405
1406 if client.has_trustworthy_ancestor_origin {
1410 return MixedSecurityProhibited::Prohibited;
1411 }
1412
1413 MixedSecurityProhibited::NotProhibited
1414}
1415
1416fn should_upgrade_mixed_content_request(
1418 request: &Request,
1419 protocol_registry: &ProtocolRegistry,
1420) -> bool {
1421 let url = request.url();
1422 if is_url_potentially_trustworthy(protocol_registry, &url) {
1424 return false;
1425 }
1426
1427 match url.host() {
1429 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1430 _ => (),
1431 }
1432
1433 if do_settings_prohibit_mixed_security_contexts(request) ==
1435 MixedSecurityProhibited::NotProhibited
1436 {
1437 return false;
1438 }
1439
1440 if !matches!(
1442 request.destination,
1443 Destination::Audio | Destination::Image | Destination::Video
1444 ) {
1445 return false;
1446 }
1447
1448 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1450 return false;
1451 }
1452
1453 true
1454}