From 3fff8eeeb61935f3e40fe72a01f3321978c9765d Mon Sep 17 00:00:00 2001 From: davidoskky Date: Thu, 10 Aug 2023 21:43:13 +0200 Subject: [PATCH] Handle most HTTP client creation in common code --- .../android/LoginActivity.kt | 1 + shared/build.gradle.kts | 2 +- .../readerforselfossv2/rest/NetworkSSL.kt | 17 ++++ .../readerforselfossv2/rest/SelfossApi.kt | 90 ------------------- .../readerforselfossv2/rest/SelfossApi.kt | 65 +++++++++++++- 5 files changed, 82 insertions(+), 93 deletions(-) create mode 100644 shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/NetworkSSL.kt delete mode 100644 shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt index 5ccfe43..5e337f9 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt @@ -144,6 +144,7 @@ class LoginActivity : AppCompatActivity(), DIAware { repository.refreshLoginInformation(url, login, password) CoroutineScope(Dispatchers.Main).launch { + repository.updateApiInformation() val result = repository.login() if (result) { val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance() diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index befcb5c..46219f7 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") implementation("io.ktor:ktor-client-logging:$ktorVersion") implementation("io.ktor:ktor-client-auth:$ktorVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") implementation("org.jsoup:jsoup:1.15.4") @@ -62,7 +63,6 @@ kotlin { val androidMain by getting { dependencies { implementation("com.squareup.okhttp3:okhttp:4.11.0") - implementation("io.ktor:ktor-client-okhttp:2.2.4") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // Sql diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/NetworkSSL.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/NetworkSSL.kt new file mode 100644 index 0000000..c2b6698 --- /dev/null +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/NetworkSSL.kt @@ -0,0 +1,17 @@ +package bou.amine.apps.readerforselfossv2.rest + +import io.ktor.client.engine.cio.CIOEngineConfig +import java.security.cert.X509Certificate +import javax.net.ssl.X509TrustManager + +class NaiveTrustManager : X509TrustManager { + override fun checkClientTrusted(chain: Array?, authType: String?) {} + + override fun checkServerTrusted(chain: Array?, authType: String?) {} + + override fun getAcceptedIssuers(): Array = arrayOf() +} + +actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) { + config.https.trustManager = NaiveTrustManager() +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt deleted file mode 100644 index 6baade8..0000000 --- a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ /dev/null @@ -1,90 +0,0 @@ -package bou.amine.apps.readerforselfossv2.rest - -import bou.amine.apps.readerforselfossv2.service.AppSettingsService -import io.github.aakira.napier.Napier -import io.ktor.client.HttpClient -import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.plugins.HttpRequestRetry -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.cache.HttpCache -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.cookies.HttpCookies -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.serialization.json.Json -import okhttp3.OkHttpClient -import org.apache.http.conn.ssl.AllowAllHostnameVerifier -import java.security.cert.X509Certificate -import javax.net.ssl.SSLContext -import javax.net.ssl.X509TrustManager - -class NaiveTrustManager : X509TrustManager { - override fun checkClientTrusted(chain: Array?, authType: String?) {} - - override fun checkServerTrusted(chain: Array?, authType: String?) {} - - override fun getAcceptedIssuers(): Array = arrayOf() -} - -actual fun createHttpClient( - appSettingsService: AppSettingsService, - api: SelfossApi -) = - HttpClient(OkHttp) { - if (appSettingsService.getSelfSigned()) { - engine { - val trustManager = NaiveTrustManager() - val sslContext = SSLContext.getInstance("TLS").apply { - init(null, arrayOf(trustManager), null) - } - preconfigured = OkHttpClient().newBuilder() - .sslSocketFactory( - sslSocketFactory = sslContext.socketFactory, - trustManager = trustManager - ) - .hostnameVerifier(AllowAllHostnameVerifier()) - .build() - } - } - install(ContentNegotiation) { - install(HttpCache) - json(Json { - prettyPrint = true - isLenient = true - ignoreUnknownKeys = true - }) - } - install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.d(message, tag = "LogApiCalls") - } - } - level = LogLevel.INFO - } - install(HttpTimeout) { - requestTimeoutMillis = appSettingsService.getApiTimeout() - } - install(HttpCookies) - install(HttpRequestRetry) { - maxRetries = 2 - retryIf { _, response -> - response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo() - } - modifyRequest { - Napier.i("Will modify", tag = "HttpSend") - CoroutineScope(Dispatchers.Main).launch { - Napier.i("Will login", tag = "HttpSend") - api.login() - Napier.i("Did login", tag = "HttpSend") - } - } - } - expectSuccess = false - } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index 25677d5..b637b90 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -4,22 +4,83 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.StatusAndData import bou.amine.apps.readerforselfossv2.model.SuccessResponse import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import io.github.aakira.napier.Napier import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.engine.cio.CIOEngineConfig +import io.ktor.client.plugins.HttpRequestRetry +import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.auth.providers.BasicAuthCredentials +import io.ktor.client.plugins.cache.HttpCache +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.cookies.HttpCookies +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging import io.ktor.client.request.get import io.ktor.client.request.headers import io.ktor.client.request.parameter import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode import io.ktor.http.Parameters +import io.ktor.serialization.kotlinx.json.json import io.ktor.util.encodeBase64 import io.ktor.utils.io.charsets.Charsets import io.ktor.utils.io.core.toByteArray +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json -expect fun createHttpClient( +expect fun setupInsecureHTTPEngine(config: CIOEngineConfig) + +fun createHttpClient( appSettingsService: AppSettingsService, api: SelfossApi -): HttpClient +) = + HttpClient(CIO) { + if (appSettingsService.getSelfSigned()) { + engine { + setupInsecureHTTPEngine(this) + } + } + install(ContentNegotiation) { + install(HttpCache) + json(Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + }) + } + install(Logging) { + logger = object : Logger { + override fun log(message: String) { + Napier.d(message, tag = "LogApiCalls") + } + } + level = LogLevel.INFO + } + install(HttpTimeout) { + requestTimeoutMillis = appSettingsService.getApiTimeout() + } + install(HttpCookies) + install(HttpRequestRetry) { + maxRetries = 2 + retryIf { _, response -> + response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo() + } + modifyRequest { + Napier.i("Will modify", tag = "HttpSend") + CoroutineScope(Dispatchers.Main).launch { + Napier.i("Will login", tag = "HttpSend") + api.login() + Napier.i("Did login", tag = "HttpSend") + } + } + } + expectSuccess = false + } class SelfossApi(private val appSettingsService: AppSettingsService) {