Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Fix HTTP client panic on Android

reqwest's default rustls-platform-verifier panics on Android because
it needs the JVM to reach the system trust manager. On Android, load
the system trust store from the filesystem via rustls-native-certs and
configure those roots explicitly.

+66 -24
+4
CHANGELOG.md
··· 437 437 - The formatter now properly indents multiline trailing comments inside of 438 438 multiline lists and tuples. 439 439 ([0xda157](https://github.com/0xda157)) 440 + 441 + - Fixed a bug where the compiler would panic on the first HTTPS request on 442 + Android. 443 + ([John Downey](https://github.com/jtdowney))
+3 -2
Cargo.lock
··· 1189 1189 "regex", 1190 1190 "reqwest", 1191 1191 "rpassword", 1192 + "rustls-native-certs", 1192 1193 "same-file", 1193 1194 "serde", 1194 1195 "serde_json", ··· 2903 2904 2904 2905 [[package]] 2905 2906 name = "rustls-native-certs" 2906 - version = "0.8.3" 2907 + version = "0.8.4" 2907 2908 source = "registry+https://github.com/rust-lang/crates.io-index" 2908 - checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" 2909 + checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" 2909 2910 dependencies = [ 2910 2911 "openssl-probe", 2911 2912 "rustls-pki-types",
+2
compiler-cli/Cargo.toml
··· 26 26 tracing-subscriber = { version = "0.3.23", features = ["fmt", "env-filter"] } 27 27 # HTTP client 28 28 reqwest = { version = "^0.13", default-features = false, features = ["rustls"] } 29 + # System trust store loading 30 + rustls-native-certs = "0.8" 29 31 # Checksums 30 32 sha2 = "0" 31 33 # Getting hostname
+57 -22
compiler-cli/src/http.rs
··· 60 60 return Ok(client); 61 61 } 62 62 63 - let certificate_path = match std::env::var("GLEAM_CACERTS_PATH") { 64 - Ok(path) => { 65 - tracing::trace!("Using GLEAM_CACERTS_PATH environment variable"); 66 - path 63 + let client = build_client()?; 64 + Ok(REQWEST_CLIENT.get_or_init(|| client)) 65 + } 66 + 67 + /// Build the HTTP client with an appropriate certificate trust store. 68 + /// 69 + /// On most platforms reqwest's default `rustls` configuration uses 70 + /// `rustls-platform-verifier`, which delegates to the operating system's trust 71 + /// manager. This respects OS-installed and enterprise root certificates along 72 + /// with the system's own trust decisions. 73 + /// 74 + /// On Android that verifier reaches the trust manager by calling into the JVM, 75 + /// and without it panics on the first request (see issue #5823). There we read 76 + /// the same system trust store from the filesystem and configure those roots 77 + /// explicitly instead. 78 + fn build_client() -> Result<Client, Error> { 79 + if cfg!(target_os = "android") { 80 + let mut certificates = system_certificates(); 81 + if let Ok(certificate_path) = std::env::var("GLEAM_CACERTS_PATH") { 82 + let certificate = read_certificate(&certificate_path)?; 83 + certificates.push(certificate); 67 84 } 68 - Err(_) => { 69 - return Ok(REQWEST_CLIENT.get_or_init(|| { 70 - Client::builder() 71 - .build() 72 - .expect("Failed to create reqwest client") 73 - })); 74 - } 75 - }; 85 + 86 + Client::builder() 87 + .tls_certs_only(certificates) 88 + .build() 89 + .map_err(Error::http) 90 + } else { 91 + let Ok(certificate_path) = std::env::var("GLEAM_CACERTS_PATH") else { 92 + return Client::builder().build().map_err(Error::http); 93 + }; 94 + 95 + tracing::trace!("Using GLEAM_CACERTS_PATH environment variable"); 96 + let certificate = read_certificate(&certificate_path)?; 97 + Client::builder() 98 + .add_root_certificate(certificate) 99 + .build() 100 + .map_err(Error::http) 101 + } 102 + } 76 103 77 - let certificate_bytes = fs::read_bytes(&certificate_path)?; 78 - let certificate = Certificate::from_pem(&certificate_bytes).map_err(|error| Error::FileIo { 104 + fn read_certificate(path: &str) -> Result<Certificate, Error> { 105 + let bytes = fs::read_bytes(path)?; 106 + Certificate::from_pem(&bytes).map_err(|error| Error::FileIo { 79 107 kind: FileKind::File, 80 108 action: FileIoAction::Parse, 81 - path: Utf8PathBuf::from(&certificate_path), 109 + path: Utf8PathBuf::from(path), 82 110 err: Some(error.to_string()), 83 - })?; 111 + }) 112 + } 84 113 85 - Ok(REQWEST_CLIENT.get_or_init(|| { 86 - Client::builder() 87 - .add_root_certificate(certificate) 88 - .build() 89 - .expect("Failed to create reqwest client") 90 - })) 114 + /// Load the system trust store (only used on Android) 115 + fn system_certificates() -> Vec<Certificate> { 116 + let loaded = rustls_native_certs::load_native_certs(); 117 + for error in &loaded.errors { 118 + tracing::warn!("Failed to load a system certificate: {error}"); 119 + } 120 + 121 + loaded 122 + .certs 123 + .iter() 124 + .filter_map(|certificate| Certificate::from_der(certificate.as_ref()).ok()) 125 + .collect() 91 126 }