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 let csp_request = convert_request_to_csp_request(request);
437 if let Some(csp_request) = csp_request.as_ref() {
438 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
440
441 if !violations.is_empty() {
442 target.process_csp_violations(request, violations);
443 }
444 };
445
446 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) {
507 Referrer::NoReferrer => None,
508 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
509 request.headers.remove(header::REFERER);
510 determine_requests_referrer(
511 request.referrer_policy,
512 referrer_source,
513 request.current_url(),
514 )
515 },
516 };
517 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
518
519 context
524 .state
525 .hsts_list
526 .read()
527 .apply_hsts_rules(request.current_url_mut());
528
529 let current_url = request.current_url();
533 let current_scheme = current_url.scheme();
534
535 context
537 .request_interceptor
538 .lock()
539 .await
540 .intercept_request(request, &mut response, context)
541 .await;
542
543 let mut response = match response {
544 Some(response) => response,
545 None => {
546 let same_origin = if let Origin::Origin(ref origin) = request.origin {
549 *origin == request.current_url_with_blob_claim().origin()
550 } else {
551 false
552 };
553
554 if let Some((response, preload_id)) =
556 fetch_params.preload_response_candidate.response().await
557 {
558 response.get_resource_timing().inner().preloaded = true;
559 context
560 .preloaded_resources
561 .lock()
562 .unwrap()
563 .remove(&preload_id);
564 response
565 }
566 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
569 current_scheme == "data" ||
571 context.protocols.is_fetchable(current_scheme) ||
574 matches!(
576 request.mode,
577 RequestMode::Navigate | RequestMode::WebSocket { .. }
578 )
579 {
580 request.response_tainting = ResponseTainting::Basic;
582
583 scheme_fetch(fetch_params, cache, target, done_chan, context).await
585 } else if request.mode == RequestMode::SameOrigin {
586 Response::network_error(NetworkError::CrossOriginResponse)
587 } else if request.mode == RequestMode::NoCors {
588 if request.redirect_mode != RedirectMode::Follow {
590 Response::network_error(NetworkError::RedirectError)
591 } else {
592 request.response_tainting = ResponseTainting::Opaque;
594
595 scheme_fetch(fetch_params, cache, target, done_chan, context).await
597 }
598 } else if !matches!(current_scheme, "http" | "https") {
599 Response::network_error(NetworkError::UnsupportedScheme)
600 } else if request.use_cors_preflight ||
601 (request.unsafe_request &&
602 (!is_cors_safelisted_method(&request.method) ||
603 request.headers.iter().any(|(name, value)| {
604 !is_cors_safelisted_request_header(&name, &value)
605 })))
606 {
607 request.response_tainting = ResponseTainting::CorsTainting;
609 let response = http_fetch(
611 fetch_params,
612 cache,
613 true,
614 true,
615 false,
616 target,
617 done_chan,
618 context,
619 )
620 .await;
621 if response.is_network_error() {
623 }
625 response
627 } else {
628 request.response_tainting = ResponseTainting::CorsTainting;
630 http_fetch(
632 fetch_params,
633 cache,
634 true,
635 false,
636 false,
637 target,
638 done_chan,
639 context,
640 )
641 .await
642 }
643 },
644 };
645
646 if recursive_flag {
648 return response;
649 }
650
651 let request = &mut fetch_params.request;
653
654 let mut response = if !response.is_network_error() && response.internal_response.is_none() {
656 if request.response_tainting == ResponseTainting::CorsTainting {
658 let header_names: Option<Vec<HeaderName>> = response
661 .headers
662 .typed_get::<AccessControlExposeHeaders>()
663 .map(|v| v.iter().collect());
664 match header_names {
665 Some(ref list)
667 if request.credentials_mode != CredentialsMode::Include &&
668 list.iter().any(|header| header == "*") =>
669 {
670 response.cors_exposed_header_name_list = response
671 .headers
672 .iter()
673 .map(|(name, _)| name.as_str().to_owned())
674 .collect();
675 },
676 Some(list) => {
678 response.cors_exposed_header_name_list =
679 list.iter().map(|h| h.as_str().to_owned()).collect();
680 },
681 _ => (),
682 }
683 }
684
685 let response_type = match request.response_tainting {
688 ResponseTainting::Basic => ResponseType::Basic,
689 ResponseTainting::CorsTainting => ResponseType::Cors,
690 ResponseTainting::Opaque => ResponseType::Opaque,
691 };
692 response.to_filtered(response_type)
693 } else {
694 response
695 };
696
697 let internal_error = {
698 let response_is_network_error = response.is_network_error();
700 let should_replace_with_nosniff_error = !response_is_network_error &&
701 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
702 let should_replace_with_mime_type_error = !response_is_network_error &&
703 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
704 let should_replace_with_mixed_content = !response_is_network_error &&
705 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
706 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
707 let (check_result, violations) =
708 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
709 if !violations.is_empty() {
710 target.process_csp_violations(request, violations);
711 }
712 check_result == csp::CheckResult::Blocked
713 });
714
715 let mut network_error_response = response
717 .get_network_error()
718 .cloned()
719 .map(Response::network_error);
720
721 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
725 error_response
726 } else {
727 response.actual_response_mut()
728 };
729
730 if internal_response.url_list.is_empty() {
732 internal_response.url_list = request
733 .url_list
734 .iter()
735 .map(|locked_url| locked_url.url())
736 .collect();
737 }
738
739 internal_response.redirect_taint = request.redirect_taint_for_request();
741
742 let mut blocked_error_response;
748
749 let internal_response = if should_replace_with_nosniff_error {
750 blocked_error_response = Response::network_error(NetworkError::Nosniff);
752 &blocked_error_response
753 } else if should_replace_with_mime_type_error {
754 blocked_error_response =
756 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
757 &blocked_error_response
758 } else if should_replace_with_mixed_content {
759 blocked_error_response = Response::network_error(NetworkError::MixedContent);
760 &blocked_error_response
761 } else if should_replace_with_csp_error {
762 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
763 &blocked_error_response
764 } else {
765 internal_response
766 };
767
768 let internal_response = if !internal_response.is_network_error() &&
774 response_type == ResponseType::Opaque &&
775 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
776 internal_response.range_requested &&
777 !request.headers.contains_key(RANGE)
778 {
779 blocked_error_response =
781 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
782 &blocked_error_response
783 } else {
784 internal_response
785 };
786
787 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
792 if not_network_error &&
793 (is_null_body_status(&internal_response.status) ||
794 matches!(request.method, Method::HEAD | Method::CONNECT))
795 {
796 let mut body = internal_response.body.lock();
799 *body = ResponseBody::Empty;
800 }
801
802 internal_response.get_network_error().cloned()
803 };
804
805 let mut response = if let Some(error) = internal_error {
807 Response::network_error(error)
808 } else {
809 response
810 };
811
812 let mut response_loaded = false;
814 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
815 wait_for_response(request, &mut response, target, done_chan, context).await;
817 response_loaded = true;
818
819 let integrity_metadata = &request.integrity_metadata;
821 if response.termination_reason.is_none() &&
822 !is_response_integrity_valid(integrity_metadata, &response)
823 {
824 Response::network_error(NetworkError::SubresourceIntegrity)
825 } else {
826 response
827 }
828 } else {
829 response
830 };
831
832 if request.synchronous {
834 target.process_response(request, &response);
837 if !response_loaded {
838 wait_for_response(request, &mut response, target, done_chan, context).await;
839 }
840 target.process_response_eof(request, &response);
842 return response;
843 }
844
845 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
847 target.process_request_body(request);
852 }
853
854 target.process_response(request, &response);
856 send_response_to_devtools(request, context, &response, None);
858 send_security_info_to_devtools(request, context, &response);
859
860 if !response_loaded {
862 wait_for_response(request, &mut response, target, done_chan, context).await;
863 }
864
865 target.process_response_eof(request, &response);
867 send_response_to_devtools(request, context, &response, None);
871
872 context
873 .state
874 .http_cache
875 .update_awaiting_consumers(request, &response)
876 .await;
877
878 response
881}
882
883async fn wait_for_response(
884 request: &Request,
885 response: &mut Response,
886 target: Target<'_>,
887 done_chan: &mut DoneChannel,
888 context: &FetchContext,
889) {
890 if let Some(ref mut ch) = *done_chan {
891 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
892 loop {
893 match ch.1.recv().await {
894 Some(Data::Payload(vec)) => {
895 if let Some(body) = devtools_body.as_mut() {
896 body.extend(&vec);
897 }
898 target.process_response_chunk(request, vec);
899 },
900 Some(Data::Error(network_error)) => {
901 if network_error == NetworkError::DecompressionError {
902 response.termination_reason = Some(TerminationReason::Fatal);
903 }
904 response.set_network_error(network_error);
905
906 break;
907 },
908 Some(Data::Done) => {
909 send_response_to_devtools(request, context, response, devtools_body);
910 break;
911 },
912 Some(Data::Cancelled) => {
913 response.aborted.store(true, Ordering::Release);
914 break;
915 },
916 _ => {
917 panic!("fetch worker should always send Done before terminating");
918 },
919 }
920 }
921 } else {
922 match *response.actual_response().body.lock() {
923 ResponseBody::Done(ref vec) if !vec.is_empty() => {
924 target.process_response_chunk(request, vec.clone());
928 if context.devtools_chan.is_some() {
929 send_response_to_devtools(request, context, response, Some(vec.clone()));
932 }
933 },
934 ResponseBody::Done(_) | ResponseBody::Empty => {},
935 _ => unreachable!(),
936 }
937 }
938}
939
940pub enum RangeRequestBounds {
942 Final(RelativePos),
944 Pending(u64),
947}
948
949impl RangeRequestBounds {
950 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
951 match self {
952 RangeRequestBounds::Final(pos) => {
953 if let Some(len) = len &&
954 pos.start <= len as i64
955 {
956 return Ok(*pos);
957 }
958 Err("Tried to process RangeRequestBounds::Final without len")
959 },
960 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
961 if let Some(len) = len {
962 Some((len - u64::min(len, *offset)) as i64)
963 } else {
964 Some(0)
965 },
966 None,
967 )),
968 }
969 }
970}
971
972fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
973 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
974 response
975 .headers
976 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
977 *response.body.lock() = ResponseBody::Done(vec![]);
978 response.status = HttpStatus::default();
979 response
980}
981
982fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
983 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
984 response
985 .headers
986 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
987 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
988 response.status = HttpStatus::default();
989 response
990}
991
992fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
994 let error = |string| Err(io::Error::other(string));
995
996 let body = match request.body.as_mut() {
997 Some(body) => body,
998 None => return error("No body found"),
999 };
1000
1001 let stream = body.clone_stream();
1002 let mut stream = stream.lock();
1003 let (body_chan, body_port) = ipc::channel().unwrap();
1004 let Some(chunk_requester) = stream.as_mut() else {
1005 log::error!(
1006 "Could not connect to the request body stream because it has already been closed."
1007 );
1008 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1009 };
1010 chunk_requester
1011 .send(BodyChunkRequest::Connect(body_chan))
1012 .map_err(|error| {
1013 log::error!(
1014 "Could not connect to the request body stream because it has already been closed: {error}"
1015 );
1016 std::io::Error::other("Could not connect to request body stream")
1017 })?;
1018 chunk_requester
1019 .send(BodyChunkRequest::Chunk)
1020 .map_err(|error| {
1021 log::error!(
1022 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1023 );
1024 std::io::Error::other("Could not request request body chunk")
1025 })?;
1026 let body_bytes = match body_port.recv().ok() {
1027 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1028 _ => return error("Certificate not sent in a single chunk"),
1029 };
1030
1031 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1032 Some(split_idx) => split_idx,
1033 None => return error("Could not find ampersand in data"),
1034 };
1035 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1036
1037 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1038 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1039 return error("Invalid secret sent. Ignoring request");
1040 }
1041
1042 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1043 Ok(bytes) => bytes,
1044 Err(_) => return error("Could not decode certificate base64"),
1045 };
1046
1047 context
1048 .state
1049 .override_manager
1050 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1051 Ok(())
1052}
1053
1054async fn scheme_fetch(
1056 fetch_params: &mut FetchParams,
1057 cache: &mut CorsCache,
1058 target: Target<'_>,
1059 done_chan: &mut DoneChannel,
1060 context: &FetchContext,
1061) -> Response {
1062 let request = &mut fetch_params.request;
1066 let url_and_blob_lock = request.current_url_with_blob_claim();
1067
1068 let scheme = url_and_blob_lock.scheme();
1069 match scheme {
1070 "about" if url_and_blob_lock.path() == "blank" => {
1071 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1072 },
1073 "about" if url_and_blob_lock.path() == "memory" => {
1074 create_about_memory(url_and_blob_lock.url(), request.timing_type())
1075 },
1076
1077 "chrome" if url_and_blob_lock.path() == "allowcert" => {
1078 if let Err(error) = handle_allowcert_request(request, context) {
1079 warn!("Could not handle allowcert request: {error}");
1080 }
1081 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1082 },
1083
1084 "http" | "https" => {
1085 http_fetch(
1086 fetch_params,
1087 cache,
1088 false,
1089 false,
1090 false,
1091 target,
1092 done_chan,
1093 context,
1094 )
1095 .await
1096 },
1097
1098 _ => match context.protocols.get(scheme) {
1099 Some(handler) => handler.load(request, done_chan, context).await,
1100 None => Response::network_error(NetworkError::UnsupportedScheme),
1101 },
1102 }
1103}
1104
1105fn is_null_body_status(status: &HttpStatus) -> bool {
1106 matches!(
1107 status.try_code(),
1108 Some(StatusCode::SWITCHING_PROTOCOLS) |
1109 Some(StatusCode::NO_CONTENT) |
1110 Some(StatusCode::RESET_CONTENT) |
1111 Some(StatusCode::NOT_MODIFIED)
1112 )
1113}
1114
1115pub fn should_be_blocked_due_to_nosniff(
1117 destination: Destination,
1118 response_headers: &HeaderMap,
1119) -> bool {
1120 if !determine_nosniff(response_headers) {
1122 return false;
1123 }
1124
1125 let mime_type = extract_mime_type_as_mime(response_headers);
1128
1129 #[inline]
1131 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1132 let javascript_mime_types: [Mime; 16] = [
1133 "application/ecmascript".parse().unwrap(),
1134 "application/javascript".parse().unwrap(),
1135 "application/x-ecmascript".parse().unwrap(),
1136 "application/x-javascript".parse().unwrap(),
1137 "text/ecmascript".parse().unwrap(),
1138 "text/javascript".parse().unwrap(),
1139 "text/javascript1.0".parse().unwrap(),
1140 "text/javascript1.1".parse().unwrap(),
1141 "text/javascript1.2".parse().unwrap(),
1142 "text/javascript1.3".parse().unwrap(),
1143 "text/javascript1.4".parse().unwrap(),
1144 "text/javascript1.5".parse().unwrap(),
1145 "text/jscript".parse().unwrap(),
1146 "text/livescript".parse().unwrap(),
1147 "text/x-ecmascript".parse().unwrap(),
1148 "text/x-javascript".parse().unwrap(),
1149 ];
1150
1151 javascript_mime_types
1152 .iter()
1153 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1154 }
1155
1156 match mime_type {
1157 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1159 Some(ref mime_type) if destination == Destination::Style => {
1161 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1162 },
1163
1164 None if destination == Destination::Style || destination.is_script_like() => true,
1165 _ => false,
1167 }
1168}
1169
1170fn should_be_blocked_due_to_mime_type(
1172 destination: Destination,
1173 response_headers: &HeaderMap,
1174) -> bool {
1175 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1177 Some(mime_type) => mime_type,
1178 None => return false,
1180 };
1181
1182 destination.is_script_like() &&
1188 match mime_type.type_() {
1189 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1190 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1191 _ => false,
1192 }
1193}
1194
1195pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1197 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1202 let is_bad_port = url.port().is_some_and(is_bad_port);
1203 if is_http_scheme && is_bad_port {
1204 return true;
1205 }
1206
1207 false
1209}
1210
1211pub fn should_request_be_blocked_as_mixed_content(
1213 request: &Request,
1214 protocol_registry: &ProtocolRegistry,
1215) -> bool {
1216 if do_settings_prohibit_mixed_security_contexts(request) ==
1220 MixedSecurityProhibited::NotProhibited
1221 {
1222 return false;
1223 }
1224
1225 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1227 return false;
1228 }
1229
1230 if request.destination == Destination::Document {
1235 return false;
1237 }
1238
1239 true
1240}
1241
1242pub fn should_response_be_blocked_as_mixed_content(
1244 request: &Request,
1245 response: &Response,
1246 protocol_registry: &ProtocolRegistry,
1247) -> bool {
1248 if do_settings_prohibit_mixed_security_contexts(request) ==
1252 MixedSecurityProhibited::NotProhibited
1253 {
1254 return false;
1255 }
1256
1257 if response
1259 .actual_response()
1260 .url()
1261 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1262 {
1263 return false;
1264 }
1265
1266 if request.destination == Destination::Document {
1271 return false;
1273 }
1274
1275 true
1276}
1277
1278fn is_bad_port(port: u16) -> bool {
1280 static BAD_PORTS: [u16; 78] = [
1281 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1282 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1283 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1284 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1285 6668, 6669, 6697, 10080,
1286 ];
1287
1288 BAD_PORTS.binary_search(&port).is_ok()
1289}
1290
1291pub fn is_form_submission_request(request: &Request) -> bool {
1293 let content_type = request.headers.typed_get::<ContentType>();
1294 content_type.is_some_and(|ct| {
1295 let mime: Mime = ct.into();
1296 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1297 })
1298}
1299
1300fn should_upgrade_request_to_potentially_trustworthy(
1302 request: &mut Request,
1303 context: &FetchContext,
1304) -> bool {
1305 fn should_upgrade_navigation_request(request: &Request) -> bool {
1306 if is_form_submission_request(request) {
1308 return true;
1309 }
1310
1311 if request
1314 .client
1315 .as_ref()
1316 .is_some_and(|client| client.is_nested_browsing_context)
1317 {
1318 return true;
1319 }
1320
1321 false
1326 }
1327
1328 if request.is_navigation_request() {
1330 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1335 request
1336 .current_url()
1337 .host_str()
1338 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1339 {
1340 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1341 request
1342 .headers
1343 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1344 }
1345
1346 if !should_upgrade_navigation_request(request) {
1347 return false;
1348 }
1349 }
1350
1351 request
1355 .client
1356 .as_ref()
1357 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1358}
1359
1360#[derive(Debug, PartialEq)]
1361pub enum MixedSecurityProhibited {
1362 Prohibited,
1363 NotProhibited,
1364}
1365
1366fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1368 if let Origin::Origin(ref origin) = request.origin {
1369 if origin.is_potentially_trustworthy() || origin.is_for_data_worker_from_secure_context() {
1373 return MixedSecurityProhibited::Prohibited;
1374 }
1375 }
1376
1377 if request.has_trustworthy_ancestor_origin {
1381 return MixedSecurityProhibited::Prohibited;
1382 }
1383
1384 MixedSecurityProhibited::NotProhibited
1385}
1386
1387fn should_upgrade_mixed_content_request(
1389 request: &Request,
1390 protocol_registry: &ProtocolRegistry,
1391) -> bool {
1392 let url = request.url();
1393 if is_url_potentially_trustworthy(protocol_registry, &url) {
1395 return false;
1396 }
1397
1398 match url.host() {
1400 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1401 _ => (),
1402 }
1403
1404 if do_settings_prohibit_mixed_security_contexts(request) ==
1406 MixedSecurityProhibited::NotProhibited
1407 {
1408 return false;
1409 }
1410
1411 if !matches!(
1413 request.destination,
1414 Destination::Audio | Destination::Image | Destination::Video
1415 ) {
1416 return false;
1417 }
1418
1419 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1421 return false;
1422 }
1423
1424 true
1425}