From 231afd3e7149c1e636f17b6c5c73514a40e9463e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:06:19 +0000 Subject: [PATCH] Update dependency com.squareup.okhttp3:okhttp-bom to v5.0.0-alpha.17 (#2451) * Update dependency com.squareup.okhttp3:okhttp-bom to v5.0.0-alpha.17 * Vendor AsyncDns stuff as they're removed in OkHttp --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com> --- .../hippo/ehviewer/ktor/OkhttpExtensions.kt | 2 - app/src/main/kotlin/okhttp3/AsyncDns.kt | 133 ++++++++++++++++++ .../kotlin/okhttp3/android/AndroidAsyncDns.kt | 95 +++++++++++++ gradle/libs.versions.toml | 2 +- 4 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/okhttp3/AsyncDns.kt create mode 100644 app/src/main/kotlin/okhttp3/android/AndroidAsyncDns.kt diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ktor/OkhttpExtensions.kt b/app/src/main/kotlin/com/hippo/ehviewer/ktor/OkhttpExtensions.kt index bacb8e847..7b6068204 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ktor/OkhttpExtensions.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ktor/OkhttpExtensions.kt @@ -3,10 +3,8 @@ package com.hippo.ehviewer.ktor import com.hippo.ehviewer.util.isAtLeastQ import io.ktor.client.engine.okhttp.OkHttpConfig import okhttp3.AsyncDns -import okhttp3.ExperimentalOkHttpApi import okhttp3.android.AndroidAsyncDns -@OptIn(ExperimentalOkHttpApi::class) fun OkHttpConfig.configureClient() { config { if (isAtLeastQ) { diff --git a/app/src/main/kotlin/okhttp3/AsyncDns.kt b/app/src/main/kotlin/okhttp3/AsyncDns.kt new file mode 100644 index 000000000..f89beb969 --- /dev/null +++ b/app/src/main/kotlin/okhttp3/AsyncDns.kt @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2022 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package okhttp3 + +import java.net.InetAddress +import java.net.UnknownHostException +import java.util.concurrent.CountDownLatch +import okio.IOException + +/** + * An async domain name service that resolves IP addresses for host names. + * + * The main implementations will typically be implemented using specific DNS libraries such as + * * Android DnsResolver + * * OkHttp DnsOverHttps + * * dnsjava Resolver + * + * Implementations of this interface must be safe for concurrent use. + */ +interface AsyncDns { + /** + * Query DNS records for `hostname`, in the order they are received. + */ + fun query( + hostname: String, + callback: Callback, + ) + + /** + * Callback to receive results from the DNS Queries. + */ + interface Callback { + /** + * Return addresses for a dns query for a single class of IPv4 (A) or IPv6 (AAAA). + * May be an empty list indicating that the host is unreachable. + */ + fun onResponse( + hostname: String, + addresses: List, + ) + + /** + * Returns an error for the DNS query. + */ + fun onFailure( + hostname: String, + e: IOException, + ) + } + + /** + * Class of DNS addresses, such that clients that treat these differently, such + * as attempting IPv6 first, can make such decisions. + */ + enum class DnsClass( + val type: Int, + ) { + IPV4(TYPE_A), + IPV6(TYPE_AAAA), + } + + companion object { + const val TYPE_A = 1 + const val TYPE_AAAA = 28 + + /** + * Adapt an AsyncDns implementation to Dns, waiting until onComplete is received + * and returning results if available. + */ + fun toDns(vararg asyncDns: AsyncDns): Dns = Dns { hostname -> + val allAddresses = mutableListOf() + val allExceptions = mutableListOf() + val latch = CountDownLatch(asyncDns.size) + + asyncDns.forEach { + it.query( + hostname, + object : Callback { + override fun onResponse( + hostname: String, + addresses: List, + ) { + synchronized(allAddresses) { + allAddresses.addAll(addresses) + } + latch.countDown() + } + + override fun onFailure( + hostname: String, + e: IOException, + ) { + synchronized(allExceptions) { + allExceptions.add(e) + } + latch.countDown() + } + }, + ) + } + + latch.await() + + // No mutations should be possible after this point + if (allAddresses.isEmpty()) { + val first = allExceptions.firstOrNull() ?: UnknownHostException("No results for $hostname") + + allExceptions.drop(1).forEach { + first.addSuppressed(it) + } + + throw first + } + + allAddresses + } + } +} diff --git a/app/src/main/kotlin/okhttp3/android/AndroidAsyncDns.kt b/app/src/main/kotlin/okhttp3/android/AndroidAsyncDns.kt new file mode 100644 index 000000000..8cee35c63 --- /dev/null +++ b/app/src/main/kotlin/okhttp3/android/AndroidAsyncDns.kt @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package okhttp3.android + +import android.net.DnsResolver +import android.net.Network +import android.os.Build +import androidx.annotation.RequiresApi +import java.net.InetAddress +import java.net.UnknownHostException +import java.util.concurrent.Executors +import okhttp3.AsyncDns + +/** + * DNS implementation based on android.net.DnsResolver, which submits a request for + * A or AAAA records, and returns the addresses or exception. + * + * Two instances must be used to get all results for an address. + * + * @param network network to use, if not selects the default network. + */ +@RequiresApi(Build.VERSION_CODES.Q) +class AndroidAsyncDns( + private val dnsClass: AsyncDns.DnsClass, + private val network: Network? = null, +) : AsyncDns { + @RequiresApi(Build.VERSION_CODES.Q) + internal val resolver = DnsResolver.getInstance() + private val executor = Executors.newSingleThreadExecutor() + + override fun query( + hostname: String, + callback: AsyncDns.Callback, + ) { + try { + resolver.query( + network, + hostname, + dnsClass.type, + DnsResolver.FLAG_EMPTY, + executor, + null, + object : DnsResolver.Callback> { + override fun onAnswer( + addresses: List, + rCode: Int, + ) { + callback.onResponse(hostname, addresses) + } + + override fun onError(e: DnsResolver.DnsException) { + callback.onFailure( + hostname, + UnknownHostException(e.message).apply { + initCause(e) + }, + ) + } + }, + ) + } catch (e: Exception) { + // Handle any errors that might leak out + // https://issuetracker.google.com/issues/319957694 + callback.onFailure( + hostname, + UnknownHostException(e.message).apply { + initCause(e) + }, + ) + } + } + + companion object { + @RequiresApi(Build.VERSION_CODES.Q) + val IPv4 = AndroidAsyncDns(dnsClass = AsyncDns.DnsClass.IPV4) + + @RequiresApi(Build.VERSION_CODES.Q) + val IPv6 = AndroidAsyncDns(dnsClass = AsyncDns.DnsClass.IPV6) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b90b15fc5..170302525 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ kotlinx-datetime = "0.7.0" kotlinx-serialization = "1.9.0" ksp = "2.2.0-2.0.2" ktor = "3.1.3" -okhttp = "5.0.0-alpha.16" +okhttp = "5.0.0-alpha.17" okio = "3.14.0" splitties = "3.0.0" telephoto = "0.15.1"