diff --git a/Cargo.lock b/Cargo.lock index 9ecd181f042..4b47262ae62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,38 +284,17 @@ dependencies = [ "syn", ] -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-tungstenite" -version = "0.23.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc" +checksum = "90e661b6cb0a6eb34d02c520b052daa3aa9ac0cc02495c9d066bbce13ead132b" dependencies = [ "futures-io", "futures-util", "log", "pin-project-lite", + "rustls-pki-types", "tokio", "tokio-rustls", "tungstenite", @@ -1047,7 +1026,7 @@ dependencies = [ "euclid", "fonts", "gaol", - "http", + "http 1.2.0", "ipc-channel", "keyboard-types", "log", @@ -1474,8 +1453,8 @@ dependencies = [ "crossbeam-channel", "devtools_traits", "embedder_traits", - "headers", - "http", + "headers 0.4.0", + "http 1.2.0", "ipc-channel", "log", "net_traits", @@ -1493,7 +1472,7 @@ version = "0.0.1" dependencies = [ "base", "bitflags 2.6.0", - "http", + "http 1.2.0", "ipc-channel", "malloc_size_of_derive", "net_traits", @@ -2887,7 +2866,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", "indexmap", "slab", "tokio", @@ -2937,8 +2935,23 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core", - "http", + "headers-core 0.2.0", + "http 0.2.12", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core 0.3.0", + "http 1.2.0", "httpdate", "mime", "sha1", @@ -2950,7 +2963,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.12", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.2.0", ] [[package]] @@ -3043,6 +3065,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -3050,7 +3083,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -3082,9 +3138,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -3097,29 +3153,72 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.24.2" +name = "hyper" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http", - "hyper", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", "log", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", "webpki-roots", ] +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "hyper_serde" version = "0.13.2" dependencies = [ "cookie 0.18.1", - "headers", - "http", - "hyper", + "headers 0.4.0", + "http 1.2.0", + "hyper 1.5.2", "mime", "serde", "serde_bytes", @@ -4607,10 +4706,12 @@ dependencies = [ "futures-core", "futures-util", "generic-array", - "headers", - "http", - "hyper", + "headers 0.4.0", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", "hyper-rustls", + "hyper-util", "hyper_serde", "imsz", "ipc-channel", @@ -4624,6 +4725,7 @@ dependencies = [ "rayon", "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "servo_allocator", @@ -4636,8 +4738,8 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-stream", - "tokio-test", "tokio-util", + "tower-service", "tungstenite", "url", "uuid", @@ -4655,9 +4757,10 @@ dependencies = [ "cookie 0.18.1", "crossbeam-channel", "embedder_traits", - "headers", - "http", - "hyper", + "headers 0.4.0", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", "hyper_serde", "ipc-channel", "log", @@ -4666,7 +4769,7 @@ dependencies = [ "num-traits", "percent-encoding", "pixels", - "rustls", + "rustls-pki-types", "serde", "servo_arc", "servo_malloc_size_of", @@ -5893,32 +5996,42 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.21.7", + "rustls-pki-types", ] [[package]] -name = "rustls-webpki" -version = "0.101.7" +name = "rustls-pki-types" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -5996,9 +6109,9 @@ dependencies = [ "fonts_traits", "fxhash", "glow", - "headers", + "headers 0.4.0", "html5ever", - "http", + "http 1.2.0", "hyper_serde", "image", "indexmap", @@ -6125,7 +6238,7 @@ dependencies = [ "devtools_traits", "embedder_traits", "euclid", - "http", + "http 1.2.0", "hyper_serde", "ipc-channel", "keyboard-types", @@ -6149,16 +6262,6 @@ dependencies = [ "webxr-api", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -6528,7 +6631,7 @@ dependencies = [ "crossbeam-channel", "dom", "euclid", - "http", + "http 1.2.0", "indexmap", "keyboard-types", "malloc_size_of", @@ -6593,10 +6696,10 @@ dependencies = [ "gilrs", "gleam", "glow", - "headers", + "headers 0.4.0", "hilog", "hitrace", - "http", + "http 1.2.0", "image", "ipc-channel", "jni", @@ -6616,6 +6719,7 @@ dependencies = [ "ohos-sys", "ohos-vsync", "raw-window-handle", + "rustls", "serde_json", "servo_allocator", "shellwords", @@ -7445,9 +7549,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", "tokio", @@ -7464,19 +7568,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-test" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" -dependencies = [ - "async-stream", - "bytes", - "futures-core", - "tokio", - "tokio-stream", -] - [[package]] name = "tokio-util" version = "0.7.13" @@ -7652,21 +7743,21 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.2.0", "httparse", "log", "rand", "rustls", + "rustls-pki-types", "sha1", "thiserror 1.0.69", - "url", "utf-8", ] @@ -7917,9 +8008,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "headers", - "http", - "hyper", + "headers 0.3.9", + "http 0.2.12", + "hyper 0.14.30", "log", "mime", "mime_guess", @@ -8165,7 +8256,7 @@ dependencies = [ "base64 0.21.7", "bytes", "cookie 0.16.2", - "http", + "http 0.2.12", "icu_segmenter", "log", "serde", @@ -8189,7 +8280,7 @@ dependencies = [ "cookie 0.18.1", "crossbeam-channel", "euclid", - "http", + "http 0.2.12", "image", "ipc-channel", "keyboard-types", @@ -8227,9 +8318,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "webrender" @@ -9056,6 +9150,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerotrie" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 3b1e262ef33..a57030c5072 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ aes-kw = { version = "0.2.1", features = ["alloc"] } aes-gcm = "0.10.3" app_units = "0.7" arrayvec = "0.7" -async-tungstenite = { version = "0.23", features = ["tokio-rustls-webpki-roots"] } +async-tungstenite = { version = "0.28", features = ["tokio-rustls-webpki-roots"] } atomic_refcell = "0.1.13" background_hang_monitor_api = { path = "components/shared/background_hang_monitor" } backtrace = "0.3" @@ -64,13 +64,15 @@ gstreamer-gl = "0.23" gstreamer-gl-sys = "0.23" gstreamer-sys = "0.23" gstreamer-video = "0.23" -headers = "0.3" +headers = "0.4" hitrace = "0.1.4" html5ever = "0.29" -http = "0.2" -hyper = "0.14" -hyper-rustls = { version = "0.24", default-features = false, features = ["acceptor", "http1", "http2", "logging", "tls12", "webpki-tokio"] } +http = "1.0" +http-body-util = "0.1" +hyper = "1.0" +hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "logging", "tls12", "webpki-tokio"] } hyper_serde = { path = "components/hyper_serde" } +hyper-util = "0.1" icu_segmenter = "1.5.0" image = "0.24" imsz = "0.2" @@ -103,8 +105,9 @@ rand_isaac = "0.3" rayon = "1" regex = "1.11" ring = "0.17.8" -rustls = { version = "0.21.12", features = ["dangerous_configuration"] } -rustls-pemfile = "1.0.4" +rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] } +rustls-pemfile = "2.0" +rustls-pki-types = "1.0" script_layout_interface = { path = "components/shared/script_layout" } script_traits = { path = "components/shared/script" } selectors = { git = "https://github.com/servo/stylo", branch = "2024-12-04" } @@ -136,11 +139,12 @@ tikv-jemallocator = "0.6.0" time_03 = { package = "time", version = "0.3", features = ["large-dates", "local-offset", "serde"] } to_shmem = { git = "https://github.com/servo/stylo", branch = "2024-12-04" } tokio = "1" -tokio-rustls = "0.24" +tokio-rustls = { version = "0.26", default-features = false, features = ["logging"] } +tower-service = "0.3" tracing = "0.1.41" tracing-perfetto = "0.1.5" tracing-subscriber = "0.3.19" -tungstenite = "0.20" +tungstenite = "0.24" uluru = "3.0" unicode-bidi = "0.3.18" unicode-properties = { version = "0.1.3", features = ["emoji"] } @@ -149,7 +153,7 @@ unicode-segmentation = "1.12.0" url = "2.5" uuid = { version = "1.11.0", features = ["v4"] } webdriver = "0.51.0" -webpki-roots = "0.25" +webpki-roots = "0.26" webrender = { git = "https://github.com/servo/webrender", branch = "0.65", features = ["capture"] } webrender_api = { git = "https://github.com/servo/webrender", branch = "0.65" } webrender_traits = { path = "components/shared/webrender" } diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 74fd8f956d9..c523e267e8f 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -33,9 +33,11 @@ futures-util = { version = "0.3.30", default-features = false } generic-array = "0.14" headers = { workspace = true } http = { workspace = true } -hyper = { workspace = true, features = ["client", "http1", "http2", "tcp", "stream"] } +http-body-util = { workspace = true } +hyper = { workspace = true, features = ["client", "http1", "http2"] } hyper-rustls = { workspace = true } hyper_serde = { workspace = true } +hyper-util = { workspace = true, features = ["client", "client-legacy", "tokio"] } imsz = { workspace = true } ipc-channel = { workspace = true } log = { workspace = true } @@ -49,6 +51,7 @@ profile_traits = { workspace = true } rayon = { workspace = true } rustls = { workspace = true } rustls-pemfile = { workspace = true } +rustls-pki-types = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } servo_allocator = { path = "../allocator" } @@ -62,6 +65,7 @@ tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] } tokio-util = { version = "0.7.12", default-features = false, features = ["codec", "io"] } tokio-rustls = { workspace = true } tokio-stream = "0.1" +tower-service = { workspace = true } tungstenite = { workspace = true } url = { workspace = true } uuid = { workspace = true } @@ -72,9 +76,9 @@ webpki-roots = { workspace = true } [dev-dependencies] flate2 = "1" futures = { version = "0.3", features = ["compat"] } -tokio-test = "0.4" -tokio-stream = { version = "0.1", features = ["net"] } hyper = { workspace = true, features = ["full"] } +hyper-util = { workspace = true, features = ["server-graceful"] } +rustls = { workspace = true, features = ["ring"] } [[test]] name = "main" diff --git a/components/net/connector.rs b/components/net/connector.rs index f1e210b9304..dffa14ae7eb 100644 --- a/components/net/connector.rs +++ b/components/net/connector.rs @@ -9,14 +9,17 @@ use std::sync::{Arc, Mutex}; use futures::task::{Context, Poll}; use futures::Future; use http::uri::{Authority, Uri as Destination}; -use hyper::client::HttpConnector as HyperHttpConnector; +use http_body_util::combinators::BoxBody; +use hyper::body::Bytes; use hyper::rt::Executor; -use hyper::service::Service; -use hyper::{Body, Client}; use hyper_rustls::HttpsConnector as HyperRustlsHttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector as HyperHttpConnector; +use hyper_util::client::legacy::Client; use log::warn; -use rustls::client::WebPkiVerifier; -use rustls::{Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore, ServerName}; +use rustls::client::WebPkiServerVerifier; +use rustls::{ClientConfig, RootCertStore}; +use rustls_pki_types::{CertificateDer, ServerName, UnixTime}; +use tower_service::Service; use crate::async_runtime::HANDLE; use crate::hosts::replace_host; @@ -80,10 +83,10 @@ pub type TlsConfig = ClientConfig; struct CertificateErrorOverrideManagerInternal { /// A mapping of certificates and their hosts, which have seen certificate errors. /// This is used to later create an override in this [CertificateErrorOverrideManager]. - certificates_failing_to_verify: HashMap, + certificates_failing_to_verify: HashMap, CertificateDer<'static>>, /// A list of certificates that should be accepted despite encountering verification /// errors. - overrides: Vec, + overrides: Vec>, } /// This data structure is used to track certificate verification errors and overrides. @@ -100,7 +103,7 @@ impl CertificateErrorOverrideManager { /// Add a certificate to this manager's list of certificates for which to ignore /// validation errors. - pub fn add_override(&self, certificate: &Certificate) { + pub fn add_override(&self, certificate: &CertificateDer<'static>) { self.0.lock().unwrap().overrides.push(certificate.clone()); } @@ -110,9 +113,9 @@ impl CertificateErrorOverrideManager { pub(crate) fn remove_certificate_failing_verification( &self, host: &str, - ) -> Option { + ) -> Option> { let server_name = match ServerName::try_from(host) { - Ok(name) => name, + Ok(name) => name.to_owned(), Err(error) => { warn!("Could not convert host string into RustTLS ServerName: {error:?}"); return None; @@ -149,11 +152,12 @@ pub fn create_tls_config( override_manager, ); rustls::ClientConfig::builder() - .with_safe_defaults() + .dangerous() .with_custom_certificate_verifier(Arc::new(verifier)) .with_no_client_auth() } +#[derive(Clone)] struct TokioExecutor {} impl Executor for TokioExecutor @@ -165,8 +169,9 @@ where } } +#[derive(Debug)] struct CertificateVerificationOverrideVerifier { - webpki_verifier: WebPkiVerifier, + webpki_verifier: Arc, ignore_certificate_errors: bool, override_manager: CertificateErrorOverrideManager, } @@ -178,18 +183,8 @@ impl CertificateVerificationOverrideVerifier { override_manager: CertificateErrorOverrideManager, ) -> Self { let root_cert_store = match ca_certficates { - CACertificates::Default => { - let mut root_cert_store = rustls::RootCertStore::empty(); - root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map( - |trust_anchor| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - trust_anchor.subject, - trust_anchor.spki, - trust_anchor.name_constraints, - ) - }, - )); - root_cert_store + CACertificates::Default => rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), }, CACertificates::Override(root_cert_store) => root_cert_store, }; @@ -197,28 +192,52 @@ impl CertificateVerificationOverrideVerifier { Self { // See https://github.com/rustls/rustls/blame/v/0.21.6/rustls/src/client/builder.rs#L141 // This is the default verifier for Rustls that we are wrapping. - webpki_verifier: WebPkiVerifier::new(root_cert_store, None), + webpki_verifier: WebPkiServerVerifier::builder(root_cert_store.into()) + .build() + .unwrap(), ignore_certificate_errors, override_manager, } } } -impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerifier { +impl rustls::client::danger::ServerCertVerifier for CertificateVerificationOverrideVerifier { + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + self.webpki_verifier + .verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + self.webpki_verifier + .verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.webpki_verifier.supported_verify_schemes() + } + fn verify_server_cert( &self, - end_entity: &Certificate, - intermediates: &[Certificate], - server_name: &ServerName, - scts: &mut dyn Iterator, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, ocsp_response: &[u8], - now: std::time::SystemTime, - ) -> Result { + now: UnixTime, + ) -> Result { let error = match self.webpki_verifier.verify_server_cert( end_entity, intermediates, server_name, - scts, ocsp_response, now, ) { @@ -228,13 +247,13 @@ impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerif if self.ignore_certificate_errors { warn!("Ignoring certficate error: {error:?}"); - return Ok(rustls::client::ServerCertVerified::assertion()); + return Ok(rustls::client::danger::ServerCertVerified::assertion()); } // If there's an override for this certificate, just accept it. for cert_with_exception in &*self.override_manager.0.lock().unwrap().overrides { if *end_entity == *cert_with_exception { - return Ok(rustls::client::ServerCertVerified::assertion()); + return Ok(rustls::client::danger::ServerCertVerified::assertion()); } } self.override_manager @@ -242,12 +261,14 @@ impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerif .lock() .unwrap() .certificates_failing_to_verify - .insert(server_name.clone(), end_entity.clone()); + .insert(server_name.to_owned(), end_entity.clone().into_owned()); Err(error) } } -pub fn create_http_client(tls_config: TlsConfig) -> Client { +pub type BoxedBody = BoxBody; + +pub fn create_http_client(tls_config: TlsConfig) -> Client { let connector = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(tls_config) .https_or_http() @@ -255,8 +276,7 @@ pub fn create_http_client(tls_config: TlsConfig) -> Client { .enable_http2() .wrap_connector(ServoHttpConnector::new()); - Client::builder() + Client::builder(TokioExecutor {}) .http1_title_case_headers(true) - .executor(TokioExecutor {}) .build(connector) } diff --git a/components/net/decoder.rs b/components/net/decoder.rs index eaa1cc663e7..9b6c8f1a3d0 100644 --- a/components/net/decoder.rs +++ b/components/net/decoder.rs @@ -28,12 +28,16 @@ use futures::task::{Context, Poll}; use futures::{Future, Stream}; use futures_util::StreamExt; use headers::{ContentLength, HeaderMapExt}; +use http_body_util::BodyExt; +use hyper::body::Body; use hyper::header::{HeaderValue, CONTENT_ENCODING, TRANSFER_ENCODING}; -use hyper::{Body, Response}; +use hyper::Response; use servo_config::pref; use tokio_util::codec::{BytesCodec, FramedRead}; use tokio_util::io::StreamReader; +use crate::connector::BoxedBody; + pub const DECODER_BUFFER_SIZE: usize = 8192; /// A response decompressor over a non-blocking stream of bytes. @@ -81,7 +85,7 @@ impl Decoder { /// This decoder will emit the underlying bytes as-is. #[inline] fn plain_text( - body: Body, + body: BoxedBody, is_secure_scheme: bool, content_length: Option, ) -> Decoder { @@ -95,7 +99,7 @@ impl Decoder { /// This decoder will buffer and decompress bytes that are encoded in the expected format. #[inline] fn pending( - body: Body, + body: BoxedBody, type_: DecoderType, is_secure_scheme: bool, content_length: Option, @@ -114,7 +118,7 @@ impl Decoder { /// how to decode the content body of the response. /// /// Uses the correct variant by inspecting the Content-Encoding header. - pub fn detect(response: Response, is_secure_scheme: bool) -> Response { + pub fn detect(response: Response, is_secure_scheme: bool) -> Response { let values = response .headers() .get_all(CONTENT_ENCODING) @@ -225,7 +229,7 @@ impl Future for Pending { } struct BodyStream { - body: Body, + body: BoxedBody, is_secure_scheme: bool, content_length: Option, total_read: u64, @@ -234,14 +238,16 @@ struct BodyStream { impl BodyStream { fn empty() -> Self { BodyStream { - body: Body::empty(), + body: http_body_util::Empty::new() + .map_err(|_| unreachable!()) + .boxed(), is_secure_scheme: false, content_length: None, total_read: 0, } } - fn new(body: Body, is_secure_scheme: bool, content_length: Option) -> Self { + fn new(body: BoxedBody, is_secure_scheme: bool, content_length: Option) -> Self { BodyStream { body, is_secure_scheme, @@ -255,8 +261,11 @@ impl Stream for BodyStream { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - match futures_core::ready!(Pin::new(&mut self.body).poll_next(cx)) { + match futures_core::ready!(Pin::new(&mut self.body).poll_frame(cx)) { Some(Ok(bytes)) => { + let Ok(bytes) = bytes.into_data() else { + return Poll::Ready(None); + }; self.total_read += bytes.len() as u64; Poll::Ready(Some(Ok(bytes))) }, diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 3b1474c713c..00f8bf1e496 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -31,7 +31,7 @@ use net_traits::{ FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue, ResourceTimingType, }; -use rustls::Certificate; +use rustls_pki_types::CertificateDer; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; @@ -675,7 +675,7 @@ fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io context .state .override_manager - .add_override(&Certificate(cert_bytes)); + .add_override(&CertificateDer::from_slice(&cert_bytes).into_owned()); Ok(()) } diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs index 9ed08c9d85b..fa67529fe1d 100644 --- a/components/net/filemanager_thread.rs +++ b/components/net/filemanager_thread.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; use embedder_traits::{EmbedderMsg, EmbedderProxy, FilterPattern}; -use headers::{ContentLength, ContentType, HeaderMap, HeaderMapExt}; +use headers::{ContentLength, ContentType, HeaderMap, HeaderMapExt, Range}; use http::header::{self, HeaderValue}; use ipc_channel::ipc::{self, IpcSender}; use log::warn; @@ -30,6 +30,7 @@ use url::Url; use uuid::Uuid; use crate::fetch::methods::{CancellationListener, Data, RangeRequestBounds}; +use crate::protocols::get_range_request_bounds; use crate::resource_thread::CoreResourceThreadPool; pub const FILE_CHUNK_SIZE: usize = 32768; //32 KB @@ -132,7 +133,7 @@ impl FileManager { file_token: &FileTokenCheck, origin: FileOrigin, response: &mut Response, - range: RangeRequestBounds, + range: Option, ) -> Result<(), BlobURLStoreError> { self.fetch_blob_buf( done_sender, @@ -140,7 +141,7 @@ impl FileManager { &id, file_token, &origin, - range, + BlobBounds::Unresolved(range), response, ) } @@ -285,13 +286,17 @@ impl FileManager { id: &Uuid, file_token: &FileTokenCheck, origin_in: &FileOrigin, - range: RangeRequestBounds, + bounds: BlobBounds, response: &mut Response, ) -> Result<(), BlobURLStoreError> { let file_impl = self.store.get_impl(id, file_token, origin_in)?; match file_impl { FileImpl::Memory(buf) => { - let range = range + let bounds = match bounds { + BlobBounds::Unresolved(range) => get_range_request_bounds(range, buf.size), + BlobBounds::Resolved(bounds) => bounds, + }; + let range = bounds .get_final(Some(buf.size)) .map_err(|_| BlobURLStoreError::InvalidRange)?; @@ -323,7 +328,11 @@ impl FileManager { let file = File::open(&metadata.path) .map_err(|e| BlobURLStoreError::External(e.to_string()))?; - let range = range + let bounds = match bounds { + BlobBounds::Unresolved(range) => get_range_request_bounds(range, metadata.size), + BlobBounds::Resolved(bounds) => bounds, + }; + let range = bounds .get_final(Some(metadata.size)) .map_err(|_| BlobURLStoreError::InvalidRange)?; @@ -362,15 +371,16 @@ impl FileManager { FileImpl::Sliced(parent_id, inner_rel_pos) => { // Next time we don't need to check validity since // we have already done that for requesting URL if necessary. + let bounds = RangeRequestBounds::Final( + RelativePos::full_range().slice_inner(&inner_rel_pos), + ); self.fetch_blob_buf( done_sender, cancellation_listener, &parent_id, file_token, origin_in, - RangeRequestBounds::Final( - RelativePos::full_range().slice_inner(&inner_rel_pos), - ), + BlobBounds::Resolved(bounds), response, ) }, @@ -378,6 +388,11 @@ impl FileManager { } } +enum BlobBounds { + Unresolved(Option), + Resolved(RangeRequestBounds), +} + /// File manager's data store. It maintains a thread-safe mapping /// from FileID to FileStoreEntry which might have different backend implementation. /// Access to the content is encapsulated as methods of this struct. diff --git a/components/net/http_cache.rs b/components/net/http_cache.rs index 8f479801c08..bf6d28de220 100644 --- a/components/net/http_cache.rs +++ b/components/net/http_cache.rs @@ -345,7 +345,7 @@ fn create_resource_with_bytes_from_resource( fn handle_range_request( request: &Request, candidates: &[&CachedResource], - range_spec: Vec<(Bound, Bound)>, + range_spec: &Range, done_chan: &mut DoneChannel, ) -> Option { let mut complete_cached_resources = candidates @@ -354,10 +354,7 @@ fn handle_range_request( let partial_cached_resources = candidates .iter() .filter(|resource| resource.status == StatusCode::PARTIAL_CONTENT); - match ( - range_spec.first().unwrap(), - complete_cached_resources.next(), - ) { + if let Some(complete_resource) = complete_cached_resources.next() { // TODO: take the full range spec into account. // If we have a complete resource, take the request range from the body. // When there isn't a complete resource available, we loop over cached partials, @@ -366,172 +363,145 @@ fn handle_range_request( // see . // TODO: add support for complete and partial resources, // whose body is in the ResponseBody::Receiving state. - (&(Bound::Included(beginning), Bound::Included(end)), Some(complete_resource)) => { - if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() { - if end == u64::MAX { - // Prevent overflow on the addition below. - return None; - } - let b = beginning as usize; - let e = end as usize + 1; - let requested = body.get(b..e); - if let Some(bytes) = requested { - let new_resource = - create_resource_with_bytes_from_resource(bytes, complete_resource); - let cached_headers = new_resource.metadata.headers.lock().unwrap(); - let cached_response = - create_cached_response(request, &new_resource, &cached_headers, done_chan); - if let Some(cached_response) = cached_response { - return Some(cached_response); + let body_len = match *complete_resource.body.lock().unwrap() { + ResponseBody::Done(ref body) => body.len(), + _ => 0, + }; + let bound = range_spec + .satisfiable_ranges(body_len.try_into().unwrap()) + .next() + .unwrap(); + match bound { + (Bound::Included(beginning), Bound::Included(end)) => { + if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() { + if end == u64::MAX { + // Prevent overflow on the addition below. + return None; } - } - } - }, - (&(Bound::Included(beginning), Bound::Included(end)), None) => { - for partial_resource in partial_cached_resources { - let headers = partial_resource.metadata.headers.lock().unwrap(); - let content_range = headers.typed_get::(); - let (res_beginning, res_end) = match content_range { - Some(range) => { - if let Some(bytes_range) = range.bytes_range() { - bytes_range - } else { - continue; - } - }, - _ => continue, - }; - if res_beginning <= beginning && res_end >= end { - let resource_body = &*partial_resource.body.lock().unwrap(); - let requested = match resource_body { - ResponseBody::Done(body) => { - let b = beginning as usize - res_beginning as usize; - let e = end as usize - res_beginning as usize + 1; - body.get(b..e) - }, - _ => continue, - }; + let b = beginning as usize; + let e = end as usize + 1; + let requested = body.get(b..e); if let Some(bytes) = requested { let new_resource = - create_resource_with_bytes_from_resource(bytes, partial_resource); - let cached_response = - create_cached_response(request, &new_resource, &headers, done_chan); + create_resource_with_bytes_from_resource(bytes, complete_resource); + let cached_headers = new_resource.metadata.headers.lock().unwrap(); + let cached_response = create_cached_response( + request, + &new_resource, + &cached_headers, + done_chan, + ); if let Some(cached_response) = cached_response { return Some(cached_response); } } } - } - }, - (&(Bound::Included(beginning), Bound::Unbounded), Some(complete_resource)) => { - if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() { - let b = beginning as usize; - let requested = body.get(b..); - if let Some(bytes) = requested { - let new_resource = - create_resource_with_bytes_from_resource(bytes, complete_resource); - let cached_headers = new_resource.metadata.headers.lock().unwrap(); - let cached_response = - create_cached_response(request, &new_resource, &cached_headers, done_chan); - if let Some(cached_response) = cached_response { - return Some(cached_response); - } - } - } - }, - (&(Bound::Included(beginning), Bound::Unbounded), None) => { - for partial_resource in partial_cached_resources { - let headers = partial_resource.metadata.headers.lock().unwrap(); - let content_range = headers.typed_get::(); - let (res_beginning, res_end, total) = if let Some(range) = content_range { - match (range.bytes_range(), range.bytes_len()) { - (Some(bytes_range), Some(total)) => (bytes_range.0, bytes_range.1, total), - _ => continue, - } - } else { - continue; - }; - if total == 0 { - // Prevent overflow in the below operations from occuring. - continue; - }; - if res_beginning < beginning && res_end == total - 1 { - let resource_body = &*partial_resource.body.lock().unwrap(); - let requested = match resource_body { - ResponseBody::Done(body) => { - let from_byte = beginning as usize - res_beginning as usize; - body.get(from_byte..) - }, - _ => continue, - }; + }, + (Bound::Included(beginning), Bound::Unbounded) => { + if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() { + let b = beginning as usize; + let requested = body.get(b..); if let Some(bytes) = requested { let new_resource = - create_resource_with_bytes_from_resource(bytes, partial_resource); - let cached_response = - create_cached_response(request, &new_resource, &headers, done_chan); + create_resource_with_bytes_from_resource(bytes, complete_resource); + let cached_headers = new_resource.metadata.headers.lock().unwrap(); + let cached_response = create_cached_response( + request, + &new_resource, + &cached_headers, + done_chan, + ); if let Some(cached_response) = cached_response { return Some(cached_response); } } } - } - }, - (&(Bound::Unbounded, Bound::Included(offset)), Some(complete_resource)) => { - if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() { - let from_byte = body.len() - offset as usize; - let requested = body.get(from_byte..); - if let Some(bytes) = requested { - let new_resource = - create_resource_with_bytes_from_resource(bytes, complete_resource); - let cached_headers = new_resource.metadata.headers.lock().unwrap(); - let cached_response = - create_cached_response(request, &new_resource, &cached_headers, done_chan); - if let Some(cached_response) = cached_response { - return Some(cached_response); - } - } - } - }, - (&(Bound::Unbounded, Bound::Included(offset)), None) => { - for partial_resource in partial_cached_resources { - let headers = partial_resource.metadata.headers.lock().unwrap(); - let content_range = headers.typed_get::(); - let (res_beginning, res_end, total) = if let Some(range) = content_range { - match (range.bytes_range(), range.bytes_len()) { - (Some(bytes_range), Some(total)) => (bytes_range.0, bytes_range.1, total), - _ => continue, - } - } else { - continue; - }; - if total < res_beginning || total < res_end || offset == 0 || offset == u64::MAX { - // Prevent overflow in the below operations from occuring. - continue; - } - if (total - res_beginning) > (offset - 1) && (total - res_end) < offset + 1 { - let resource_body = &*partial_resource.body.lock().unwrap(); - let requested = match resource_body { - ResponseBody::Done(body) => { - let from_byte = body.len() - offset as usize; - body.get(from_byte..) + }, + _ => return None, + } + } else { + for partial_resource in partial_cached_resources { + let headers = partial_resource.metadata.headers.lock().unwrap(); + let content_range = headers.typed_get::(); + + let Some(body_len) = content_range.as_ref().and_then(|range| range.bytes_len()) else { + continue; + }; + match range_spec.satisfiable_ranges(body_len - 1).next().unwrap() { + (Bound::Included(beginning), Bound::Included(end)) => { + let (res_beginning, res_end) = match content_range { + Some(range) => { + if let Some(bytes_range) = range.bytes_range() { + bytes_range + } else { + continue; + } }, _ => continue, }; - if let Some(bytes) = requested { - let new_resource = - create_resource_with_bytes_from_resource(bytes, partial_resource); - let cached_response = - create_cached_response(request, &new_resource, &headers, done_chan); - if let Some(cached_response) = cached_response { - return Some(cached_response); + if res_beginning <= beginning && res_end >= end { + let resource_body = &*partial_resource.body.lock().unwrap(); + let requested = match resource_body { + ResponseBody::Done(body) => { + let b = beginning as usize - res_beginning as usize; + let e = end as usize - res_beginning as usize + 1; + body.get(b..e) + }, + _ => continue, + }; + if let Some(bytes) = requested { + let new_resource = + create_resource_with_bytes_from_resource(bytes, partial_resource); + let cached_response = + create_cached_response(request, &new_resource, &headers, done_chan); + if let Some(cached_response) = cached_response { + return Some(cached_response); + } } } - } + }, + + (Bound::Included(beginning), Bound::Unbounded) => { + let (res_beginning, res_end, total) = if let Some(range) = content_range { + match (range.bytes_range(), range.bytes_len()) { + (Some(bytes_range), Some(total)) => { + (bytes_range.0, bytes_range.1, total) + }, + _ => continue, + } + } else { + continue; + }; + if total == 0 { + // Prevent overflow in the below operations from occuring. + continue; + }; + if res_beginning <= beginning && res_end == total - 1 { + let resource_body = &*partial_resource.body.lock().unwrap(); + let requested = match resource_body { + ResponseBody::Done(body) => { + let from_byte = beginning as usize - res_beginning as usize; + body.get(from_byte..) + }, + _ => continue, + }; + if let Some(bytes) = requested { + let new_resource = + create_resource_with_bytes_from_resource(bytes, partial_resource); + let cached_response = + create_cached_response(request, &new_resource, &headers, done_chan); + if let Some(cached_response) = cached_response { + return Some(cached_response); + } + } + } + }, + + _ => continue, } - }, - // All the cases with Bound::Excluded should be unreachable anyway - _ => return None, + } } + None } @@ -607,12 +577,7 @@ impl HttpCache { } // Support for range requests if let Some(range_spec) = request.headers.typed_get::() { - return handle_range_request( - request, - candidates.as_slice(), - range_spec.iter().collect(), - done_chan, - ); + return handle_range_request(request, candidates.as_slice(), &range_spec, done_chan); } while let Some(cached_resource) = candidates.pop() { // Not a Range request. diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 9f115d18065..d795ef12841 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use core::convert::Infallible; use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; use std::sync::{Arc as StdArc, Condvar, Mutex, RwLock}; @@ -19,7 +18,7 @@ use devtools_traits::{ use embedder_traits::{ EmbedderMsg, EmbedderProxy, PromptCredentialsInput, PromptDefinition, PromptOrigin, }; -use futures::{future, StreamExt, TryFutureExt, TryStreamExt}; +use futures::{future, TryFutureExt, TryStreamExt}; use headers::authorization::Basic; use headers::{ AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods, @@ -32,10 +31,14 @@ use http::header::{ CONTENT_TYPE, }; use http::{HeaderMap, Method, Request as HyperRequest, StatusCode}; +use http_body_util::combinators::BoxBody; +use http_body_util::{BodyExt, Full}; +use hyper::body::{Bytes, Frame}; use hyper::ext::ReasonPhrase; use hyper::header::{HeaderName, TRANSFER_ENCODING}; -use hyper::{Body, Client, Response as HyperResponse}; +use hyper::Response as HyperResponse; use hyper_serde::Serde; +use hyper_util::client::legacy::Client; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::{debug, error, info, log_enabled, warn}; @@ -101,7 +104,7 @@ pub struct HttpState { pub http_cache_state: HttpCacheState, pub auth_cache: RwLock, pub history_states: RwLock>>, - pub client: Client, + pub client: Client, pub override_manager: CertificateErrorOverrideManager, pub embedder_proxy: Mutex, } @@ -440,7 +443,7 @@ enum BodyChunk { enum BodyStream { /// A receiver that can be used in Body::wrap_stream, /// for streaming the request over the network. - Chunked(TokioReceiver>), + Chunked(TokioReceiver, hyper::Error>>), /// A body whose bytes are buffered /// and sent in one chunk over the network. Buffered(UnboundedReceiver), @@ -450,7 +453,7 @@ enum BodyStream { /// used to enqueue chunks. enum BodySink { /// A Tokio sender used to feed chunks to the network stream. - Chunked(TokioSender>), + Chunked(TokioSender, hyper::Error>>), /// A Crossbeam sender used to send chunks to the fetch worker, /// where they will be buffered /// in order to ensure they are not streamed them over the network. @@ -463,7 +466,7 @@ impl BodySink { BodySink::Chunked(ref sender) => { let sender = sender.clone(); HANDLE.lock().unwrap().as_mut().unwrap().spawn(async move { - let _ = sender.send(bytes).await; + let _ = sender.send(Ok(Frame::data(bytes.into()))).await; }); }, BodySink::Buffered(ref sender) => { @@ -484,7 +487,7 @@ impl BodySink { #[allow(clippy::too_many_arguments)] async fn obtain_response( - client: &Client, + client: &Client, url: &ServoUrl, method: &Method, request_headers: &mut HeaderMap, @@ -584,7 +587,7 @@ async fn obtain_response( let body = match stream { BodyStream::Chunked(receiver) => { let stream = ReceiverStream::new(receiver); - Body::wrap_stream(stream.map(Ok::<_, Infallible>)) + BoxBody::new(http_body_util::StreamBody::new(stream)) }, BodyStream::Buffered(mut receiver) => { // Accumulate bytes received over IPC into a vector. @@ -598,7 +601,7 @@ async fn obtain_response( None => warn!("Failed to read all chunks from request body."), } } - body.into() + Full::new(body.into()).map_err(|_| unreachable!()).boxed() }, }; HyperRequest::builder() @@ -609,7 +612,11 @@ async fn obtain_response( HyperRequest::builder() .method(method) .uri(encoded_url) - .body(Body::empty()) + .body( + http_body_util::Empty::new() + .map_err(|_| unreachable!()) + .boxed(), + ) }; context @@ -695,7 +702,10 @@ async fn obtain_response( None }; - future::ready(Ok((Decoder::detect(res, is_secure_scheme), msg))) + future::ready(Ok(( + Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme), + msg, + ))) }) .map_err(move |error| { NetworkError::from_hyper_error( diff --git a/components/net/protocols/blob.rs b/components/net/protocols/blob.rs index 6059897c219..ac751f80c38 100644 --- a/components/net/protocols/blob.rs +++ b/components/net/protocols/blob.rs @@ -16,9 +16,7 @@ use net_traits::{NetworkError, ResourceFetchTiming}; use tokio::sync::mpsc::unbounded_channel; use crate::fetch::methods::{Data, DoneChannel, FetchContext}; -use crate::protocols::{ - get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler, -}; +use crate::protocols::{partial_content, range_not_satisfiable_error, ProtocolHandler}; #[derive(Default)] pub struct BlobProtocolHander {} @@ -42,9 +40,6 @@ impl ProtocolHandler for BlobProtocolHander { let range_header = request.headers.typed_get::(); let is_range_request = range_header.is_some(); - // We will get a final version of this range once we have - // the length of the data backing the blob. - let range = get_range_request_bounds(range_header); let (id, origin) = match parse_blob_url(&url) { Ok((id, origin)) => (id, origin), @@ -73,7 +68,7 @@ impl ProtocolHandler for BlobProtocolHander { &context.file_token, origin, &mut response, - range, + range_header, ) { let _ = done_sender.send(Data::Done); let err = match err { diff --git a/components/net/protocols/file.rs b/components/net/protocols/file.rs index 65e134e4635..df5cdfc3274 100644 --- a/components/net/protocols/file.rs +++ b/components/net/protocols/file.rs @@ -58,7 +58,9 @@ impl ProtocolHandler for FileProtocolHander { let range_header = request.headers.typed_get::(); let is_range_request = range_header.is_some(); - let Ok(range) = get_range_request_bounds(range_header).get_final(file_size) else { + let Ok(range) = get_range_request_bounds(range_header, file_size.unwrap_or(0)) + .get_final(file_size) + else { range_not_satisfiable_error(&mut response); return Box::pin(ready(response)); }; diff --git a/components/net/protocols/mod.rs b/components/net/protocols/mod.rs index 02b8f65614b..51d43ab9d2a 100644 --- a/components/net/protocols/mod.rs +++ b/components/net/protocols/mod.rs @@ -106,19 +106,15 @@ pub fn range_not_satisfiable_error(response: &mut Response) { } /// Get the range bounds if the `Range` header is present. -pub fn get_range_request_bounds(range: Option) -> RangeRequestBounds { +pub fn get_range_request_bounds(range: Option, len: u64) -> RangeRequestBounds { if let Some(ref range) = range { - let (start, end) = match range - .iter() - .collect::, Bound)>>() - .first() - { - Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None), - Some(&(Bound::Included(start), Bound::Included(end))) => { + let (start, end) = match range.satisfiable_ranges(len).next() { + Some((Bound::Included(start), Bound::Unbounded)) => (start, None), + Some((Bound::Included(start), Bound::Included(end))) => { // `end` should be less or equal to `start`. (start, Some(i64::max(start as i64, end as i64))) }, - Some(&(Bound::Unbounded, Bound::Included(offset))) => { + Some((Bound::Unbounded, Bound::Included(offset))) => { return RangeRequestBounds::Pending(offset); }, _ => (0, None), diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 97e2791f5bb..5cb56c2399d 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -61,8 +61,8 @@ fn load_root_cert_store_from_file(file_path: String) -> io::Result, _> = rustls_pemfile::certs(&mut pem).collect(); + root_cert_store.add_parsable_certificates(certs?); Ok(root_cert_store) } diff --git a/components/net/tests/fetch.rs b/components/net/tests/fetch.rs index 0b1a7ac196c..eef8ba89a66 100644 --- a/components/net/tests/fetch.rs +++ b/components/net/tests/fetch.rs @@ -21,7 +21,9 @@ use headers::{ }; use http::header::{self, HeaderMap, HeaderName, HeaderValue}; use http::{Method, StatusCode}; -use hyper::{Body, Request as HyperRequest, Response as HyperResponse}; +use http_body_util::combinators::BoxBody; +use hyper::body::{Bytes, Incoming}; +use hyper::{Request as HyperRequest, Response as HyperResponse}; use mime::{self, Mime}; use net::fetch::cors_cache::CorsCache; use net::fetch::methods::{self, CancellationListener, FetchContext}; @@ -41,13 +43,12 @@ use net_traits::{ }; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; -use tokio_test::block_on; use uuid::Uuid; use crate::http_loader::{expect_devtools_http_request, expect_devtools_http_response}; use crate::{ create_embedder_proxy, create_http_state, fetch, fetch_with_context, fetch_with_cors_cache, - make_server, make_ssl_server, new_fetch_context, DEFAULT_USER_AGENT, + make_body, make_server, make_ssl_server, new_fetch_context, DEFAULT_USER_AGENT, }; // TODO write a struct that impls Handler for storing test values @@ -55,9 +56,11 @@ use crate::{ #[test] fn test_fetch_response_is_not_network_error() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -89,9 +92,11 @@ fn test_fetch_on_bad_port_is_network_error() { #[test] fn test_fetch_response_body_matches_const_message() { static MESSAGE: &'static [u8] = b"Hello World!"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -191,7 +196,7 @@ fn test_fetch_blob() { expected: bytes.to_vec(), }; - block_on(methods::fetch(&mut request, &mut target, &context)); + crate::HANDLE.block_on(methods::fetch(&mut request, &mut target, &context)); let fetch_response = receiver.recv().unwrap(); assert!(!fetch_response.is_network_error()); @@ -285,38 +290,41 @@ fn test_fetch_bogus_scheme() { fn test_cors_preflight_fetch() { static ACK: &'static [u8] = b"ACK"; let state = Arc::new(AtomicUsize::new(0)); - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 - { - assert!(request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); - assert!(!request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); - assert!(!request - .headers() - .get(header::REFERER) - .unwrap() - .to_str() - .unwrap() - .contains("a.html")); - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - response - .headers_mut() - .typed_insert(AccessControlAllowCredentials); - response - .headers_mut() - .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); - } else { - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - *response.body_mut() = ACK.to_vec().into(); - } - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + if request.method() == Method::OPTIONS && + state.clone().fetch_add(1, Ordering::SeqCst) == 0 + { + assert!(request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); + assert!(!request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); + assert!(!request + .headers() + .get(header::REFERER) + .unwrap() + .to_str() + .unwrap() + .contains("a.html")); + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + response + .headers_mut() + .typed_insert(AccessControlAllowCredentials); + response + .headers_mut() + .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); + } else { + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + *response.body_mut() = make_body(ACK.to_vec()); + } + }; let (server, url) = make_server(handler); let target_url = url.clone().join("a.html").unwrap(); @@ -340,34 +348,37 @@ fn test_cors_preflight_cache_fetch() { let state = Arc::new(AtomicUsize::new(0)); let counter = state.clone(); let mut cache = CorsCache::default(); - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 - { - assert!(request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); - assert!(!request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - response - .headers_mut() - .typed_insert(AccessControlAllowCredentials); - response - .headers_mut() - .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); - response - .headers_mut() - .typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0))); - } else { - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - *response.body_mut() = ACK.to_vec().into(); - } - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + if request.method() == Method::OPTIONS && + state.clone().fetch_add(1, Ordering::SeqCst) == 0 + { + assert!(request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); + assert!(!request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + response + .headers_mut() + .typed_insert(AccessControlAllowCredentials); + response + .headers_mut() + .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); + response + .headers_mut() + .typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0))); + } else { + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + *response.body_mut() = make_body(ACK.to_vec()); + } + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); @@ -403,31 +414,34 @@ fn test_cors_preflight_cache_fetch() { fn test_cors_preflight_fetch_network_error() { static ACK: &'static [u8] = b"ACK"; let state = Arc::new(AtomicUsize::new(0)); - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 - { - assert!(request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); - assert!(!request - .headers() - .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - response - .headers_mut() - .typed_insert(AccessControlAllowCredentials); - response - .headers_mut() - .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); - } else { - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); - *response.body_mut() = ACK.to_vec().into(); - } - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + if request.method() == Method::OPTIONS && + state.clone().fetch_add(1, Ordering::SeqCst) == 0 + { + assert!(request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)); + assert!(!request + .headers() + .contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS)); + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + response + .headers_mut() + .typed_insert(AccessControlAllowCredentials); + response + .headers_mut() + .typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET])); + } else { + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + *response.body_mut() = make_body(ACK.to_vec()); + } + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); @@ -443,18 +457,20 @@ fn test_cors_preflight_fetch_network_error() { #[test] fn test_fetch_response_is_basic_filtered() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .insert(header::SET_COOKIE, HeaderValue::from_static("")); - // this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec - response.headers_mut().insert( - HeaderName::from_static("set-cookie2"), - HeaderValue::from_bytes(&vec![]).unwrap(), - ); + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .insert(header::SET_COOKIE, HeaderValue::from_static("")); + // this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec + response.headers_mut().insert( + HeaderName::from_static("set-cookie2"), + HeaderValue::from_bytes(&vec![]).unwrap(), + ); - *response.body_mut() = MESSAGE.to_vec().into(); - }; + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -476,47 +492,49 @@ fn test_fetch_response_is_basic_filtered() { #[test] fn test_fetch_response_is_cors_filtered() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - // this is mandatory for the Cors Check to pass - // TODO test using different url encodings with this value ie. punycode - response - .headers_mut() - .typed_insert(AccessControlAllowOrigin::ANY); + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + // this is mandatory for the Cors Check to pass + // TODO test using different url encodings with this value ie. punycode + response + .headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); - // these are the headers that should be kept after filtering - response.headers_mut().typed_insert(CacheControl::new()); - response.headers_mut().insert( - header::CONTENT_LANGUAGE, - HeaderValue::from_bytes(&vec![]).unwrap(), - ); - response - .headers_mut() - .typed_insert(ContentType::from(mime::TEXT_HTML)); - response - .headers_mut() - .typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0))); - response - .headers_mut() - .typed_insert(LastModified::from(SystemTime::now())); - response.headers_mut().typed_insert(Pragma::no_cache()); + // these are the headers that should be kept after filtering + response.headers_mut().typed_insert(CacheControl::new()); + response.headers_mut().insert( + header::CONTENT_LANGUAGE, + HeaderValue::from_bytes(&vec![]).unwrap(), + ); + response + .headers_mut() + .typed_insert(ContentType::from(mime::TEXT_HTML)); + response + .headers_mut() + .typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0))); + response + .headers_mut() + .typed_insert(LastModified::from(SystemTime::now())); + response.headers_mut().typed_insert(Pragma::no_cache()); - // these headers should not be kept after filtering, even though they are given a pass - response - .headers_mut() - .insert(header::SET_COOKIE, HeaderValue::from_static("")); - response.headers_mut().insert( - HeaderName::from_static("set-cookie2"), - HeaderValue::from_bytes(&vec![]).unwrap(), - ); - response - .headers_mut() - .typed_insert(AccessControlAllowHeaders::from_iter(vec![ - HeaderName::from_static("set-cookie"), + // these headers should not be kept after filtering, even though they are given a pass + response + .headers_mut() + .insert(header::SET_COOKIE, HeaderValue::from_static("")); + response.headers_mut().insert( HeaderName::from_static("set-cookie2"), - ])); + HeaderValue::from_bytes(&vec![]).unwrap(), + ); + response + .headers_mut() + .typed_insert(AccessControlAllowHeaders::from_iter(vec![ + HeaderName::from_static("set-cookie"), + HeaderName::from_static("set-cookie2"), + ])); - *response.body_mut() = MESSAGE.to_vec().into(); - }; + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); // an origin mis-match will stop it from defaulting to a basic filtered response @@ -546,9 +564,11 @@ fn test_fetch_response_is_cors_filtered() { #[test] fn test_fetch_response_is_opaque_filtered() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); // an origin mis-match will fall through to an Opaque filtered response @@ -577,24 +597,26 @@ fn test_fetch_response_is_opaque_filtered() { #[test] fn test_fetch_response_is_opaque_redirect_filtered() { static MESSAGE: &'static [u8] = b""; - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let redirects = request - .uri() - .path() - .split("/") - .collect::() - .parse::() - .unwrap_or(0); + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let redirects = request + .uri() + .path() + .split("/") + .collect::() + .parse::() + .unwrap_or(0); - if redirects == 1 { - *response.body_mut() = MESSAGE.to_vec().into(); - } else { - *response.status_mut() = StatusCode::FOUND; - response - .headers_mut() - .insert(header::LOCATION, HeaderValue::from_static("1")); - } - }; + if redirects == 1 { + *response.body_mut() = make_body(MESSAGE.to_vec()); + } else { + *response.status_mut() = StatusCode::FOUND; + response + .headers_mut() + .insert(header::LOCATION, HeaderValue::from_static("1")); + } + }; let (server, url) = make_server(handler); @@ -626,9 +648,11 @@ fn test_fetch_with_local_urls_only() { // If flag `local_urls_only` is set, fetching a non-local URL must result in network error. static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, server_url) = make_server(handler); let do_fetch = |url: ServoUrl| { @@ -661,9 +685,11 @@ fn test_fetch_with_local_urls_only() { #[test] fn test_fetch_with_hsts() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_ssl_server(handler); @@ -710,14 +736,16 @@ fn test_fetch_with_hsts() { #[test] fn test_load_adds_host_to_hsts_list_when_url_is_https() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .typed_insert(StrictTransportSecurity::excluding_subdomains( - Duration::from_secs(31536000), - )); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .typed_insert(StrictTransportSecurity::excluding_subdomains( + Duration::from_secs(31536000), + )); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, mut url) = make_ssl_server(handler); url.as_mut_url().set_scheme("https").unwrap(); @@ -772,9 +800,11 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() { #[test] fn test_fetch_self_signed() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, mut url) = make_ssl_server(handler); url.as_mut_url().set_scheme("https").unwrap(); @@ -834,9 +864,11 @@ fn test_fetch_self_signed() { #[test] fn test_fetch_with_sri_network_error() { static MESSAGE: &'static [u8] = b"alert('Hello, Network Error');"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -858,9 +890,11 @@ fn test_fetch_with_sri_network_error() { #[test] fn test_fetch_with_sri_sucess() { static MESSAGE: &'static [u8] = b"alert('Hello, world.');"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -888,18 +922,20 @@ fn test_fetch_blocked_nosniff() { const HEADER: &'static str = "x-content-type-options"; const VALUE: &'static [u8] = b"nosniff"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - let mime_header = ContentType::from(mime.clone()); - response.headers_mut().typed_insert(mime_header); - assert!(response.headers().contains_key(header::CONTENT_TYPE)); - // Add the nosniff header - response.headers_mut().insert( - HeaderName::from_static(HEADER), - HeaderValue::from_bytes(VALUE).unwrap(), - ); + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + let mime_header = ContentType::from(mime.clone()); + response.headers_mut().typed_insert(mime_header); + assert!(response.headers().contains_key(header::CONTENT_TYPE)); + // Add the nosniff header + response.headers_mut().insert( + HeaderName::from_static(HEADER), + HeaderValue::from_bytes(VALUE).unwrap(), + ); - *response.body_mut() = MESSAGE.to_vec().into(); - }; + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); @@ -926,25 +962,27 @@ fn test_fetch_blocked_nosniff() { } fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let redirects = request - .uri() - .path() - .split("/") - .collect::() - .parse::() - .unwrap_or(0); + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let redirects = request + .uri() + .path() + .split("/") + .collect::() + .parse::() + .unwrap_or(0); - if redirects >= redirect_cap { - *response.body_mut() = message.to_vec().into(); - } else { - *response.status_mut() = StatusCode::FOUND; - let url = format!("{redirects}", redirects = redirects + 1); - response - .headers_mut() - .insert(header::LOCATION, HeaderValue::from_str(&url).unwrap()); - } - }; + if redirects >= redirect_cap { + *response.body_mut() = make_body(message.to_vec()); + } else { + *response.status_mut() = StatusCode::FOUND; + let url = format!("{redirects}", redirects = redirects + 1); + response + .headers_mut() + .insert(header::LOCATION, HeaderValue::from_str(&url).unwrap()); + } + }; let (server, url) = make_server(handler); @@ -997,44 +1035,46 @@ fn test_fetch_redirect_updates_method_runner( method: Method, ) { let handler_method = method.clone(); - let handler_tx = Arc::new(Mutex::new(tx)); + let handler_tx = Arc::new(tx); - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let redirects = request - .uri() - .path() - .split("/") - .collect::() - .parse::() - .unwrap_or(0); + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let redirects = request + .uri() + .path() + .split("/") + .collect::() + .parse::() + .unwrap_or(0); - let mut test_pass = true; + let mut test_pass = true; - if redirects == 0 { - *response.status_mut() = StatusCode::TEMPORARY_REDIRECT; - response - .headers_mut() - .insert(header::LOCATION, HeaderValue::from_static("1")); - } else if redirects == 1 { - // this makes sure that the request method does't change from the wrong status code - if handler_method != Method::GET && request.method() == Method::GET { + if redirects == 0 { + *response.status_mut() = StatusCode::TEMPORARY_REDIRECT; + response + .headers_mut() + .insert(header::LOCATION, HeaderValue::from_static("1")); + } else if redirects == 1 { + // this makes sure that the request method does't change from the wrong status code + if handler_method != Method::GET && request.method() == Method::GET { + test_pass = false; + } + *response.status_mut() = status_code; + response + .headers_mut() + .insert(header::LOCATION, HeaderValue::from_static("2")); + } else if request.method() != Method::GET { test_pass = false; } - *response.status_mut() = status_code; - response - .headers_mut() - .insert(header::LOCATION, HeaderValue::from_static("2")); - } else if request.method() != Method::GET { - test_pass = false; - } - // the first time this handler is reached, nothing is being tested, so don't send anything - if redirects > 0 { - handler_tx.lock().unwrap().send(test_pass).unwrap(); - } - }; + // the first time this handler is reached, nothing is being tested, so don't send anything + if redirects > 0 { + handler_tx.send(test_pass).unwrap(); + } + }; - let (server, url) = make_server(handler); + let (server, url) = crate::make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) .origin(url.origin()) @@ -1114,9 +1154,11 @@ fn response_is_done(response: &Response) -> bool { #[test] fn test_fetch_async_returns_complete_response() { static MESSAGE: &'static [u8] = b"this message should be retrieved in full"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1131,9 +1173,11 @@ fn test_fetch_async_returns_complete_response() { #[test] fn test_opaque_filtered_fetch_async_returns_complete_response() { static MESSAGE: &'static [u8] = b""; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); // an origin mis-match will fall through to an Opaque filtered response @@ -1149,24 +1193,26 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() { #[test] fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { static MESSAGE: &'static [u8] = b""; - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let redirects = request - .uri() - .path() - .split("/") - .collect::() - .parse::() - .unwrap_or(0); + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let redirects = request + .uri() + .path() + .split("/") + .collect::() + .parse::() + .unwrap_or(0); - if redirects == 1 { - *response.body_mut() = MESSAGE.to_vec().into(); - } else { - *response.status_mut() = StatusCode::FOUND; - response - .headers_mut() - .insert(header::LOCATION, HeaderValue::from_static("1")); - } - }; + if redirects == 1 { + *response.body_mut() = make_body(MESSAGE.to_vec()); + } else { + *response.status_mut() = StatusCode::FOUND; + response + .headers_mut() + .insert(header::LOCATION, HeaderValue::from_static("1")); + } + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1186,9 +1232,11 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { #[cfg(not(target_os = "windows"))] fn test_fetch_with_devtools() { static MESSAGE: &'static [u8] = b"Yay!"; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.body_mut() = MESSAGE.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.body_mut() = make_body(MESSAGE.to_vec()); + }; let (server, url) = make_server(handler); diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs index c7478cb06da..499776f934a 100644 --- a/components/net/tests/http_loader.rs +++ b/components/net/tests/http_loader.rs @@ -12,7 +12,7 @@ use std::time::Duration; use base::id::TEST_PIPELINE_ID; use cookie::Cookie as CookiePair; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{unbounded, Receiver}; use devtools_traits::{ ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest, HttpResponse as DevtoolsHttpResponse, NetworkEvent, @@ -26,7 +26,9 @@ use headers::{ use http::header::{self, HeaderMap, HeaderValue}; use http::uri::Authority; use http::{HeaderName, Method, StatusCode}; -use hyper::{Body, Request as HyperRequest, Response as HyperResponse}; +use http_body_util::combinators::BoxBody; +use hyper::body::{Body, Bytes, Incoming}; +use hyper::{Request as HyperRequest, Response as HyperResponse}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use net::cookie::ServoCookie; @@ -43,12 +45,11 @@ use net_traits::request::{ use net_traits::response::{Response, ResponseBody}; use net_traits::{CookieSource, FetchTaskTarget, NetworkError, ReferrerPolicy}; use servo_url::{ImmutableOrigin, ServoUrl}; -use tokio_test::block_on; use url::Url; use crate::{ - create_embedder_proxy_and_receiver, fetch, fetch_with_context, make_server, new_fetch_context, - receive_credential_prompt_msgs, + create_embedder_proxy_and_receiver, fetch, fetch_with_context, make_body, make_server, + new_fetch_context, receive_credential_prompt_msgs, }; fn mock_origin() -> ImmutableOrigin { @@ -119,7 +120,8 @@ fn create_request_body_with_content(content: Vec) -> RequestBody { fn test_check_default_headers_loaded_in_every_request() { let expected_headers = Arc::new(Mutex::new(None)); let expected_headers_clone = expected_headers.clone(); - let handler = move |request: HyperRequest, _: &mut HyperResponse| { + let handler = move |request: HyperRequest, + _: &mut HyperResponse>| { assert_eq!( request.headers().clone(), expected_headers_clone.lock().unwrap().take().unwrap() @@ -220,7 +222,8 @@ fn test_check_default_headers_loaded_in_every_request() { #[test] fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length_should_be_set_to_0( ) { - let handler = move |request: HyperRequest, _: &mut HyperResponse| { + let handler = move |request: HyperRequest, + _: &mut HyperResponse>| { assert_eq!( request.headers().typed_get::(), Some(ContentLength(0)) @@ -249,12 +252,14 @@ fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length #[test] fn test_request_and_response_data_with_network_messages() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .typed_insert(Host::from("foo.bar".parse::().unwrap())); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .typed_insert(Host::from("foo.bar".parse::().unwrap())); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut request_headers = HeaderMap::new(); @@ -362,12 +367,14 @@ fn test_request_and_response_data_with_network_messages() { #[test] #[cfg(not(target_os = "windows"))] fn test_request_and_response_message_from_devtool_without_pipeline_id() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .typed_insert(Host::from("foo.bar".parse::().unwrap())); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .typed_insert(Host::from("foo.bar".parse::().unwrap())); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -389,21 +396,25 @@ fn test_request_and_response_message_from_devtool_without_pipeline_id() { #[test] fn test_redirected_request_to_devtools() { - let post_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::GET); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let post_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::GET); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (post_server, post_url) = make_server(post_handler); let post_redirect_url = post_url.clone(); - let pre_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::POST); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let pre_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::POST); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (pre_server, pre_url) = make_server(pre_handler); let mut request = RequestBuilder::new(pre_url.clone(), Referrer::NoReferrer) @@ -438,21 +449,25 @@ fn test_redirected_request_to_devtools() { #[test] fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() { - let post_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::GET); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let post_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::GET); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (post_server, post_url) = make_server(post_handler); let post_redirect_url = post_url.clone(); - let pre_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::POST); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let pre_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::POST); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (pre_server, pre_url) = make_server(pre_handler); let mut request = RequestBuilder::new(pre_url.clone(), Referrer::NoReferrer) @@ -473,16 +488,18 @@ fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() { #[test] fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_content_encoding_deflate( ) { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::CONTENT_ENCODING, - HeaderValue::from_static("deflate"), - ); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write(b"Yay!").unwrap(); - let encoded_content = e.finish().unwrap(); - *response.body_mut() = encoded_content.into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::CONTENT_ENCODING, + HeaderValue::from_static("deflate"), + ); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write(b"Yay!").unwrap(); + let encoded_content = e.finish().unwrap(); + *response.body_mut() = make_body(encoded_content); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -507,15 +524,17 @@ fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_co #[test] fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_content_encoding_gzip() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip")); - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write(b"Yay!").unwrap(); - let encoded_content = e.finish().unwrap(); - *response.body_mut() = encoded_content.into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip")); + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write(b"Yay!").unwrap(); + let encoded_content = e.finish().unwrap(); + *response.body_mut() = make_body(encoded_content); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -540,24 +559,26 @@ fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_conte #[test] fn test_load_doesnt_send_request_body_on_any_redirect() { - use hyper::body::HttpBody; - - let post_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::GET); - assert_eq!(request.size_hint().exact(), Some(0)); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let post_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::GET); + assert_eq!(request.size_hint().exact(), Some(0)); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (post_server, post_url) = make_server(post_handler); let post_redirect_url = post_url.clone(); - let pre_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.size_hint().exact(), Some(13)); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let pre_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.size_hint().exact(), Some(13)); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (pre_server, pre_url) = make_server(pre_handler); let content = b"Body on POST!"; @@ -581,14 +602,16 @@ fn test_load_doesnt_send_request_body_on_any_redirect() { #[test] fn test_load_doesnt_add_host_to_hsts_list_when_url_is_http_even_if_hsts_headers_are_present() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .typed_insert(StrictTransportSecurity::excluding_subdomains( - Duration::from_secs(31536000), - )); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .typed_insert(StrictTransportSecurity::excluding_subdomains( + Duration::from_secs(31536000), + )); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -623,13 +646,15 @@ fn test_load_doesnt_add_host_to_hsts_list_when_url_is_http_even_if_hsts_headers_ #[test] fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_in_response() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::SET_COOKIE, - HeaderValue::from_static("mozillaIs=theBest"), - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::SET_COOKIE, + HeaderValue::from_static("mozillaIs=theBest"), + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut context = new_fetch_context(None, None, None); @@ -665,13 +690,15 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ #[test] fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_resource_manager() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request.headers().get(header::COOKIE).unwrap().as_bytes(), - b"mozillaIs=theBest" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request.headers().get(header::COOKIE).unwrap().as_bytes(), + b"mozillaIs=theBest" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut context = new_fetch_context(None, None, None); @@ -710,13 +737,15 @@ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_re #[test] fn test_load_sends_cookie_if_nonhttp() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request.headers().get(header::COOKIE).unwrap().as_bytes(), - b"mozillaIs=theBest" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request.headers().get(header::COOKIE).unwrap().as_bytes(), + b"mozillaIs=theBest" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut context = new_fetch_context(None, None, None); @@ -756,13 +785,15 @@ fn test_load_sends_cookie_if_nonhttp() { #[test] #[cfg(not(target_os = "windows"))] fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::SET_COOKIE, - HeaderValue::from_static("mozillaIs=theBest; HttpOnly"), - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::SET_COOKIE, + HeaderValue::from_static("mozillaIs=theBest; HttpOnly"), + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut context = new_fetch_context(None, None, None); @@ -803,13 +834,15 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( #[test] #[cfg(not(target_os = "windows"))] fn test_when_cookie_received_marked_secure_is_ignored_for_http() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::SET_COOKIE, - HeaderValue::from_static("mozillaIs=theBest; Secure"), - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::SET_COOKIE, + HeaderValue::from_static("mozillaIs=theBest; Secure"), + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut context = new_fetch_context(None, None, None); @@ -837,14 +870,16 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { #[test] fn test_load_sets_content_length_to_length_of_request_body() { let content = b"This is a request body"; - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let content_length = ContentLength(content.len() as u64); - assert_eq!( - request.headers().typed_get::(), - Some(content_length) - ); - *response.body_mut() = content.to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let content_length = ContentLength(content.len() as u64); + assert_eq!( + request.headers().typed_get::(), + Some(content_length) + ); + *response.body_mut() = make_body(content.to_vec()); + }; let (server, url) = make_server(handler); let request_body = create_request_body_with_content(content.to_vec()); @@ -871,18 +906,20 @@ fn test_load_sets_content_length_to_length_of_request_body() { #[test] fn test_load_uses_explicit_accept_from_headers_in_load_data() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request - .headers() - .get(header::ACCEPT) - .unwrap() - .to_str() - .unwrap(), - "text/html" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request + .headers() + .get(header::ACCEPT) + .unwrap() + .to_str() + .unwrap(), + "text/html" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut accept_headers = HeaderMap::new(); @@ -909,18 +946,20 @@ fn test_load_uses_explicit_accept_from_headers_in_load_data() { #[test] fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request - .headers() - .get(header::ACCEPT) - .unwrap() - .to_str() - .unwrap(), - "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request + .headers() + .get(header::ACCEPT) + .unwrap() + .to_str() + .unwrap(), + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -944,18 +983,20 @@ fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() { #[test] fn test_load_uses_explicit_accept_encoding_from_load_data_headers() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request - .headers() - .get(header::ACCEPT_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request + .headers() + .get(header::ACCEPT_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut accept_encoding_headers = HeaderMap::new(); @@ -982,18 +1023,20 @@ fn test_load_uses_explicit_accept_encoding_from_load_data_headers() { #[test] fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!( - request - .headers() - .get(header::ACCEPT_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "gzip, deflate, br" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!( + request + .headers() + .get(header::ACCEPT_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "gzip, deflate, br" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1019,31 +1062,35 @@ fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() { fn test_load_errors_when_there_a_redirect_loop() { let url_b_for_a = Arc::new(Mutex::new(None::)); let url_b_for_a_clone = url_b_for_a.clone(); - let handler_a = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str( - &url_b_for_a_clone - .lock() - .unwrap() - .as_ref() - .unwrap() - .to_string(), - ) - .unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let handler_a = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str( + &url_b_for_a_clone + .lock() + .unwrap() + .as_ref() + .unwrap() + .to_string(), + ) + .unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (server_a, url_a) = make_server(handler_a); let url_a_for_b = url_a.clone(); - let handler_b = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let handler_b = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (server_b, url_b) = make_server(handler_b); *url_b_for_a.lock().unwrap() = Some(url_b.clone()); @@ -1071,35 +1118,39 @@ fn test_load_succeeds_with_a_redirect_loop() { let url_b_for_a = Arc::new(Mutex::new(None::)); let url_b_for_a_clone = url_b_for_a.clone(); let handled_a = AtomicBool::new(false); - let handler_a = move |_: HyperRequest, response: &mut HyperResponse| { - if !handled_a.swap(true, Ordering::SeqCst) { - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str( - &url_b_for_a_clone - .lock() - .unwrap() - .as_ref() - .unwrap() - .to_string(), - ) - .unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - } else { - *response.body_mut() = b"Success".to_vec().into() - } - }; + let handler_a = + move |_: HyperRequest, + response: &mut HyperResponse>| { + if !handled_a.swap(true, Ordering::SeqCst) { + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str( + &url_b_for_a_clone + .lock() + .unwrap() + .as_ref() + .unwrap() + .to_string(), + ) + .unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + } else { + *response.body_mut() = make_body(b"Success".to_vec()); + } + }; let (server_a, url_a) = make_server(handler_a); let url_a_for_b = url_a.clone(); - let handler_b = move |_: HyperRequest, response: &mut HyperResponse| { - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let handler_b = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (server_b, url_b) = make_server(handler_b); *url_b_for_a.lock().unwrap() = Some(url_b.clone()); @@ -1126,21 +1177,25 @@ fn test_load_succeeds_with_a_redirect_loop() { #[test] fn test_load_follows_a_redirect() { - let post_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::GET); - *response.body_mut() = b"Yay!".to_vec().into(); - }; + let post_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::GET); + *response.body_mut() = make_body(b"Yay!".to_vec()); + }; let (post_server, post_url) = make_server(post_handler); let post_redirect_url = post_url.clone(); - let pre_handler = move |request: HyperRequest, response: &mut HyperResponse| { - assert_eq!(request.method(), Method::GET); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - }; + let pre_handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + assert_eq!(request.method(), Method::GET); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + }; let (pre_server, pre_url) = make_server(pre_handler); let mut request = RequestBuilder::new(pre_url.clone(), Referrer::NoReferrer) @@ -1167,29 +1222,31 @@ fn test_load_follows_a_redirect() { fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { let shared_url_y = Arc::new(Mutex::new(None::)); let shared_url_y_clone = shared_url_y.clone(); - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let path = request.uri().path(); - if path == "/com/" { - assert_eq!( - request.headers().get(header::COOKIE).unwrap().as_bytes(), - b"mozillaIsNot=dotOrg" - ); - let location = shared_url_y.lock().unwrap().as_ref().unwrap().to_string(); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&location.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - } else if path == "/org/" { - assert_eq!( - request.headers().get(header::COOKIE).unwrap().as_bytes(), - b"mozillaIs=theBest" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - } else { - panic!("unexpected path {:?}", path) - } - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let path = request.uri().path(); + if path == "/com/" { + assert_eq!( + request.headers().get(header::COOKIE).unwrap().as_bytes(), + b"mozillaIsNot=dotOrg" + ); + let location = shared_url_y.lock().unwrap().as_ref().unwrap().to_string(); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&location.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + } else if path == "/org/" { + assert_eq!( + request.headers().get(header::COOKIE).unwrap().as_bytes(), + b"mozillaIs=theBest" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + } else { + panic!("unexpected path {:?}", path) + } + }; let (server, url) = make_server(handler); let port = url.port().unwrap(); @@ -1248,29 +1305,31 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { #[test] fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let path = request.uri().path(); - if path == "/initial/" { - response.headers_mut().insert( - header::SET_COOKIE, - HeaderValue::from_static("mozillaIs=theBest; path=/;"), - ); - let location = "/subsequent/".to_string(); - response.headers_mut().insert( - header::LOCATION, - HeaderValue::from_str(&location.to_string()).unwrap(), - ); - *response.status_mut() = StatusCode::MOVED_PERMANENTLY; - } else if path == "/subsequent/" { - assert_eq!( - request.headers().get(header::COOKIE).unwrap().as_bytes(), - b"mozillaIs=theBest" - ); - *response.body_mut() = b"Yay!".to_vec().into(); - } else { - panic!("unexpected path {:?}", path) - } - }; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let path = request.uri().path(); + if path == "/initial/" { + response.headers_mut().insert( + header::SET_COOKIE, + HeaderValue::from_static("mozillaIs=theBest; path=/;"), + ); + let location = "/subsequent/".to_string(); + response.headers_mut().insert( + header::LOCATION, + HeaderValue::from_str(&location.to_string()).unwrap(), + ); + *response.status_mut() = StatusCode::MOVED_PERMANENTLY; + } else if path == "/subsequent/" { + assert_eq!( + request.headers().get(header::COOKIE).unwrap().as_bytes(), + b"mozillaIs=theBest" + ); + *response.body_mut() = make_body(b"Yay!".to_vec()); + } else { + panic!("unexpected path {:?}", path) + } + }; let (server, url) = make_server(handler); let url = url.join("/initial/").unwrap(); @@ -1297,13 +1356,15 @@ fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() { #[test] fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { - let handler = move |request: HyperRequest, _response: &mut HyperResponse| { - let expected = Authorization::basic("username", "test"); - assert_eq!( - request.headers().typed_get::>(), - Some(expected) - ); - }; + let handler = + move |request: HyperRequest, + _response: &mut HyperResponse>| { + let expected = Authorization::basic("username", "test"); + assert_eq!( + request.headers().typed_get::>(), + Some(expected) + ); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1344,9 +1405,11 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { #[test] fn test_auth_ui_needs_www_auth() { - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - *response.status_mut() = StatusCode::UNAUTHORIZED; - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + *response.status_mut() = StatusCode::UNAUTHORIZED; + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1406,12 +1469,14 @@ fn test_fetch_compressed_response_update_count() { ]; const DATA_DECOMPRESSED_LEN: usize = 10500; - let handler = move |_: HyperRequest, response: &mut HyperResponse| { - response - .headers_mut() - .insert(header::CONTENT_ENCODING, HeaderValue::from_static("br")); - *response.body_mut() = DATA_BROTLI_COMPRESSED.to_vec().into(); - }; + let handler = + move |_: HyperRequest, + response: &mut HyperResponse>| { + response + .headers_mut() + .insert(header::CONTENT_ENCODING, HeaderValue::from_static("br")); + *response.body_mut() = make_body(DATA_BROTLI_COMPRESSED.to_vec()); + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1423,7 +1488,7 @@ fn test_fetch_compressed_response_update_count() { .build(); struct FetchResponseCollector { - sender: Sender, + sender: Option>, update_count: usize, } impl FetchTaskTarget for FetchResponseCollector { @@ -1435,23 +1500,23 @@ fn test_fetch_compressed_response_update_count() { } /// Fired when the response is fully fetched fn process_response_eof(&mut self, _: &Request, _: &Response) { - let _ = self.sender.send(self.update_count); + let _ = self.sender.take().unwrap().send(self.update_count); } } - let (sender, receiver) = unbounded(); + let (sender, receiver) = tokio::sync::oneshot::channel(); let mut target = FetchResponseCollector { - sender: sender, + sender: Some(sender), update_count: 0, }; - let response_update_count = block_on(async move { + let response_update_count = crate::HANDLE.block_on(async move { methods::fetch( &mut request, &mut target, &mut new_fetch_context(None, None, None), ) .await; - receiver.recv().unwrap() + receiver.await.unwrap() }); server.close(); @@ -1484,18 +1549,20 @@ fn test_origin_serialization_compatability() { #[test] fn test_user_credentials_prompt_when_proxy_authentication_is_required() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let expected = Authorization::basic("username", "test"); - if let Some(credentials) = request.headers().typed_get::>() { - if credentials == expected { - *response.status_mut() = StatusCode::OK; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let expected = Authorization::basic("username", "test"); + if let Some(credentials) = request.headers().typed_get::>() { + if credentials == expected { + *response.status_mut() = StatusCode::OK; + } else { + *response.status_mut() = StatusCode::UNAUTHORIZED; + } } else { - *response.status_mut() = StatusCode::UNAUTHORIZED; + *response.status_mut() = StatusCode::PROXY_AUTHENTICATION_REQUIRED; } - } else { - *response.status_mut() = StatusCode::PROXY_AUTHENTICATION_REQUIRED; - } - }; + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1530,18 +1597,20 @@ fn test_user_credentials_prompt_when_proxy_authentication_is_required() { #[test] fn test_prompt_credentials_when_client_receives_unauthorized_response() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let expected = Authorization::basic("username", "test"); - if let Some(credentials) = request.headers().typed_get::>() { - if credentials == expected { - *response.status_mut() = StatusCode::OK; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let expected = Authorization::basic("username", "test"); + if let Some(credentials) = request.headers().typed_get::>() { + if credentials == expected { + *response.status_mut() = StatusCode::OK; + } else { + *response.status_mut() = StatusCode::UNAUTHORIZED; + } } else { *response.status_mut() = StatusCode::UNAUTHORIZED; } - } else { - *response.status_mut() = StatusCode::UNAUTHORIZED; - } - }; + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1575,18 +1644,20 @@ fn test_prompt_credentials_when_client_receives_unauthorized_response() { #[test] fn test_prompt_credentials_user_cancels_dialog_input() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let expected = Authorization::basic("username", "test"); - if let Some(credentials) = request.headers().typed_get::>() { - if credentials == expected { - *response.status_mut() = StatusCode::OK; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let expected = Authorization::basic("username", "test"); + if let Some(credentials) = request.headers().typed_get::>() { + if credentials == expected { + *response.status_mut() = StatusCode::OK; + } else { + *response.status_mut() = StatusCode::UNAUTHORIZED; + } } else { *response.status_mut() = StatusCode::UNAUTHORIZED; } - } else { - *response.status_mut() = StatusCode::UNAUTHORIZED; - } - }; + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) @@ -1616,18 +1687,20 @@ fn test_prompt_credentials_user_cancels_dialog_input() { #[test] fn test_prompt_credentials_user_input_incorrect_credentials() { - let handler = move |request: HyperRequest, response: &mut HyperResponse| { - let expected = Authorization::basic("username", "test"); - if let Some(credentials) = request.headers().typed_get::>() { - if credentials == expected { - *response.status_mut() = StatusCode::OK; + let handler = + move |request: HyperRequest, + response: &mut HyperResponse>| { + let expected = Authorization::basic("username", "test"); + if let Some(credentials) = request.headers().typed_get::>() { + if credentials == expected { + *response.status_mut() = StatusCode::OK; + } else { + *response.status_mut() = StatusCode::UNAUTHORIZED; + } } else { *response.status_mut() = StatusCode::UNAUTHORIZED; } - } else { - *response.status_mut() = StatusCode::UNAUTHORIZED; - } - }; + }; let (server, url) = make_server(handler); let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) diff --git a/components/net/tests/main.rs b/components/net/tests/main.rs index da62192644a..934f0e2102e 100644 --- a/components/net/tests/main.rs +++ b/components/net/tests/main.rs @@ -30,11 +30,13 @@ use crossbeam_channel::{unbounded, Sender}; use devtools_traits::DevtoolsControlMsg; use embedder_traits::{EmbedderProxy, EmbedderReceiver, EventLoopWaker}; use futures::future::ready; -use futures::StreamExt; -use hyper::server::conn::Http; -use hyper::server::Server as HyperServer; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Request as HyperRequest, Response as HyperResponse}; +use http_body_util::combinators::BoxBody; +use http_body_util::{BodyExt, Empty, Full}; +use hyper::body::{Bytes, Incoming}; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{Request as HyperRequest, Response as HyperResponse}; +use hyper_util::rt::tokio::TokioIo; use net::connector::{create_http_client, create_tls_config}; use net::fetch::cors_cache::CorsCache; use net::fetch::methods::{self, CancellationListener, FetchContext}; @@ -46,30 +48,26 @@ use net_traits::filemanager_thread::FileTokenCheck; use net_traits::request::Request; use net_traits::response::Response; use net_traits::{FetchTaskTarget, ResourceFetchTiming, ResourceTimingType}; -use rustls::{self, Certificate, PrivateKey}; use rustls_pemfile::{certs, pkcs8_private_keys}; +use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; use tokio::net::{TcpListener, TcpStream}; use tokio::runtime::{Builder, Runtime}; use tokio_rustls::{self, TlsAcceptor}; -use tokio_stream::wrappers::TcpListenerStream; -use tokio_test::block_on; -pub static HANDLE: LazyLock> = LazyLock::new(|| { - Mutex::new( - Builder::new_multi_thread() - .enable_io() - .worker_threads(10) - .build() - .unwrap(), - ) +pub static HANDLE: LazyLock = LazyLock::new(|| { + Builder::new_multi_thread() + .enable_io() + .worker_threads(10) + .build() + .unwrap() }); const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow."; struct FetchResponseCollector { - sender: Sender, + sender: Option>, } fn create_embedder_proxy() -> EmbedderProxy { @@ -149,6 +147,8 @@ fn receive_credential_prompt_msgs( } fn create_http_state(fc: Option) -> HttpState { + let _ = rustls::crypto::ring::default_provider().install_default(); + let override_manager = net::connector::CertificateErrorOverrideManager::new(); HttpState { hsts_list: RwLock::new(net::hsts::HstsList::default()), @@ -197,7 +197,7 @@ impl FetchTaskTarget for FetchResponseCollector { fn process_response_chunk(&mut self, _: &Request, _: Vec) {} /// Fired when the response is fully fetched fn process_response_eof(&mut self, _: &Request, response: &Response) { - let _ = self.sender.send(response.clone()); + let _ = self.sender.take().unwrap().send(response.clone()); } } @@ -206,18 +206,22 @@ fn fetch(request: &mut Request, dc: Option>) -> Respo } fn fetch_with_context(request: &mut Request, mut context: &mut FetchContext) -> Response { - let (sender, receiver) = unbounded(); - let mut target = FetchResponseCollector { sender: sender }; - block_on(async move { + let (sender, receiver) = tokio::sync::oneshot::channel(); + let mut target = FetchResponseCollector { + sender: Some(sender), + }; + HANDLE.block_on(async move { methods::fetch(request, &mut target, &mut context).await; - receiver.recv().unwrap() + receiver.await.unwrap() }) } fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Response { - let (sender, receiver) = unbounded(); - let mut target = FetchResponseCollector { sender: sender }; - block_on(async move { + let (sender, receiver) = tokio::sync::oneshot::channel(); + let mut target = FetchResponseCollector { + sender: Some(sender), + }; + HANDLE.block_on(async move { methods::fetch_with_cors_cache( request, cache, @@ -225,13 +229,13 @@ fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Respon &mut new_fetch_context(None, None, None), ) .await; - receiver.recv().unwrap() + receiver.await.unwrap() }) } pub(crate) struct Server { pub close_channel: tokio::sync::oneshot::Sender<()>, - pub certificates: Option>, + pub certificates: Option>>, } impl Server { @@ -242,34 +246,59 @@ impl Server { fn make_server(handler: H) -> (Server, ServoUrl) where - H: Fn(HyperRequest, &mut HyperResponse) + Send + Sync + 'static, + H: Fn(HyperRequest, &mut HyperResponse>) + + Send + + Sync + + 'static, { let handler = Arc::new(handler); + let listener = StdTcpListener::bind("0.0.0.0:0").unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = HANDLE.block_on(async move { TcpListener::from_std(listener).unwrap() }); + let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port()); let url = ServoUrl::parse(&url_string).unwrap(); - let (tx, rx) = tokio::sync::oneshot::channel::<()>(); + + let graceful = hyper_util::server::graceful::GracefulShutdown::new(); + + let (tx, mut rx) = tokio::sync::oneshot::channel::<()>(); let server = async move { - HyperServer::from_tcp(listener) - .unwrap() - .serve(make_service_fn(move |_| { - let handler = handler.clone(); - ready(Ok::<_, Infallible>(service_fn( - move |req: HyperRequest| { - let mut response = HyperResponse::new(Vec::::new().into()); - handler(req, &mut response); - ready(Ok::<_, Infallible>(response)) - }, - ))) - })) - .with_graceful_shutdown(async move { - rx.await.ok(); - }) - .await - .expect("Could not start server"); + loop { + let stream = tokio::select! { + stream = listener.accept() => stream.unwrap().0, + _val = &mut rx => { + let _ = graceful.shutdown(); + break; + } + }; + + let handler = handler.clone(); + + let stream = stream.into_std().unwrap(); + stream + .set_read_timeout(Some(std::time::Duration::new(5, 0))) + .unwrap(); + let stream = TcpStream::from_std(stream).unwrap(); + + let http = http1::Builder::new(); + let conn = http.serve_connection( + TokioIo::new(stream), + service_fn(move |req: HyperRequest| { + let mut response = + HyperResponse::new(Empty::new().map_err(|_| unreachable!()).boxed()); + handler(req, &mut response); + ready(Ok::<_, Infallible>(response)) + }), + ); + let conn = graceful.watch(conn); + HANDLE.spawn(async move { + let _ = conn.await; + }); + } }; - HANDLE.lock().unwrap().spawn(server); + let _ = HANDLE.spawn(server); ( Server { close_channel: tx, @@ -281,37 +310,40 @@ where /// Given a path to a file containing PEM certificates, load and parse them into /// a vector of RusTLS [Certificate]s. -fn load_certificates_from_pem(path: &PathBuf) -> std::io::Result> { +fn load_certificates_from_pem(path: &PathBuf) -> std::io::Result>> { let file = File::open(path)?; let mut reader = BufReader::new(file); - let certs = certs(&mut reader)?; - Ok(certs.into_iter().map(Certificate).collect()) + certs(&mut reader).collect::, _>>() } /// Given a path to a file containing PEM keys, load and parse them into /// a vector of RusTLS [PrivateKey]s. -fn load_private_key_from_file(path: &PathBuf) -> Result> { +fn load_private_key_from_file( + path: &PathBuf, +) -> Result, Box> { let file = File::open(&path)?; let mut reader = BufReader::new(file); - let mut keys = pkcs8_private_keys(&mut reader)?; + let mut keys = pkcs8_private_keys(&mut reader).collect::, _>>()?; match keys.len() { 0 => Err(format!("No PKCS8-encoded private key found in {path:?}").into()), - 1 => Ok(PrivateKey(keys.remove(0))), + 1 => Ok(PrivateKeyDer::try_from(keys.remove(0))?), _ => Err(format!("More than one PKCS8-encoded private key found in {path:?}").into()), } } fn make_ssl_server(handler: H) -> (Server, ServoUrl) where - H: Fn(HyperRequest, &mut HyperResponse) + Send + Sync + 'static, + H: Fn(HyperRequest, &mut HyperResponse>) + + Send + + Sync + + 'static, { let handler = Arc::new(handler); let listener = StdTcpListener::bind("[::0]:0").unwrap(); - let listener = HANDLE - .lock() - .unwrap() - .block_on(async move { TcpListener::from_std(listener).unwrap() }); + listener.set_nonblocking(true).unwrap(); + let listener = HANDLE.block_on(async move { TcpListener::from_std(listener).unwrap() }); + let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port()); let url = ServoUrl::parse(&url_string).unwrap(); @@ -325,27 +357,20 @@ where let key = load_private_key_from_file(&key_path).expect("Invalid key"); let config = rustls::ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_single_cert(certificates.clone(), key) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err)) .expect("Could not create rustls ServerConfig"); let acceptor = TlsAcceptor::from(Arc::new(config)); - let mut listener = TcpListenerStream::new(listener); let (tx, mut rx) = tokio::sync::oneshot::channel::<()>(); let server = async move { loop { let stream = tokio::select! { - stream = listener.next() => stream, + stream = listener.accept() => stream.unwrap().0, _ = &mut rx => break }; - let stream = match stream { - Some(stream) => stream.expect("Could not accept stream: "), - _ => break, - }; - let stream = stream.into_std().unwrap(); stream .set_read_timeout(Some(std::time::Duration::new(5, 0))) @@ -363,11 +388,12 @@ where }, }; - let _ = Http::new() + let _ = http1::Builder::new() .serve_connection( - stream, - service_fn(move |req: HyperRequest| { - let mut response = HyperResponse::new(Body::empty()); + TokioIo::new(stream), + service_fn(move |req: HyperRequest| { + let mut response = + HyperResponse::new(Empty::new().map_err(|_| unreachable!()).boxed()); handler(req, &mut response); ready(Ok::<_, Infallible>(response)) }), @@ -376,7 +402,7 @@ where } }; - HANDLE.lock().unwrap().spawn(server); + HANDLE.spawn(server); ( Server { @@ -386,3 +412,9 @@ where url, ) } + +pub fn make_body(bytes: Vec) -> BoxBody { + Full::new(Bytes::from(bytes)) + .map_err(|_| unreachable!()) + .boxed() +} diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml index 87336b44670..18b29376014 100644 --- a/components/shared/net/Cargo.toml +++ b/components/shared/net/Cargo.toml @@ -23,6 +23,7 @@ headers = { workspace = true } http = { workspace = true } hyper = { workspace = true } hyper_serde = { workspace = true } +hyper-util = { workspace = true, features = ["client-legacy"] } ipc-channel = { workspace = true } log = { workspace = true } malloc_size_of = { workspace = true } @@ -31,7 +32,7 @@ mime = { workspace = true } num-traits = { workspace = true } percent-encoding = { workspace = true } pixels = { path = "../../pixels" } -rustls = { workspace = true } +rustls-pki-types = { workspace = true } serde = { workspace = true } servo_arc = { workspace = true } servo_rand = { path = "../../rand" } diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index be1f6378ced..5b3c9d1f0e8 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -15,8 +15,8 @@ use cookie::Cookie; use crossbeam_channel::{unbounded, Receiver, Sender}; use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use http::{Error as HttpError, HeaderMap, StatusCode}; -use hyper::Error as HyperError; use hyper_serde::Serde; +use hyper_util::client::legacy::Error as HyperError; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error as IpcError; @@ -24,7 +24,7 @@ use malloc_size_of::malloc_size_of_is_0; use malloc_size_of_derive::MallocSizeOf; use mime::Mime; use request::RequestId; -use rustls::Certificate; +use rustls_pki_types::CertificateDer; use serde::{Deserialize, Serialize}; use servo_rand::RngCore; use servo_url::{ImmutableOrigin, ServoUrl}; @@ -882,10 +882,10 @@ pub enum NetworkError { } impl NetworkError { - pub fn from_hyper_error(error: &HyperError, certificate: Option) -> Self { + pub fn from_hyper_error(error: &HyperError, certificate: Option) -> Self { let error_string = error.to_string(); match certificate { - Some(certificate) => NetworkError::SslValidation(error_string, certificate.0), + Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()), _ => NetworkError::Internal(error_string), } } diff --git a/components/webdriver_server/Cargo.toml b/components/webdriver_server/Cargo.toml index 543f1978ad2..7a1416e89f7 100644 --- a/components/webdriver_server/Cargo.toml +++ b/components/webdriver_server/Cargo.toml @@ -18,7 +18,7 @@ compositing_traits = { workspace = true } cookie = { workspace = true } crossbeam-channel = { workspace = true } euclid = { workspace = true } -http = { workspace = true } +http = { version = "0.2" } image = { workspace = true } ipc-channel = { workspace = true } keyboard-types = { workspace = true } diff --git a/deny.toml b/deny.toml index 08093cfa250..a82459c407a 100644 --- a/deny.toml +++ b/deny.toml @@ -155,6 +155,14 @@ skip = [ # wgpu depends on thiserror 2, while rest is still on 1 "thiserror", "thiserror-impl", + + # duplicated by webdriver + "h2", + "headers", + "headers-core", + "http", + "http-body", + "hyper", ] # github.com organizations to allow git sources for diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index d04e2f4098c..f31a585c345 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -65,6 +65,7 @@ getopts = { workspace = true } hitrace = { workspace = true, optional = true } mime_guess = { workspace = true } url = { workspace = true } +rustls = { version = "0.23", default-features = false, features = ["ring"] } tokio = { workspace = true } tracing = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true, features = ["env-filter"] } diff --git a/ports/servoshell/desktop/cli.rs b/ports/servoshell/desktop/cli.rs index 3110fdaba60..ff9315a4bb9 100644 --- a/ports/servoshell/desktop/cli.rs +++ b/ports/servoshell/desktop/cli.rs @@ -16,6 +16,7 @@ use crate::panic_hook; pub fn main() { crate::crash_handler::install(); crate::init_tracing(); + crate::init_crypto(); crate::resources::init(); // Parse the command line options and store them globally diff --git a/ports/servoshell/egl/android/simpleservo.rs b/ports/servoshell/egl/android/simpleservo.rs index b90530ec732..4d249e844d1 100644 --- a/ports/servoshell/egl/android/simpleservo.rs +++ b/ports/servoshell/egl/android/simpleservo.rs @@ -61,6 +61,7 @@ pub fn init( callbacks: Box, ) -> Result<(), &'static str> { crate::init_tracing(); + crate::init_crypto(); resources::set(Box::new(ResourceReaderInstance::new())); if let Some(prefs) = init_opts.prefs { diff --git a/ports/servoshell/egl/ohos/simpleservo.rs b/ports/servoshell/egl/ohos/simpleservo.rs index 8aff4d41a26..54bfccc8f3c 100644 --- a/ports/servoshell/egl/ohos/simpleservo.rs +++ b/ports/servoshell/egl/ohos/simpleservo.rs @@ -43,6 +43,7 @@ pub fn init( ) -> Result { info!("Entered simpleservo init function"); crate::init_tracing(); + crate::init_crypto(); let resource_dir = PathBuf::from(&options.resource_dir).join("servo"); resources::set(Box::new(ResourceReaderInstance::new(resource_dir))); let mut args = vec!["servoshell".to_string()]; diff --git a/ports/servoshell/lib.rs b/ports/servoshell/lib.rs index c55d6926cb5..607540b7637 100644 --- a/ports/servoshell/lib.rs +++ b/ports/servoshell/lib.rs @@ -38,6 +38,12 @@ pub fn main() { desktop::cli::main() } +pub fn init_crypto() { + rustls::crypto::ring::default_provider() + .install_default() + .expect("Error initializing crypto provider"); +} + pub fn init_tracing() { #[cfg(feature = "tracing")] {