fix(tor): wire Coil image loading through Tor via custom Fetcher.Factory
Port Android's OkHttpFactory pattern to desktop — creates a custom Fetcher.Factory<Uri> that calls DesktopHttpClient.currentClient() per image request, routing all image loads through the SOCKS proxy when Tor is active. Avoids the OkHttpNetworkFetcher.factory() import issue by directly constructing NetworkFetcher with the Tor-aware Call.Factory, matching the proven Android implementation. This closes the last network leak — ALL egress paths now route through Tor when enabled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+44
-2
@@ -23,11 +23,22 @@ package com.vitorpamplona.amethyst.desktop.service.images
|
|||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.PlatformContext
|
import coil3.PlatformContext
|
||||||
import coil3.SingletonImageLoader
|
import coil3.SingletonImageLoader
|
||||||
|
import coil3.Uri
|
||||||
import coil3.annotation.DelicateCoilApi
|
import coil3.annotation.DelicateCoilApi
|
||||||
|
import coil3.annotation.ExperimentalCoilApi
|
||||||
import coil3.disk.DiskCache
|
import coil3.disk.DiskCache
|
||||||
|
import coil3.fetch.Fetcher
|
||||||
import coil3.memory.MemoryCache
|
import coil3.memory.MemoryCache
|
||||||
|
import coil3.network.CacheStrategy
|
||||||
|
import coil3.network.ConcurrentRequestStrategy
|
||||||
|
import coil3.network.ConnectivityChecker
|
||||||
|
import coil3.network.NetworkFetcher
|
||||||
|
import coil3.network.okhttp.asNetworkClient
|
||||||
|
import coil3.request.Options
|
||||||
import coil3.size.Precision
|
import coil3.size.Precision
|
||||||
import coil3.svg.SvgDecoder
|
import coil3.svg.SvgDecoder
|
||||||
|
import com.vitorpamplona.amethyst.desktop.network.DesktopHttpClient
|
||||||
|
import okhttp3.Call
|
||||||
import okio.Path.Companion.toOkioPath
|
import okio.Path.Companion.toOkioPath
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -44,8 +55,7 @@ object DesktopImageLoaderSetup {
|
|||||||
.diskCache { newDiskCache() }
|
.diskCache { newDiskCache() }
|
||||||
.precision(Precision.INEXACT)
|
.precision(Precision.INEXACT)
|
||||||
.components {
|
.components {
|
||||||
// TODO: Wire Coil through Tor — OkHttpNetworkFetcher.factory() not resolving in JVM module
|
add(TorAwareOkHttpFactory { DesktopHttpClient.currentClient() })
|
||||||
// add(coil3.network.okhttp.OkHttpNetworkFetcher.factory { DesktopHttpClient.currentClient() })
|
|
||||||
add(SvgDecoder.Factory())
|
add(SvgDecoder.Factory())
|
||||||
add(SkiaGifDecoder.Factory())
|
add(SkiaGifDecoder.Factory())
|
||||||
add(DesktopBase64Fetcher.Factory)
|
add(DesktopBase64Fetcher.Factory)
|
||||||
@@ -94,3 +104,35 @@ object DesktopImageLoaderSetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Coil Fetcher.Factory that routes all image requests through the Tor-aware client.
|
||||||
|
* Pattern ported from Android's OkHttpFactory in ImageLoaderSetup.kt.
|
||||||
|
*
|
||||||
|
* Each image request calls [clientProvider] to get the current OkHttpClient,
|
||||||
|
* which returns the SOCKS proxy client when Tor is active or the direct client when off.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
|
private class TorAwareOkHttpFactory(
|
||||||
|
val clientProvider: () -> Call.Factory,
|
||||||
|
) : Fetcher.Factory<Uri> {
|
||||||
|
private val cacheStrategyLazy = lazy { CacheStrategy.DEFAULT }
|
||||||
|
|
||||||
|
override fun create(
|
||||||
|
data: Uri,
|
||||||
|
options: Options,
|
||||||
|
imageLoader: ImageLoader,
|
||||||
|
): Fetcher? {
|
||||||
|
if (data.scheme != "http" && data.scheme != "https") return null
|
||||||
|
|
||||||
|
return NetworkFetcher(
|
||||||
|
url = data.toString(),
|
||||||
|
options = options,
|
||||||
|
networkClient = lazy { clientProvider().asNetworkClient() },
|
||||||
|
diskCache = lazy { imageLoader.diskCache },
|
||||||
|
cacheStrategy = cacheStrategyLazy,
|
||||||
|
connectivityChecker = lazy { ConnectivityChecker(options.context) },
|
||||||
|
concurrentRequestStrategy = lazy { ConcurrentRequestStrategy.UNCOORDINATED },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user