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 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, IpcSender};
19use log::{debug, trace, warn};
20use malloc_size_of_derive::MallocSizeOf;
21use mime::{self, Mime};
22use net_traits::fetch::headers::{determine_nosniff, extract_mime_type_as_mime};
23use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
24use net_traits::http_status::HttpStatus;
25use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
26use net_traits::request::{
27 BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator,
28 InsecureRequestsPolicy, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestBody,
29 RequestId, RequestMode, ResponseTainting, is_cors_safelisted_method,
30 is_cors_safelisted_request_header,
31};
32use net_traits::response::{Response, ResponseBody, ResponseType, TerminationReason};
33use net_traits::{
34 FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
35 ResourceFetchTimingContainer, ResourceTimeValue, ResourceTimingType, WebSocketDomAction,
36 WebSocketNetworkEvent, set_default_accept_language,
37};
38use parking_lot::Mutex;
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::cors_cache::CorsCache;
53use crate::fetch::fetch_params::{
54 ConsumePreloadedResources, FetchParams, SharedPreloadedResources,
55};
56use crate::filemanager_thread::FileManager;
57use crate::http_loader::{HttpState, determine_requests_referrer, http_fetch, set_default_accept};
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: ResourceFetchTimingContainer,
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 context.timing.set_attributes(&[
182 ResourceAttribute::FetchStart,
183 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
184 ]);
185 fetch_with_cors_cache(request, &mut CorsCache::default(), target, context).await
186}
187
188pub async fn fetch_with_cors_cache(
192 request: Request,
193 cache: &mut CorsCache,
194 target: Target<'_>,
195 context: &FetchContext,
196) -> Response {
197 let mut fetch_params = FetchParams::new(request);
199 let mut request_body_stream_closer =
203 AutoRequestBodyStreamCloser::new(fetch_params.request.body.as_ref());
204 let request = &mut fetch_params.request;
205
206 request.populate_request_from_client();
208
209 if
218 matches!(request.current_url().scheme(), "http" | "https")
220 && matches!(request.mode, RequestMode::SameOrigin | RequestMode::CorsMode | RequestMode::NoCors)
222 && matches!(request.method, Method::GET)
224 && (!request.unsafe_request || request.headers.is_empty())
226 {
227 if let Some(client) = request.client.as_ref() {
229 assert!(request.origin == client.origin);
231 if let Some(candidate) =
239 client.consume_preloaded_resource(request, context.preloaded_resources.clone())
240 {
241 fetch_params.preload_response_candidate = candidate;
242 }
243 }
244 }
245
246 set_default_accept(request);
248
249 set_default_accept_language(&mut request.headers);
252
253 let should_track_in_flight_record = request.keep_alive && request.is_subresource_request();
262 let pipeline_id = request.pipeline_id;
263
264 if should_track_in_flight_record {
265 let record = InFlightKeepAliveRecord {
268 request_id: request.id,
269 keep_alive_body_length: request.keep_alive_body_length(),
270 };
271 let mut in_flight_records = context.in_flight_keep_alive_records.lock();
273 in_flight_records
274 .entry(pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
275 .or_default()
276 .push(record);
277 };
278 let request_id = request.id;
279
280 let response = main_fetch(&mut fetch_params, cache, false, target, &mut None, context).await;
282
283 if transfers_request_body_stream_to_later_manual_redirect(&fetch_params.request, &response) {
284 request_body_stream_closer.disarm();
285 }
286
287 if should_track_in_flight_record {
289 context
290 .in_flight_keep_alive_records
291 .lock()
292 .get_mut(&pipeline_id.expect("Must always set a pipeline ID for keep-alive requests"))
293 .expect("Must always have initialized tracked requests before starting fetch")
294 .retain(|record| record.request_id != request_id);
295 }
296
297 response
300}
301
302pub(crate) fn convert_request_to_csp_request(request: &Request) -> Option<csp::Request> {
303 let origin = match &request.origin {
304 Origin::Client => return None,
305 Origin::Origin(origin) => origin,
306 };
307
308 let csp_request = csp::Request {
309 url: request.url().into_url(),
310 current_url: request.current_url().into_url(),
311 origin: origin.clone().into_url_origin(),
312 redirect_count: request.redirect_count,
313 destination: request.destination,
314 initiator: match request.initiator {
315 Initiator::Download => csp::Initiator::Download,
316 Initiator::ImageSet => csp::Initiator::ImageSet,
317 Initiator::Manifest => csp::Initiator::Manifest,
318 Initiator::Prefetch => csp::Initiator::Prefetch,
319 _ => csp::Initiator::None,
320 },
321 nonce: request.cryptographic_nonce_metadata.clone(),
322 integrity_metadata: request.integrity_metadata.clone(),
323 parser_metadata: match request.parser_metadata {
324 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
325 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
326 ParserMetadata::Default => csp::ParserMetadata::None,
327 },
328 };
329 Some(csp_request)
330}
331
332pub fn should_request_be_blocked_by_csp(
334 csp_request: &csp::Request,
335 policy_container: &PolicyContainer,
336) -> (csp::CheckResult, Vec<csp::Violation>) {
337 policy_container
338 .csp_list
339 .as_ref()
340 .map(|c| c.should_request_be_blocked(csp_request))
341 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
342}
343
344pub fn report_violations_for_request_by_csp(
346 csp_request: &csp::Request,
347 policy_container: &PolicyContainer,
348) -> Vec<csp::Violation> {
349 policy_container
350 .csp_list
351 .as_ref()
352 .map(|c| c.report_violations_for_request(csp_request))
353 .unwrap_or_default()
354}
355
356fn should_response_be_blocked_by_csp(
357 csp_request: &csp::Request,
358 response: &Response,
359 policy_container: &PolicyContainer,
360) -> (csp::CheckResult, Vec<csp::Violation>) {
361 if response.is_network_error() {
362 return (csp::CheckResult::Allowed, Vec::new());
363 }
364 let csp_response = csp::Response {
365 url: response
366 .actual_response()
367 .url()
368 .cloned()
369 .map(|mut url| {
374 match csp_request.url.scheme() {
375 "ws" | "wss" => {
376 url.as_mut_url()
377 .set_scheme(csp_request.url.scheme())
378 .expect("failed to set URL scheme");
379 },
380 _ => {},
381 };
382 url
383 })
384 .expect("response must have a url")
385 .into_url(),
386 redirect_count: csp_request.redirect_count,
387 };
388 policy_container
389 .csp_list
390 .as_ref()
391 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
392 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
393}
394
395pub async fn main_fetch(
397 fetch_params: &mut FetchParams,
398 cache: &mut CorsCache,
399 recursive_flag: bool,
400 target: Target<'_>,
401 done_chan: &mut DoneChannel,
402 context: &FetchContext,
403) -> Response {
404 let request = &mut fetch_params.request;
406 send_early_httprequest_to_devtools(request, context);
407 let mut response = None;
409
410 if let Some(ref details) = request.crash {
412 response = Some(Response::network_error(NetworkError::Crash(
413 details.clone(),
414 )));
415 }
416
417 if request.local_urls_only &&
420 !matches!(
421 request.current_url().scheme(),
422 "about" | "blob" | "data" | "filesystem"
423 )
424 {
425 response = Some(Response::network_error(NetworkError::UnsupportedScheme));
426 }
427
428 let policy_container = match &request.policy_container {
430 RequestPolicyContainer::Client => unreachable!(),
431 RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
432 };
433 let csp_request = convert_request_to_csp_request(request);
434 if let Some(csp_request) = csp_request.as_ref() {
435 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
437
438 if !violations.is_empty() {
439 target.process_csp_violations(request, violations);
440 }
441 };
442
443 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
448 should_upgrade_mixed_content_request(request, &context.protocols)
449 {
450 trace!(
451 "upgrading {} targeting {:?}",
452 request.current_url(),
453 request.destination
454 );
455 if let Some(new_scheme) = match request.current_url().scheme() {
456 "http" => Some("https"),
457 "ws" => Some("wss"),
458 _ => None,
459 } {
460 request
461 .current_url_mut()
462 .as_mut_url()
463 .set_scheme(new_scheme)
464 .unwrap();
465 }
466 } else {
467 trace!(
468 "not upgrading {} targeting {:?} with {:?}",
469 request.current_url(),
470 request.destination,
471 request.insecure_requests_policy
472 );
473 }
474 if let Some(csp_request) = csp_request.as_ref() {
475 let (check_result, violations) =
479 should_request_be_blocked_by_csp(csp_request, &policy_container);
480
481 if !violations.is_empty() {
482 target.process_csp_violations(request, violations);
483 }
484
485 if check_result == csp::CheckResult::Blocked {
486 warn!("Request blocked by CSP");
487 response = Some(Response::network_error(NetworkError::ContentSecurityPolicy))
488 }
489 };
490 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
491 response = Some(Response::network_error(NetworkError::InvalidPort));
492 }
493 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
494 response = Some(Response::network_error(NetworkError::MixedContent));
495 }
496
497 if request.referrer_policy == ReferrerPolicy::EmptyString {
500 request.referrer_policy = policy_container.get_referrer_policy();
501 }
502
503 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
504 Referrer::NoReferrer => None,
505 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
506 request.headers.remove(header::REFERER);
507 determine_requests_referrer(
508 request.referrer_policy,
509 referrer_source,
510 request.current_url(),
511 )
512 },
513 };
514 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
515
516 context
521 .state
522 .hsts_list
523 .read()
524 .apply_hsts_rules(request.current_url_mut());
525
526 let current_url = request.current_url();
530 let current_scheme = current_url.scheme();
531
532 context
534 .request_interceptor
535 .lock()
536 .await
537 .intercept_request(request, &mut response, context)
538 .await;
539
540 let mut response = match response {
541 Some(response) => response,
542 None => {
543 let same_origin = if let Origin::Origin(ref origin) = request.origin {
546 *origin == request.current_url_with_blob_claim().origin()
547 } else {
548 false
549 };
550
551 if let Some((response, preload_id)) =
553 fetch_params.preload_response_candidate.response().await
554 {
555 response.get_resource_timing().inner().preloaded = true;
556 context
557 .preloaded_resources
558 .lock()
559 .unwrap()
560 .remove(&preload_id);
561 response
562 }
563 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
566 current_scheme == "data" ||
568 context.protocols.is_fetchable(current_scheme) ||
571 matches!(
573 request.mode,
574 RequestMode::Navigate | RequestMode::WebSocket { .. }
575 )
576 {
577 request.response_tainting = ResponseTainting::Basic;
579
580 scheme_fetch(fetch_params, cache, target, done_chan, context).await
582 } else if request.mode == RequestMode::SameOrigin {
583 Response::network_error(NetworkError::CrossOriginResponse)
584 } else if request.mode == RequestMode::NoCors {
585 if request.redirect_mode != RedirectMode::Follow {
587 Response::network_error(NetworkError::RedirectError)
588 } else {
589 request.response_tainting = ResponseTainting::Opaque;
591
592 scheme_fetch(fetch_params, cache, target, done_chan, context).await
594 }
595 } else if !matches!(current_scheme, "http" | "https") {
596 Response::network_error(NetworkError::UnsupportedScheme)
597 } else if request.use_cors_preflight ||
598 (request.unsafe_request &&
599 (!is_cors_safelisted_method(&request.method) ||
600 request.headers.iter().any(|(name, value)| {
601 !is_cors_safelisted_request_header(&name, &value)
602 })))
603 {
604 request.response_tainting = ResponseTainting::CorsTainting;
606 let response = http_fetch(
608 fetch_params,
609 cache,
610 true,
611 true,
612 false,
613 target,
614 done_chan,
615 context,
616 )
617 .await;
618 if response.is_network_error() {
620 }
622 response
624 } else {
625 request.response_tainting = ResponseTainting::CorsTainting;
627 http_fetch(
629 fetch_params,
630 cache,
631 true,
632 false,
633 false,
634 target,
635 done_chan,
636 context,
637 )
638 .await
639 }
640 },
641 };
642
643 if recursive_flag {
645 return response;
646 }
647
648 let request = &mut fetch_params.request;
650
651 let mut response = if !response.is_network_error() && response.internal_response.is_none() {
653 if request.response_tainting == ResponseTainting::CorsTainting {
655 let header_names: Option<Vec<HeaderName>> = response
658 .headers
659 .typed_get::<AccessControlExposeHeaders>()
660 .map(|v| v.iter().collect());
661 match header_names {
662 Some(ref list)
664 if request.credentials_mode != CredentialsMode::Include &&
665 list.iter().any(|header| header == "*") =>
666 {
667 response.cors_exposed_header_name_list = response
668 .headers
669 .iter()
670 .map(|(name, _)| name.as_str().to_owned())
671 .collect();
672 },
673 Some(list) => {
675 response.cors_exposed_header_name_list =
676 list.iter().map(|h| h.as_str().to_owned()).collect();
677 },
678 _ => (),
679 }
680 }
681
682 let response_type = match request.response_tainting {
685 ResponseTainting::Basic => ResponseType::Basic,
686 ResponseTainting::CorsTainting => ResponseType::Cors,
687 ResponseTainting::Opaque => ResponseType::Opaque,
688 };
689 response.to_filtered(response_type)
690 } else {
691 response
692 };
693
694 let internal_error = {
695 let response_is_network_error = response.is_network_error();
697 let should_replace_with_nosniff_error = !response_is_network_error &&
698 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
699 let should_replace_with_mime_type_error = !response_is_network_error &&
700 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
701 let should_replace_with_mixed_content = !response_is_network_error &&
702 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
703 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
704 let (check_result, violations) =
705 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
706 if !violations.is_empty() {
707 target.process_csp_violations(request, violations);
708 }
709 check_result == csp::CheckResult::Blocked
710 });
711
712 let mut network_error_response = response
714 .get_network_error()
715 .cloned()
716 .map(Response::network_error);
717
718 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
722 error_response
723 } else {
724 response.actual_response_mut()
725 };
726
727 if internal_response.url_list.is_empty() {
729 internal_response.url_list = request
730 .url_list
731 .iter()
732 .map(|locked_url| locked_url.url())
733 .collect();
734 }
735
736 internal_response.redirect_taint = request.redirect_taint_for_request();
738
739 let mut blocked_error_response;
745
746 let internal_response = if should_replace_with_nosniff_error {
747 blocked_error_response = Response::network_error(NetworkError::Nosniff);
749 &blocked_error_response
750 } else if should_replace_with_mime_type_error {
751 blocked_error_response =
753 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
754 &blocked_error_response
755 } else if should_replace_with_mixed_content {
756 blocked_error_response = Response::network_error(NetworkError::MixedContent);
757 &blocked_error_response
758 } else if should_replace_with_csp_error {
759 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
760 &blocked_error_response
761 } else {
762 internal_response
763 };
764
765 let internal_response = if !internal_response.is_network_error() &&
771 response_type == ResponseType::Opaque &&
772 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
773 internal_response.range_requested &&
774 !request.headers.contains_key(RANGE)
775 {
776 blocked_error_response =
778 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
779 &blocked_error_response
780 } else {
781 internal_response
782 };
783
784 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
789 if not_network_error &&
790 (is_null_body_status(&internal_response.status) ||
791 matches!(request.method, Method::HEAD | Method::CONNECT))
792 {
793 let mut body = internal_response.body.lock();
796 *body = ResponseBody::Empty;
797 }
798
799 internal_response.get_network_error().cloned()
800 };
801
802 let mut response = if let Some(error) = internal_error {
804 Response::network_error(error)
805 } else {
806 response
807 };
808
809 let mut response_loaded = false;
811 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
812 wait_for_response(request, &mut response, target, done_chan, context).await;
814 response_loaded = true;
815
816 let integrity_metadata = &request.integrity_metadata;
818 if response.termination_reason.is_none() &&
819 !is_response_integrity_valid(integrity_metadata, &response)
820 {
821 Response::network_error(NetworkError::SubresourceIntegrity)
822 } else {
823 response
824 }
825 } else {
826 response
827 };
828
829 if request.synchronous {
831 target.process_response(request, &response);
834 if !response_loaded {
835 wait_for_response(request, &mut response, target, done_chan, context).await;
836 }
837 target.process_response_eof(request, &response);
839 return response;
840 }
841
842 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
844 target.process_request_body(request);
849 }
850
851 target.process_response(request, &response);
853 send_response_to_devtools(request, context, &response, None);
855 send_security_info_to_devtools(request, context, &response);
856
857 if !response_loaded {
859 wait_for_response(request, &mut response, target, done_chan, context).await;
860 }
861
862 target.process_response_eof(request, &response);
864 send_response_to_devtools(request, context, &response, None);
868
869 context
870 .state
871 .http_cache
872 .update_awaiting_consumers(request, &response)
873 .await;
874
875 response
878}
879
880async fn wait_for_response(
881 request: &Request,
882 response: &mut Response,
883 target: Target<'_>,
884 done_chan: &mut DoneChannel,
885 context: &FetchContext,
886) {
887 if let Some(ref mut ch) = *done_chan {
888 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
889 loop {
890 match ch.1.recv().await {
891 Some(Data::Payload(vec)) => {
892 if let Some(body) = devtools_body.as_mut() {
893 body.extend(&vec);
894 }
895 target.process_response_chunk(request, vec);
896 },
897 Some(Data::Error(network_error)) => {
898 if network_error == NetworkError::DecompressionError {
899 response.termination_reason = Some(TerminationReason::Fatal);
900 }
901 response.set_network_error(network_error);
902
903 break;
904 },
905 Some(Data::Done) => {
906 send_response_to_devtools(request, context, response, devtools_body);
907 break;
908 },
909 Some(Data::Cancelled) => {
910 response.aborted.store(true, Ordering::Release);
911 break;
912 },
913 _ => {
914 panic!("fetch worker should always send Done before terminating");
915 },
916 }
917 }
918 } else {
919 match *response.actual_response().body.lock() {
920 ResponseBody::Done(ref vec) if !vec.is_empty() => {
921 target.process_response_chunk(request, vec.clone());
925 if context.devtools_chan.is_some() {
926 send_response_to_devtools(request, context, response, Some(vec.clone()));
929 }
930 },
931 ResponseBody::Done(_) | ResponseBody::Empty => {},
932 _ => unreachable!(),
933 }
934 }
935}
936
937pub enum RangeRequestBounds {
939 Final(RelativePos),
941 Pending(u64),
944}
945
946impl RangeRequestBounds {
947 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
948 match self {
949 RangeRequestBounds::Final(pos) => {
950 if let Some(len) = len {
951 if pos.start <= len as i64 {
952 return Ok(*pos);
953 }
954 }
955 Err("Tried to process RangeRequestBounds::Final without len")
956 },
957 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
958 if let Some(len) = len {
959 Some((len - u64::min(len, *offset)) as i64)
960 } else {
961 Some(0)
962 },
963 None,
964 )),
965 }
966 }
967}
968
969fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
970 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
971 response
972 .headers
973 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
974 *response.body.lock() = ResponseBody::Done(vec![]);
975 response.status = HttpStatus::default();
976 response
977}
978
979fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
980 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
981 response
982 .headers
983 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
984 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
985 response.status = HttpStatus::default();
986 response
987}
988
989fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
991 let error = |string| Err(io::Error::other(string));
992
993 let body = match request.body.as_mut() {
994 Some(body) => body,
995 None => return error("No body found"),
996 };
997
998 let stream = body.clone_stream();
999 let mut stream = stream.lock();
1000 let (body_chan, body_port) = ipc::channel().unwrap();
1001 let Some(chunk_requester) = stream.as_mut() else {
1002 log::error!(
1003 "Could not connect to the request body stream because it has already been closed."
1004 );
1005 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1006 };
1007 chunk_requester
1008 .send(BodyChunkRequest::Connect(body_chan))
1009 .map_err(|error| {
1010 log::error!(
1011 "Could not connect to the request body stream because it has already been closed: {error}"
1012 );
1013 std::io::Error::other("Could not connect to request body stream")
1014 })?;
1015 chunk_requester
1016 .send(BodyChunkRequest::Chunk)
1017 .map_err(|error| {
1018 log::error!(
1019 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1020 );
1021 std::io::Error::other("Could not request request body chunk")
1022 })?;
1023 let body_bytes = match body_port.recv().ok() {
1024 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1025 _ => return error("Certificate not sent in a single chunk"),
1026 };
1027
1028 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1029 Some(split_idx) => split_idx,
1030 None => return error("Could not find ampersand in data"),
1031 };
1032 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1033
1034 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1035 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1036 return error("Invalid secret sent. Ignoring request");
1037 }
1038
1039 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1040 Ok(bytes) => bytes,
1041 Err(_) => return error("Could not decode certificate base64"),
1042 };
1043
1044 context
1045 .state
1046 .override_manager
1047 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1048 Ok(())
1049}
1050
1051async fn scheme_fetch(
1053 fetch_params: &mut FetchParams,
1054 cache: &mut CorsCache,
1055 target: Target<'_>,
1056 done_chan: &mut DoneChannel,
1057 context: &FetchContext,
1058) -> Response {
1059 let request = &mut fetch_params.request;
1063 let url_and_blob_lock = request.current_url_with_blob_claim();
1064
1065 let scheme = url_and_blob_lock.scheme();
1066 match scheme {
1067 "about" if url_and_blob_lock.path() == "blank" => {
1068 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1069 },
1070 "about" if url_and_blob_lock.path() == "memory" => {
1071 create_about_memory(url_and_blob_lock.url(), request.timing_type())
1072 },
1073
1074 "chrome" if url_and_blob_lock.path() == "allowcert" => {
1075 if let Err(error) = handle_allowcert_request(request, context) {
1076 warn!("Could not handle allowcert request: {error}");
1077 }
1078 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1079 },
1080
1081 "http" | "https" => {
1082 http_fetch(
1083 fetch_params,
1084 cache,
1085 false,
1086 false,
1087 false,
1088 target,
1089 done_chan,
1090 context,
1091 )
1092 .await
1093 },
1094
1095 _ => match context.protocols.get(scheme) {
1096 Some(handler) => handler.load(request, done_chan, context).await,
1097 None => Response::network_error(NetworkError::UnsupportedScheme),
1098 },
1099 }
1100}
1101
1102fn is_null_body_status(status: &HttpStatus) -> bool {
1103 matches!(
1104 status.try_code(),
1105 Some(StatusCode::SWITCHING_PROTOCOLS) |
1106 Some(StatusCode::NO_CONTENT) |
1107 Some(StatusCode::RESET_CONTENT) |
1108 Some(StatusCode::NOT_MODIFIED)
1109 )
1110}
1111
1112pub fn should_be_blocked_due_to_nosniff(
1114 destination: Destination,
1115 response_headers: &HeaderMap,
1116) -> bool {
1117 if !determine_nosniff(response_headers) {
1119 return false;
1120 }
1121
1122 let mime_type = extract_mime_type_as_mime(response_headers);
1125
1126 #[inline]
1128 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1129 let javascript_mime_types: [Mime; 16] = [
1130 "application/ecmascript".parse().unwrap(),
1131 "application/javascript".parse().unwrap(),
1132 "application/x-ecmascript".parse().unwrap(),
1133 "application/x-javascript".parse().unwrap(),
1134 "text/ecmascript".parse().unwrap(),
1135 "text/javascript".parse().unwrap(),
1136 "text/javascript1.0".parse().unwrap(),
1137 "text/javascript1.1".parse().unwrap(),
1138 "text/javascript1.2".parse().unwrap(),
1139 "text/javascript1.3".parse().unwrap(),
1140 "text/javascript1.4".parse().unwrap(),
1141 "text/javascript1.5".parse().unwrap(),
1142 "text/jscript".parse().unwrap(),
1143 "text/livescript".parse().unwrap(),
1144 "text/x-ecmascript".parse().unwrap(),
1145 "text/x-javascript".parse().unwrap(),
1146 ];
1147
1148 javascript_mime_types
1149 .iter()
1150 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1151 }
1152
1153 match mime_type {
1154 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1156 Some(ref mime_type) if destination == Destination::Style => {
1158 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1159 },
1160
1161 None if destination == Destination::Style || destination.is_script_like() => true,
1162 _ => false,
1164 }
1165}
1166
1167fn should_be_blocked_due_to_mime_type(
1169 destination: Destination,
1170 response_headers: &HeaderMap,
1171) -> bool {
1172 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1174 Some(mime_type) => mime_type,
1175 None => return false,
1177 };
1178
1179 destination.is_script_like() &&
1185 match mime_type.type_() {
1186 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1187 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1188 _ => false,
1189 }
1190}
1191
1192pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1194 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1199 let is_bad_port = url.port().is_some_and(is_bad_port);
1200 if is_http_scheme && is_bad_port {
1201 return true;
1202 }
1203
1204 false
1206}
1207
1208pub fn should_request_be_blocked_as_mixed_content(
1210 request: &Request,
1211 protocol_registry: &ProtocolRegistry,
1212) -> bool {
1213 if do_settings_prohibit_mixed_security_contexts(request) ==
1217 MixedSecurityProhibited::NotProhibited
1218 {
1219 return false;
1220 }
1221
1222 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1224 return false;
1225 }
1226
1227 if request.destination == Destination::Document {
1232 return false;
1234 }
1235
1236 true
1237}
1238
1239pub fn should_response_be_blocked_as_mixed_content(
1241 request: &Request,
1242 response: &Response,
1243 protocol_registry: &ProtocolRegistry,
1244) -> bool {
1245 if do_settings_prohibit_mixed_security_contexts(request) ==
1249 MixedSecurityProhibited::NotProhibited
1250 {
1251 return false;
1252 }
1253
1254 if response
1256 .actual_response()
1257 .url()
1258 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1259 {
1260 return false;
1261 }
1262
1263 if request.destination == Destination::Document {
1268 return false;
1270 }
1271
1272 true
1273}
1274
1275fn is_bad_port(port: u16) -> bool {
1277 static BAD_PORTS: [u16; 78] = [
1278 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1279 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1280 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1281 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1282 6668, 6669, 6697, 10080,
1283 ];
1284
1285 BAD_PORTS.binary_search(&port).is_ok()
1286}
1287
1288pub fn is_form_submission_request(request: &Request) -> bool {
1290 let content_type = request.headers.typed_get::<ContentType>();
1291 content_type.is_some_and(|ct| {
1292 let mime: Mime = ct.into();
1293 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1294 })
1295}
1296
1297fn should_upgrade_request_to_potentially_trustworthy(
1299 request: &mut Request,
1300 context: &FetchContext,
1301) -> bool {
1302 fn should_upgrade_navigation_request(request: &Request) -> bool {
1303 if is_form_submission_request(request) {
1305 return true;
1306 }
1307
1308 if request
1311 .client
1312 .as_ref()
1313 .is_some_and(|client| client.is_nested_browsing_context)
1314 {
1315 return true;
1316 }
1317
1318 false
1323 }
1324
1325 if request.is_navigation_request() {
1327 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1332 request
1333 .current_url()
1334 .host_str()
1335 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1336 {
1337 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1338 request
1339 .headers
1340 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1341 }
1342
1343 if !should_upgrade_navigation_request(request) {
1344 return false;
1345 }
1346 }
1347
1348 request
1352 .client
1353 .as_ref()
1354 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1355}
1356
1357#[derive(Debug, PartialEq)]
1358pub enum MixedSecurityProhibited {
1359 Prohibited,
1360 NotProhibited,
1361}
1362
1363fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1365 if let Origin::Origin(ref origin) = request.origin {
1366 if origin.is_potentially_trustworthy() || origin.is_for_data_worker_from_secure_context() {
1370 return MixedSecurityProhibited::Prohibited;
1371 }
1372 }
1373
1374 if request.has_trustworthy_ancestor_origin {
1378 return MixedSecurityProhibited::Prohibited;
1379 }
1380
1381 MixedSecurityProhibited::NotProhibited
1382}
1383
1384fn should_upgrade_mixed_content_request(
1386 request: &Request,
1387 protocol_registry: &ProtocolRegistry,
1388) -> bool {
1389 let url = request.url();
1390 if is_url_potentially_trustworthy(protocol_registry, &url) {
1392 return false;
1393 }
1394
1395 match url.host() {
1397 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1398 _ => (),
1399 }
1400
1401 if do_settings_prohibit_mixed_security_contexts(request) ==
1403 MixedSecurityProhibited::NotProhibited
1404 {
1405 return false;
1406 }
1407
1408 if !matches!(
1410 request.destination,
1411 Destination::Audio | Destination::Image | Destination::Video
1412 ) {
1413 return false;
1414 }
1415
1416 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1418 return false;
1419 }
1420
1421 true
1422}