Skip to content

Commit e8379be

Browse files
authored
fix: move both HTTP clients to OkHttp for parity with manager (MorpheApp#254)
- Adding a github patch source could sometimes crash. Switching to OkHttp solves this issue and brings it to parity with manager.
1 parent 8792b4b commit e8379be

4 files changed

Lines changed: 46 additions & 14 deletions

File tree

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ dependencies {
113113

114114
// -- Networking (GUI) --------------------------------------------------
115115
implementation(libs.ktor.client.core)
116-
implementation(libs.ktor.client.cio)
116+
implementation(libs.ktor.client.okhttp)
117117
implementation(libs.ktor.client.content.negotiation)
118118
implementation(libs.ktor.serialization.kotlinx.json)
119119
implementation(libs.ktor.client.logging)
@@ -353,6 +353,8 @@ tasks {
353353
// Ktor uses ServiceLoader
354354
exclude(dependency("io.ktor:.*"))
355355
exclude(dependency("org.slf4j:.*"))
356+
exclude(dependency("com.squareup.okhttp3:.*"))
357+
exclude(dependency("com.squareup.okio:.*"))
356358
// Koin uses reflection
357359
exclude(dependency("io.insert-koin:.*"))
358360
// Coroutines Swing provides Dispatchers.Main via ServiceLoader

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jadb = { module = "app.morphe:jadb", version.ref = "jadb" }
5656

5757
# Ktor Client
5858
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
59-
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
59+
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
6060
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
6161
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
6262
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }

src/main/kotlin/app/morphe/desktop/command/CliHttpClient.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
package app.morphe.desktop.command
77

88
import io.ktor.client.HttpClient
9-
import io.ktor.client.engine.cio.CIO
9+
import io.ktor.client.engine.okhttp.OkHttp
1010
import io.ktor.client.plugins.HttpTimeout
1111
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
1212
import io.ktor.serialization.kotlinx.json.json
1313
import kotlinx.serialization.json.Json
14+
import okhttp3.Dns
15+
import okhttp3.Protocol
16+
import java.net.Inet4Address
1417

1518
/**
1619
* Lazy initialized HttpClient for CLI commands. One client per process is fine for short-lived
@@ -20,20 +23,32 @@ import kotlinx.serialization.json.Json
2023
*/
2124
object CliHttpClient {
2225
val instance: HttpClient by lazy {
23-
HttpClient(CIO) {
26+
HttpClient(OkHttp) {
27+
engine {
28+
config {
29+
// Prefer IPv4 so an advertised but unroutable IPv6 address does not
30+
// burn the connect timeout before the working A record is tried.
31+
dns { hostname ->
32+
val all = Dns.SYSTEM.lookup(hostname)
33+
all.filterIsInstance<Inet4Address>().ifEmpty { all }
34+
}
35+
// Pin HTTP/1.1 to avoid intermittent HTTP/2 stream resets against
36+
// GitHub-backed download endpoints.
37+
protocols(listOf(Protocol.HTTP_1_1))
38+
followRedirects(true)
39+
followSslRedirects(true)
40+
}
41+
}
2442
install(ContentNegotiation) {
2543
json(Json { ignoreUnknownKeys = true })
2644
}
2745
// Idle/socket timeouts (not a total cap) so large .mpp downloads don't fail
28-
// for being big. Only genuine stalls or issues fail. Default CIO requestTimeout is 15s.
46+
// for being big. Only genuine stalls or issues fail.
2947
install(HttpTimeout) {
3048
connectTimeoutMillis = 30_000
3149
socketTimeoutMillis = 60_000
3250
}
3351
// Retry/429 handling lives in HttpService (single layer), not a client plugin.
34-
engine {
35-
requestTimeout = 0
36-
}
3752
}
3853
}
3954
}

src/main/kotlin/app/morphe/gui/di/AppModule.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ import app.morphe.gui.data.repository.UpdateCheckRepository
1212
import app.morphe.engine.PatchedAppStore
1313
import app.morphe.gui.util.PatchService
1414
import io.ktor.client.*
15-
import io.ktor.client.engine.cio.*
15+
import io.ktor.client.engine.okhttp.*
1616
import io.ktor.client.plugins.HttpTimeout
1717
import io.ktor.client.plugins.contentnegotiation.*
1818
import io.ktor.client.plugins.logging.*
1919
import io.ktor.serialization.kotlinx.json.*
2020
import kotlinx.serialization.json.Json
21+
import okhttp3.Dns
22+
import okhttp3.Protocol
2123
import org.koin.dsl.module
24+
import java.net.Inet4Address
2225
import app.morphe.gui.ui.screens.home.HomeViewModel
2326
import app.morphe.gui.ui.screens.patches.PatchesViewModel
2427
import app.morphe.gui.ui.screens.patches.PatchSelectionViewModel
@@ -41,7 +44,23 @@ val appModule = module {
4144

4245
// Ktor HTTP Client
4346
single {
44-
HttpClient(CIO) {
47+
HttpClient(OkHttp) {
48+
engine {
49+
config {
50+
// Prefer IPv4. A host with an advertised but unroutable IPv6
51+
// address otherwise burns the whole connect timeout before
52+
// anything tries the working A record.
53+
dns { hostname ->
54+
val all = Dns.SYSTEM.lookup(hostname)
55+
all.filterIsInstance<Inet4Address>().ifEmpty { all }
56+
}
57+
// Pin HTTP/1.1. Avoids intermittent HTTP/2 PROTOCOL_ERROR stream
58+
// resets seen against GitHub-backed download endpoints.
59+
protocols(listOf(Protocol.HTTP_1_1))
60+
followRedirects(true)
61+
followSslRedirects(true)
62+
}
63+
}
4564
install(ContentNegotiation) {
4665
json(get())
4766
}
@@ -61,10 +80,6 @@ val appModule = module {
6180
socketTimeoutMillis = 60_000
6281
}
6382
// Retry/429 handling lives in HttpService (single layer). Not a client plugin, to avoid compounding retries.
64-
engine {
65-
// Disable the engine-level total-call cap; the timeouts above govern.
66-
requestTimeout = 0
67-
}
6883
}
6984
}
7085

0 commit comments

Comments
 (0)