Skip to content

Commit 3511e1a

Browse files
committed
OkHttpClient.Builder network pinning on Android
Cleanly pinning network on Android - #8286 Redo the Async DNS API, but internal for now - #8318
1 parent fed29a2 commit 3511e1a

File tree

16 files changed

+822
-246
lines changed

16 files changed

+822
-246
lines changed

android-test/src/androidTest/java/okhttp/android/test/AndroidAsyncDnsTest.kt android-test/src/androidTest/java/okhttp/android/test/AndroidDnsTest.kt

+43-29
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*
1616
*/
17+
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
18+
1719
package okhttp.android.test
1820

1921
import android.content.Context
@@ -32,11 +34,13 @@ import java.net.UnknownHostException
3234
import java.util.concurrent.CountDownLatch
3335
import mockwebserver3.MockResponse
3436
import mockwebserver3.junit4.MockWebServerRule
35-
import okhttp3.AsyncDns
37+
import okhttp3.Dns
3638
import okhttp3.HttpUrl.Companion.toHttpUrl
3739
import okhttp3.OkHttpClient
3840
import okhttp3.Request
39-
import okhttp3.android.AndroidAsyncDns
41+
import okhttp3.android.ANDROID
42+
import okhttp3.android.AndroidDns
43+
import okhttp3.android.internal.AsyncDns
4044
import okhttp3.tls.HandshakeCertificates
4145
import okhttp3.tls.HeldCertificate
4246
import okio.IOException
@@ -50,7 +54,7 @@ import org.junit.Test
5054
/**
5155
* Run with "./gradlew :android-test:connectedCheck -PandroidBuild=true" and make sure ANDROID_SDK_ROOT is set.
5256
*/
53-
class AndroidAsyncDnsTest {
57+
class AndroidDnsTest {
5458
@JvmField @Rule
5559
val serverRule = MockWebServerRule()
5660
private lateinit var client: OkHttpClient
@@ -74,7 +78,7 @@ class AndroidAsyncDnsTest {
7478

7579
client =
7680
OkHttpClient.Builder()
77-
.dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6))
81+
.dns(Dns.ANDROID)
7882
.sslSocketFactory(localhost.sslSocketFactory(), localhost.trustManager)
7983
.build()
8084

@@ -131,25 +135,33 @@ class AndroidAsyncDnsTest {
131135
val latch = CountDownLatch(1)
132136

133137
// assumes an IPv4 address
134-
AndroidAsyncDns.IPv4.query(
135-
hostname,
136-
object : AsyncDns.Callback {
137-
override fun onResponse(
138-
hostname: String,
139-
addresses: List<InetAddress>,
140-
) {
141-
allAddresses.addAll(addresses)
142-
latch.countDown()
143-
}
144-
145-
override fun onFailure(
146-
hostname: String,
147-
e: IOException,
148-
) {
149-
exception = e
150-
latch.countDown()
151-
}
152-
},
138+
AndroidDns(AndroidDns.DnsClass.IPV4).query(
139+
hostname = hostname,
140+
originatingCall = null,
141+
callback =
142+
object : AsyncDns.Callback {
143+
override fun onAddresses(
144+
hasMore: Boolean,
145+
hostname: String,
146+
addresses: List<InetAddress>,
147+
) {
148+
allAddresses.addAll(addresses)
149+
if (!hasMore) {
150+
latch.countDown()
151+
}
152+
}
153+
154+
override fun onFailure(
155+
hasMore: Boolean,
156+
hostname: String,
157+
e: IOException,
158+
) {
159+
exception = e
160+
if (!hasMore) {
161+
latch.countDown()
162+
}
163+
}
164+
},
153165
)
154166

155167
latch.await()
@@ -188,7 +200,7 @@ class AndroidAsyncDnsTest {
188200

189201
val client =
190202
OkHttpClient.Builder()
191-
.dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6))
203+
.dns(Dns.ANDROID)
192204
.socketFactory(network.socketFactory)
193205
.build()
194206

@@ -200,11 +212,13 @@ class AndroidAsyncDnsTest {
200212
}
201213
}
202214

203-
private fun assumeNetwork() {
204-
try {
205-
InetAddress.getByName("www.google.com")
206-
} catch (uhe: UnknownHostException) {
207-
throw AssumptionViolatedException(uhe.message, uhe)
215+
companion object {
216+
fun assumeNetwork() {
217+
try {
218+
InetAddress.getByName("www.google.com")
219+
} catch (uhe: UnknownHostException) {
220+
throw AssumptionViolatedException(uhe.message, uhe)
221+
}
208222
}
209223
}
210224
}

okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsRecordCodecTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import assertk.assertions.isEqualTo
2323
import java.net.InetAddress
2424
import java.net.UnknownHostException
2525
import kotlin.test.assertFailsWith
26-
import okhttp3.AsyncDns.Companion.TYPE_A
27-
import okhttp3.AsyncDns.Companion.TYPE_AAAA
26+
import okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_A
27+
import okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_AAAA
2828
import okhttp3.dnsoverhttps.DnsRecordCodec.decodeAnswers
2929
import okio.ByteString.Companion.decodeHex
3030
import org.junit.jupiter.api.Test

okhttp/api/android/okhttp.api

+18-35
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,6 @@ public final class okhttp3/Address {
2727
public final fun url ()Lokhttp3/HttpUrl;
2828
}
2929

30-
public abstract interface class okhttp3/AsyncDns {
31-
public static final field Companion Lokhttp3/AsyncDns$Companion;
32-
public static final field TYPE_A I
33-
public static final field TYPE_AAAA I
34-
public abstract fun query (Ljava/lang/String;Lokhttp3/AsyncDns$Callback;)V
35-
}
36-
37-
public abstract interface class okhttp3/AsyncDns$Callback {
38-
public abstract fun onFailure (Ljava/lang/String;Ljava/io/IOException;)V
39-
public abstract fun onResponse (Ljava/lang/String;Ljava/util/List;)V
40-
}
41-
42-
public final class okhttp3/AsyncDns$Companion {
43-
public static final field TYPE_A I
44-
public static final field TYPE_AAAA I
45-
public final fun toDns ([Lokhttp3/AsyncDns;)Lokhttp3/Dns;
46-
}
47-
48-
public final class okhttp3/AsyncDns$DnsClass : java/lang/Enum {
49-
public static final field IPV4 Lokhttp3/AsyncDns$DnsClass;
50-
public static final field IPV6 Lokhttp3/AsyncDns$DnsClass;
51-
public static fun getEntries ()Lkotlin/enums/EnumEntries;
52-
public final fun getType ()I
53-
public static fun valueOf (Ljava/lang/String;)Lokhttp3/AsyncDns$DnsClass;
54-
public static fun values ()[Lokhttp3/AsyncDns$DnsClass;
55-
}
56-
5730
public abstract interface class okhttp3/Authenticator {
5831
public static final field Companion Lokhttp3/Authenticator$Companion;
5932
public static final field JAVA_NET_AUTHENTICATOR Lokhttp3/Authenticator;
@@ -1292,15 +1265,25 @@ public abstract class okhttp3/WebSocketListener {
12921265
public fun onOpen (Lokhttp3/WebSocket;Lokhttp3/Response;)V
12931266
}
12941267

1295-
public final class okhttp3/android/AndroidAsyncDns : okhttp3/AsyncDns {
1296-
public static final field Companion Lokhttp3/android/AndroidAsyncDns$Companion;
1297-
public fun <init> (Lokhttp3/AsyncDns$DnsClass;Landroid/net/Network;)V
1298-
public synthetic fun <init> (Lokhttp3/AsyncDns$DnsClass;Landroid/net/Network;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1299-
public fun query (Ljava/lang/String;Lokhttp3/AsyncDns$Callback;)V
1268+
public final class okhttp3/android/AndroidDnsKt {
1269+
public static final fun forNetwork (Lokhttp3/Dns$Companion;Landroid/net/Network;)Lokhttp3/Dns;
1270+
}
1271+
1272+
public final class okhttp3/android/AndroidSocketFactory : javax/net/SocketFactory {
1273+
public fun <init> (Landroid/net/Network;)V
1274+
public fun createSocket ()Ljava/net/Socket;
1275+
public fun createSocket (Ljava/lang/String;I)Ljava/net/Socket;
1276+
public fun createSocket (Ljava/lang/String;ILjava/net/InetAddress;I)Ljava/net/Socket;
1277+
public fun createSocket (Ljava/net/InetAddress;I)Ljava/net/Socket;
1278+
public fun createSocket (Ljava/net/InetAddress;ILjava/net/InetAddress;I)Ljava/net/Socket;
1279+
public fun equals (Ljava/lang/Object;)Z
1280+
public final fun getNetwork ()Landroid/net/Network;
1281+
public fun hashCode ()I
1282+
public fun toString ()Ljava/lang/String;
13001283
}
13011284

1302-
public final class okhttp3/android/AndroidAsyncDns$Companion {
1303-
public final fun getIPv4 ()Lokhttp3/android/AndroidAsyncDns;
1304-
public final fun getIPv6 ()Lokhttp3/android/AndroidAsyncDns;
1285+
public final class okhttp3/android/NetworkSelection {
1286+
public static final field INSTANCE Lokhttp3/android/NetworkSelection;
1287+
public final fun withNetwork (Lokhttp3/OkHttpClient$Builder;Landroid/net/Network;)Lokhttp3/OkHttpClient$Builder;
13051288
}
13061289

okhttp/api/jvm/okhttp.api

-27
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,6 @@ public final class okhttp3/Address {
2727
public final fun url ()Lokhttp3/HttpUrl;
2828
}
2929

30-
public abstract interface class okhttp3/AsyncDns {
31-
public static final field Companion Lokhttp3/AsyncDns$Companion;
32-
public static final field TYPE_A I
33-
public static final field TYPE_AAAA I
34-
public abstract fun query (Ljava/lang/String;Lokhttp3/AsyncDns$Callback;)V
35-
}
36-
37-
public abstract interface class okhttp3/AsyncDns$Callback {
38-
public abstract fun onFailure (Ljava/lang/String;Ljava/io/IOException;)V
39-
public abstract fun onResponse (Ljava/lang/String;Ljava/util/List;)V
40-
}
41-
42-
public final class okhttp3/AsyncDns$Companion {
43-
public static final field TYPE_A I
44-
public static final field TYPE_AAAA I
45-
public final fun toDns ([Lokhttp3/AsyncDns;)Lokhttp3/Dns;
46-
}
47-
48-
public final class okhttp3/AsyncDns$DnsClass : java/lang/Enum {
49-
public static final field IPV4 Lokhttp3/AsyncDns$DnsClass;
50-
public static final field IPV6 Lokhttp3/AsyncDns$DnsClass;
51-
public static fun getEntries ()Lkotlin/enums/EnumEntries;
52-
public final fun getType ()I
53-
public static fun valueOf (Ljava/lang/String;)Lokhttp3/AsyncDns$DnsClass;
54-
public static fun values ()[Lokhttp3/AsyncDns$DnsClass;
55-
}
56-
5730
public abstract interface class okhttp3/Authenticator {
5831
public static final field Companion Lokhttp3/Authenticator$Companion;
5932
public static final field JAVA_NET_AUTHENTICATOR Lokhttp3/Authenticator;

okhttp/src/androidMain/kotlin/okhttp3/android/AndroidAsyncDns.kt okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt

+46-10
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@ import androidx.annotation.RequiresApi
2424
import java.net.InetAddress
2525
import java.net.UnknownHostException
2626
import java.util.concurrent.Executors
27-
import okhttp3.AsyncDns
27+
import okhttp3.Call
28+
import okhttp3.Dns
2829
import okhttp3.ExperimentalOkHttpApi
30+
import okhttp3.android.AndroidDns.DnsClass
31+
import okhttp3.android.internal.AsyncDns
32+
import okhttp3.android.internal.BlockingAsyncDns.Companion.asBlocking
33+
import okhttp3.android.internal.CombinedAsyncDns.Companion.union
2934

3035
/**
3136
* DNS implementation based on android.net.DnsResolver, which submits a request for
@@ -37,8 +42,8 @@ import okhttp3.ExperimentalOkHttpApi
3742
*/
3843
@RequiresApi(Build.VERSION_CODES.Q)
3944
@ExperimentalOkHttpApi
40-
class AndroidAsyncDns(
41-
private val dnsClass: AsyncDns.DnsClass,
45+
internal class AndroidDns internal constructor(
46+
private val dnsClass: DnsClass,
4247
private val network: Network? = null,
4348
) : AsyncDns {
4449
@RequiresApi(Build.VERSION_CODES.Q)
@@ -47,6 +52,7 @@ class AndroidAsyncDns(
4752

4853
override fun query(
4954
hostname: String,
55+
originatingCall: Call?,
5056
callback: AsyncDns.Callback,
5157
) {
5258
try {
@@ -62,15 +68,17 @@ class AndroidAsyncDns(
6268
addresses: List<InetAddress>,
6369
rCode: Int,
6470
) {
65-
callback.onResponse(hostname, addresses)
71+
callback.onAddresses(hasMore = false, hostname = hostname, addresses = addresses)
6672
}
6773

6874
override fun onError(e: DnsResolver.DnsException) {
6975
callback.onFailure(
70-
hostname,
71-
UnknownHostException(e.message).apply {
72-
initCause(e)
73-
},
76+
hasMore = false,
77+
hostname = hostname,
78+
e =
79+
UnknownHostException(e.message).apply {
80+
initCause(e)
81+
},
7482
)
7583
}
7684
},
@@ -79,6 +87,7 @@ class AndroidAsyncDns(
7987
// Handle any errors that might leak out
8088
// https://issuetracker.google.com/issues/319957694
8189
callback.onFailure(
90+
hasMore = false,
8291
hostname,
8392
UnknownHostException(e.message).apply {
8493
initCause(e)
@@ -89,10 +98,37 @@ class AndroidAsyncDns(
8998

9099
@ExperimentalOkHttpApi
91100
companion object {
101+
internal fun forNetwork(network: Network): AsyncDns {
102+
return union(
103+
AndroidDns(dnsClass = DnsClass.IPV4, network = network),
104+
AndroidDns(dnsClass = DnsClass.IPV6, network = network),
105+
)
106+
}
107+
92108
@RequiresApi(Build.VERSION_CODES.Q)
93-
val IPv4 = AndroidAsyncDns(dnsClass = AsyncDns.DnsClass.IPV4)
109+
val IPv4 = AndroidDns(dnsClass = DnsClass.IPV4)
94110

95111
@RequiresApi(Build.VERSION_CODES.Q)
96-
val IPv6 = AndroidAsyncDns(dnsClass = AsyncDns.DnsClass.IPV6)
112+
val IPv6 = AndroidDns(dnsClass = DnsClass.IPV6)
113+
114+
internal const val TYPE_A = 1
115+
internal const val TYPE_AAAA = 28
116+
}
117+
118+
/**
119+
* Class of DNS addresses, such that clients that treat these differently, such
120+
* as attempting IPv6 first, can make such decisions.
121+
*/
122+
@ExperimentalOkHttpApi
123+
internal enum class DnsClass(val type: Int) {
124+
IPV4(TYPE_A),
125+
IPV6(TYPE_AAAA),
97126
}
98127
}
128+
129+
internal val Dns.Companion.ANDROID: Dns
130+
@RequiresApi(Build.VERSION_CODES.Q)
131+
get() = union(AndroidDns(dnsClass = DnsClass.IPV4), AndroidDns(dnsClass = DnsClass.IPV6)).asBlocking()
132+
133+
@RequiresApi(Build.VERSION_CODES.Q)
134+
fun Dns.Companion.forNetwork(network: Network): Dns = AndroidDns.forNetwork(network).asBlocking()

0 commit comments

Comments
 (0)