1use std::collections::HashSet;
6use std::iter::FromIterator;
7use std::sync::Arc as StdArc;
8use std::time::{Duration, SystemTime};
9
10use async_recursion::async_recursion;
11use content_security_policy::percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
12use devtools_traits::ChromeToDevtoolsControlMsg;
13use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
14use futures::{TryFutureExt, TryStreamExt, future};
15use headers::authorization::Basic;
16use headers::{
17 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
18 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
19 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
20 UserAgent,
21};
22use http::header::{
23 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
24 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
25 WWW_AUTHENTICATE,
26};
27use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
28use http_body_util::combinators::BoxBody;
29use http_body_util::{BodyExt, Full};
30use hyper::Response as HyperResponse;
31use hyper::body::{Bytes, Frame};
32use hyper::ext::ReasonPhrase;
33use hyper::header::{HeaderName, TRANSFER_ENCODING};
34use ipc_channel::IpcError;
35use ipc_channel::ipc::{self, IpcSender};
36use ipc_channel::router::ROUTER;
37use log::{debug, error, info, log_enabled, warn};
38use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
39use net_traits::blob_url_store::UrlWithBlobClaim;
40use net_traits::fetch::headers::get_value_from_header_list;
41use net_traits::http_status::HttpStatus;
42use net_traits::policy_container::RequestPolicyContainer;
43use net_traits::pub_domains::{is_same_site, reg_suffix};
44use net_traits::request::Origin::Origin as SpecificOrigin;
45use net_traits::request::{
46 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
47 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
48 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
49 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
50 is_cors_safelisted_request_header,
51};
52use net_traits::response::{
53 CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType,
54};
55use net_traits::{
56 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, NetworkError, RedirectEndValue, RedirectStartValue,
57 ReferrerPolicy, ResourceAttribute, ResourceFetchTimingContainer, ResourceTimeValue,
58 TlsSecurityInfo, TlsSecurityState,
59};
60use parking_lot::{Mutex, RwLock};
61use profile_traits::mem::{Report, ReportKind};
62use profile_traits::path;
63#[cfg(feature = "tracing")]
64use profile_traits::trace_span;
65use rustc_hash::FxHashMap;
66use servo_base::cross_process_instant::CrossProcessInstant;
67use servo_base::generic_channel::GenericSharedMemory;
68use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId};
69use servo_url::{ImmutableOrigin, ServoUrl};
70use tokio::sync::mpsc::{
71 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
72 unbounded_channel,
73};
74use tokio_stream::wrappers::ReceiverStream;
75#[cfg(feature = "tracing")]
76use tracing::Instrument;
77
78use crate::async_runtime::spawn_task;
79use crate::connector::{
80 CertificateErrorOverrideManager, ServoClient, TlsHandshakeInfo, create_tls_config,
81};
82use crate::cookie::ServoCookie;
83use crate::cookie_storage::CookieStorage;
84use crate::decoder::Decoder;
85use crate::devtools::{
86 prepare_devtools_request, send_request_to_devtools, send_response_values_to_devtools,
87};
88use crate::embedder::NetToEmbedderMsg;
89use crate::fetch::cors_cache::CorsCache;
90use crate::fetch::fetch_params::FetchParams;
91use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
92use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
93use crate::hsts::HstsList;
94use crate::http_cache::{
95 CacheKey, CachedResourcesOrGuard, HttpCache, construct_response, invalidate, refresh,
96};
97use crate::resource_thread::{AuthCache, AuthCacheEntry};
98use crate::websocket_loader::start_websocket;
99
100#[derive(Clone, Debug, Eq, PartialEq)]
102pub enum HttpCacheEntryState {
103 ReadyToConstruct,
107 PendingStore(usize),
109}
110
111pub struct HttpState {
112 pub hsts_list: RwLock<HstsList>,
113 pub cookie_jar: RwLock<CookieStorage>,
114 pub http_cache: HttpCache,
115 pub auth_cache: RwLock<AuthCache>,
116 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
117 pub client: ServoClient,
118 pub override_manager: CertificateErrorOverrideManager,
119 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
120}
121
122impl HttpState {
123 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
124 vec![
125 Report {
126 path: path!["memory-cache", suffix],
127 kind: ReportKind::ExplicitJemallocHeapSize,
128 size: self.http_cache.size_of(ops),
129 },
130 Report {
131 path: path!["hsts-list", suffix],
132 kind: ReportKind::ExplicitJemallocHeapSize,
133 size: self.hsts_list.read().size_of(ops),
134 },
135 ]
136 }
137
138 async fn request_authentication(
139 &self,
140 request: &Request,
141 response: &Response,
142 ) -> Option<AuthenticationResponse> {
143 let webview_id = request.target_webview_id?;
145 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
146
147 if request.mode != RequestMode::Navigate {
149 return None;
150 }
151
152 let (sender, receiver) = tokio::sync::oneshot::channel();
153 self.embedder_proxy
154 .send(NetToEmbedderMsg::RequestAuthentication(
155 webview_id,
156 request.url(),
157 for_proxy,
158 sender,
159 ));
160 receiver.await.ok()?
161 }
162}
163
164pub(crate) fn set_default_accept(request: &mut Request) {
166 if request.headers.contains_key(header::ACCEPT) {
168 return;
169 }
170
171 let value = if request.initiator == Initiator::Prefetch {
173 DOCUMENT_ACCEPT_HEADER_VALUE
174 } else {
175 match request.destination {
178 Destination::Document | Destination::Frame | Destination::IFrame => {
179 DOCUMENT_ACCEPT_HEADER_VALUE
180 },
181 Destination::Image => {
182 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
183 },
184 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
185 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
186 _ => HeaderValue::from_static("*/*"),
188 }
189 };
190
191 request.headers.insert(header::ACCEPT, value);
193}
194
195fn set_default_accept_encoding(headers: &mut HeaderMap) {
196 if headers.contains_key(header::ACCEPT_ENCODING) {
197 return;
198 }
199
200 headers.insert(
202 header::ACCEPT_ENCODING,
203 HeaderValue::from_static("gzip, deflate, br, zstd"),
204 );
205}
206
207fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
209 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
211 return None;
212 }
213 strip_url_for_use_as_referrer(referrer_url, false)
215}
216
217fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
219 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
221 return None;
222 }
223 strip_url_for_use_as_referrer(referrer_url, true)
225}
226
227fn strict_origin_when_cross_origin(
229 referrer_url: ServoUrl,
230 current_url: ServoUrl,
231) -> Option<ServoUrl> {
232 if referrer_url.origin() == current_url.origin() {
234 return strip_url_for_use_as_referrer(referrer_url, false);
235 }
236 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
238 return None;
239 }
240 strip_url_for_use_as_referrer(referrer_url, true)
242}
243
244fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
246 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
248 true
249 } else if site_a.is_tuple() && site_b.is_tuple() {
250 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
252 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
253
254 let host_a_reg = reg_suffix(&host_a);
255 let host_b_reg = reg_suffix(&host_b);
256
257 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
259 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
260 } else {
261 false
263 }
264}
265
266fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
268 const MAX_REFERRER_URL_LENGTH: usize = 4096;
269 if url.is_local_scheme() {
271 return None;
272 }
273 {
275 let url = url.as_mut_url();
276 let _ = url.set_username("");
277 let _ = url.set_password(None);
278 url.set_fragment(None);
279 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
283 url.set_path("");
284 url.set_query(None);
285 }
286 }
287 Some(url)
289}
290
291fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
293 if referrer_url.origin() == current_url.origin() {
295 return strip_url_for_use_as_referrer(referrer_url, false);
296 }
297 None
299}
300
301fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
303 if referrer_url.origin() == current_url.origin() {
305 return strip_url_for_use_as_referrer(referrer_url, false);
306 }
307 strip_url_for_use_as_referrer(referrer_url, true)
309}
310
311pub fn determine_requests_referrer(
313 referrer_policy: ReferrerPolicy,
314 referrer_source: ServoUrl,
315 current_url: ServoUrl,
316) -> Option<ServoUrl> {
317 match referrer_policy {
318 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
319 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
320 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
321 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
322 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
323 strict_origin_when_cross_origin(referrer_source, current_url)
324 },
325 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
326 ReferrerPolicy::OriginWhenCrossOrigin => {
327 origin_when_cross_origin(referrer_source, current_url)
328 },
329 ReferrerPolicy::NoReferrerWhenDowngrade => {
330 no_referrer_when_downgrade(referrer_source, current_url)
331 },
332 }
333}
334
335fn set_request_cookies(
336 url: &ServoUrl,
337 headers: &mut HeaderMap,
338 cookie_jar: &RwLock<CookieStorage>,
339) {
340 let mut cookie_jar = cookie_jar.write();
341 cookie_jar.remove_expired_cookies_for_url(url);
342 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
343 headers.insert(
344 header::COOKIE,
345 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
346 );
347 }
348}
349
350fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
351 let mut cookie_jar = cookie_jar.write();
352 let source = CookieSource::HTTP;
353
354 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
355 cookie_jar.push(cookie, request, source);
356 }
357}
358
359fn set_cookies_from_headers(
360 url: &ServoUrl,
361 headers: &HeaderMap,
362 cookie_jar: &RwLock<CookieStorage>,
363) {
364 for cookie in headers.get_all(header::SET_COOKIE) {
365 let cookie_bytes = cookie.as_bytes();
366 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
367 continue;
368 }
369 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
370 set_cookie_for_url(cookie_jar, url, cookie_str);
371 }
372 }
373}
374
375fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
376 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
384 TlsSecurityState::Insecure
386 } else {
387 TlsSecurityState::Secure
389 };
390
391 TlsSecurityInfo {
392 state,
393 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
395 cipher_suite: handshake.cipher_suite.clone(),
396 kea_group_name: handshake.kea_group_name.clone(),
397 signature_scheme_name: handshake.signature_scheme_name.clone(),
398 alpn_protocol: handshake.alpn_protocol.clone(),
399 certificate_chain_der: handshake.certificate_chain_der.clone(),
400 certificate_transparency: None,
401 hsts: hsts_enabled,
402 hpkp: false,
403 used_ech: handshake.used_ech,
404 used_delegated_credentials: false,
405 used_ocsp: false,
406 used_private_dns: false,
407 }
408}
409
410fn auth_from_cache(
411 auth_cache: &RwLock<AuthCache>,
412 origin: &ImmutableOrigin,
413) -> Option<Authorization<Basic>> {
414 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
415 let user_name = &auth_entry.user_name;
416 let password = &auth_entry.password;
417 Some(Authorization::basic(user_name, password))
418 } else {
419 None
420 }
421}
422
423enum BodyChunk {
426 Chunk(GenericSharedMemory),
428 Done,
430}
431
432enum BodyStream {
434 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
437 Buffered(UnboundedReceiver<BodyChunk>),
440}
441
442enum BodySink {
445 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
447 Buffered(UnboundedSender<BodyChunk>),
451}
452
453impl BodySink {
454 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
455 match self {
456 BodySink::Chunked(sender) => {
457 let sender = sender.clone();
458 spawn_task(async move {
459 let _ = sender
460 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
461 .await;
462 });
463 },
464 BodySink::Buffered(sender) => {
465 let _ = sender.send(BodyChunk::Chunk(bytes));
466 },
467 }
468 }
469
470 fn close(&self) {
471 match self {
472 BodySink::Chunked(_) => { },
473 BodySink::Buffered(sender) => {
474 let _ = sender.send(BodyChunk::Done);
475 },
476 }
477 }
478}
479
480fn request_body_stream_closed_error(action: &str) -> NetworkError {
481 NetworkError::Crash(format!(
482 "Request body stream has already been closed while trying to {action}."
483 ))
484}
485
486fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
487 match error {
488 Some(error) => {
489 error!("Request body stream has already been closed while trying to {action}: {error}")
490 },
491 None => error!("Request body stream has already been closed while trying to {action}."),
492 }
493}
494
495fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
496 warn!(
497 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
498 );
499}
500
501const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
502
503#[allow(clippy::too_many_arguments)]
504#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
505async fn obtain_response(
507 client: &ServoClient,
508 url: &ServoUrl,
509 method: &Method,
510 request_headers: &mut HeaderMap,
511 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
512 source_is_null: bool,
513 pipeline_id: &Option<PipelineId>,
514 request_id: Option<&str>,
515 destination: Destination,
516 is_xhr: bool,
517 context: &FetchContext,
518 fetch_terminated: UnboundedSender<bool>,
519 browsing_context_id: Option<BrowsingContextId>,
520) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
521 let mut headers = request_headers.clone();
522
523 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
524
525 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
527
528 let request = if let Some(chunk_requester) = body_sender {
529 let (sink, stream) = if source_is_null {
530 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
533
534 let (sender, receiver) = channel(1);
535 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
536 } else {
537 let (sender, receiver) = unbounded_channel();
544 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
545 };
546
547 obtain_response_setup_router_callback(
548 devtools_bytes.clone(),
549 chunk_requester,
550 sink,
551 fetch_terminated,
552 )?;
553
554 let body = match stream {
555 BodyStream::Chunked(receiver) => {
556 let stream = ReceiverStream::new(receiver);
557 BoxBody::new(http_body_util::StreamBody::new(stream))
558 },
559 BodyStream::Buffered(mut receiver) => {
560 let mut body = vec![];
562 loop {
563 match receiver.recv().await {
564 Some(BodyChunk::Chunk(bytes)) => {
565 body.extend_from_slice(&bytes);
566 },
567 Some(BodyChunk::Done) => break,
568 None => warn!("Failed to read all chunks from request body."),
569 }
570 }
571 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
572 },
573 };
574 HyperRequest::builder()
575 .method(method)
576 .uri(encoded_url)
577 .body(body)
578 } else {
579 HyperRequest::builder()
580 .method(method)
581 .uri(encoded_url)
582 .body(
583 http_body_util::Empty::new()
584 .map_err(|_| unreachable!())
585 .boxed(),
586 )
587 };
588
589 let connect_start = CrossProcessInstant::now();
592 context.timing.set_attributes(&[
593 ResourceAttribute::DomainLookupStart,
594 ResourceAttribute::ConnectStart(connect_start),
595 ]);
596
597 if url.scheme() == "https" {
601 context
602 .timing
603 .set_attribute(ResourceAttribute::SecureConnectionStart);
604 }
605
606 let mut request = match request {
607 Ok(request) => request,
608 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
609 };
610 *request.headers_mut() = headers.clone();
611
612 let connect_end = CrossProcessInstant::now();
613 context
614 .timing
615 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
616
617 let request_id = request_id.map(|v| v.to_owned());
618 let pipeline_id = *pipeline_id;
619 let closure_url = url.clone();
620 let method = method.clone();
621 let send_start = CrossProcessInstant::now();
622
623 let host = request.uri().host().unwrap_or("").to_owned();
624 let override_manager = context.state.override_manager.clone();
625 let headers = headers.clone();
626 let is_secure_scheme = url.is_secure_scheme();
627
628 let client_future = client
629 .request(request)
630 .and_then(move |res| {
631 let send_end = CrossProcessInstant::now();
632
633 let msg = if let Some(request_id) = request_id {
636 if let Some(pipeline_id) = pipeline_id {
637 if let Some(browsing_context_id) = browsing_context_id {
638 Some(prepare_devtools_request(
639 request_id,
640 closure_url,
641 method.clone(),
642 headers,
643 Some(devtools_bytes.lock().clone()),
644 pipeline_id,
645 (connect_end - connect_start).unsigned_abs(),
646 (send_end - send_start).unsigned_abs(),
647 destination,
648 is_xhr,
649 browsing_context_id,
650 ))
651 } else {
652 debug!("Not notifying devtools (no browsing_context_id)");
653 None
654 }
655 } else {
660 debug!("Not notifying devtools (no pipeline_id)");
661 None
662 }
663 } else {
664 debug!("Not notifying devtools (no request_id)");
665 None
666 };
667
668 future::ready(Ok((
669 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
670 msg,
671 )))
672 })
673 .map_err(move |error| {
674 warn!("network error: {error:?}");
675 NetworkError::from_hyper_error(
676 &error,
677 override_manager.remove_certificate_failing_verification(host.as_str()),
678 )
679 });
680
681 #[cfg(feature = "tracing")]
682 {
683 client_future.instrument(trace_span!("HyperRequest")).await
684 }
685
686 #[cfg(not(feature = "tracing"))]
687 {
688 client_future.await
689 }
690}
691
692fn obtain_response_setup_router_callback(
694 devtools_bytes: StdArc<Mutex<Vec<u8>>>,
695 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
696 sink: BodySink,
697 fetch_terminated: UnboundedSender<bool>,
698) -> Result<(), NetworkError> {
699 let (body_chan, body_port) = ipc::channel().unwrap();
700
701 {
702 let mut lock = chunk_requester.lock();
703 if let Some(chunk_requester) = lock.as_mut() {
704 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
705 log_request_body_stream_closed("connect to the request body stream", Some(&error));
706 return Err(request_body_stream_closed_error(
707 "connect to the request body stream",
708 ));
709 }
710
711 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
714 log_request_body_stream_closed(
715 "request the first request body chunk",
716 Some(&error),
717 );
718 return Err(request_body_stream_closed_error(
719 "request the first request body chunk",
720 ));
721 }
722 } else {
723 log_request_body_stream_closed("connect to the request body stream", None);
724 return Err(request_body_stream_closed_error(
725 "connect to the request body stream",
726 ));
727 }
728 }
729
730 ROUTER.add_typed_route(
731 body_port,
732 Box::new(move |message| {
733 info!("Received message");
734 let bytes = match message.unwrap() {
735 BodyChunkResponse::Chunk(bytes) => bytes,
736 BodyChunkResponse::Done => {
737 if fetch_terminated.send(false).is_err() {
739 log_fetch_terminated_send_failure(
740 false,
741 "handling request body completion",
742 );
743 }
744 sink.close();
745
746 return;
747 },
748 BodyChunkResponse::Error => {
749 if fetch_terminated.send(true).is_err() {
753 log_fetch_terminated_send_failure(
754 true,
755 "handling request body stream error",
756 );
757 }
758 sink.close();
759
760 return;
761 },
762 };
763
764 devtools_bytes.lock().extend_from_slice(&bytes);
765
766 sink.transmit_bytes(bytes);
769
770 let mut chunk_requester = chunk_requester.lock();
773 if let Some(chunk_requester) = chunk_requester.as_mut() {
774 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
775 log_request_body_stream_closed(
776 "request the next request body chunk",
777 Some(&error),
778 );
779 if fetch_terminated.send(true).is_err() {
780 log_fetch_terminated_send_failure(
781 true,
782 "handling failure to request the next request body chunk",
783 );
784 }
785 sink.close();
786 }
787 } else {
788 log_request_body_stream_closed("request the next request body chunk", None);
789 if fetch_terminated.send(true).is_err() {
790 log_fetch_terminated_send_failure(
791 true,
792 "handling a closed request body stream while requesting the next chunk",
793 );
794 }
795 sink.close();
796 }
797 }),
798 );
799
800 Ok(())
801}
802
803#[async_recursion]
805#[allow(clippy::too_many_arguments)]
806pub(crate) async fn http_fetch(
807 fetch_params: &mut FetchParams,
808 cache: &mut CorsCache,
809 cors_flag: bool,
810 cors_preflight_flag: bool,
811 authentication_fetch_flag: bool,
812 target: Target<'async_recursion>,
813 done_chan: &mut DoneChannel,
814 context: &FetchContext,
815) -> Response {
816 *done_chan = None;
818 let request = &mut fetch_params.request;
820
821 let mut response: Option<Response> = None;
823
824 if request.service_workers_mode == ServiceWorkersMode::All {
826 if let Some(ref res) = response {
831 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
839 (res.response_type == ResponseType::OpaqueRedirect &&
840 request.redirect_mode != RedirectMode::Manual) ||
841 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
842 res.is_network_error()
843 {
844 return Response::network_error(NetworkError::ConnectionFailure);
845 }
846
847 }
850 }
851
852 if response.is_none() {
854 if cors_preflight_flag {
856 let method_cache_match = cache.match_method(request, request.method.clone());
857
858 let method_mismatch = !method_cache_match &&
859 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
860 let header_mismatch = request.headers.iter().any(|(name, value)| {
861 !cache.match_header(request, name) &&
862 !is_cors_safelisted_request_header(&name, &value)
863 });
864
865 if method_mismatch || header_mismatch {
866 let preflight_response = cors_preflight_fetch(request, cache, context).await;
869 if let Some(error) = preflight_response.get_network_error() {
871 return Response::network_error(error.clone());
872 }
873 }
874 }
875
876 if request.redirect_mode == RedirectMode::Follow {
879 request.service_workers_mode = ServiceWorkersMode::None;
880 }
881
882 context
886 .timing
887 .set_attribute(ResourceAttribute::RequestStart);
888
889 let mut fetch_result = http_network_or_cache_fetch(
892 fetch_params,
893 authentication_fetch_flag,
894 cors_flag,
895 done_chan,
896 context,
897 )
898 .await;
899
900 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
903 return Response::network_error(NetworkError::CorsGeneral);
904 }
905
906 fetch_result.return_internal = false;
909 response = Some(fetch_result);
910 }
911
912 let request = &mut fetch_params.request;
913
914 let mut response = response.unwrap();
916
917 if (request.response_tainting == ResponseTainting::Opaque ||
921 response.response_type == ResponseType::Opaque) &&
922 cross_origin_resource_policy_check(request, &response) ==
923 CrossOriginResourcePolicy::Blocked
924 {
925 return Response::network_error(NetworkError::CrossOriginResponse);
926 }
927
928 if response
930 .actual_response()
931 .status
932 .try_code()
933 .is_some_and(is_redirect_status)
934 {
935 if response.actual_response().status != StatusCode::SEE_OTHER {
939 }
941
942 response = match request.redirect_mode {
944 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
946 RedirectMode::Manual => {
947 if request.mode == RequestMode::Navigate {
950 let location_url =
954 location_url_for_response(&response, request.current_url().fragment());
955 response.actual_response_mut().location_url = location_url;
956 response
957 } else {
958 response.to_filtered(ResponseType::OpaqueRedirect)
960 }
961 },
962 RedirectMode::Follow => {
963 response.return_internal = true;
965 http_redirect_fetch(
966 fetch_params,
967 cache,
968 response,
969 cors_flag,
970 target,
971 done_chan,
972 context,
973 )
974 .await
975 },
976 };
977 }
978
979 response.return_internal = true;
981 context
982 .timing
983 .set_attribute(ResourceAttribute::RedirectCount(
984 fetch_params.request.redirect_count as u16,
985 ));
986
987 response.resource_timing = context.timing.clone();
988
989 response
991}
992
993struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
995
996impl RedirectEndTimer {
997 fn neuter(&mut self) {
998 self.0 = None;
999 }
1000}
1001
1002impl Drop for RedirectEndTimer {
1003 fn drop(&mut self) {
1004 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1005
1006 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1007 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1008 })
1009 }
1010}
1011
1012static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1014 CONTENT_ENCODING,
1015 CONTENT_LANGUAGE,
1016 CONTENT_LOCATION,
1017 CONTENT_TYPE,
1018];
1019
1020fn location_url_for_response(
1022 response: &Response,
1023 request_fragment: Option<&str>,
1024) -> Option<Result<ServoUrl, String>> {
1025 assert!(
1027 response
1028 .actual_response()
1029 .status
1030 .try_code()
1031 .is_some_and(is_redirect_status)
1032 );
1033 let mut location = response
1035 .actual_response()
1036 .headers
1037 .get(header::LOCATION)
1038 .and_then(|header_value| {
1039 HeaderValue::to_str(header_value)
1040 .map(|location_string| {
1041 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1043 .map_err(|error| error.to_string())
1044 })
1045 .ok()
1046 });
1047
1048 if let Some(Ok(ref mut location)) = location {
1050 if location.fragment().is_none() {
1051 location.set_fragment(request_fragment);
1052 }
1053 }
1054 location
1056}
1057
1058#[async_recursion]
1060pub async fn http_redirect_fetch(
1061 fetch_params: &mut FetchParams,
1062 cache: &mut CorsCache,
1063 mut response: Response,
1064 cors_flag: bool,
1065 target: Target<'async_recursion>,
1066 done_chan: &mut DoneChannel,
1067 context: &FetchContext,
1068) -> Response {
1069 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1070
1071 let request = &mut fetch_params.request;
1073
1074 assert!(response.return_internal);
1076
1077 let location_url = location_url_for_response(&response, request.current_url().fragment());
1079 response.actual_response_mut().location_url = location_url.clone();
1080
1081 let location_url = match location_url {
1082 None => return response,
1084 Some(Err(err)) => {
1086 return Response::network_error(NetworkError::ResourceLoadError(
1087 "Location URL parse failure: ".to_owned() + &err,
1088 ));
1089 },
1090 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1092 return Response::network_error(NetworkError::UnsupportedScheme);
1093 },
1094 Some(Ok(url)) => url,
1095 };
1096
1097 context.timing.set_attributes(&[
1102 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1103 ResourceAttribute::FetchStart,
1104 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1105 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1106 ]);
1107
1108 if request.redirect_count >= 20 {
1110 return Response::network_error(NetworkError::TooManyRedirects);
1111 }
1112
1113 request.redirect_count += 1;
1115
1116 let same_origin = match request.origin {
1119 Origin::Origin(ref origin) => *origin == location_url.origin(),
1120 Origin::Client => panic!(
1121 "Request origin should not be client for {}",
1122 request.current_url()
1123 ),
1124 };
1125
1126 let has_credentials = has_credentials(&location_url);
1127
1128 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1129 return Response::network_error(NetworkError::CorsCredentials);
1130 }
1131
1132 if cors_flag && location_url.origin() != request.current_url().origin() {
1133 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1134 }
1135
1136 if cors_flag && has_credentials {
1138 return Response::network_error(NetworkError::CorsCredentials);
1139 }
1140
1141 if response.actual_response().status != StatusCode::SEE_OTHER &&
1144 request.body.as_ref().is_some_and(|b| b.source_is_null())
1145 {
1146 return Response::network_error(NetworkError::ConnectionFailure);
1147 }
1148
1149 if response
1151 .actual_response()
1152 .status
1153 .try_code()
1154 .is_some_and(|code| {
1155 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1157 request.method == Method::POST) ||
1158 (code == StatusCode::SEE_OTHER &&
1160 request.method != Method::HEAD &&
1161 request.method != Method::GET)
1162 })
1163 {
1164 request.method = Method::GET;
1166 request.body = None;
1167 for name in REQUEST_BODY_HEADER_NAMES {
1169 request.headers.remove(name);
1170 }
1171 }
1172
1173 if location_url.origin() != request.current_url().origin() {
1177 request.headers.remove(AUTHORIZATION);
1180 }
1181
1182 if let Some(body) = request.body.as_mut() {
1185 body.extract_source();
1186 }
1187
1188 request
1192 .url_list
1193 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1194 location_url,
1195 ));
1196
1197 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1199
1200 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1203
1204 let fetch_response = main_fetch(
1206 fetch_params,
1207 cache,
1208 recursive_flag,
1209 target,
1210 done_chan,
1211 context,
1212 )
1213 .await;
1214
1215 context.timing.set_attribute(ResourceAttribute::RedirectEnd(
1217 RedirectEndValue::ResponseEnd,
1218 ));
1219 redirect_end_timer.neuter();
1220
1221 fetch_response
1222}
1223
1224#[async_recursion]
1226#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1227async fn http_network_or_cache_fetch(
1228 fetch_params: &mut FetchParams,
1229 authentication_fetch_flag: bool,
1230 cors_flag: bool,
1231 done_chan: &mut DoneChannel,
1232 context: &FetchContext,
1233) -> Response {
1234 let http_fetch_params: &mut FetchParams;
1236 let mut fetch_params_copy: FetchParams;
1237
1238 let mut response: Option<Response> = None;
1242
1243 let mut revalidating_flag = false;
1245
1246 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1250 TraversableForUserPrompts::NoTraversable &&
1251 fetch_params.request.redirect_mode == RedirectMode::Error
1252 {
1253 http_fetch_params = fetch_params;
1254 &mut http_fetch_params.request
1255 }
1256 else {
1258 fetch_params_copy =
1261 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1262 http_fetch_params = &mut fetch_params_copy;
1263
1264 &mut http_fetch_params.request
1265 };
1266
1267 let include_credentials = match http_request.credentials_mode {
1269 CredentialsMode::Include => true,
1271 CredentialsMode::CredentialsSameOrigin
1273 if http_request.response_tainting == ResponseTainting::Basic =>
1274 {
1275 true
1276 },
1277 _ => false,
1278 };
1279
1280 let content_length = http_request
1287 .body
1288 .as_ref()
1289 .and_then(|body| body.len().map(|size| size as u64));
1290
1291 let mut content_length_header_value = None;
1293
1294 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1297 content_length_header_value = Some(0);
1298 }
1299
1300 if let Some(content_length) = content_length {
1304 content_length_header_value = Some(content_length);
1305 };
1306
1307 if let Some(content_length_header_value) = content_length_header_value {
1310 http_request
1311 .headers
1312 .typed_insert(ContentLength(content_length_header_value));
1313 }
1314
1315 if http_request.keep_alive {
1317 if let Some(content_length) = content_length {
1318 let in_flight_keep_alive_bytes: u64 = context
1323 .in_flight_keep_alive_records
1324 .lock()
1325 .get(
1326 &http_request
1327 .pipeline_id
1328 .expect("Must always set a pipeline ID for keep-alive requests"),
1329 )
1330 .map(|records| {
1331 records
1335 .iter()
1336 .map(|record| {
1337 if record.request_id == http_request.id {
1338 0
1341 } else {
1342 record.keep_alive_body_length
1343 }
1344 })
1345 .sum()
1346 })
1347 .unwrap_or_default();
1348 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1350 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1351 }
1352 }
1353 }
1354
1355 match http_request.referrer {
1357 Referrer::ReferrerUrl(ref http_request_referrer) |
1358 Referrer::Client(ref http_request_referrer) => {
1359 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1362 http_request.headers.typed_insert(referer);
1364 } else {
1365 error!("Failed to parse {} as referrer", http_request_referrer);
1369 }
1370 },
1371 _ => {},
1372 };
1373
1374 append_a_request_origin_header(http_request);
1376
1377 append_the_fetch_metadata_headers(http_request);
1379
1380 if http_request.initiator == Initiator::Prefetch {
1383 if let Ok(value) = HeaderValue::from_str("prefetch") {
1384 http_request.headers.insert("Sec-Purpose", value);
1385 }
1386 }
1387
1388 if !http_request.headers.contains_key(header::USER_AGENT) {
1391 http_request
1392 .headers
1393 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1394 }
1395
1396 append_cache_data_to_headers(http_request);
1398
1399 if http_request.headers.contains_key(header::RANGE) {
1402 if let Ok(value) = HeaderValue::from_str("identity") {
1403 http_request.headers.insert("Accept-Encoding", value);
1404 }
1405 }
1406
1407 http_request.headers.remove(header::HOST);
1411 set_default_accept_encoding(&mut http_request.headers);
1413
1414 let current_url = http_request.current_url();
1415
1416 if include_credentials {
1419 set_request_cookies(
1423 ¤t_url,
1424 &mut http_request.headers,
1425 &context.state.cookie_jar,
1426 );
1427 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1429 let mut authorization_value = None;
1431
1432 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1434 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1435 authorization_value = Some(basic);
1436 }
1437 }
1438
1439 if authentication_fetch_flag &&
1441 authorization_value.is_none() &&
1442 has_credentials(¤t_url)
1443 {
1444 authorization_value = Some(Authorization::basic(
1445 current_url.username(),
1446 current_url.password().unwrap_or(""),
1447 ));
1448 }
1449
1450 if let Some(basic) = authorization_value {
1452 http_request.headers.typed_insert(basic);
1453 }
1454 }
1455 }
1456
1457 let should_wait = {
1459 let mut cache_guard = block_for_cache_ready(
1461 context,
1462 http_request,
1463 done_chan,
1464 &mut revalidating_flag,
1465 &mut response,
1466 )
1467 .await;
1468
1469 if response.is_none() {
1473 if http_request.cache_mode == CacheMode::OnlyIfCached {
1475 return Response::network_error(NetworkError::CacheError);
1477 }
1478
1479 drop(cache_guard);
1482 let forward_response =
1483 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1484 .await;
1485
1486 let http_request = &mut http_fetch_params.request;
1487 let request_key = CacheKey::new(http_request);
1488 cache_guard = context
1489 .state
1490 .http_cache
1491 .get_or_guard(request_key.clone())
1492 .await;
1493 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1497 if let Some(guard) = cache_guard.try_as_mut() {
1498 invalidate(http_request, &forward_response, guard).await;
1499 }
1500 context
1501 .state
1502 .http_cache
1503 .invalidate_related_urls(http_request, &forward_response, &request_key)
1504 .await;
1505 }
1506
1507 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1509 *done_chan = None;
1512 if let Some(guard) = cache_guard.try_as_mut() {
1513 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1514 }
1515
1516 if let Some(response) = &mut response {
1517 response.cache_state = CacheState::Validated;
1518 }
1519 }
1520
1521 if response.is_none() {
1523 let forward_response = response.insert(forward_response);
1525
1526 if http_request.cache_mode != CacheMode::NoStore {
1529 cache_guard.insert(http_request, forward_response);
1532 }
1533 }
1534 false
1535 } else {
1536 true
1537 }
1538 }; if should_wait {
1541 wait_for_inflight_requests(done_chan, &mut response).await;
1546 }
1547
1548 let http_request = &mut http_fetch_params.request;
1549 let mut response = response.unwrap();
1550
1551 response.url_list = http_request
1553 .url_list
1554 .iter()
1555 .map(|claimed_url| claimed_url.url())
1556 .collect();
1557
1558 if http_request.headers.contains_key(RANGE) {
1560 response.range_requested = true;
1561 }
1562
1563 response.request_includes_credentials = include_credentials;
1565
1566 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1572 !cors_flag &&
1573 include_credentials &&
1574 response.headers.contains_key(WWW_AUTHENTICATE)
1575 {
1576 let request = &mut fetch_params.request;
1579
1580 if request.body.is_some() {
1582 }
1584
1585 if !request.use_url_credentials || authentication_fetch_flag {
1587 let Some(credentials) = context
1588 .state
1589 .request_authentication(request, &response)
1590 .await
1591 else {
1592 return response;
1593 };
1594
1595 if let Err(err) = request
1596 .current_url_mut()
1597 .set_username(&credentials.username)
1598 {
1599 error!("error setting username for url: {:?}", err);
1600 return response;
1601 };
1602
1603 if let Err(err) = request
1604 .current_url_mut()
1605 .set_password(Some(&credentials.password))
1606 {
1607 error!("error setting password for url: {:?}", err);
1608 return response;
1609 };
1610 }
1611
1612 *done_chan = None;
1615
1616 response = http_network_or_cache_fetch(
1618 fetch_params,
1619 true, cors_flag,
1621 done_chan,
1622 context,
1623 )
1624 .await;
1625 }
1626
1627 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1629 let request = &mut fetch_params.request;
1630 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1633 return Response::network_error(NetworkError::ResourceLoadError(
1634 "Can't find Window object".into(),
1635 ));
1636 }
1637
1638 let Some(credentials) = context
1646 .state
1647 .request_authentication(request, &response)
1648 .await
1649 else {
1650 return response;
1651 };
1652
1653 let entry = AuthCacheEntry {
1655 user_name: credentials.username,
1656 password: credentials.password,
1657 };
1658 {
1659 let mut auth_cache = context.state.auth_cache.write();
1660 let key = request.current_url().origin().ascii_serialization();
1661 auth_cache.entries.insert(key, entry);
1662 }
1663
1664 *done_chan = None;
1667
1668 response = http_network_or_cache_fetch(
1670 fetch_params,
1671 false, cors_flag,
1673 done_chan,
1674 context,
1675 )
1676 .await;
1677 }
1678
1679 if authentication_fetch_flag {
1687 }
1689
1690 response
1692}
1693
1694#[servo_tracing::instrument(skip_all)]
1702async fn block_for_cache_ready<'a>(
1703 context: &'a FetchContext,
1704 http_request: &mut Request,
1705 done_chan: &mut DoneChannel,
1706 revalidating_flag: &mut bool,
1707 response: &mut Option<Response>,
1708) -> CachedResourcesOrGuard<'a> {
1709 let entry_key = CacheKey::new(http_request);
1710 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1711
1712 match guard_result {
1713 CachedResourcesOrGuard::Guard(_) => {
1714 *done_chan = None;
1715 },
1716 CachedResourcesOrGuard::Value(ref cached_resources) => {
1717 let stored_response = construct_response(http_request, done_chan, cached_resources);
1723 if let Some(response_from_cache) = stored_response {
1725 let response_headers = response_from_cache.response.headers.clone();
1726 let (cached_response, needs_revalidation) =
1728 match (http_request.cache_mode, &http_request.mode) {
1729 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1730 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1731 (Some(response_from_cache.response), false)
1732 },
1733 (CacheMode::OnlyIfCached, _) |
1734 (CacheMode::NoStore, _) |
1735 (CacheMode::Reload, _) => (None, false),
1736 (_, _) => (
1737 Some(response_from_cache.response),
1738 response_from_cache.needs_validation,
1739 ),
1740 };
1741
1742 if needs_revalidation {
1743 *revalidating_flag = true;
1744 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1746 let http_date: SystemTime = http_date.into();
1747 http_request
1748 .headers
1749 .typed_insert(IfModifiedSince::from(http_date));
1750 }
1751 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1752 http_request
1753 .headers
1754 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1755 }
1756 } else {
1757 *response = cached_response;
1759 if let Some(response) = response {
1760 response.cache_state = CacheState::Local;
1761 }
1762 }
1763 if response.is_none() {
1764 *done_chan = None;
1767 }
1768 }
1769 },
1770 }
1771 guard_result
1772}
1773
1774async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1777 if let Some(ref mut ch) = *done_chan {
1778 assert!(response.is_some());
1782
1783 loop {
1784 match ch.1.recv().await {
1785 Some(Data::Payload(_)) => {},
1786 Some(Data::Done) => break, Some(Data::Cancelled) => {
1788 break;
1790 },
1791 _ => panic!("HTTP cache should always send Done or Cancelled"),
1792 }
1793 }
1794 }
1795 *done_chan = None;
1797}
1798
1799#[derive(PartialEq)]
1803enum CrossOriginResourcePolicy {
1804 Allowed,
1805 Blocked,
1806}
1807
1808fn cross_origin_resource_policy_check(
1811 request: &Request,
1812 response: &Response,
1813) -> CrossOriginResourcePolicy {
1814 if request.mode != RequestMode::NoCors {
1816 return CrossOriginResourcePolicy::Allowed;
1817 }
1818
1819 let current_url_origin = request.current_url().origin();
1821 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1822 *origin == request.current_url().origin()
1823 } else {
1824 false
1825 };
1826
1827 if same_origin {
1828 return CrossOriginResourcePolicy::Allowed;
1829 }
1830
1831 let policy = response
1833 .headers
1834 .get(HeaderName::from_static("cross-origin-resource-policy"))
1835 .map(|h| h.to_str().unwrap_or(""))
1836 .unwrap_or("");
1837
1838 if policy == "same-origin" {
1840 return CrossOriginResourcePolicy::Blocked;
1841 }
1842
1843 if let Origin::Origin(ref request_origin) = request.origin {
1845 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1846 if schemeless_same_origin &&
1847 (request_origin.scheme() == Some("https") ||
1848 response.https_state == HttpsState::None)
1849 {
1850 return CrossOriginResourcePolicy::Allowed;
1851 }
1852 };
1853
1854 if policy == "same-site" {
1856 return CrossOriginResourcePolicy::Blocked;
1857 }
1858
1859 CrossOriginResourcePolicy::Allowed
1860}
1861
1862struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
1864
1865impl ResponseEndTimer {
1866 fn neuter(&mut self) {
1867 self.0 = None;
1868 }
1869}
1870
1871impl Drop for ResponseEndTimer {
1872 fn drop(&mut self) {
1873 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1874
1875 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1876 t.set_attribute(ResourceAttribute::ResponseEnd);
1877 })
1878 }
1879}
1880
1881#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1883async fn http_network_fetch(
1884 fetch_params: &mut FetchParams,
1885 credentials_flag: bool,
1886 done_chan: &mut DoneChannel,
1887 context: &FetchContext,
1888) -> Response {
1889 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1890
1891 let request = &mut fetch_params.request;
1893
1894 let url = request.current_url();
1905 let request_id = request.id.0.to_string();
1906 if log_enabled!(log::Level::Info) {
1907 info!("{:?} request for {}", request.method, url);
1908 for header in request.headers.iter() {
1909 debug!(" - {:?}", header);
1910 }
1911 }
1912
1913 let is_xhr = request.destination == Destination::None;
1917
1918 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
1920
1921 let body = request.body.as_ref().map(|body| body.clone_stream());
1922
1923 if body.is_none() {
1924 let _ = fetch_terminated_sender.send(false);
1929 }
1930
1931 let browsing_context_id = request.target_webview_id.map(Into::into);
1932
1933 let (res, msg) = match &request.mode {
1934 RequestMode::WebSocket {
1935 protocols,
1936 original_url: _,
1937 } => {
1938 let (resource_event_sender, dom_action_receiver) = {
1941 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
1942 (
1943 websocket_chan.sender.clone(),
1944 websocket_chan.receiver.take().unwrap(),
1945 )
1946 };
1947
1948 let mut tls_config = create_tls_config(
1949 context.ca_certificates.clone(),
1950 context.ignore_certificate_errors,
1951 context.state.override_manager.clone(),
1952 );
1953 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
1954
1955 let response = match start_websocket(
1956 context.state.clone(),
1957 resource_event_sender,
1958 protocols,
1959 request,
1960 tls_config,
1961 dom_action_receiver,
1962 )
1963 .await
1964 {
1965 Ok(response) => response,
1966 Err(error) => {
1967 return Response::network_error(NetworkError::WebsocketConnectionFailure(
1968 format!("{error:?}"),
1969 ));
1970 },
1971 };
1972
1973 let response = response.map(|r| match r {
1974 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
1975 None => http_body_util::Empty::new()
1976 .map_err(|_| unreachable!())
1977 .boxed(),
1978 });
1979 (Decoder::detect(response, url.is_secure_scheme()), None)
1980 },
1981 _ => {
1982 let response_future = obtain_response(
1983 &context.state.client,
1984 &url,
1985 &request.method,
1986 &mut request.headers,
1987 body,
1988 request
1989 .body
1990 .as_ref()
1991 .is_some_and(|body| body.source_is_null()),
1992 &request.pipeline_id,
1993 Some(&request_id),
1994 request.destination,
1995 is_xhr,
1996 context,
1997 fetch_terminated_sender,
1998 browsing_context_id,
1999 );
2000
2001 let (res, msg) = match response_future.await {
2003 Ok(wrapped_response) => wrapped_response,
2004 Err(error) => return Response::network_error(error),
2005 };
2006 (res, msg)
2007 },
2008 };
2009
2010 if log_enabled!(log::Level::Info) {
2011 debug!("{:?} response for {}", res.version(), url);
2012 for header in res.headers().iter() {
2013 debug!(" - {:?}", header);
2014 }
2015 }
2016
2017 match fetch_terminated_receiver.recv().await {
2020 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2021 Some(false) => {},
2022 _ => warn!("Failed to receive confirmation request was streamed without error."),
2023 }
2024
2025 let header_strings: Vec<&str> = res
2026 .headers()
2027 .get_all("Timing-Allow-Origin")
2028 .iter()
2029 .map(|header_value| header_value.to_str().unwrap_or(""))
2030 .collect();
2031 let wildcard_present = header_strings.contains(&"*");
2032 let req_origin_in_timing_allow = header_strings
2036 .iter()
2037 .any(|header_str| match request.origin {
2038 SpecificOrigin(ref immutable_request_origin) => {
2039 *header_str == immutable_request_origin.ascii_serialization()
2040 },
2041 _ => false,
2042 });
2043
2044 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2045 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2046 _ => false,
2047 });
2048
2049 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2050 context.timing.inner().mark_timing_check_failed();
2051 }
2052
2053 let timing = context.timing.inner().clone();
2054 let mut response = Response::new(url.clone(), timing);
2055
2056 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2057 let mut hsts_enabled = url
2058 .host_str()
2059 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2060
2061 if url.scheme() == "https" {
2062 if let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>() {
2063 hsts_enabled = sts.max_age().as_secs() > 0;
2065 }
2066 }
2067 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2068 }
2069
2070 let status_text = res
2071 .extensions()
2072 .get::<ReasonPhrase>()
2073 .map(ReasonPhrase::as_bytes)
2074 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2075 .map(Vec::from)
2076 .unwrap_or_default();
2077 response.status = HttpStatus::new(res.status(), status_text);
2078
2079 info!("got {:?} response for {:?}", res.status(), request.url());
2080 response.headers = res.headers().clone();
2081 response.referrer = request.referrer.to_url().cloned();
2082 response.referrer_policy = request.referrer_policy;
2083
2084 let res_body = response.body.clone();
2085
2086 let (done_sender, done_receiver) = unbounded_channel();
2088 *done_chan = Some((done_sender.clone(), done_receiver));
2089
2090 let devtools_sender = context.devtools_chan.clone();
2091 let cancellation_listener = context.cancellation_listener.clone();
2092 if cancellation_listener.cancelled() {
2093 return Response::network_error(NetworkError::LoadCancelled);
2094 }
2095
2096 *res_body.lock() = ResponseBody::Receiving(vec![]);
2097 let res_body2 = res_body.clone();
2098
2099 if let Some(ref sender) = devtools_sender {
2100 if let Some(m) = msg {
2101 send_request_to_devtools(m, sender);
2102 }
2103 }
2104
2105 let done_sender2 = done_sender.clone();
2106 let done_sender3 = done_sender.clone();
2107 let timing_ptr2 = context.timing.clone();
2108 let timing_ptr3 = context.timing.clone();
2109 let devtools_request = request.clone();
2110 let url1 = devtools_request.url();
2111 let url2 = url1.clone();
2112
2113 let status = response.status.clone();
2114 let headers = response.headers.clone();
2115 let devtools_chan = context.devtools_chan.clone();
2116
2117 spawn_task(
2118 res.into_body()
2119 .try_fold(res_body, move |res_body, chunk| {
2120 if cancellation_listener.cancelled() {
2121 *res_body.lock() = ResponseBody::Done(vec![]);
2122 let _ = done_sender.send(Data::Cancelled);
2123 return future::ready(Err(std::io::Error::new(
2124 std::io::ErrorKind::Interrupted,
2125 "Fetch aborted",
2126 )));
2127 }
2128 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2129 let bytes = chunk;
2130 body.extend_from_slice(&bytes);
2131 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2132 }
2133 future::ready(Ok(res_body))
2134 })
2135 .and_then(move |res_body| {
2136 debug!("successfully finished response for {:?}", url1);
2137 let mut body = res_body.lock();
2138 let completed_body = match *body {
2139 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2140 _ => vec![],
2141 };
2142 let devtools_response_body = completed_body.clone();
2143 *body = ResponseBody::Done(completed_body);
2144 send_response_values_to_devtools(
2145 Some(headers),
2146 status,
2147 Some(devtools_response_body),
2148 CacheState::None,
2149 &devtools_request,
2150 devtools_chan,
2151 );
2152 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2153 let _ = done_sender2.send(Data::Done);
2154 future::ready(Ok(()))
2155 })
2156 .map_err(move |error| {
2157 if let std::io::ErrorKind::InvalidData = error.kind() {
2158 debug!("Content decompression error for {:?}", url2);
2159 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2160 let mut body = res_body2.lock();
2161
2162 *body = ResponseBody::Done(vec![]);
2163 }
2164 debug!("finished response for {:?}", url2);
2165 let mut body = res_body2.lock();
2166 let completed_body = match *body {
2167 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2168 _ => vec![],
2169 };
2170 *body = ResponseBody::Done(completed_body);
2171 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2172 let _ = done_sender3.send(Data::Done);
2173 }),
2174 );
2175
2176 response.https_state = match url.scheme() {
2182 "https" => HttpsState::Modern,
2183 _ => HttpsState::None,
2184 };
2185
2186 if credentials_flag {
2199 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2200 }
2201 context
2202 .state
2203 .hsts_list
2204 .write()
2205 .update_hsts_list_from_response(&url, &response.headers);
2206
2207 response_end_timer.neuter();
2221 response
2222}
2223
2224async fn cors_preflight_fetch(
2226 request: &Request,
2227 cache: &mut CorsCache,
2228 context: &FetchContext,
2229) -> Response {
2230 let mut preflight = RequestBuilder::new(
2235 request.target_webview_id,
2236 request.current_url_with_blob_claim(),
2237 request.referrer.clone(),
2238 )
2239 .method(Method::OPTIONS)
2240 .origin(match &request.origin {
2241 Origin::Client => {
2242 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2243 },
2244 Origin::Origin(origin) => origin.clone(),
2245 })
2246 .pipeline_id(request.pipeline_id)
2247 .initiator(request.initiator)
2248 .destination(request.destination)
2249 .referrer_policy(request.referrer_policy)
2250 .mode(RequestMode::CorsMode)
2251 .response_tainting(ResponseTainting::CorsTainting)
2252 .policy_container(match &request.policy_container {
2253 RequestPolicyContainer::Client => {
2254 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2255 },
2256 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2257 })
2258 .url_list(
2259 request
2260 .url_list
2261 .iter()
2262 .map(|claimed_url| claimed_url.url())
2263 .collect(),
2264 )
2265 .build();
2266
2267 preflight
2269 .headers
2270 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2271
2272 preflight
2274 .headers
2275 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2276 request.method.clone(),
2277 ));
2278
2279 let headers = get_cors_unsafe_header_names(&request.headers);
2281
2282 if !headers.is_empty() {
2284 preflight.headers.insert(
2287 ACCESS_CONTROL_REQUEST_HEADERS,
2288 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2289 .unwrap_or(HeaderValue::from_static("")),
2290 );
2291 }
2292
2293 let mut fetch_params = FetchParams::new(preflight);
2296 let response =
2297 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2298
2299 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2301 let mut methods = if response
2304 .headers
2305 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2306 {
2307 match response.headers.typed_get::<AccessControlAllowMethods>() {
2308 Some(methods) => methods.iter().collect(),
2309 None => {
2311 return Response::network_error(NetworkError::CorsAllowMethods);
2312 },
2313 }
2314 } else {
2315 vec![]
2316 };
2317
2318 let header_names = if response
2321 .headers
2322 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2323 {
2324 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2325 Some(names) => names.iter().collect(),
2326 None => {
2328 return Response::network_error(NetworkError::CorsAllowHeaders);
2329 },
2330 }
2331 } else {
2332 vec![]
2333 };
2334
2335 debug!(
2336 "CORS check: Allowed methods: {:?}, current method: {:?}",
2337 methods, request.method
2338 );
2339
2340 if methods.is_empty() && request.use_cors_preflight {
2343 methods = vec![request.method.clone()];
2344 }
2345
2346 if methods
2349 .iter()
2350 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2351 !is_cors_safelisted_method(&request.method) &&
2352 (request.credentials_mode == CredentialsMode::Include ||
2353 methods.iter().all(|method| method.as_ref() != "*"))
2354 {
2355 return Response::network_error(NetworkError::CorsMethod);
2356 }
2357
2358 debug!(
2359 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2360 header_names, request.headers
2361 );
2362
2363 if request.headers.iter().any(|(name, _)| {
2366 is_cors_non_wildcard_request_header_name(name) &&
2367 header_names.iter().all(|header_name| header_name != name)
2368 }) {
2369 return Response::network_error(NetworkError::CorsAuthorization);
2370 }
2371
2372 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2376 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2377 let header_names_contains_star = header_names
2378 .iter()
2379 .any(|header_name| header_name.as_str() == "*");
2380 for unsafe_name in unsafe_names.iter() {
2381 if !header_names_set.contains(unsafe_name) &&
2382 (request.credentials_mode == CredentialsMode::Include ||
2383 !header_names_contains_star)
2384 {
2385 return Response::network_error(NetworkError::CorsHeaders);
2386 }
2387 }
2388
2389 let max_age: Option<Duration> = response
2392 .headers
2393 .typed_get::<AccessControlMaxAge>()
2394 .map(|acma| acma.into());
2395
2396 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2398
2399 for method in &methods {
2410 cache.match_method_and_update(request, method.clone(), max_age);
2411 }
2412
2413 for header_name in &header_names {
2418 cache.match_header_and_update(request, header_name, max_age);
2419 }
2420
2421 return response;
2423 }
2424
2425 Response::network_error(NetworkError::CorsGeneral)
2427}
2428
2429fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2431 let Some(origins) =
2433 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2434 else {
2435 return Err(());
2437 };
2438 let origin = origins.into_iter().map(char::from).collect::<String>();
2439
2440 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2442 return Ok(());
2443 }
2444
2445 if serialize_request_origin(request).to_string() != origin {
2447 return Err(());
2448 }
2449
2450 if request.credentials_mode != CredentialsMode::Include {
2452 return Ok(());
2453 }
2454
2455 let credentials = response
2457 .headers
2458 .typed_get::<AccessControlAllowCredentials>();
2459
2460 if credentials.is_some() {
2462 return Ok(());
2463 }
2464
2465 Err(())
2467}
2468
2469fn has_credentials(url: &ServoUrl) -> bool {
2470 !url.username().is_empty() || url.password().is_some()
2471}
2472
2473fn is_no_store_cache(headers: &HeaderMap) -> bool {
2474 headers.contains_key(header::IF_MODIFIED_SINCE) |
2475 headers.contains_key(header::IF_NONE_MATCH) |
2476 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2477 headers.contains_key(header::IF_MATCH) |
2478 headers.contains_key(header::IF_RANGE)
2479}
2480
2481fn is_redirect_status(status: StatusCode) -> bool {
2483 matches!(
2484 status,
2485 StatusCode::MOVED_PERMANENTLY |
2486 StatusCode::FOUND |
2487 StatusCode::SEE_OTHER |
2488 StatusCode::TEMPORARY_REDIRECT |
2489 StatusCode::PERMANENT_REDIRECT
2490 )
2491}
2492
2493fn serialize_request_origin(request: &Request) -> headers::Origin {
2495 let Origin::Origin(origin) = &request.origin else {
2497 panic!("origin cannot be \"client\" at this point in time");
2498 };
2499
2500 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2502 return headers::Origin::NULL;
2503 }
2504
2505 serialize_origin(origin)
2507}
2508
2509pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2511 match origin {
2512 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2513 ImmutableOrigin::Tuple(scheme, host, port) => {
2514 let port = match (scheme.as_ref(), port) {
2517 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2518 _ => Some(*port),
2519 };
2520
2521 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2523 .unwrap_or(headers::Origin::NULL)
2524 },
2525 }
2526}
2527
2528fn append_a_request_origin_header(request: &mut Request) {
2530 let Origin::Origin(request_origin) = &request.origin else {
2532 panic!("origin cannot be \"client\" at this point in time");
2533 };
2534
2535 let mut serialized_origin = serialize_request_origin(request);
2537
2538 if request.response_tainting == ResponseTainting::CorsTainting ||
2541 matches!(request.mode, RequestMode::WebSocket { .. })
2542 {
2543 request.headers.typed_insert(serialized_origin);
2544 }
2545 else if !matches!(request.method, Method::GET | Method::HEAD) {
2547 if request.mode != RequestMode::CorsMode {
2549 match request.referrer_policy {
2550 ReferrerPolicy::NoReferrer => {
2551 serialized_origin = headers::Origin::NULL;
2553 },
2554 ReferrerPolicy::NoReferrerWhenDowngrade |
2555 ReferrerPolicy::StrictOrigin |
2556 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2557 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2560 if scheme == "https" && request.current_url().scheme() != "https" {
2561 serialized_origin = headers::Origin::NULL;
2562 }
2563 }
2564 },
2565 ReferrerPolicy::SameOrigin => {
2566 if *request_origin != request.current_url().origin() {
2569 serialized_origin = headers::Origin::NULL;
2570 }
2571 },
2572 _ => {
2573 },
2575 };
2576 }
2577
2578 request.headers.typed_insert(serialized_origin);
2580 }
2581}
2582
2583fn append_the_fetch_metadata_headers(r: &mut Request) {
2585 if !r.url().is_potentially_trustworthy() {
2587 return;
2588 }
2589
2590 set_the_sec_fetch_dest_header(r);
2592
2593 set_the_sec_fetch_mode_header(r);
2595
2596 set_the_sec_fetch_site_header(r);
2598
2599 set_the_sec_fetch_user_header(r);
2601}
2602
2603fn append_cache_data_to_headers(http_request: &mut Request) {
2605 match http_request.cache_mode {
2606 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2610 http_request.cache_mode = CacheMode::NoStore;
2611 },
2612
2613 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2623 http_request
2624 .headers
2625 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2626 },
2627
2628 CacheMode::Reload | CacheMode::NoStore => {
2630 if !http_request.headers.contains_key(header::PRAGMA) {
2633 http_request.headers.typed_insert(Pragma::no_cache());
2634 }
2635
2636 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2639 http_request
2640 .headers
2641 .typed_insert(CacheControl::new().with_no_cache());
2642 }
2643 },
2644
2645 _ => {},
2646 }
2647}
2648
2649fn set_the_sec_fetch_dest_header(r: &mut Request) {
2651 debug_assert!(r.url().is_potentially_trustworthy());
2653
2654 let header = r.destination;
2658
2659 r.headers.typed_insert(SecFetchDest(header));
2661}
2662
2663fn set_the_sec_fetch_mode_header(r: &mut Request) {
2665 debug_assert!(r.url().is_potentially_trustworthy());
2667
2668 let header = &r.mode;
2671
2672 r.headers.typed_insert(SecFetchMode::from(header));
2674}
2675
2676fn set_the_sec_fetch_site_header(r: &mut Request) {
2678 let Origin::Origin(request_origin) = &r.origin else {
2681 panic!("request origin cannot be \"client\" at this point")
2682 };
2683
2684 debug_assert!(r.url().is_potentially_trustworthy());
2686
2687 let mut header = SecFetchSite::SameOrigin;
2690
2691 if header != SecFetchSite::None {
2696 for url in &r.url_list {
2697 if url.origin() == *request_origin {
2699 continue;
2700 }
2701
2702 header = SecFetchSite::CrossSite;
2704
2705 if !is_same_site(request_origin, &url.origin()) {
2707 break;
2708 }
2709
2710 header = SecFetchSite::SameSite;
2712 }
2713 }
2714
2715 r.headers.typed_insert(header);
2717}
2718
2719fn set_the_sec_fetch_user_header(r: &mut Request) {
2721 debug_assert!(r.url().is_potentially_trustworthy());
2723
2724 if !r.is_navigation_request() {
2727 return;
2728 }
2729
2730 let header = SecFetchUser;
2733
2734 r.headers.typed_insert(header);
2736}
2737
2738fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2740 let referrer_policy: ReferrerPolicy = response
2743 .headers
2744 .typed_get::<headers::ReferrerPolicy>()
2745 .into();
2746
2747 if referrer_policy != ReferrerPolicy::EmptyString {
2749 request.referrer_policy = referrer_policy;
2750 }
2751}