git.iko.soy/opencode-android
on e66c7ed help sign in
35 lines 1.9 kB history raw
1package soy.iko.opencode.data.network
3import soy.iko.opencode.data.model.ServerProfile
5/**
6 * Shared helpers for resolving a [ServerProfile]'s certificate-pin field and the
7 * http→https upgrade it implies. The logic is used in three places that must agree:
8 * [HttpClientFactory] (the REST/SSE channel), [soy.iko.opencode.OpencodeApp]'s
9 * PinnedImageCallFactory (the image-loading channel), and [soy.iko.opencode.ui.components.RemoteImage]
10 * (resolving image base URLs). Previously each call site carried its own private copy
11 * with a "must be kept in sync" comment; the helpers here are the single source of truth.
12 */
13internal object CertPinUtil {
15 /** Split a certificate-pin field into individual OkHttp pins. Accepts whitespace- or
16 * comma-separated "sha256/<base64>" entries; blank entries are dropped. */
17 fun parseCertPins(raw: String?): List<String> =
18 raw?.split(Regex("[\\s,]+"))?.map { it.trim() }?.filter { it.isNotEmpty() } ?: emptyList()
20 /** The base URL after applying [ServerProfile.requireHttps]: a cleartext http:// URL is
21 * upgraded to https:// so the "Require HTTPS" choice is enforced at the transport layer.
22 * A configured certificate pin also forces TLS: a pin is meaningless over cleartext and
23 * would otherwise be silently dropped, so we honor the opt-in security control by upgrading
24 * rather than connecting unpinned in the clear. Relies on [HttpClientFactory.normalizeBaseUrl]
25 * for scheme/trailing-slash normalization. */
26 fun effectiveBaseUrl(profile: ServerProfile): String {
27 val normalized = HttpClientFactory.normalizeBaseUrl(profile.baseUrl)
28 val forceHttps = profile.requireHttps || parseCertPins(profile.certPin).isNotEmpty()
29 return if (forceHttps && normalized.lowercase().startsWith("http://")) {
30 "https://" + normalized.substring("http://".length)
31 } else {
32 normalized
33 }
34 }