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