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