conditional cronet

This commit is contained in:
mertalev
2026-01-17 02:31:35 -05:00
parent d83e74dc88
commit e739aa1667
7 changed files with 83 additions and 21 deletions

View File

@@ -109,6 +109,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version" implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "com.google.net.cronet:cronet-okhttp:0.1.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "androidx.work:work-runtime-ktx:$work_version" implementation "androidx.work:work-runtime-ktx:$work_version"
implementation "androidx.concurrent:concurrent-futures:$concurrent_version" implementation "androidx.concurrent:concurrent-futures:$concurrent_version"

View File

@@ -19,6 +19,9 @@ object SSLConfig {
var trustManager: X509TrustManager? = null var trustManager: X509TrustManager? = null
private set private set
var requiresCustomSSL: Boolean = false
private set
private val listeners = mutableListOf<() -> Unit>() private val listeners = mutableListOf<() -> Unit>()
private var configHash: Int = 0 private var configHash: Int = 0
@@ -34,7 +37,8 @@ object SSLConfig {
clientCertHash: Int clientCertHash: Int
) { ) {
val newHash = computeHash(allowSelfSigned, serverHost, clientCertHash) val newHash = computeHash(allowSelfSigned, serverHost, clientCertHash)
if (newHash == configHash && sslSocketFactory != null) { val newRequiresCustomSSL = allowSelfSigned || keyManagers != null
if (newHash == configHash && sslSocketFactory != null && requiresCustomSSL == newRequiresCustomSSL) {
return // Config unchanged, skip return // Config unchanged, skip
} }
@@ -43,6 +47,7 @@ object SSLConfig {
sslSocketFactory = sslContext.socketFactory sslSocketFactory = sslContext.socketFactory
trustManager = trustManagers?.filterIsInstance<X509TrustManager>()?.firstOrNull() trustManager = trustManagers?.filterIsInstance<X509TrustManager>()?.firstOrNull()
?: getDefaultTrustManager() ?: getDefaultTrustManager()
requiresCustomSSL = newRequiresCustomSSL
configHash = newHash configHash = newHash
notifyListeners() notifyListeners()
} }

View File

@@ -8,6 +8,7 @@ import android.graphics.ImageDecoder
import android.os.Build import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import app.alextran.immich.core.SSLConfig import app.alextran.immich.core.SSLConfig
import com.google.net.cronet.okhttptransport.CronetCallFactory
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@@ -16,6 +17,7 @@ import okhttp3.Response
import okhttp3.Cache import okhttp3.Cache
import okhttp3.ConnectionPool import okhttp3.ConnectionPool
import okhttp3.Dispatcher import okhttp3.Dispatcher
import org.chromium.net.CronetEngine
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
@@ -33,6 +35,7 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
private val lockedBitmaps = ConcurrentHashMap<Long, Bitmap>() private val lockedBitmaps = ConcurrentHashMap<Long, Bitmap>()
init { init {
appContext = context.applicationContext
cacheDir = context.cacheDir cacheDir = context.cacheDir
client = buildClient() client = buildClient()
} }
@@ -47,8 +50,10 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
private val decodePool = private val decodePool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2 + 1) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2 + 1)
private var appContext: Context? = null
private var cacheDir: File? = null private var cacheDir: File? = null
private var client: OkHttpClient? = null private var client: Call.Factory? = null
private var cronetEngine: CronetEngine? = null
init { init {
System.loadLibrary("native_buffer") System.loadLibrary("native_buffer")
@@ -62,15 +67,42 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
external fun unlockBitmapPixels(bitmap: Bitmap) external fun unlockBitmapPixels(bitmap: Bitmap)
private fun invalidateClient() { private fun invalidateClient() {
client?.let { (client as? OkHttpClient)?.let {
it.dispatcher.cancelAll() it.dispatcher.cancelAll()
it.connectionPool.evictAll() it.connectionPool.evictAll()
it.cache?.close() it.cache?.close()
} }
cronetEngine?.shutdown()
cronetEngine = null
client = buildClient() client = buildClient()
} }
private fun buildClient(): OkHttpClient { private fun buildClient(): Call.Factory {
val dir = cacheDir ?: throw IllegalStateException("Cache dir not set")
return if (SSLConfig.requiresCustomSSL) {
buildOkHttpClient(dir)
} else {
buildCronetClient(dir)
}
}
private fun buildCronetClient(cacheDir: File): Call.Factory {
val ctx = appContext ?: throw IllegalStateException("Context not set")
val storageDir = File(cacheDir, "cronet").apply { mkdirs() }
val engine = CronetEngine.Builder(ctx)
.enableHttp2(true)
.enableQuic(true)
.enableBrotli(true)
.setStoragePath(storageDir.absolutePath)
.build()
.also { cronetEngine = it }
return CronetCallFactory.newBuilder(engine).build()
}
private fun buildOkHttpClient(cacheDir: File): OkHttpClient {
val dir = File(cacheDir, "okhttp")
val connectionPool = ConnectionPool( val connectionPool = ConnectionPool(
maxIdleConnections = KEEP_ALIVE_CONNECTIONS, maxIdleConnections = KEEP_ALIVE_CONNECTIONS,
keepAliveDuration = KEEP_ALIVE_DURATION_MINUTES, keepAliveDuration = KEEP_ALIVE_DURATION_MINUTES,
@@ -81,10 +113,7 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
.dispatcher(Dispatcher().apply { maxRequestsPerHost = MAX_REQUESTS_PER_HOST }) .dispatcher(Dispatcher().apply { maxRequestsPerHost = MAX_REQUESTS_PER_HOST })
.connectionPool(connectionPool) .connectionPool(connectionPool)
cacheDir?.let { dir -> builder.cache(Cache((File(dir, "thumbnails")), CACHE_SIZE_BYTES))
val cacheSubdir = File(dir, "thumbnails")
builder.cache(Cache(cacheSubdir, CACHE_SIZE_BYTES))
}
val sslSocketFactory = SSLConfig.sslSocketFactory val sslSocketFactory = SSLConfig.sslSocketFactory
val trustManager = SSLConfig.trustManager val trustManager = SSLConfig.trustManager

View File

@@ -110,6 +110,9 @@ class RemoteImageApiDelegate: NSObject, URLSessionDataDelegate {
defer { remove(requestId: requestId) } defer { remove(requestId: requestId) }
if let error = error { if let error = error {
if request.isCancelled || (error as NSError).code == NSURLErrorCancelled {
return request.completion(Self.cancelledResult)
}
return request.completion(.failure(error)) return request.completion(.failure(error))
} }

View File

@@ -34,10 +34,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.17.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@@ -297,6 +297,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.2"
code_assets:
dependency: transitive
description:
name: code_assets
sha256: ae0db647e668cbb295a3527f0938e4039e004c80099dce2f964102373f5ce0b5
url: "https://pub.dev"
source: hosted
version: "0.19.10"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@@ -341,10 +349,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: cronet_http name: cronet_http
sha256: "1b99ad5ae81aa9d2f12900e5f17d3681f3828629bb7f7fe7ad88076a34209840" sha256: "1fff7f26ac0c4cda97fe2a9aa082494baee4775f167c27ba45f6c8e88571e3ab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.7.0"
crop_image: crop_image:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -381,10 +389,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_http name: cupertino_http
sha256: "72187f715837290a63479a5b0ae709f4fedad0ed6bd0441c275eceaa02d5abae" sha256: "82cbec60c90bf785a047a9525688b6dacac444e177e1d5a5876963d3c50369e8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.4.0"
custom_lint: custom_lint:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -904,6 +912,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.1" version: "0.8.1"
hooks:
dependency: transitive
description:
name: hooks
sha256: "5410b9f4f6c9f01e8ff0eb81c9801ea13a3c3d39f8f0b1613cda08e27eab3c18"
url: "https://pub.dev"
source: hosted
version: "0.20.5"
hooks_riverpod: hooks_riverpod:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1105,10 +1121,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: jni name: jni
sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 sha256: "8706a77e94c76fe9ec9315e18949cc9479cc03af97085ca9c1077b61323ea12d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.14.2" version: "0.15.2"
js: js:
dependency: transitive dependency: transitive
description: description:
@@ -1269,6 +1285,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
native_toolchain_c:
dependency: transitive
description:
name: native_toolchain_c
sha256: f8872ea6c7a50ce08db9ae280ca2b8efdd973157ce462826c82f3c3051d154ce
url: "https://pub.dev"
source: hosted
version: "0.17.2"
native_video_player: native_video_player:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1306,10 +1330,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: objective_c name: objective_c
sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" sha256: "55eb67ede1002d9771b3f9264d2c9d30bc364f0267bc1c6cc0883280d5f0c7cb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.1.0" version: "9.2.2"
octo_image: octo_image:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -2235,5 +2259,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.9.0 <4.0.0" dart: ">=3.10.0 <4.0.0"
flutter: ">=3.35.7" flutter: ">=3.35.7"

View File

@@ -86,8 +86,8 @@ dependencies:
uuid: ^4.5.1 uuid: ^4.5.1
wakelock_plus: ^1.3.0 wakelock_plus: ^1.3.0
worker_manager: ^7.2.7 worker_manager: ^7.2.7
cronet_http: ^1.5.0 cronet_http: ^1.7.0
cupertino_http: ^2.3.0 cupertino_http: ^2.4.0
dev_dependencies: dev_dependencies:
auto_route_generator: ^9.0.0 auto_route_generator: ^9.0.0