1use std::collections::hash_map::HashMap;
6use std::convert::TryFrom;
7use std::sync::Arc;
8
9use futures::task::{Context, Poll};
10use futures::{Future, TryFutureExt};
11use http::uri::{Authority, Uri as Destination};
12use http_body_util::combinators::BoxBody;
13use hyper::body::Bytes;
14use hyper::rt::Executor;
15use hyper_rustls::HttpsConnector as HyperRustlsHttpsConnector;
16use hyper_util::client::legacy::Client;
17use hyper_util::client::legacy::connect::HttpConnector as HyperHttpConnector;
18use hyper_util::client::legacy::connect::proxy::Tunnel;
19use hyper_util::rt::TokioIo;
20use log::warn;
21use parking_lot::Mutex;
22use rustls::client::WebPkiServerVerifier;
23use rustls::{ClientConfig, RootCertStore};
24use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
25use tokio::net::TcpStream;
26use tower::Service;
27
28use crate::async_runtime::spawn_task;
29use crate::hosts::replace_host;
30
31pub const BUF_SIZE: usize = 32768;
32
33#[derive(Clone)]
34pub struct ServoHttpConnector {
35 inner: HyperHttpConnector,
36}
37
38impl ServoHttpConnector {
39 fn new() -> ServoHttpConnector {
40 let mut inner = HyperHttpConnector::new();
41 inner.enforce_http(false);
42 inner.set_happy_eyeballs_timeout(None);
43 ServoHttpConnector { inner }
44 }
45}
46
47impl Service<Destination> for ServoHttpConnector {
48 type Response = TokioIo<TcpStream>;
49 type Error = ConnectionError;
50 type Future =
51 std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
52
53 fn call(&mut self, dest: Destination) -> Self::Future {
54 let mut new_dest = dest.clone();
56 let mut parts = dest.into_parts();
57
58 if let Some(auth) = parts.authority {
59 let host = auth.host();
60 let host = replace_host(host);
61
62 let authority = if let Some(port) = auth.port() {
63 format!("{}:{}", host, port.as_str())
64 } else {
65 (*host).to_string()
66 };
67
68 if let Ok(authority) = Authority::from_maybe_shared(authority) {
69 parts.authority = Some(authority);
70 if let Ok(dest) = Destination::from_parts(parts) {
71 new_dest = dest
72 }
73 }
74 }
75
76 Box::pin(
77 self.inner
78 .call(new_dest)
79 .map_err(|e| ConnectionError::HttpError(format!("{e}"))),
80 )
81 }
82
83 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84 Ok(()).into()
85 }
86}
87
88pub type Connector = HyperRustlsHttpsConnector<ServoHttpConnector>;
89pub type TlsConfig = ClientConfig;
90
91#[derive(Clone, Debug, Default)]
92struct CertificateErrorOverrideManagerInternal {
93 certificates_failing_to_verify: HashMap<ServerName<'static>, CertificateDer<'static>>,
96 overrides: Vec<CertificateDer<'static>>,
99}
100
101#[derive(Clone, Debug, Default)]
106pub struct CertificateErrorOverrideManager(Arc<Mutex<CertificateErrorOverrideManagerInternal>>);
107
108impl CertificateErrorOverrideManager {
109 pub fn new() -> Self {
110 Self(Default::default())
111 }
112
113 pub fn add_override(&self, certificate: &CertificateDer<'static>) {
116 self.0.lock().overrides.push(certificate.clone());
117 }
118
119 pub(crate) fn remove_certificate_failing_verification(
123 &self,
124 host: &str,
125 ) -> Option<CertificateDer<'static>> {
126 let server_name = match ServerName::try_from(host) {
127 Ok(name) => name.to_owned(),
128 Err(error) => {
129 warn!("Could not convert host string into RustTLS ServerName: {error:?}");
130 return None;
131 },
132 };
133 self.0
134 .lock()
135 .certificates_failing_to_verify
136 .remove(&server_name)
137 }
138}
139
140#[derive(Clone, Debug)]
141pub enum CACertificates {
142 Default,
143 Override(RootCertStore),
144}
145
146pub fn create_tls_config(
153 ca_certificates: CACertificates,
154 ignore_certificate_errors: bool,
155 override_manager: CertificateErrorOverrideManager,
156) -> TlsConfig {
157 let verifier = CertificateVerificationOverrideVerifier::new(
158 ca_certificates,
159 ignore_certificate_errors,
160 override_manager,
161 );
162 rustls::ClientConfig::builder()
163 .dangerous()
164 .with_custom_certificate_verifier(Arc::new(verifier))
165 .with_no_client_auth()
166}
167
168#[derive(Clone)]
169struct TokioExecutor {}
170
171impl<F> Executor<F> for TokioExecutor
172where
173 F: Future<Output = ()> + 'static + std::marker::Send,
174{
175 fn execute(&self, fut: F) {
176 spawn_task(fut);
177 }
178}
179
180#[derive(Debug)]
181struct CertificateVerificationOverrideVerifier {
182 webpki_verifier: Arc<WebPkiServerVerifier>,
183 ignore_certificate_errors: bool,
184 override_manager: CertificateErrorOverrideManager,
185}
186
187impl CertificateVerificationOverrideVerifier {
188 fn new(
189 ca_certficates: CACertificates,
190 ignore_certificate_errors: bool,
191 override_manager: CertificateErrorOverrideManager,
192 ) -> Self {
193 let root_cert_store = match ca_certficates {
194 CACertificates::Default => rustls::RootCertStore {
195 roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
196 },
197 CACertificates::Override(root_cert_store) => root_cert_store,
198 };
199
200 Self {
201 webpki_verifier: WebPkiServerVerifier::builder(root_cert_store.into())
204 .build()
205 .unwrap(),
206 ignore_certificate_errors,
207 override_manager,
208 }
209 }
210}
211
212impl rustls::client::danger::ServerCertVerifier for CertificateVerificationOverrideVerifier {
213 fn verify_tls12_signature(
214 &self,
215 message: &[u8],
216 cert: &CertificateDer<'_>,
217 dss: &rustls::DigitallySignedStruct,
218 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
219 self.webpki_verifier
220 .verify_tls12_signature(message, cert, dss)
221 }
222
223 fn verify_tls13_signature(
224 &self,
225 message: &[u8],
226 cert: &CertificateDer<'_>,
227 dss: &rustls::DigitallySignedStruct,
228 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
229 self.webpki_verifier
230 .verify_tls13_signature(message, cert, dss)
231 }
232
233 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
234 self.webpki_verifier.supported_verify_schemes()
235 }
236
237 fn verify_server_cert(
238 &self,
239 end_entity: &CertificateDer<'_>,
240 intermediates: &[CertificateDer<'_>],
241 server_name: &ServerName<'_>,
242 ocsp_response: &[u8],
243 now: UnixTime,
244 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
245 let error = match self.webpki_verifier.verify_server_cert(
246 end_entity,
247 intermediates,
248 server_name,
249 ocsp_response,
250 now,
251 ) {
252 Ok(result) => return Ok(result),
253 Err(error) => error,
254 };
255
256 if self.ignore_certificate_errors {
257 warn!("Ignoring certficate error: {error:?}");
258 return Ok(rustls::client::danger::ServerCertVerified::assertion());
259 }
260
261 for cert_with_exception in &*self.override_manager.0.lock().overrides {
263 if *end_entity == *cert_with_exception {
264 return Ok(rustls::client::danger::ServerCertVerified::assertion());
265 }
266 }
267 self.override_manager
268 .0
269 .lock()
270 .certificates_failing_to_verify
271 .insert(server_name.to_owned(), end_entity.clone().into_owned());
272 Err(error)
273 }
274}
275
276pub type BoxedBody = BoxBody<Bytes, hyper::Error>;
277
278fn create_maybe_proxy_connector() -> MaybeProxyConnector {
279 let network_http_proxy_uri = servo_config::pref!(network_http_proxy_uri);
280 if !network_http_proxy_uri.is_empty() {
281 if let Ok(http_proxy_uri) = network_http_proxy_uri.parse() {
282 log::info!("Using proxy specified via {:?}", http_proxy_uri);
283 return MaybeProxyConnector::Right(TunnelErrorMasker(Tunnel::new(
284 http_proxy_uri,
285 ServoHttpConnector::new(),
286 )));
287 }
288 }
289
290 MaybeProxyConnector::Left(ServoHttpConnector::new())
291}
292
293pub type MaybeProxyConnector = tower::util::Either<ServoHttpConnector, TunnelErrorMasker>;
295
296#[derive(Debug)]
297pub enum ConnectionError {
299 HttpError(String),
300 ProxyError(String),
302}
303
304impl std::fmt::Display for ConnectionError {
305 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306 write!(f, "{self:?}")
307 }
308}
309
310impl std::error::Error for ConnectionError {}
311
312#[derive(Clone)]
313pub struct TunnelErrorMasker(Tunnel<ServoHttpConnector>);
315
316impl Service<Destination> for TunnelErrorMasker {
318 type Response = TokioIo<TcpStream>;
319 type Error = ConnectionError;
320 type Future =
321 std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
322
323 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
324 self.0
325 .poll_ready(cx)
326 .map_err(|e| ConnectionError::ProxyError(format!("{e}")))
327 }
328
329 fn call(&mut self, req: Destination) -> Self::Future {
330 Box::pin(
331 self.0
332 .call(req)
333 .map_err(|e| ConnectionError::ProxyError(format!("{e}"))),
334 )
335 }
336}
337
338pub type ServoClient = Client<hyper_rustls::HttpsConnector<MaybeProxyConnector>, BoxedBody>;
339
340pub fn create_http_client(tls_config: TlsConfig) -> ServoClient {
341 let maybe_proxy_connector = create_maybe_proxy_connector();
342 let connector = hyper_rustls::HttpsConnectorBuilder::new()
343 .with_tls_config(tls_config)
344 .https_or_http()
345 .enable_http1()
346 .enable_http2()
347 .wrap_connector(maybe_proxy_connector);
348
349 Client::builder(TokioExecutor {})
350 .http1_title_case_headers(true)
351 .build(connector)
352}