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