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, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestBody,
29 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 ResourceTimeValue, ResourceTimingType, WebSocketDomAction, WebSocketNetworkEvent,
36 set_default_accept_language,
37};
38use parking_lot::Mutex;
39use rustc_hash::FxHashMap;
40use rustls_pki_types::CertificateDer;
41use serde::{Deserialize, Serialize};
42use servo_arc::Arc as ServoArc;
43use servo_base::generic_channel::CallbackSetter;
44use servo_base::id::PipelineId;
45use servo_url::{Host, ImmutableOrigin, ServoUrl};
46use tokio::sync::Mutex as TokioMutex;
47use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender};
48
49use crate::connector::CACertificates;
50use crate::fetch::cors_cache::CorsCache;
51use crate::fetch::fetch_params::{
52 ConsumePreloadedResources, FetchParams, SharedPreloadedResources,
53};
54use crate::filemanager_thread::FileManager;
55use crate::http_loader::{
56 HttpState, determine_requests_referrer, http_fetch, send_early_httprequest_to_devtools,
57 send_response_to_devtools, send_security_info_to_devtools, set_default_accept,
58};
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(Vec<u8>),
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: ServoArc<Mutex<ResourceFetchTiming>>,
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 {
183 let mut timing_guard = context.timing.lock();
184 timing_guard.set_attribute(ResourceAttribute::FetchStart);
185 timing_guard.set_attribute(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 let origin = match &request.origin {
306 Origin::Client => return None,
307 Origin::Origin(origin) => origin,
308 };
309
310 let csp_request = csp::Request {
311 url: request.url().into_url(),
312 current_url: request.current_url().into_url(),
313 origin: origin.clone().into_url_origin(),
314 redirect_count: request.redirect_count,
315 destination: request.destination,
316 initiator: match request.initiator {
317 Initiator::Download => csp::Initiator::Download,
318 Initiator::ImageSet => csp::Initiator::ImageSet,
319 Initiator::Manifest => csp::Initiator::Manifest,
320 Initiator::Prefetch => csp::Initiator::Prefetch,
321 _ => csp::Initiator::None,
322 },
323 nonce: request.cryptographic_nonce_metadata.clone(),
324 integrity_metadata: request.integrity_metadata.clone(),
325 parser_metadata: match request.parser_metadata {
326 ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted,
327 ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted,
328 ParserMetadata::Default => csp::ParserMetadata::None,
329 },
330 };
331 Some(csp_request)
332}
333
334pub fn should_request_be_blocked_by_csp(
336 csp_request: &csp::Request,
337 policy_container: &PolicyContainer,
338) -> (csp::CheckResult, Vec<csp::Violation>) {
339 policy_container
340 .csp_list
341 .as_ref()
342 .map(|c| c.should_request_be_blocked(csp_request))
343 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
344}
345
346pub fn report_violations_for_request_by_csp(
348 csp_request: &csp::Request,
349 policy_container: &PolicyContainer,
350) -> Vec<csp::Violation> {
351 policy_container
352 .csp_list
353 .as_ref()
354 .map(|c| c.report_violations_for_request(csp_request))
355 .unwrap_or_default()
356}
357
358fn should_response_be_blocked_by_csp(
359 csp_request: &csp::Request,
360 response: &Response,
361 policy_container: &PolicyContainer,
362) -> (csp::CheckResult, Vec<csp::Violation>) {
363 if response.is_network_error() {
364 return (csp::CheckResult::Allowed, Vec::new());
365 }
366 let csp_response = csp::Response {
367 url: response
368 .actual_response()
369 .url()
370 .cloned()
371 .map(|mut url| {
376 match csp_request.url.scheme() {
377 "ws" | "wss" => {
378 url.as_mut_url()
379 .set_scheme(csp_request.url.scheme())
380 .expect("failed to set URL scheme");
381 },
382 _ => {},
383 };
384 url
385 })
386 .expect("response must have a url")
387 .into_url(),
388 redirect_count: csp_request.redirect_count,
389 };
390 policy_container
391 .csp_list
392 .as_ref()
393 .map(|c| c.should_response_to_request_be_blocked(csp_request, &csp_response))
394 .unwrap_or((csp::CheckResult::Allowed, Vec::new()))
395}
396
397pub async fn main_fetch(
399 fetch_params: &mut FetchParams,
400 cache: &mut CorsCache,
401 recursive_flag: bool,
402 target: Target<'_>,
403 done_chan: &mut DoneChannel,
404 context: &FetchContext,
405) -> Response {
406 let request = &mut fetch_params.request;
408 send_early_httprequest_to_devtools(request, context);
409 let mut response = None;
411
412 if let Some(ref details) = request.crash {
414 response = Some(Response::network_error(NetworkError::Crash(
415 details.clone(),
416 )));
417 }
418
419 if request.local_urls_only &&
422 !matches!(
423 request.current_url().scheme(),
424 "about" | "blob" | "data" | "filesystem"
425 )
426 {
427 response = Some(Response::network_error(NetworkError::UnsupportedScheme));
428 }
429
430 let policy_container = match &request.policy_container {
432 RequestPolicyContainer::Client => unreachable!(),
433 RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
434 };
435 let csp_request = convert_request_to_csp_request(request);
436 if let Some(csp_request) = csp_request.as_ref() {
437 let violations = report_violations_for_request_by_csp(csp_request, &policy_container);
439
440 if !violations.is_empty() {
441 target.process_csp_violations(request, violations);
442 }
443 };
444
445 if should_upgrade_request_to_potentially_trustworthy(request, context) ||
450 should_upgrade_mixed_content_request(request, &context.protocols)
451 {
452 trace!(
453 "upgrading {} targeting {:?}",
454 request.current_url(),
455 request.destination
456 );
457 if let Some(new_scheme) = match request.current_url().scheme() {
458 "http" => Some("https"),
459 "ws" => Some("wss"),
460 _ => None,
461 } {
462 request
463 .current_url_mut()
464 .as_mut_url()
465 .set_scheme(new_scheme)
466 .unwrap();
467 }
468 } else {
469 trace!(
470 "not upgrading {} targeting {:?} with {:?}",
471 request.current_url(),
472 request.destination,
473 request.insecure_requests_policy
474 );
475 }
476 if let Some(csp_request) = csp_request.as_ref() {
477 let (check_result, violations) =
481 should_request_be_blocked_by_csp(csp_request, &policy_container);
482
483 if !violations.is_empty() {
484 target.process_csp_violations(request, violations);
485 }
486
487 if check_result == csp::CheckResult::Blocked {
488 warn!("Request blocked by CSP");
489 response = Some(Response::network_error(NetworkError::ContentSecurityPolicy))
490 }
491 };
492 if should_request_be_blocked_due_to_a_bad_port(&request.current_url()) {
493 response = Some(Response::network_error(NetworkError::InvalidPort));
494 }
495 if should_request_be_blocked_as_mixed_content(request, &context.protocols) {
496 response = Some(Response::network_error(NetworkError::MixedContent));
497 }
498
499 if request.referrer_policy == ReferrerPolicy::EmptyString {
502 request.referrer_policy = policy_container.get_referrer_policy();
503 }
504
505 let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
506 Referrer::NoReferrer => None,
507 Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {
508 request.headers.remove(header::REFERER);
509 determine_requests_referrer(
510 request.referrer_policy,
511 referrer_source,
512 request.current_url(),
513 )
514 },
515 };
516 request.referrer = referrer_url.map_or(Referrer::NoReferrer, Referrer::ReferrerUrl);
517
518 context
523 .state
524 .hsts_list
525 .read()
526 .apply_hsts_rules(request.current_url_mut());
527
528 let current_url = request.current_url();
532 let current_scheme = current_url.scheme();
533
534 context
536 .request_interceptor
537 .lock()
538 .await
539 .intercept_request(request, &mut response, context)
540 .await;
541
542 let mut response = match response {
543 Some(response) => response,
544 None => {
545 let same_origin = if let Origin::Origin(ref origin) = request.origin {
548 *origin == request.current_url_with_blob_claim().origin()
549 } else {
550 false
551 };
552
553 if let Some((response, preload_id)) =
555 fetch_params.preload_response_candidate.response().await
556 {
557 response.get_resource_timing().lock().preloaded = true;
558 context
559 .preloaded_resources
560 .lock()
561 .unwrap()
562 .remove(&preload_id);
563 response
564 }
565 else if (same_origin && request.response_tainting == ResponseTainting::Basic) ||
568 current_scheme == "data" ||
570 context.protocols.is_fetchable(current_scheme) ||
573 matches!(
575 request.mode,
576 RequestMode::Navigate | RequestMode::WebSocket { .. }
577 )
578 {
579 request.response_tainting = ResponseTainting::Basic;
581
582 scheme_fetch(fetch_params, cache, target, done_chan, context).await
584 } else if request.mode == RequestMode::SameOrigin {
585 Response::network_error(NetworkError::CrossOriginResponse)
586 } else if request.mode == RequestMode::NoCors {
587 if request.redirect_mode != RedirectMode::Follow {
589 Response::network_error(NetworkError::RedirectError)
590 } else {
591 request.response_tainting = ResponseTainting::Opaque;
593
594 scheme_fetch(fetch_params, cache, target, done_chan, context).await
596 }
597 } else if !matches!(current_scheme, "http" | "https") {
598 Response::network_error(NetworkError::UnsupportedScheme)
599 } else if request.use_cors_preflight ||
600 (request.unsafe_request &&
601 (!is_cors_safelisted_method(&request.method) ||
602 request.headers.iter().any(|(name, value)| {
603 !is_cors_safelisted_request_header(&name, &value)
604 })))
605 {
606 request.response_tainting = ResponseTainting::CorsTainting;
608 let response = http_fetch(
610 fetch_params,
611 cache,
612 true,
613 true,
614 false,
615 target,
616 done_chan,
617 context,
618 )
619 .await;
620 if response.is_network_error() {
622 }
624 response
626 } else {
627 request.response_tainting = ResponseTainting::CorsTainting;
629 http_fetch(
631 fetch_params,
632 cache,
633 true,
634 false,
635 false,
636 target,
637 done_chan,
638 context,
639 )
640 .await
641 }
642 },
643 };
644
645 if recursive_flag {
647 return response;
648 }
649
650 let request = &mut fetch_params.request;
652
653 let mut response = if !response.is_network_error() && response.internal_response.is_none() {
655 if request.response_tainting == ResponseTainting::CorsTainting {
657 let header_names: Option<Vec<HeaderName>> = response
659 .headers
660 .typed_get::<AccessControlExposeHeaders>()
661 .map(|v| v.iter().collect());
662 match header_names {
663 Some(ref list)
665 if request.credentials_mode != CredentialsMode::Include &&
666 list.iter().any(|header| header == "*") =>
667 {
668 response.cors_exposed_header_name_list = response
669 .headers
670 .iter()
671 .map(|(name, _)| name.as_str().to_owned())
672 .collect();
673 },
674 Some(list) => {
676 response.cors_exposed_header_name_list =
677 list.iter().map(|h| h.as_str().to_owned()).collect();
678 },
679 _ => (),
680 }
681 }
682
683 let response_type = match request.response_tainting {
685 ResponseTainting::Basic => ResponseType::Basic,
686 ResponseTainting::CorsTainting => ResponseType::Cors,
687 ResponseTainting::Opaque => ResponseType::Opaque,
688 };
689 response.to_filtered(response_type)
690 } else {
691 response
692 };
693
694 let internal_error = {
695 let response_is_network_error = response.is_network_error();
697 let should_replace_with_nosniff_error = !response_is_network_error &&
698 should_be_blocked_due_to_nosniff(request.destination, &response.headers);
699 let should_replace_with_mime_type_error = !response_is_network_error &&
700 should_be_blocked_due_to_mime_type(request.destination, &response.headers);
701 let should_replace_with_mixed_content = !response_is_network_error &&
702 should_response_be_blocked_as_mixed_content(request, &response, &context.protocols);
703 let should_replace_with_csp_error = csp_request.is_some_and(|csp_request| {
704 let (check_result, violations) =
705 should_response_be_blocked_by_csp(&csp_request, &response, &policy_container);
706 if !violations.is_empty() {
707 target.process_csp_violations(request, violations);
708 }
709 check_result == csp::CheckResult::Blocked
710 });
711
712 let mut network_error_response = response
714 .get_network_error()
715 .cloned()
716 .map(Response::network_error);
717
718 let response_type = response.response_type.clone(); let internal_response = if let Some(error_response) = network_error_response.as_mut() {
722 error_response
723 } else {
724 response.actual_response_mut()
725 };
726
727 if internal_response.url_list.is_empty() {
729 internal_response.url_list = request
730 .url_list
731 .iter()
732 .map(|locked_url| locked_url.url())
733 .collect();
734 }
735
736 internal_response.redirect_taint = request.redirect_taint_for_request();
738
739 let mut blocked_error_response;
745
746 let internal_response = if should_replace_with_nosniff_error {
747 blocked_error_response = Response::network_error(NetworkError::Nosniff);
749 &blocked_error_response
750 } else if should_replace_with_mime_type_error {
751 blocked_error_response =
753 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
754 &blocked_error_response
755 } else if should_replace_with_mixed_content {
756 blocked_error_response = Response::network_error(NetworkError::MixedContent);
757 &blocked_error_response
758 } else if should_replace_with_csp_error {
759 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
760 &blocked_error_response
761 } else {
762 internal_response
763 };
764
765 let internal_response = if !internal_response.is_network_error() &&
771 response_type == ResponseType::Opaque &&
772 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
773 internal_response.range_requested &&
774 !request.headers.contains_key(RANGE)
775 {
776 blocked_error_response =
778 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
779 &blocked_error_response
780 } else {
781 internal_response
782 };
783
784 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
789 if not_network_error &&
790 (is_null_body_status(&internal_response.status) ||
791 matches!(request.method, Method::HEAD | Method::CONNECT))
792 {
793 let mut body = internal_response.body.lock();
796 *body = ResponseBody::Empty;
797 }
798
799 internal_response.get_network_error().cloned()
800 };
801
802 let mut response = if let Some(error) = internal_error {
804 Response::network_error(error)
805 } else {
806 response
807 };
808
809 let mut response_loaded = false;
811 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
812 wait_for_response(request, &mut response, target, done_chan, context).await;
814 response_loaded = true;
815
816 let integrity_metadata = &request.integrity_metadata;
818 if response.termination_reason.is_none() &&
819 !is_response_integrity_valid(integrity_metadata, &response)
820 {
821 Response::network_error(NetworkError::SubresourceIntegrity)
822 } else {
823 response
824 }
825 } else {
826 response
827 };
828
829 if request.synchronous {
831 target.process_response(request, &response);
834 if !response_loaded {
835 wait_for_response(request, &mut response, target, done_chan, context).await;
836 }
837 target.process_response_eof(request, &response);
839 return response;
840 }
841
842 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
844 target.process_request_body(request);
849 }
850
851 target.process_response(request, &response);
853 send_response_to_devtools(request, context, &response, None);
855 send_security_info_to_devtools(request, context, &response);
856
857 if !response_loaded {
859 wait_for_response(request, &mut response, target, done_chan, context).await;
860 }
861
862 target.process_response_eof(request, &response);
864 send_response_to_devtools(request, context, &response, None);
868
869 context
870 .state
871 .http_cache
872 .update_awaiting_consumers(request, &response)
873 .await;
874
875 response
878}
879
880async fn wait_for_response(
881 request: &Request,
882 response: &mut Response,
883 target: Target<'_>,
884 done_chan: &mut DoneChannel,
885 context: &FetchContext,
886) {
887 if let Some(ref mut ch) = *done_chan {
888 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
889 loop {
890 match ch.1.recv().await {
891 Some(Data::Payload(vec)) => {
892 if let Some(body) = devtools_body.as_mut() {
893 body.extend(&vec);
894 }
895 target.process_response_chunk(request, vec);
896 },
897 Some(Data::Error(network_error)) => {
898 if network_error == NetworkError::DecompressionError {
899 response.termination_reason = Some(TerminationReason::Fatal);
900 }
901 response.set_network_error(network_error);
902
903 break;
904 },
905 Some(Data::Done) => {
906 send_response_to_devtools(request, context, response, devtools_body);
907 break;
908 },
909 Some(Data::Cancelled) => {
910 response.aborted.store(true, Ordering::Release);
911 break;
912 },
913 _ => {
914 panic!("fetch worker should always send Done before terminating");
915 },
916 }
917 }
918 } else {
919 match *response.actual_response().body.lock() {
920 ResponseBody::Done(ref vec) if !vec.is_empty() => {
921 target.process_response_chunk(request, vec.clone());
925 if context.devtools_chan.is_some() {
926 send_response_to_devtools(request, context, response, Some(vec.clone()));
929 }
930 },
931 ResponseBody::Done(_) | ResponseBody::Empty => {},
932 _ => unreachable!(),
933 }
934 }
935}
936
937pub enum RangeRequestBounds {
939 Final(RelativePos),
941 Pending(u64),
944}
945
946impl RangeRequestBounds {
947 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
948 match self {
949 RangeRequestBounds::Final(pos) => {
950 if let Some(len) = len {
951 if pos.start <= len as i64 {
952 return Ok(*pos);
953 }
954 }
955 Err("Tried to process RangeRequestBounds::Final without len")
956 },
957 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
958 if let Some(len) = len {
959 Some((len - u64::min(len, *offset)) as i64)
960 } else {
961 Some(0)
962 },
963 None,
964 )),
965 }
966 }
967}
968
969fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
970 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
971 response
972 .headers
973 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
974 *response.body.lock() = ResponseBody::Done(vec![]);
975 response.status = HttpStatus::default();
976 response
977}
978
979fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
980 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
981 response
982 .headers
983 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
984 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
985 response.status = HttpStatus::default();
986 response
987}
988
989fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
991 let error = |string| Err(io::Error::other(string));
992
993 let body = match request.body.as_mut() {
994 Some(body) => body,
995 None => return error("No body found"),
996 };
997
998 let stream = body.clone_stream();
999 let mut stream = stream.lock();
1000 let (body_chan, body_port) = ipc::channel().unwrap();
1001 let Some(chunk_requester) = stream.as_mut() else {
1002 log::error!(
1003 "Could not connect to the request body stream because it has already been closed."
1004 );
1005 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1006 };
1007 chunk_requester
1008 .send(BodyChunkRequest::Connect(body_chan))
1009 .map_err(|error| {
1010 log::error!(
1011 "Could not connect to the request body stream because it has already been closed: {error}"
1012 );
1013 std::io::Error::other("Could not connect to request body stream")
1014 })?;
1015 chunk_requester
1016 .send(BodyChunkRequest::Chunk)
1017 .map_err(|error| {
1018 log::error!(
1019 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1020 );
1021 std::io::Error::other("Could not request request body chunk")
1022 })?;
1023 let body_bytes = match body_port.recv().ok() {
1024 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1025 _ => return error("Certificate not sent in a single chunk"),
1026 };
1027
1028 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1029 Some(split_idx) => split_idx,
1030 None => return error("Could not find ampersand in data"),
1031 };
1032 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1033
1034 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1035 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1036 return error("Invalid secret sent. Ignoring request");
1037 }
1038
1039 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1040 Ok(bytes) => bytes,
1041 Err(_) => return error("Could not decode certificate base64"),
1042 };
1043
1044 context
1045 .state
1046 .override_manager
1047 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1048 Ok(())
1049}
1050
1051async fn scheme_fetch(
1053 fetch_params: &mut FetchParams,
1054 cache: &mut CorsCache,
1055 target: Target<'_>,
1056 done_chan: &mut DoneChannel,
1057 context: &FetchContext,
1058) -> Response {
1059 let request = &mut fetch_params.request;
1063 let url_and_blob_lock = request.current_url_with_blob_claim();
1064
1065 let scheme = url_and_blob_lock.scheme();
1066 match scheme {
1067 "about" if url_and_blob_lock.path() == "blank" => {
1068 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1069 },
1070 "about" if url_and_blob_lock.path() == "memory" => {
1071 create_about_memory(url_and_blob_lock.url(), request.timing_type())
1072 },
1073
1074 "chrome" if url_and_blob_lock.path() == "allowcert" => {
1075 if let Err(error) = handle_allowcert_request(request, context) {
1076 warn!("Could not handle allowcert request: {error}");
1077 }
1078 create_blank_reply(url_and_blob_lock.url(), request.timing_type())
1079 },
1080
1081 "http" | "https" => {
1082 http_fetch(
1083 fetch_params,
1084 cache,
1085 false,
1086 false,
1087 false,
1088 target,
1089 done_chan,
1090 context,
1091 )
1092 .await
1093 },
1094
1095 _ => match context.protocols.get(scheme) {
1096 Some(handler) => handler.load(request, done_chan, context).await,
1097 None => Response::network_error(NetworkError::UnsupportedScheme),
1098 },
1099 }
1100}
1101
1102fn is_null_body_status(status: &HttpStatus) -> bool {
1103 matches!(
1104 status.try_code(),
1105 Some(StatusCode::SWITCHING_PROTOCOLS) |
1106 Some(StatusCode::NO_CONTENT) |
1107 Some(StatusCode::RESET_CONTENT) |
1108 Some(StatusCode::NOT_MODIFIED)
1109 )
1110}
1111
1112pub fn should_be_blocked_due_to_nosniff(
1114 destination: Destination,
1115 response_headers: &HeaderMap,
1116) -> bool {
1117 if !determine_nosniff(response_headers) {
1119 return false;
1120 }
1121
1122 let mime_type = extract_mime_type_as_mime(response_headers);
1125
1126 #[inline]
1128 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1129 let javascript_mime_types: [Mime; 16] = [
1130 "application/ecmascript".parse().unwrap(),
1131 "application/javascript".parse().unwrap(),
1132 "application/x-ecmascript".parse().unwrap(),
1133 "application/x-javascript".parse().unwrap(),
1134 "text/ecmascript".parse().unwrap(),
1135 "text/javascript".parse().unwrap(),
1136 "text/javascript1.0".parse().unwrap(),
1137 "text/javascript1.1".parse().unwrap(),
1138 "text/javascript1.2".parse().unwrap(),
1139 "text/javascript1.3".parse().unwrap(),
1140 "text/javascript1.4".parse().unwrap(),
1141 "text/javascript1.5".parse().unwrap(),
1142 "text/jscript".parse().unwrap(),
1143 "text/livescript".parse().unwrap(),
1144 "text/x-ecmascript".parse().unwrap(),
1145 "text/x-javascript".parse().unwrap(),
1146 ];
1147
1148 javascript_mime_types
1149 .iter()
1150 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1151 }
1152
1153 match mime_type {
1154 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1156 Some(ref mime_type) if destination == Destination::Style => {
1158 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1159 },
1160
1161 None if destination == Destination::Style || destination.is_script_like() => true,
1162 _ => false,
1164 }
1165}
1166
1167fn should_be_blocked_due_to_mime_type(
1169 destination: Destination,
1170 response_headers: &HeaderMap,
1171) -> bool {
1172 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1174 Some(mime_type) => mime_type,
1175 None => return false,
1177 };
1178
1179 destination.is_script_like() &&
1185 match mime_type.type_() {
1186 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1187 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1188 _ => false,
1189 }
1190}
1191
1192pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1194 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1199 let is_bad_port = url.port().is_some_and(is_bad_port);
1200 if is_http_scheme && is_bad_port {
1201 return true;
1202 }
1203
1204 false
1206}
1207
1208pub fn should_request_be_blocked_as_mixed_content(
1210 request: &Request,
1211 protocol_registry: &ProtocolRegistry,
1212) -> bool {
1213 if do_settings_prohibit_mixed_security_contexts(request) ==
1217 MixedSecurityProhibited::NotProhibited
1218 {
1219 return false;
1220 }
1221
1222 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1224 return false;
1225 }
1226
1227 if request.destination == Destination::Document {
1232 return false;
1234 }
1235
1236 true
1237}
1238
1239pub fn should_response_be_blocked_as_mixed_content(
1241 request: &Request,
1242 response: &Response,
1243 protocol_registry: &ProtocolRegistry,
1244) -> bool {
1245 if do_settings_prohibit_mixed_security_contexts(request) ==
1249 MixedSecurityProhibited::NotProhibited
1250 {
1251 return false;
1252 }
1253
1254 if response
1256 .actual_response()
1257 .url()
1258 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1259 {
1260 return false;
1261 }
1262
1263 if request.destination == Destination::Document {
1268 return false;
1270 }
1271
1272 true
1273}
1274
1275fn is_bad_port(port: u16) -> bool {
1277 static BAD_PORTS: [u16; 78] = [
1278 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1279 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1280 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1281 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1282 6668, 6669, 6697, 10080,
1283 ];
1284
1285 BAD_PORTS.binary_search(&port).is_ok()
1286}
1287
1288pub fn is_form_submission_request(request: &Request) -> bool {
1290 let content_type = request.headers.typed_get::<ContentType>();
1291 content_type.is_some_and(|ct| {
1292 let mime: Mime = ct.into();
1293 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1294 })
1295}
1296
1297fn should_upgrade_request_to_potentially_trustworthy(
1299 request: &mut Request,
1300 context: &FetchContext,
1301) -> bool {
1302 fn should_upgrade_navigation_request(request: &Request) -> bool {
1303 if is_form_submission_request(request) {
1305 return true;
1306 }
1307
1308 if request
1311 .client
1312 .as_ref()
1313 .is_some_and(|client| client.is_nested_browsing_context)
1314 {
1315 return true;
1316 }
1317
1318 false
1323 }
1324
1325 if request.is_navigation_request() {
1327 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1332 request
1333 .current_url()
1334 .host_str()
1335 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1336 {
1337 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1338 request
1339 .headers
1340 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1341 }
1342
1343 if !should_upgrade_navigation_request(request) {
1344 return false;
1345 }
1346 }
1347
1348 request
1352 .client
1353 .as_ref()
1354 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1355}
1356
1357#[derive(Debug, PartialEq)]
1358pub enum MixedSecurityProhibited {
1359 Prohibited,
1360 NotProhibited,
1361}
1362
1363fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1365 if let Origin::Origin(ref origin) = request.origin {
1366 let is_origin_data_url_worker = matches!(
1368 *origin,
1369 ImmutableOrigin::Opaque(servo_url::OpaqueOrigin::SecureWorkerFromDataUrl(_))
1370 );
1371
1372 if origin.is_potentially_trustworthy() || is_origin_data_url_worker {
1375 return MixedSecurityProhibited::Prohibited;
1376 }
1377 }
1378
1379 if request.has_trustworthy_ancestor_origin {
1383 return MixedSecurityProhibited::Prohibited;
1384 }
1385
1386 MixedSecurityProhibited::NotProhibited
1387}
1388
1389fn should_upgrade_mixed_content_request(
1391 request: &Request,
1392 protocol_registry: &ProtocolRegistry,
1393) -> bool {
1394 let url = request.url();
1395 if is_url_potentially_trustworthy(protocol_registry, &url) {
1397 return false;
1398 }
1399
1400 match url.host() {
1402 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1403 _ => (),
1404 }
1405
1406 if do_settings_prohibit_mixed_security_contexts(request) ==
1408 MixedSecurityProhibited::NotProhibited
1409 {
1410 return false;
1411 }
1412
1413 if !matches!(
1415 request.destination,
1416 Destination::Audio | Destination::Image | Destination::Video
1417 ) {
1418 return false;
1419 }
1420
1421 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1423 return false;
1424 }
1425
1426 true
1427}