1use std::cell::RefCell;
6use std::collections::hash_map::HashMap;
7use std::convert::TryFrom;
8use std::error::Error;
9use std::sync::{Arc, LazyLock};
10use std::time::Duration;
11use std::{fmt, io};
12
13use futures::task::{Context, Poll};
14use futures::{Future, TryFutureExt};
15use http::uri::{Authority, Uri as Destination};
16use http_body_util::combinators::BoxBody;
17use hyper::body::Bytes;
18use hyper::rt::Executor;
19use hyper_rustls::{HttpsConnector as HyperRustlsHttpsConnector, MaybeHttpsStream};
20use hyper_util::client::legacy::Client;
21use hyper_util::client::legacy::connect::proxy::Tunnel;
22use hyper_util::client::legacy::connect::{
23 Connected, Connection, HttpConnector as HyperHttpConnector,
24};
25use hyper_util::rt::{TokioIo, TokioTimer};
26use log::warn;
27use parking_lot::Mutex;
28use rustls::client::danger::ServerCertVerifier;
29use rustls::client::{ClientConnection, EchStatus};
30use rustls::crypto::{CryptoProvider, aws_lc_rs};
31use rustls::{CipherSuite, ClientConfig, NamedGroup, ProtocolVersion};
32use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
33use servo_config::pref;
34use tokio::net::TcpStream;
35use tower::Service;
36
37use crate::async_runtime::spawn_task;
38use crate::hosts::replace_host;
39
40pub const BUF_SIZE: usize = 32768;
41
42pub const ALPN_H2: &str = "h2";
44
45#[derive(Clone)]
46pub struct ServoHttpConnector {
47 inner: HyperHttpConnector,
48}
49
50impl ServoHttpConnector {
51 fn new() -> ServoHttpConnector {
52 let mut inner = HyperHttpConnector::new();
53 inner.enforce_http(false);
54 inner.set_happy_eyeballs_timeout(None);
55 inner.set_connect_timeout(Some(Duration::from_secs(pref!(network_connection_timeout))));
56 ServoHttpConnector { inner }
57 }
58}
59
60impl Service<Destination> for ServoHttpConnector {
61 type Response = TokioIo<TcpStream>;
62 type Error = ConnectionError;
63 type Future =
64 std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
65
66 fn call(&mut self, dest: Destination) -> Self::Future {
67 let mut new_dest = dest.clone();
69 let mut parts = dest.into_parts();
70
71 if let Some(auth) = parts.authority {
72 let host = auth.host();
73 let host = replace_host(host);
74
75 let authority = if let Some(port) = auth.port() {
76 format!("{}:{}", host, port.as_str())
77 } else {
78 (*host).to_string()
79 };
80
81 if let Ok(authority) = Authority::from_maybe_shared(authority) {
82 parts.authority = Some(authority);
83 if let Ok(dest) = Destination::from_parts(parts) {
84 new_dest = dest
85 }
86 }
87 }
88
89 Box::pin(
90 self.inner
91 .call(new_dest)
92 .map_err(|error| ConnectionError::HttpError(BoxError::from(error))),
93 )
94 }
95
96 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
97 Ok(()).into()
98 }
99}
100
101type BoxError = Box<dyn std::error::Error + Send + Sync>;
102
103#[derive(Clone)]
104pub struct InstrumentedConnector<T> {
105 inner: HyperRustlsHttpsConnector<T>,
106}
107
108impl<T> InstrumentedConnector<T> {
109 fn new(inner: HyperRustlsHttpsConnector<T>) -> Self {
110 Self { inner }
111 }
112}
113
114impl<T> From<HyperRustlsHttpsConnector<T>> for InstrumentedConnector<T> {
115 fn from(inner: HyperRustlsHttpsConnector<T>) -> Self {
116 Self::new(inner)
117 }
118}
119
120pub struct InstrumentedStream<T> {
121 inner: MaybeHttpsStream<T>,
122 tls_info: RefCell<Option<TlsHandshakeInfo>>,
123}
124
125impl<T: Unpin> Unpin for InstrumentedStream<T> {}
126
127impl<T> fmt::Debug for InstrumentedStream<T> {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.debug_struct("InstrumentedStream")
130 .field("tls_info", &self.tls_info)
131 .finish()
132 }
133}
134
135#[derive(Clone, Debug)]
136pub struct TlsHandshakeInfo {
137 pub protocol_version: Option<ProtocolVersion>,
138 pub cipher_suite: Option<CipherSuite>,
139 pub kea_group_name: Option<NamedGroup>,
140 pub signature_scheme_name: Option<String>,
141 pub alpn_protocol: Option<String>,
142 pub certificate_chain_der: Vec<Vec<u8>>,
143 pub used_ech: bool,
144}
145
146impl TlsHandshakeInfo {
147 fn from_connection(conn: &ClientConnection) -> Self {
148 let protocol_version = conn.protocol_version();
149 let cipher_suite = conn.negotiated_cipher_suite().map(|suite| suite.suite());
150 let kea_group_name = conn
151 .negotiated_key_exchange_group()
152 .map(|group| group.name());
153 let certificate_chain_der = conn
154 .peer_certificates()
155 .map(|certs| certs.iter().map(|cert| cert.as_ref().to_vec()).collect())
156 .unwrap_or_default();
157 let alpn_protocol = conn
158 .alpn_protocol()
159 .map(|proto| String::from_utf8_lossy(proto).into_owned());
160 let used_ech = matches!(conn.ech_status(), EchStatus::Accepted);
161
162 Self {
163 protocol_version,
164 cipher_suite,
165 kea_group_name,
166 signature_scheme_name: None,
167 alpn_protocol,
168 certificate_chain_der,
169 used_ech,
170 }
171 }
172}
173
174impl<T> InstrumentedStream<T>
175where
176 T: Connection + hyper::rt::Read + hyper::rt::Write + Unpin,
177{
178 fn from_maybe_https_stream(stream: MaybeHttpsStream<T>) -> Self {
179 match stream {
180 MaybeHttpsStream::Http(inner) => Self {
181 inner: MaybeHttpsStream::Http(inner),
182 tls_info: RefCell::new(None),
183 },
184 MaybeHttpsStream::Https(tls_stream) => {
185 let (_tcp, tls) = tls_stream.inner().get_ref();
186 let tls_info = TlsHandshakeInfo::from_connection(tls);
187
188 Self {
189 inner: MaybeHttpsStream::Https(tls_stream),
190 tls_info: RefCell::new(Some(tls_info)),
191 }
192 },
193 }
194 }
195}
196
197impl<T> Connection for InstrumentedStream<T>
198where
199 T: Connection + hyper::rt::Read + hyper::rt::Write + Unpin,
200{
201 fn connected(&self) -> Connected {
202 let connected = match &self.inner {
203 MaybeHttpsStream::Http(stream) => stream.connected(),
204 MaybeHttpsStream::Https(stream) => {
205 let (tcp, tls) = stream.inner().get_ref();
206 if tls.alpn_protocol() == Some(ALPN_H2.as_bytes()) {
207 tcp.inner().connected().negotiated_h2()
208 } else {
209 tcp.inner().connected()
210 }
211 },
212 };
213 if let Some(info) = self.tls_info.borrow_mut().take() {
214 connected.extra(info)
215 } else {
216 connected
217 }
218 }
219}
220
221impl<T> hyper::rt::Read for InstrumentedStream<T>
222where
223 T: Connection + hyper::rt::Read + hyper::rt::Write + Unpin,
224{
225 fn poll_read(
226 self: std::pin::Pin<&mut Self>,
227 cx: &mut Context<'_>,
228 buf: hyper::rt::ReadBufCursor<'_>,
229 ) -> Poll<Result<(), io::Error>> {
230 std::pin::Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
231 }
232}
233
234impl<T> hyper::rt::Write for InstrumentedStream<T>
235where
236 T: Connection + hyper::rt::Read + hyper::rt::Write + Unpin,
237{
238 fn poll_write(
239 self: std::pin::Pin<&mut Self>,
240 cx: &mut Context<'_>,
241 buf: &[u8],
242 ) -> Poll<Result<usize, io::Error>> {
243 std::pin::Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
244 }
245
246 fn poll_flush(
247 self: std::pin::Pin<&mut Self>,
248 cx: &mut Context<'_>,
249 ) -> Poll<Result<(), io::Error>> {
250 std::pin::Pin::new(&mut self.get_mut().inner).poll_flush(cx)
251 }
252
253 fn poll_shutdown(
254 self: std::pin::Pin<&mut Self>,
255 cx: &mut Context<'_>,
256 ) -> Poll<Result<(), io::Error>> {
257 std::pin::Pin::new(&mut self.get_mut().inner).poll_shutdown(cx)
258 }
259
260 fn is_write_vectored(&self) -> bool {
261 self.inner.is_write_vectored()
262 }
263
264 fn poll_write_vectored(
265 self: std::pin::Pin<&mut Self>,
266 cx: &mut Context<'_>,
267 bufs: &[io::IoSlice<'_>],
268 ) -> Poll<Result<usize, io::Error>> {
269 std::pin::Pin::new(&mut self.get_mut().inner).poll_write_vectored(cx, bufs)
270 }
271}
272
273impl<T> Service<Destination> for InstrumentedConnector<T>
274where
275 T: Service<Destination>,
276 T::Response: Connection + hyper::rt::Read + hyper::rt::Write + Send + Unpin + 'static,
277 T::Future: Send + 'static,
278 T::Error: Into<BoxError>,
279{
280 type Response = InstrumentedStream<T::Response>;
281 type Error = BoxError;
282 type Future = std::pin::Pin<
283 Box<dyn Future<Output = Result<InstrumentedStream<T::Response>, BoxError>> + Send>,
284 >;
285
286 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
287 self.inner.poll_ready(cx).map_err(Into::into)
288 }
289
290 fn call(&mut self, dst: Destination) -> Self::Future {
291 let future = self.inner.call(dst);
292 Box::pin(async move {
293 let stream = future.await.map_err(|error| -> BoxError { error })?;
294 Ok(InstrumentedStream::from_maybe_https_stream(stream))
295 })
296 }
297}
298
299pub type Connector = InstrumentedConnector<ServoHttpConnector>;
300pub type TlsConfig = ClientConfig;
301
302#[derive(Clone, Debug, Default)]
303struct CertificateErrorOverrideManagerInternal {
304 certificates_failing_to_verify: HashMap<ServerName<'static>, CertificateDer<'static>>,
307 overrides: Vec<CertificateDer<'static>>,
310}
311
312#[derive(Clone, Debug, Default)]
317pub struct CertificateErrorOverrideManager(Arc<Mutex<CertificateErrorOverrideManagerInternal>>);
318
319impl CertificateErrorOverrideManager {
320 pub fn new() -> Self {
321 Self(Default::default())
322 }
323
324 pub fn add_override(&self, certificate: &CertificateDer<'static>) {
327 self.0.lock().overrides.push(certificate.clone());
328 }
329
330 pub(crate) fn remove_certificate_failing_verification(
334 &self,
335 host: &str,
336 ) -> Option<CertificateDer<'static>> {
337 let server_name = match ServerName::try_from(host) {
338 Ok(name) => name.to_owned(),
339 Err(error) => {
340 warn!("Could not convert host string into RustTLS ServerName: {error:?}");
341 return None;
342 },
343 };
344 self.0
345 .lock()
346 .certificates_failing_to_verify
347 .remove(&server_name)
348 }
349}
350
351#[derive(Clone, Debug, Default)]
352pub enum CACertificates<'de> {
353 #[default]
354 Default,
355 Override(Vec<CertificateDer<'de>>),
356}
357
358#[servo_tracing::instrument(skip_all)]
365pub fn create_tls_config(
366 ca_certificates: CACertificates<'static>,
367 ignore_certificate_errors: bool,
368 override_manager: CertificateErrorOverrideManager,
369) -> TlsConfig {
370 let verifier = CertificateVerificationOverrideVerifier::new(
371 ca_certificates,
372 ignore_certificate_errors,
373 override_manager,
374 );
375 rustls::ClientConfig::builder()
378 .dangerous()
379 .with_custom_certificate_verifier(Arc::new(verifier))
380 .with_no_client_auth()
381}
382
383#[derive(Clone)]
384struct TokioExecutor {}
385
386impl<F> Executor<F> for TokioExecutor
387where
388 F: Future<Output = ()> + 'static + std::marker::Send,
389{
390 fn execute(&self, fut: F) {
391 spawn_task(fut);
392 }
393}
394
395static CRYPTO_PROVIDER_CACHE: LazyLock<Arc<CryptoProvider>> = LazyLock::new(|| {
396 CryptoProvider::get_default()
397 .cloned()
398 .unwrap_or_else(|| {
401 warn!("Default crypto provider not initialized before first access in connector.");
402 Arc::new(aws_lc_rs::default_provider())
403 })
404});
405
406static RUSTLS_PLATFORM_VERIFIER_CACHE: LazyLock<Arc<rustls_platform_verifier::Verifier>> =
411 LazyLock::new(|| {
412 Arc::new(
413 rustls_platform_verifier::Verifier::new(CRYPTO_PROVIDER_CACHE.clone())
414 .expect("Could not initialize platform certificate verifier"),
415 )
416 });
417
418#[inline]
425pub fn prewarm_tls() {
426 #[servo_tracing::instrument]
427 fn prewarm_tls_impl() {
428 let mut sink = [0u8; 32];
429 let _ = CRYPTO_PROVIDER_CACHE.secure_random.fill(&mut sink);
431 }
434
435 if let Err(error) = std::thread::Builder::new()
436 .name("Net-TLS-prewarm".into())
437 .spawn(prewarm_tls_impl)
438 {
439 warn!("Failed to spawn thread to prewarm TLS: {error:?}");
440 }
441}
442
443#[derive(Debug)]
444struct CertificateVerificationOverrideVerifier {
445 main_verifier: Arc<dyn ServerCertVerifier>,
446 ignore_certificate_errors: bool,
447 override_manager: CertificateErrorOverrideManager,
448}
449
450impl CertificateVerificationOverrideVerifier {
451 fn new(
452 ca_certficates: CACertificates<'static>,
453 ignore_certificate_errors: bool,
454 override_manager: CertificateErrorOverrideManager,
455 ) -> Self {
456 let use_webpki_roots = cfg!(target_os = "android") || pref!(network_use_webpki_roots);
465 let main_verifier = if !use_webpki_roots {
466 let verifier = match ca_certficates {
467 CACertificates::Default => RUSTLS_PLATFORM_VERIFIER_CACHE.clone(),
468 CACertificates::Override(_certificates) => {
471 #[cfg(target_os = "android")]
472 unreachable!("Android should always use the WebPKI verifier.");
473 #[cfg(not(target_os = "android"))]
474 {
475 let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots(
476 _certificates,
477 CRYPTO_PROVIDER_CACHE.clone(),
478 )
479 .expect("Could not initialize platform certificate verifier");
480 Arc::new(verifier)
481 }
482 },
483 };
484 verifier as Arc<dyn ServerCertVerifier>
485 } else {
486 let mut root_store =
487 rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
488 match ca_certficates {
489 CACertificates::Default => {},
490 CACertificates::Override(certificates) => {
491 for certificate in certificates {
492 if root_store.add(certificate).is_err() {
493 log::error!("Could not add an override certificate.");
494 }
495 }
496 },
497 }
498 rustls::client::WebPkiServerVerifier::builder(root_store.into())
499 .build()
500 .expect("Could not initialize platform certificate verifier.")
501 as Arc<dyn ServerCertVerifier>
502 };
503
504 Self {
505 main_verifier,
506 ignore_certificate_errors,
507 override_manager,
508 }
509 }
510}
511
512impl rustls::client::danger::ServerCertVerifier for CertificateVerificationOverrideVerifier {
513 fn verify_tls12_signature(
514 &self,
515 message: &[u8],
516 cert: &CertificateDer<'_>,
517 dss: &rustls::DigitallySignedStruct,
518 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
519 self.main_verifier
520 .verify_tls12_signature(message, cert, dss)
521 }
522
523 fn verify_tls13_signature(
524 &self,
525 message: &[u8],
526 cert: &CertificateDer<'_>,
527 dss: &rustls::DigitallySignedStruct,
528 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
529 self.main_verifier
530 .verify_tls13_signature(message, cert, dss)
531 }
532
533 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
534 self.main_verifier.supported_verify_schemes()
535 }
536
537 fn verify_server_cert(
538 &self,
539 end_entity: &CertificateDer<'_>,
540 intermediates: &[CertificateDer<'_>],
541 server_name: &ServerName<'_>,
542 ocsp_response: &[u8],
543 now: UnixTime,
544 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
545 let error = match self.main_verifier.verify_server_cert(
546 end_entity,
547 intermediates,
548 server_name,
549 ocsp_response,
550 now,
551 ) {
552 Ok(result) => return Ok(result),
553 Err(error) => error,
554 };
555
556 if self.ignore_certificate_errors {
557 warn!("Ignoring certficate error: {error:?}");
558 return Ok(rustls::client::danger::ServerCertVerified::assertion());
559 }
560
561 for cert_with_exception in &*self.override_manager.0.lock().overrides {
563 if *end_entity == *cert_with_exception {
564 return Ok(rustls::client::danger::ServerCertVerified::assertion());
565 }
566 }
567 self.override_manager
568 .0
569 .lock()
570 .certificates_failing_to_verify
571 .insert(server_name.to_owned(), end_entity.clone().into_owned());
572 Err(error)
573 }
574}
575
576pub type BoxedBody = BoxBody<Bytes, hyper::Error>;
577
578#[derive(Debug)]
579pub enum ConnectionError {
581 HttpError(BoxError),
582 ProxyError(BoxError),
584}
585
586impl std::fmt::Display for ConnectionError {
587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
588 write!(f, "{self:?}")
589 }
590}
591
592impl std::error::Error for ConnectionError {
593 fn source(&self) -> Option<&(dyn Error + 'static)> {
594 match self {
595 ConnectionError::HttpError(error) => error.source(),
596 ConnectionError::ProxyError(error) => error.source(),
597 }
598 }
599}
600
601#[derive(Clone)]
602pub struct ProxyConnector {
605 client: ServoHttpConnector,
607 matcher: std::sync::Arc<hyper_util::client::proxy::matcher::Matcher>,
609}
610
611impl ProxyConnector {
612 fn new() -> Self {
613 let matcher_builder = hyper_util::client::proxy::matcher::Matcher::builder()
614 .http(servo_config::pref!(network_http_proxy_uri))
615 .https(servo_config::pref!(network_https_proxy_uri))
616 .no(servo_config::pref!(network_http_no_proxy));
617 ProxyConnector {
618 client: ServoHttpConnector::new(),
619 matcher: std::sync::Arc::new(matcher_builder.build()),
620 }
621 }
622}
623
624impl Service<Destination> for ProxyConnector {
626 type Response = TokioIo<TcpStream>;
627 type Error = ConnectionError;
628 type Future =
629 std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
630
631 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
632 self.client
633 .poll_ready(cx)
634 .map_err(|error| ConnectionError::ProxyError(BoxError::from(error)))
635 }
636
637 fn call(&mut self, req: Destination) -> Self::Future {
638 match self.matcher.intercept(&req) {
639 Some(intercept) => {
640 let mut tunnel = Tunnel::new(intercept.uri().clone(), self.client.clone());
641 let final_tunnel = if let Some(auth) = intercept.basic_auth() {
642 tunnel.with_auth(auth.clone())
643 } else {
644 tunnel
645 }
646 .call(req)
647 .map_err(|error| ConnectionError::ProxyError(BoxError::from(error)));
648 Box::pin(final_tunnel)
649 },
650 None => Box::pin(
651 self.client
652 .call(req)
653 .map_err(|error| ConnectionError::ProxyError(BoxError::from(error))),
654 ),
655 }
656 }
657}
658
659pub type ServoClient = Client<InstrumentedConnector<ProxyConnector>, BoxedBody>;
660
661pub fn create_http_client(tls_config: TlsConfig) -> ServoClient {
662 let connector = hyper_rustls::HttpsConnectorBuilder::new()
663 .with_tls_config(tls_config)
664 .https_or_http()
665 .enable_http1()
666 .enable_http2()
667 .wrap_connector(ProxyConnector::new());
668
669 Client::builder(TokioExecutor {})
670 .http1_title_case_headers(true)
671 .timer(TokioTimer::new())
673 .pool_timer(TokioTimer::new())
675 .pool_max_idle_per_host(6)
680 .build(InstrumentedConnector::from(connector))
681}