|
| 1 | +From 59c8c1ef7eaed1168877fbc53d437b947b9bf06a Mon Sep 17 00:00:00 2001 |
| 2 | +From: Kuldeep-Dilliwar <kuldeepdilliwar@gmail.com> |
| 3 | +Date: Fri, 23 Jan 2026 11:54:43 +0530 |
| 4 | +Subject: [PATCH 06/10] Update client.rs to work on android |
| 5 | + |
| 6 | +Universal Compatibility: |
| 7 | +Termux Users: It detects /data/data/com.termux/..., enters Branch A, and uses use_preconfigured_tls. This strictly bypasses the Android system calls, fixing the crash you experienced. |
| 8 | +Corporate Users (Termux): It detects the environment variable inside Branch A and merges it with the Termux certs. |
| 9 | +Real Android Apps: It detects the missing Termux file, skips Branch A, and enters Branch B (if an env var exists) or Branch C (default). This uses add_root_certificate, which safely adds to the Android System Keychain instead of replacing it. |
| 10 | +No Extra Dependencies: |
| 11 | +It uses your custom simple_base64_decode helper (guarded by #[cfg(target_os = "android")] so it doesn't break Linux/Windows builds). |
| 12 | +It avoids adding base64 or pem crates to Cargo.toml. |
| 13 | +Debuggability: |
| 14 | +You kept the eprintln! inside the error handler (guarded for Android). If something goes wrong in the future, you will see the exact TLS error immediately. |
| 15 | +--- |
| 16 | + .../crates/turbo-tasks-fetch/src/client.rs | 118 ++++++++++++++++++ |
| 17 | + 1 file changed, 118 insertions(+) |
| 18 | + |
| 19 | +diff --git a/turbopack/crates/turbo-tasks-fetch/src/client.rs b/turbopack/crates/turbo-tasks-fetch/src/client.rs |
| 20 | +index 27011018e4..7063c9e917 100644 |
| 21 | +--- a/turbopack/crates/turbo-tasks-fetch/src/client.rs |
| 22 | ++++ b/turbopack/crates/turbo-tasks-fetch/src/client.rs |
| 23 | +@@ -74,6 +74,84 @@ impl FetchClientConfig { |
| 24 | + }, |
| 25 | + )) |
| 26 | + } |
| 27 | ++ #[cfg(target_os = "android")] |
| 28 | ++ { |
| 29 | ++ let termux_path = std::path::Path::new("/data/data/com.termux/files/usr/etc/tls/cert.pem"); |
| 30 | ++ let env_var = std::env::var("TURBO_SSL_CERT_FILE"); |
| 31 | ++ |
| 32 | ++ // --- BRANCH A: TERMUX MODE (Fix the Crash) --- |
| 33 | ++ if termux_path.exists() { |
| 34 | ++ println!("[Turbopack] Termux environment detected."); |
| 35 | ++ |
| 36 | ++ let mut root_store = rustls::RootCertStore::empty(); |
| 37 | ++ let mut paths_to_load = vec![termux_path.to_path_buf()]; |
| 38 | ++ |
| 39 | ++ // If user also provided a custom cert, add it to our manual list |
| 40 | ++ if let Ok(p) = env_var { |
| 41 | ++ paths_to_load.push(std::path::PathBuf::from(p)); |
| 42 | ++ } |
| 43 | ++ |
| 44 | ++ for cert_path in paths_to_load { |
| 45 | ++ if let Ok(pem_bytes) = std::fs::read(&cert_path) { |
| 46 | ++ let content = String::from_utf8_lossy(&pem_bytes); |
| 47 | ++ // ... (Reuse the parsing logic from before) ... |
| 48 | ++ let header = "-----BEGIN CERTIFICATE-----"; |
| 49 | ++ let footer = "-----END CERTIFICATE-----"; |
| 50 | ++ let mut current_idx = 0; |
| 51 | ++ while let Some(start_offset) = content[current_idx..].find(header) { |
| 52 | ++ let start = current_idx + start_offset + header.len(); |
| 53 | ++ if let Some(end_offset) = content[start..].find(footer) { |
| 54 | ++ let end = start + end_offset; |
| 55 | ++ let base64_str = content[start..end].lines().map(|l| l.trim()).collect::<String>(); |
| 56 | ++ if let Ok(der_bytes) = simple_base64_decode(&base64_str) { |
| 57 | ++ let cert = rustls::pki_types::CertificateDer::from(der_bytes); |
| 58 | ++ let _ = root_store.add(cert); |
| 59 | ++ } |
| 60 | ++ current_idx = end + footer.len(); |
| 61 | ++ } else { break; } |
| 62 | ++ } |
| 63 | ++ } |
| 64 | ++ } |
| 65 | ++ |
| 66 | ++ // REPLACEMENT: We bypass the system entirely because the system crashes in Termux. |
| 67 | ++ let tls_config = rustls::ClientConfig::builder() |
| 68 | ++ .with_root_certificates(root_store) |
| 69 | ++ .with_no_client_auth(); |
| 70 | ++ builder = builder.use_preconfigured_tls(tls_config); |
| 71 | ++ } |
| 72 | ++ // --- BRANCH B: REAL APP MODE (Add, don't Replace) --- |
| 73 | ++ else if let Ok(p) = env_var { |
| 74 | ++ // We are NOT in Termux, but we have a custom cert. |
| 75 | ++ // We want to KEEP the Android System certs and ADD this one. |
| 76 | ++ println!("[Turbopack] Custom cert env var detected (Android App mode)."); |
| 77 | ++ |
| 78 | ++ if let Ok(pem_bytes) = std::fs::read(&p) { |
| 79 | ++ let content = String::from_utf8_lossy(&pem_bytes); |
| 80 | ++ // ... (Reuse the same parsing logic) ... |
| 81 | ++ let header = "-----BEGIN CERTIFICATE-----"; |
| 82 | ++ let footer = "-----END CERTIFICATE-----"; |
| 83 | ++ let mut current_idx = 0; |
| 84 | ++ while let Some(start_offset) = content[current_idx..].find(header) { |
| 85 | ++ let start = current_idx + start_offset + header.len(); |
| 86 | ++ if let Some(end_offset) = content[start..].find(footer) { |
| 87 | ++ let end = start + end_offset; |
| 88 | ++ let base64_str = content[start..end].lines().map(|l| l.trim()).collect::<String>(); |
| 89 | ++ |
| 90 | ++ if let Ok(der_bytes) = simple_base64_decode(&base64_str) { |
| 91 | ++ // ADDITION: We use reqwest's API to add to the existing system list. |
| 92 | ++ if let Ok(cert) = reqwest::Certificate::from_der(&der_bytes) { |
| 93 | ++ builder = builder.add_root_certificate(cert); |
| 94 | ++ } |
| 95 | ++ } |
| 96 | ++ current_idx = end + footer.len(); |
| 97 | ++ } else { break; } |
| 98 | ++ } |
| 99 | ++ } |
| 100 | ++ } |
| 101 | ++ // --- BRANCH C: STANDARD MODE --- |
| 102 | ++ // No Termux, No Env Var. Do nothing. |
| 103 | ++ // reqwest will automatically use Android System Certs via JNI. |
| 104 | ++ } |
| 105 | + builder.build() |
| 106 | + } |
| 107 | + } |
| 108 | +@@ -120,6 +198,19 @@ impl FetchClientConfig { |
| 109 | + match response_result { |
| 110 | + Ok(resp) => Ok(Vc::cell(Ok(resp.resolved_cell()))), |
| 111 | + Err(err) => { |
| 112 | ++ |
| 113 | ++ #[cfg(target_os = "android")] |
| 114 | ++ { |
| 115 | ++ // --- DEBUGGING START --- |
| 116 | ++ eprintln!("\n[Turbopack] NETWORK ERROR DEBUG:"); |
| 117 | ++ eprintln!("URL: {}", url_ref); |
| 118 | ++ eprintln!("Error: {:?}", err); // Prints the high level error |
| 119 | ++ if let Some(source) = std::error::Error::source(&err) { |
| 120 | ++ eprintln!("Caused by: {:?}", source); // Prints the deep TLS error |
| 121 | ++ } |
| 122 | ++ // --- DEBUGGING END --- |
| 123 | ++ } |
| 124 | ++ |
| 125 | + // the client failed to construct or the HTTP request failed |
| 126 | + mark_session_dependent(); |
| 127 | + Ok(Vc::cell(Err( |
| 128 | +@@ -130,6 +221,33 @@ impl FetchClientConfig { |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | ++ |
| 133 | ++#[cfg(target_os = "android")] |
| 134 | ++fn simple_base64_decode(input: &str) -> Result<Vec<u8>, ()> { |
| 135 | ++ let mut buffer = Vec::new(); |
| 136 | ++ let mut bits: u32 = 0; |
| 137 | ++ let mut bit_count = 0; |
| 138 | ++ for byte in input.bytes() { |
| 139 | ++ let val = match byte { |
| 140 | ++ b'A'..=b'Z' => byte - b'A', |
| 141 | ++ b'a'..=b'z' => byte - b'a' + 26, |
| 142 | ++ b'0'..=b'9' => byte - b'0' + 52, |
| 143 | ++ b'+' => 62, |
| 144 | ++ b'/' => 63, |
| 145 | ++ b'=' => continue, |
| 146 | ++ _ => continue, |
| 147 | ++ }; |
| 148 | ++ bits = (bits << 6) | (val as u32); |
| 149 | ++ bit_count += 6; |
| 150 | ++ if bit_count >= 8 { |
| 151 | ++ bit_count -= 8; |
| 152 | ++ buffer.push((bits >> bit_count) as u8); |
| 153 | ++ bits &= (1 << bit_count) - 1; |
| 154 | ++ } |
| 155 | ++ } |
| 156 | ++ Ok(buffer) |
| 157 | ++} |
| 158 | ++ |
| 159 | + #[doc(hidden)] |
| 160 | + pub fn __test_only_reqwest_client_cache_clear() { |
| 161 | + CLIENT_CACHE.clear() |
| 162 | +-- |
| 163 | +2.53.0 |
| 164 | + |
0 commit comments