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, InternalRequest, Origin, ParserMetadata, RedirectMode, Referrer,
29 Request, RequestBody, 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 if request.is_internal_request == InternalRequest::Yes {
304 return None;
305 }
306 let origin = match &request.origin {
307 Origin::Client => return None,
308 Origin::Origin(origin) => origin,
309 };
310
311 let csp_request = csp::Request {
312 url: request.url().into_url(),
313 current_url: request.current_url().into_url(),
314 origin: origin.clone().into_url_origin(),
315 redirect_count: request.redirect_count,
316 destination: request.destination,
317 initiator: match request.initiator {
318 Initiator::Download => csp::Initiator::Download,
319 Initiator::ImageSet => csp::Initiator::ImageSet,
320 Initiator::Manifest => csp::Initiator::Manifest,
321 Initiator::Prefetch => csp::Initiator::Prefetch,
322 _ => csp::Initiator::None,
323 },
324 nonce: request.cryptographic_nonce_metadata.clone(),
325 integrity_metadata: request.integrity_metadata.clone(),
326 parser_metadata: match request.parser_metadata {
327 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
328 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
329 ParserMetadata::Default => csp::ParserMetadata::None,
330 },
331 };
332 Some(csp_request)
333}
334
335pub fn should_request_be_blocked_by_csp(
337 csp_request: &csp::Request,
338 policy_container: &PolicyContainer,
339) -> (csp::CheckResult, Vec<csp::Violation>) {
340 policy_container
341 .csp_list
342 .as_ref()
343 .map(|c| c.should_request_be_blocked(csp_request))
344 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
345}
346
347pub fn report_violations_for_request_by_csp(
349 csp_request: &csp::Request,
350 policy_container: &PolicyContainer,
351) -> Vec<csp::Violation> {
352 policy_container
353 .csp_list
354 .as_ref()
355 .map(|c| c.report_violations_for_request(csp_request))
356 .unwrap_or_default()
357}
358
359fn should_response_be_blocked_by_csp(
360 csp_request: &csp::Request,
361 response: &Response,
362 policy_container: &PolicyContainer,
363) -> (csp::CheckResult, Vec<csp::Violation>) {
364 if response.is_network_error() {
365 return (csp::CheckResult::Allowed, Vec::new());
366 }
367 let csp_response = csp::Response {
368 url: response
369 .actual_response()
370 .url()
371 .cloned()
372 .map(|mut url| {
377 match csp_request.url.scheme() {
378 "ws" | "wss" => {
379 url.as_mut_url()
380 .set_scheme(csp_request.url.scheme())
381 .expect("failed to set URL scheme");
382 },
383 _ => {},
384 };
385 url
386 })
387 .expect("response must have a url")
388 .into_url(),
389 redirect_count: csp_request.redirect_count,
390 };
391 policy_container
392 .csp_list
393 .as_ref()
394 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
395 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
396}
397
398pub async fn main_fetch(
400 fetch_params: &mut FetchParams,
401 cache: &mut CorsCache,
402 recursive_flag: bool,
403 target: Target<'_>,
404 done_chan: &mut DoneChannel,
405 context: &FetchContext,
406) -> Response {
407 let request = &mut fetch_params.request;
409 send_early_httprequest_to_devtools(request, context);
410 let mut response = None;
412
413 if let Some(ref details) = request.crash {
415 response = Some(Response::network_error(NetworkError::Crash(
416 details.clone(),
417 )));
418 }
419
420 if request.local_urls_only &&
423 !matches!(
424 request.current_url().scheme(),
425 "about" | "blob" | "data" | "filesystem"
426 )
427 {
428 response = Some(Response::network_error(NetworkError::UnsupportedScheme));
429 }
430
431 let policy_container = match &request.policy_container {
433 RequestPolicyContainer::Client => unreachable!(),
434 RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
435 };
436
437 let csp_request = convert_request_to_csp_request(request);
439 if let Some(csp_request) = csp_request.as_ref() {
440 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
442
443 if !violations.is_empty() {
444 target.process_csp_violations(request, violations);
445 }
446 };
447
448 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
451 should_upgrade_mixed_content_request(request, &context.protocols)
452 {
453 trace!(
454 "upgrading {} targeting {:?}",
455 request.current_url(),
456 request.destination
457 );
458 if let Some(new_scheme) = match request.current_url().scheme() {
459 "http" => Some("https"),
460 "ws" => Some("wss"),
461 _ => None,
462 } {
463 request
464 .current_url_mut()
465 .as_mut_url()
466 .set_scheme(new_scheme)
467 .unwrap();
468 }
469 } else {
470 trace!(
471 "not upgrading {} targeting {:?} with {:?}",
472 request.current_url(),
473 request.destination,
474 request.insecure_requests_policy
475 );
476 }
477 if let Some(csp_request) = csp_request.as_ref() {
478 let (check_result, violations) =
482 should_request_be_blocked_by_csp(csp_request, &policy_container);
483
484 if !violations.is_empty() {
485 target.process_csp_violations(request, violations);
486 }
487
488 if check_result == csp::CheckResult::Blocked {
489 warn!("Request blocked by CSP");
490 response = Some(Response::network_error(NetworkError::ContentSecurityPolicy))
491 }
492 };
493 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
494 response = Some(Response::network_error(NetworkError::InvalidPort));
495 }
496 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
497 response = Some(Response::network_error(NetworkError::MixedContent));
498 }
499
500 if request.referrer_policy == ReferrerPolicy::EmptyString {
503 request.referrer_policy = policy_container.get_referrer_policy();
504 }
505
506 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
509 Referrer::NoReferrer => None,
510 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
511 request.headers.remove(header::REFERER);
512 determine_requests_referrer(
513 request.referrer_policy,
514 referrer_source,
515 request.current_url(),
516 )
517 },
518 };
519 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
520
521 context
523 .state
524 .hsts_list
525 .read()
526 .apply_hsts_rules(request.current_url_mut());
527
528 let current_url = request.current_url();
532 let current_scheme = current_url.scheme();
533
534 context
536 .request_interceptor
537 .lock()
538 .await
539 .intercept_request(request, &mut response, context)
540 .await;
541
542 let mut response = match response {
543 Some(response) => response,
544 None => {
547 let same_origin = if let Origin::Origin(ref origin) = request.origin {
548 *origin == request.current_url_with_blob_claim().origin()
549 } else {
550 false
551 };
552
553 if let Some((response, preload_id)) =
555 fetch_params.preload_response_candidate.response().await
556 {
557 response.get_resource_timing().inner().preloaded = true;
558 context
559 .preloaded_resources
560 .lock()
561 .unwrap()
562 .remove(&preload_id);
563 response
564 }
565 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
568 current_scheme == "data" ||
570 context.protocols.is_fetchable(current_scheme) ||
573 matches!(
575 request.mode,
576 RequestMode::Navigate | RequestMode::WebSocket { .. }
577 )
578 {
579 request.response_tainting = ResponseTainting::Basic;
581
582 scheme_fetch(fetch_params, cache, target, done_chan, context).await
584 } else if request.mode == RequestMode::SameOrigin {
585 Response::network_error(NetworkError::CrossOriginResponse)
586 } else if request.mode == RequestMode::NoCors {
587 if request.redirect_mode != RedirectMode::Follow {
589 Response::network_error(NetworkError::RedirectError)
590 } else {
591 request.response_tainting = ResponseTainting::Opaque;
593
594 scheme_fetch(fetch_params, cache, target, done_chan, context).await
596 }
597 } else if !matches!(current_scheme, "http" | "https") {
598 Response::network_error(NetworkError::UnsupportedScheme)
599 } else if request.use_cors_preflight ||
600 (request.unsafe_request &&
601 (!is_cors_safelisted_method(&request.method) ||
602 request.headers.iter().any(|(name, value)| {
603 !is_cors_safelisted_request_header(&name, &value)
604 })))
605 {
606 request.response_tainting = ResponseTainting::CorsTainting;
608
609 let response = http_fetch(
612 fetch_params,
613 cache,
614 true,
615 true,
616 false,
617 target,
618 done_chan,
619 context,
620 )
621 .await;
622 if response.is_network_error() {
624 }
626 response
628 } else {
629 request.response_tainting = ResponseTainting::CorsTainting;
631
632 http_fetch(
634 fetch_params,
635 cache,
636 true,
637 false,
638 false,
639 target,
640 done_chan,
641 context,
642 )
643 .await
644 }
645 },
646 };
647
648 if recursive_flag {
650 return response;
651 }
652
653 let request = &mut fetch_params.request;
655
656 if !response.is_network_error() && response.internal_response.is_none() {
658 if request.response_tainting == ResponseTainting::CorsTainting {
660 let header_names: Option<Vec<HeaderName>> = response
663 .headers
664 .typed_get::<AccessControlExposeHeaders>()
665 .map(|v| v.iter().collect());
666
667 if let Some(ref list) = header_names {
668 if request.credentials_mode != CredentialsMode::Include &&
672 list.iter().any(|header| header == "*")
673 {
674 response.cors_exposed_header_name_list = response
675 .headers
676 .iter()
677 .map(|(name, _)| name.as_str().to_owned())
678 .collect();
679 } else {
680 response.cors_exposed_header_name_list =
683 list.iter().map(|h| h.as_str().to_owned()).collect();
684 }
685 }
686 }
687
688 let response_type = match request.response_tainting {
691 ResponseTainting::Basic => ResponseType::Basic,
692 ResponseTainting::CorsTainting => ResponseType::Cors,
693 ResponseTainting::Opaque => ResponseType::Opaque,
694 };
695 response = response.to_filtered(response_type);
696 }
697
698 let internal_error = {
699 let response_is_network_error = response.is_network_error();
701 let should_replace_with_nosniff_error = !response_is_network_error &&
702 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
703 let should_replace_with_mime_type_error = !response_is_network_error &&
704 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
705 let should_replace_with_mixed_content = !response_is_network_error &&
706 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
707 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
708 let (check_result, violations) =
709 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
710 if !violations.is_empty() {
711 target.process_csp_violations(request, violations);
712 }
713 check_result == csp::CheckResult::Blocked
714 });
715
716 let mut network_error_response = response
718 .get_network_error()
719 .cloned()
720 .map(Response::network_error);
721
722 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
726 error_response
727 } else {
728 response.actual_response_mut()
729 };
730
731 if internal_response.url_list.is_empty() {
733 internal_response.url_list = request
734 .url_list
735 .iter()
736 .map(|locked_url| locked_url.url())
737 .collect();
738 }
739
740 internal_response.redirect_taint = request.redirect_taint_for_request();
742
743 let mut blocked_error_response;
755
756 let internal_response = if should_replace_with_nosniff_error {
757 blocked_error_response = Response::network_error(NetworkError::Nosniff);
759 &blocked_error_response
760 } else if should_replace_with_mime_type_error {
761 blocked_error_response =
763 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
764 &blocked_error_response
765 } else if should_replace_with_mixed_content {
766 blocked_error_response = Response::network_error(NetworkError::MixedContent);
767 &blocked_error_response
768 } else if should_replace_with_csp_error {
769 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
770 &blocked_error_response
771 } else {
772 internal_response
773 };
774
775 let internal_response = if !internal_response.is_network_error() &&
781 response_type == ResponseType::Opaque &&
782 internal_response.status.is_a_range_status() &&
783 internal_response.range_requested &&
784 !request.headers.contains_key(RANGE)
785 {
786 blocked_error_response =
788 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
789 &blocked_error_response
790 } else {
791 internal_response
792 };
793
794 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
799 if not_network_error &&
800 (is_null_body_status(&internal_response.status) ||
801 matches!(request.method, Method::HEAD | Method::CONNECT))
802 {
803 let mut body = internal_response.body.lock();
806 *body = ResponseBody::Empty;
807 }
808
809 internal_response.get_network_error().cloned()
810 };
811
812 if let Some(error) = internal_error {
814 response = Response::network_error(error);
815 }
816
817 let mut response_loaded = false;
819 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
820 wait_for_response(request, &mut response, target, done_chan, context).await;
822 response_loaded = true;
823
824 let integrity_metadata = &request.integrity_metadata;
826 if response.termination_reason.is_none() &&
827 !is_response_integrity_valid(integrity_metadata, &response)
828 {
829 Response::network_error(NetworkError::SubresourceIntegrity)
830 } else {
831 response
832 }
833 } else {
834 response
835 };
836
837 if request.synchronous {
839 target.process_response(request, &response);
842 if !response_loaded {
843 wait_for_response(request, &mut response, target, done_chan, context).await;
844 }
845 target.process_response_eof(request, &response);
847 return response;
848 }
849
850 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
852 target.process_request_body(request);
857 }
858
859 target.process_response(request, &response);
861 send_response_to_devtools(request, context, &response, None);
863 send_security_info_to_devtools(request, context, &response);
864
865 if !response_loaded {
867 wait_for_response(request, &mut response, target, done_chan, context).await;
868 }
869
870 target.process_response_eof(request, &response);
872 send_response_to_devtools(request, context, &response, None);
876
877 context
878 .state
879 .http_cache
880 .update_awaiting_consumers(request, &response)
881 .await;
882
883 response
886}
887
888async fn wait_for_response(
889 request: &Request,
890 response: &mut Response,
891 target: Target<'_>,
892 done_chan: &mut DoneChannel,
893 context: &FetchContext,
894) {
895 if let Some(ref mut ch) = *done_chan {
896 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
897 loop {
898 match ch.1.recv().await {
899 Some(Data::Payload(vec)) => {
900 if let Some(body) = devtools_body.as_mut() {
901 body.extend(&vec);
902 }
903 target.process_response_chunk(request, vec);
904 },
905 Some(Data::Error(network_error)) => {
906 if network_error == NetworkError::DecompressionError {
907 response.termination_reason = Some(TerminationReason::Fatal);
908 }
909 response.set_network_error(network_error);
910
911 break;
912 },
913 Some(Data::Done) => {
914 send_response_to_devtools(request, context, response, devtools_body);
915 break;
916 },
917 Some(Data::Cancelled) => {
918 response.aborted.store(true, Ordering::Release);
919 break;
920 },
921 _ => {
922 panic!("fetch worker should always send Done before terminating");
923 },
924 }
925 }
926 } else {
927 match *response.actual_response().body.lock() {
928 ResponseBody::Done(ref vec) if !vec.is_empty() => {
929 target.process_response_chunk(request, vec.clone());
933 if context.devtools_chan.is_some() {
934 send_response_to_devtools(request, context, response, Some(vec.clone()));
937 }
938 },
939 ResponseBody::Done(_) | ResponseBody::Empty => {},
940 _ => unreachable!(),
941 }
942 }
943}
944
945pub enum RangeRequestBounds {
947 Final(RelativePos),
949 Pending(u64),
952}
953
954impl RangeRequestBounds {
955 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
956 match self {
957 RangeRequestBounds::Final(pos) => {
958 if let Some(len) = len &&
959 pos.start <= len as i64
960 {
961 return Ok(*pos);
962 }
963 Err("Tried to process RangeRequestBounds::Final without len")
964 },
965 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
966 if let Some(len) = len {
967 Some((len - u64::min(len, *offset)) as i64)
968 } else {
969 Some(0)
970 },
971 None,
972 )),
973 }
974 }
975}
976
977fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
978 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
979 response
980 .headers
981 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
982 *response.body.lock() = ResponseBody::Done(vec![]);
983 response.status = HttpStatus::default();
984 response
985}
986
987fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
988 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
989 response
990 .headers
991 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
992 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
993 response.status = HttpStatus::default();
994 response
995}
996
997fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
999 let error = |string| Err(io::Error::other(string));
1000
1001 let body = match request.body.as_mut() {
1002 Some(body) => body,
1003 None => return error("No body found"),
1004 };
1005
1006 let stream = body.clone_stream();
1007 let mut stream = stream.lock();
1008 let (body_chan, body_port) = ipc::channel().unwrap();
1009 let Some(chunk_requester) = stream.as_mut() else {
1010 log::error!(
1011 "Could not connect to the request body stream because it has already been closed."
1012 );
1013 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1014 };
1015 chunk_requester
1016 .send(BodyChunkRequest::Connect(body_chan))
1017 .map_err(|error| {
1018 log::error!(
1019 "Could not connect to the request body stream because it has already been closed: {error}"
1020 );
1021 std::io::Error::other("Could not connect to request body stream")
1022 })?;
1023 chunk_requester
1024 .send(BodyChunkRequest::Chunk)
1025 .map_err(|error| {
1026 log::error!(
1027 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1028 );
1029 std::io::Error::other("Could not request request body chunk")
1030 })?;
1031 let body_bytes = match body_port.recv().ok() {
1032 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1033 _ => return error("Certificate not sent in a single chunk"),
1034 };
1035
1036 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1037 Some(split_idx) => split_idx,
1038 None => return error("Could not find ampersand in data"),
1039 };
1040 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1041
1042 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1043 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1044 return error("Invalid secret sent. Ignoring request");
1045 }
1046
1047 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1048 Ok(bytes) => bytes,
1049 Err(_) => return error("Could not decode certificate base64"),
1050 };
1051
1052 context
1053 .state
1054 .override_manager
1055 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1056 Ok(())
1057}
1058
1059async fn scheme_fetch(
1061 fetch_params: &mut FetchParams,
1062 cache: &mut CorsCache,
1063 target: Target<'_>,
1064 done_chan: &mut DoneChannel,
1065 context: &FetchContext,
1066) -> Response {
1067 let request = &mut fetch_params.request;
1071 let url_and_blob_lock = request.current_url_with_blob_claim();
1072
1073 let scheme = url_and_blob_lock.scheme();
1074 match scheme {
1075 "about" if url_and_blob_lock.path() == "blank" => {
1076 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1077 },
1078 "about" if url_and_blob_lock.path() == "memory" => {
1079 create_about_memory(url_and_blob_lock.url(), request.timing_type())
1080 },
1081
1082 "chrome" if url_and_blob_lock.path() == "allowcert" => {
1083 if let Err(error) = handle_allowcert_request(request, context) {
1084 warn!("Could not handle allowcert request: {error}");
1085 }
1086 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1087 },
1088
1089 "http" | "https" => {
1090 http_fetch(
1091 fetch_params,
1092 cache,
1093 false,
1094 false,
1095 false,
1096 target,
1097 done_chan,
1098 context,
1099 )
1100 .await
1101 },
1102
1103 _ => match context.protocols.get(scheme) {
1104 Some(handler) => handler.load(request, done_chan, context).await,
1105 None => Response::network_error(NetworkError::UnsupportedScheme),
1106 },
1107 }
1108}
1109
1110fn is_null_body_status(status: &HttpStatus) -> bool {
1111 matches!(
1112 status.try_code(),
1113 Some(StatusCode::SWITCHING_PROTOCOLS) |
1114 Some(StatusCode::NO_CONTENT) |
1115 Some(StatusCode::RESET_CONTENT) |
1116 Some(StatusCode::NOT_MODIFIED)
1117 )
1118}
1119
1120pub fn should_be_blocked_due_to_nosniff(
1122 destination: Destination,
1123 response_headers: &HeaderMap,
1124) -> bool {
1125 if !determine_nosniff(response_headers) {
1127 return false;
1128 }
1129
1130 let mime_type = extract_mime_type_as_mime(response_headers);
1133
1134 #[inline]
1136 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1137 let javascript_mime_types: [Mime; 16] = [
1138 "application/ecmascript".parse().unwrap(),
1139 "application/javascript".parse().unwrap(),
1140 "application/x-ecmascript".parse().unwrap(),
1141 "application/x-javascript".parse().unwrap(),
1142 "text/ecmascript".parse().unwrap(),
1143 "text/javascript".parse().unwrap(),
1144 "text/javascript1.0".parse().unwrap(),
1145 "text/javascript1.1".parse().unwrap(),
1146 "text/javascript1.2".parse().unwrap(),
1147 "text/javascript1.3".parse().unwrap(),
1148 "text/javascript1.4".parse().unwrap(),
1149 "text/javascript1.5".parse().unwrap(),
1150 "text/jscript".parse().unwrap(),
1151 "text/livescript".parse().unwrap(),
1152 "text/x-ecmascript".parse().unwrap(),
1153 "text/x-javascript".parse().unwrap(),
1154 ];
1155
1156 javascript_mime_types
1157 .iter()
1158 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1159 }
1160
1161 match mime_type {
1162 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1164 Some(ref mime_type) if destination == Destination::Style => {
1166 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1167 },
1168
1169 None if destination == Destination::Style || destination.is_script_like() => true,
1170 _ => false,
1172 }
1173}
1174
1175fn should_be_blocked_due_to_mime_type(
1177 destination: Destination,
1178 response_headers: &HeaderMap,
1179) -> bool {
1180 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1182 Some(mime_type) => mime_type,
1183 None => return false,
1185 };
1186
1187 destination.is_script_like() &&
1193 match mime_type.type_() {
1194 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1195 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1196 _ => false,
1197 }
1198}
1199
1200pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1202 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1207 let is_bad_port = url.port().is_some_and(is_bad_port);
1208 if is_http_scheme && is_bad_port {
1209 return true;
1210 }
1211
1212 false
1214}
1215
1216pub fn should_request_be_blocked_as_mixed_content(
1218 request: &Request,
1219 protocol_registry: &ProtocolRegistry,
1220) -> bool {
1221 if do_settings_prohibit_mixed_security_contexts(request) ==
1225 MixedSecurityProhibited::NotProhibited
1226 {
1227 return false;
1228 }
1229
1230 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1232 return false;
1233 }
1234
1235 if request.destination == Destination::Document {
1240 return false;
1242 }
1243
1244 true
1245}
1246
1247pub fn should_response_be_blocked_as_mixed_content(
1249 request: &Request,
1250 response: &Response,
1251 protocol_registry: &ProtocolRegistry,
1252) -> bool {
1253 if do_settings_prohibit_mixed_security_contexts(request) ==
1257 MixedSecurityProhibited::NotProhibited
1258 {
1259 return false;
1260 }
1261
1262 if response
1264 .actual_response()
1265 .url()
1266 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1267 {
1268 return false;
1269 }
1270
1271 if request.destination == Destination::Document {
1276 return false;
1278 }
1279
1280 true
1281}
1282
1283fn is_bad_port(port: u16) -> bool {
1285 static BAD_PORTS: [u16; 83] = [
1286 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95,
1287 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179,
1288 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601,
1289 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 6000, 6566,
1290 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080,
1291 ];
1292
1293 BAD_PORTS.binary_search(&port).is_ok()
1294}
1295
1296pub fn is_form_submission_request(request: &Request) -> bool {
1298 let content_type = request.headers.typed_get::<ContentType>();
1299 content_type.is_some_and(|ct| {
1300 let mime: Mime = ct.into();
1301 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1302 })
1303}
1304
1305fn should_upgrade_request_to_potentially_trustworthy(
1307 request: &mut Request,
1308 context: &FetchContext,
1309) -> bool {
1310 fn should_upgrade_navigation_request(request: &Request) -> bool {
1311 if is_form_submission_request(request) {
1313 return true;
1314 }
1315
1316 if request
1319 .client
1320 .as_ref()
1321 .is_some_and(|client| client.is_nested_browsing_context)
1322 {
1323 return true;
1324 }
1325
1326 false
1331 }
1332
1333 if request.is_navigation_request() {
1335 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1340 request
1341 .current_url()
1342 .host_str()
1343 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1344 {
1345 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1346 request
1347 .headers
1348 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1349 }
1350
1351 if !should_upgrade_navigation_request(request) {
1352 return false;
1353 }
1354 }
1355
1356 request
1360 .client
1361 .as_ref()
1362 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1363}
1364
1365#[derive(Debug, PartialEq)]
1366pub enum MixedSecurityProhibited {
1367 Prohibited,
1368 NotProhibited,
1369}
1370
1371fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1373 if let Origin::Origin(ref origin) = request.origin {
1374 if origin.is_potentially_trustworthy() || origin.is_for_data_worker_from_secure_context() {
1378 return MixedSecurityProhibited::Prohibited;
1379 }
1380 }
1381
1382 if request.has_trustworthy_ancestor_origin {
1386 return MixedSecurityProhibited::Prohibited;
1387 }
1388
1389 MixedSecurityProhibited::NotProhibited
1390}
1391
1392fn should_upgrade_mixed_content_request(
1394 request: &Request,
1395 protocol_registry: &ProtocolRegistry,
1396) -> bool {
1397 let url = request.url();
1398 if is_url_potentially_trustworthy(protocol_registry, &url) {
1400 return false;
1401 }
1402
1403 match url.host() {
1405 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1406 _ => (),
1407 }
1408
1409 if do_settings_prohibit_mixed_security_contexts(request) ==
1411 MixedSecurityProhibited::NotProhibited
1412 {
1413 return false;
1414 }
1415
1416 if !matches!(
1418 request.destination,
1419 Destination::Audio | Destination::Image | Destination::Video
1420 ) {
1421 return false;
1422 }
1423
1424 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1426 return false;
1427 }
1428
1429 true
1430}