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(res) => res,
544 None => {
545 let same_origin = if let Origin::Origin(ref origin) = request.origin {
548 *origin == current_url.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.clone_from(&request.url_list)
730 }
731
732 internal_response.redirect_taint = request.redirect_taint_for_request();
734
735 let mut blocked_error_response;
741
742 let internal_response = if should_replace_with_nosniff_error {
743 blocked_error_response = Response::network_error(NetworkError::Nosniff);
745 &blocked_error_response
746 } else if should_replace_with_mime_type_error {
747 blocked_error_response =
749 Response::network_error(NetworkError::MimeType("Blocked by MIME type".into()));
750 &blocked_error_response
751 } else if should_replace_with_mixed_content {
752 blocked_error_response = Response::network_error(NetworkError::MixedContent);
753 &blocked_error_response
754 } else if should_replace_with_csp_error {
755 blocked_error_response = Response::network_error(NetworkError::ContentSecurityPolicy);
756 &blocked_error_response
757 } else {
758 internal_response
759 };
760
761 let internal_response = if !internal_response.is_network_error() &&
767 response_type == ResponseType::Opaque &&
768 internal_response.status.code() == StatusCode::PARTIAL_CONTENT &&
769 internal_response.range_requested &&
770 !request.headers.contains_key(RANGE)
771 {
772 blocked_error_response =
774 Response::network_error(NetworkError::PartialResponseToNonRangeRequestError);
775 &blocked_error_response
776 } else {
777 internal_response
778 };
779
780 let not_network_error = !response_is_network_error && !internal_response.is_network_error();
785 if not_network_error &&
786 (is_null_body_status(&internal_response.status) ||
787 matches!(request.method, Method::HEAD | Method::CONNECT))
788 {
789 let mut body = internal_response.body.lock();
792 *body = ResponseBody::Empty;
793 }
794
795 internal_response.get_network_error().cloned()
796 };
797
798 let mut response = if let Some(error) = internal_error {
800 Response::network_error(error)
801 } else {
802 response
803 };
804
805 let mut response_loaded = false;
807 let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
808 wait_for_response(request, &mut response, target, done_chan, context).await;
810 response_loaded = true;
811
812 let integrity_metadata = &request.integrity_metadata;
814 if response.termination_reason.is_none() &&
815 !is_response_integrity_valid(integrity_metadata, &response)
816 {
817 Response::network_error(NetworkError::SubresourceIntegrity)
818 } else {
819 response
820 }
821 } else {
822 response
823 };
824
825 if request.synchronous {
827 target.process_response(request, &response);
830 if !response_loaded {
831 wait_for_response(request, &mut response, target, done_chan, context).await;
832 }
833 target.process_response_eof(request, &response);
835 return response;
836 }
837
838 if request.body.is_some() && matches!(current_scheme, "http" | "https") {
840 target.process_request_body(request);
845 }
846
847 target.process_response(request, &response);
849 send_response_to_devtools(request, context, &response, None);
851 send_security_info_to_devtools(request, context, &response);
852
853 if !response_loaded {
855 wait_for_response(request, &mut response, target, done_chan, context).await;
856 }
857
858 target.process_response_eof(request, &response);
860 send_response_to_devtools(request, context, &response, None);
864
865 context
866 .state
867 .http_cache
868 .update_awaiting_consumers(request, &response)
869 .await;
870
871 response
874}
875
876async fn wait_for_response(
877 request: &Request,
878 response: &mut Response,
879 target: Target<'_>,
880 done_chan: &mut DoneChannel,
881 context: &FetchContext,
882) {
883 if let Some(ref mut ch) = *done_chan {
884 let mut devtools_body = context.devtools_chan.as_ref().map(|_| Vec::new());
885 loop {
886 match ch.1.recv().await {
887 Some(Data::Payload(vec)) => {
888 if let Some(body) = devtools_body.as_mut() {
889 body.extend(&vec);
890 }
891 target.process_response_chunk(request, vec);
892 },
893 Some(Data::Error(network_error)) => {
894 if network_error == NetworkError::DecompressionError {
895 response.termination_reason = Some(TerminationReason::Fatal);
896 }
897 response.set_network_error(network_error);
898
899 break;
900 },
901 Some(Data::Done) => {
902 send_response_to_devtools(request, context, response, devtools_body);
903 break;
904 },
905 Some(Data::Cancelled) => {
906 response.aborted.store(true, Ordering::Release);
907 break;
908 },
909 _ => {
910 panic!("fetch worker should always send Done before terminating");
911 },
912 }
913 }
914 } else {
915 match *response.actual_response().body.lock() {
916 ResponseBody::Done(ref vec) if !vec.is_empty() => {
917 target.process_response_chunk(request, vec.clone());
921 if context.devtools_chan.is_some() {
922 send_response_to_devtools(request, context, response, Some(vec.clone()));
925 }
926 },
927 ResponseBody::Done(_) | ResponseBody::Empty => {},
928 _ => unreachable!(),
929 }
930 }
931}
932
933pub enum RangeRequestBounds {
935 Final(RelativePos),
937 Pending(u64),
940}
941
942impl RangeRequestBounds {
943 pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, &'static str> {
944 match self {
945 RangeRequestBounds::Final(pos) => {
946 if let Some(len) = len {
947 if pos.start <= len as i64 {
948 return Ok(*pos);
949 }
950 }
951 Err("Tried to process RangeRequestBounds::Final without len")
952 },
953 RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts(
954 if let Some(len) = len {
955 Some((len - u64::min(len, *offset)) as i64)
956 } else {
957 Some(0)
958 },
959 None,
960 )),
961 }
962 }
963}
964
965fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
966 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
967 response
968 .headers
969 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
970 *response.body.lock() = ResponseBody::Done(vec![]);
971 response.status = HttpStatus::default();
972 response
973}
974
975fn create_about_memory(url: ServoUrl, timing_type: ResourceTimingType) -> Response {
976 let mut response = Response::new(url, ResourceFetchTiming::new(timing_type));
977 response
978 .headers
979 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
980 *response.body.lock() = ResponseBody::Done(resources::read_bytes(Resource::AboutMemoryHTML));
981 response.status = HttpStatus::default();
982 response
983}
984
985fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> {
987 let error = |string| Err(io::Error::other(string));
988
989 let body = match request.body.as_mut() {
990 Some(body) => body,
991 None => return error("No body found"),
992 };
993
994 let stream = body.clone_stream();
995 let mut stream = stream.lock();
996 let (body_chan, body_port) = ipc::channel().unwrap();
997 let Some(chunk_requester) = stream.as_mut() else {
998 log::error!(
999 "Could not connect to the request body stream because it has already been closed."
1000 );
1001 return Err(std::io::Error::other("Could not send BodyChunkRequest"));
1002 };
1003 chunk_requester
1004 .send(BodyChunkRequest::Connect(body_chan))
1005 .map_err(|error| {
1006 log::error!(
1007 "Could not connect to the request body stream because it has already been closed: {error}"
1008 );
1009 std::io::Error::other("Could not connect to request body stream")
1010 })?;
1011 chunk_requester
1012 .send(BodyChunkRequest::Chunk)
1013 .map_err(|error| {
1014 log::error!(
1015 "Could not request the first request body chunk because the body stream has already been closed: {error}"
1016 );
1017 std::io::Error::other("Could not request request body chunk")
1018 })?;
1019 let body_bytes = match body_port.recv().ok() {
1020 Some(BodyChunkResponse::Chunk(bytes)) => bytes,
1021 _ => return error("Certificate not sent in a single chunk"),
1022 };
1023
1024 let split_idx = match body_bytes.iter().position(|b| *b == b'&') {
1025 Some(split_idx) => split_idx,
1026 None => return error("Could not find ampersand in data"),
1027 };
1028 let (secret, cert_base64) = body_bytes.split_at(split_idx);
1029
1030 let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok());
1031 if secret != Some(*net_traits::PRIVILEGED_SECRET) {
1032 return error("Invalid secret sent. Ignoring request");
1033 }
1034
1035 let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) {
1036 Ok(bytes) => bytes,
1037 Err(_) => return error("Could not decode certificate base64"),
1038 };
1039
1040 context
1041 .state
1042 .override_manager
1043 .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
1044 Ok(())
1045}
1046
1047async fn scheme_fetch(
1049 fetch_params: &mut FetchParams,
1050 cache: &mut CorsCache,
1051 target: Target<'_>,
1052 done_chan: &mut DoneChannel,
1053 context: &FetchContext,
1054) -> Response {
1055 let request = &mut fetch_params.request;
1059 let url = request.current_url();
1060
1061 let scheme = url.scheme();
1062 match scheme {
1063 "about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()),
1064 "about" if url.path() == "memory" => create_about_memory(url, request.timing_type()),
1065
1066 "chrome" if url.path() == "allowcert" => {
1067 if let Err(error) = handle_allowcert_request(request, context) {
1068 warn!("Could not handle allowcert request: {error}");
1069 }
1070 create_blank_reply(url, request.timing_type())
1071 },
1072
1073 "http" | "https" => {
1074 http_fetch(
1075 fetch_params,
1076 cache,
1077 false,
1078 false,
1079 false,
1080 target,
1081 done_chan,
1082 context,
1083 )
1084 .await
1085 },
1086
1087 _ => match context.protocols.get(scheme) {
1088 Some(handler) => handler.load(request, done_chan, context).await,
1089 None => Response::network_error(NetworkError::UnsupportedScheme),
1090 },
1091 }
1092}
1093
1094fn is_null_body_status(status: &HttpStatus) -> bool {
1095 matches!(
1096 status.try_code(),
1097 Some(StatusCode::SWITCHING_PROTOCOLS) |
1098 Some(StatusCode::NO_CONTENT) |
1099 Some(StatusCode::RESET_CONTENT) |
1100 Some(StatusCode::NOT_MODIFIED)
1101 )
1102}
1103
1104pub fn should_be_blocked_due_to_nosniff(
1106 destination: Destination,
1107 response_headers: &HeaderMap,
1108) -> bool {
1109 if !determine_nosniff(response_headers) {
1111 return false;
1112 }
1113
1114 let mime_type = extract_mime_type_as_mime(response_headers);
1117
1118 #[inline]
1120 fn is_javascript_mime_type(mime_type: &Mime) -> bool {
1121 let javascript_mime_types: [Mime; 16] = [
1122 "application/ecmascript".parse().unwrap(),
1123 "application/javascript".parse().unwrap(),
1124 "application/x-ecmascript".parse().unwrap(),
1125 "application/x-javascript".parse().unwrap(),
1126 "text/ecmascript".parse().unwrap(),
1127 "text/javascript".parse().unwrap(),
1128 "text/javascript1.0".parse().unwrap(),
1129 "text/javascript1.1".parse().unwrap(),
1130 "text/javascript1.2".parse().unwrap(),
1131 "text/javascript1.3".parse().unwrap(),
1132 "text/javascript1.4".parse().unwrap(),
1133 "text/javascript1.5".parse().unwrap(),
1134 "text/jscript".parse().unwrap(),
1135 "text/livescript".parse().unwrap(),
1136 "text/x-ecmascript".parse().unwrap(),
1137 "text/x-javascript".parse().unwrap(),
1138 ];
1139
1140 javascript_mime_types
1141 .iter()
1142 .any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
1143 }
1144
1145 match mime_type {
1146 Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
1148 Some(ref mime_type) if destination == Destination::Style => {
1150 mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
1151 },
1152
1153 None if destination == Destination::Style || destination.is_script_like() => true,
1154 _ => false,
1156 }
1157}
1158
1159fn should_be_blocked_due_to_mime_type(
1161 destination: Destination,
1162 response_headers: &HeaderMap,
1163) -> bool {
1164 let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
1166 Some(mime_type) => mime_type,
1167 None => return false,
1169 };
1170
1171 destination.is_script_like() &&
1177 match mime_type.type_() {
1178 mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
1179 mime::TEXT if mime_type.subtype() == mime::CSV => true,
1180 _ => false,
1181 }
1182}
1183
1184pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool {
1186 let is_http_scheme = matches!(url.scheme(), "http" | "https");
1191 let is_bad_port = url.port().is_some_and(is_bad_port);
1192 if is_http_scheme && is_bad_port {
1193 return true;
1194 }
1195
1196 false
1198}
1199
1200pub fn should_request_be_blocked_as_mixed_content(
1202 request: &Request,
1203 protocol_registry: &ProtocolRegistry,
1204) -> bool {
1205 if do_settings_prohibit_mixed_security_contexts(request) ==
1209 MixedSecurityProhibited::NotProhibited
1210 {
1211 return false;
1212 }
1213
1214 if is_url_potentially_trustworthy(protocol_registry, &request.current_url()) {
1216 return false;
1217 }
1218
1219 if request.destination == Destination::Document {
1224 return false;
1226 }
1227
1228 true
1229}
1230
1231pub fn should_response_be_blocked_as_mixed_content(
1233 request: &Request,
1234 response: &Response,
1235 protocol_registry: &ProtocolRegistry,
1236) -> bool {
1237 if do_settings_prohibit_mixed_security_contexts(request) ==
1241 MixedSecurityProhibited::NotProhibited
1242 {
1243 return false;
1244 }
1245
1246 if response
1248 .actual_response()
1249 .url()
1250 .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url))
1251 {
1252 return false;
1253 }
1254
1255 if request.destination == Destination::Document {
1260 return false;
1262 }
1263
1264 true
1265}
1266
1267fn is_bad_port(port: u16) -> bool {
1269 static BAD_PORTS: [u16; 78] = [
1270 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101,
1271 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
1272 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
1273 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061, 6000, 6566, 6665, 6666, 6667,
1274 6668, 6669, 6697, 10080,
1275 ];
1276
1277 BAD_PORTS.binary_search(&port).is_ok()
1278}
1279
1280pub fn is_form_submission_request(request: &Request) -> bool {
1282 let content_type = request.headers.typed_get::<ContentType>();
1283 content_type.is_some_and(|ct| {
1284 let mime: Mime = ct.into();
1285 mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED
1286 })
1287}
1288
1289fn should_upgrade_request_to_potentially_trustworthy(
1291 request: &mut Request,
1292 context: &FetchContext,
1293) -> bool {
1294 fn should_upgrade_navigation_request(request: &Request) -> bool {
1295 if is_form_submission_request(request) {
1297 return true;
1298 }
1299
1300 if request
1303 .client
1304 .as_ref()
1305 .is_some_and(|client| client.is_nested_browsing_context)
1306 {
1307 return true;
1308 }
1309
1310 false
1315 }
1316
1317 if request.is_navigation_request() {
1319 if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) ||
1324 request
1325 .current_url()
1326 .host_str()
1327 .is_none_or(|host| context.state.hsts_list.read().is_host_secure(host))
1328 {
1329 debug!("Appending the Upgrade-Insecure-Requests header to request’s header list");
1330 request
1331 .headers
1332 .insert("Upgrade-Insecure-Requests", HeaderValue::from_static("1"));
1333 }
1334
1335 if !should_upgrade_navigation_request(request) {
1336 return false;
1337 }
1338 }
1339
1340 request
1344 .client
1345 .as_ref()
1346 .is_some_and(|client| client.insecure_requests_policy == InsecureRequestsPolicy::Upgrade)
1347}
1348
1349#[derive(Debug, PartialEq)]
1350pub enum MixedSecurityProhibited {
1351 Prohibited,
1352 NotProhibited,
1353}
1354
1355fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited {
1357 if let Origin::Origin(ref origin) = request.origin {
1358 let is_origin_data_url_worker = matches!(
1360 *origin,
1361 ImmutableOrigin::Opaque(servo_url::OpaqueOrigin::SecureWorkerFromDataUrl(_))
1362 );
1363
1364 if origin.is_potentially_trustworthy() || is_origin_data_url_worker {
1367 return MixedSecurityProhibited::Prohibited;
1368 }
1369 }
1370
1371 if request.has_trustworthy_ancestor_origin {
1375 return MixedSecurityProhibited::Prohibited;
1376 }
1377
1378 MixedSecurityProhibited::NotProhibited
1379}
1380
1381fn should_upgrade_mixed_content_request(
1383 request: &Request,
1384 protocol_registry: &ProtocolRegistry,
1385) -> bool {
1386 let url = request.url();
1387 if is_url_potentially_trustworthy(protocol_registry, &url) {
1389 return false;
1390 }
1391
1392 match url.host() {
1394 Some(Host::Ipv4(_)) | Some(Host::Ipv6(_)) => return false,
1395 _ => (),
1396 }
1397
1398 if do_settings_prohibit_mixed_security_contexts(request) ==
1400 MixedSecurityProhibited::NotProhibited
1401 {
1402 return false;
1403 }
1404
1405 if !matches!(
1407 request.destination,
1408 Destination::Audio | Destination::Image | Destination::Video
1409 ) {
1410 return false;
1411 }
1412
1413 if request.destination == Destination::Image && request.initiator == Initiator::ImageSet {
1415 return false;
1416 }
1417
1418 true
1419}