diff --git a/okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/internal/-DnsOverHttpsCall.kt b/okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/internal/-DnsOverHttpsCall.kt index 76ca09005d29..680c58e9ffd5 100644 --- a/okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/internal/-DnsOverHttpsCall.kt +++ b/okhttp-dnsoverhttps/src/main/kotlin/okhttp3/dnsoverhttps/internal/-DnsOverHttpsCall.kt @@ -120,7 +120,7 @@ internal class DnsOverHttpsCall( when (resourceRecord) { is ResourceRecord.Https -> { Dns.Record.ServiceMetadata( - hostname = request.hostname, + hostname = resourceRecord.targetName.takeIf { it != "" } ?: request.hostname, alpnIds = resourceRecord.alpnIds?.mapNotNull { alpnId -> try { diff --git a/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.kt b/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.kt index 411cb445ee67..e9229a6396e9 100644 --- a/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.kt +++ b/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.kt @@ -52,6 +52,10 @@ import okhttp3.dnsoverhttps.internal.Question import okhttp3.dnsoverhttps.internal.ResourceRecord import okhttp3.dnsoverhttps.internal.TYPE_A import okhttp3.dnsoverhttps.internal.TYPE_AAAA +import okhttp3.internal.dns.DnsEvent +import okhttp3.internal.dns.EntryPoint +import okhttp3.internal.dns.invoke +import okhttp3.internal.dns.toEventsQueue import okhttp3.testing.PlatformRule import okio.Buffer import okio.ByteString.Companion.decodeHex @@ -343,6 +347,7 @@ class DnsOverHttpsTest( ResourceRecord.Https( name = "lysine.dev", timeToLive = 5, + targetName = "cdn.lysine.dev", alpnIds = listOf(Protocol.HTTP_2.toString()), port = 8843, ipAddressHints = @@ -355,7 +360,7 @@ class DnsOverHttpsTest( ) val call = dns.newCall(Dns.Request("lysine.dev")) - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( @@ -363,7 +368,7 @@ class DnsOverHttpsTest( records = listOf( Dns.Record.ServiceMetadata( - hostname = "lysine.dev", + hostname = "cdn.lysine.dev", alpnIds = listOf(Protocol.HTTP_2), port = 8843, ipAddressHints = @@ -402,6 +407,55 @@ class DnsOverHttpsTest( ) } + @Test + fun serviceMetadataEmptyTargetNameAliasesToRequestHostname() { + assumeTrue(entryPoint == EntryPoint.NewCall) + + dns = buildLocalhost(bootstrapClient, includeIPv6 = true, includeHttps = true) + server["lysine.dev"] = + listOf( + ResourceRecord.IpAddress( + name = "lysine.dev", + timeToLive = 5, + address = InetAddress.getByName("10.20.30.40"), + ), + ResourceRecord.Https( + name = "lysine.dev", + timeToLive = 5, + targetName = "", + alpnIds = listOf(Protocol.HTTP_2.toString()), + ), + ) + + val call = dns.newCall(Dns.Request("lysine.dev")) + val dnsEvents = call.toEventsQueue() + + assertThat(dnsEvents.take()).isEqualTo( + DnsEvent.Records( + last = false, + records = + listOf( + Dns.Record.ServiceMetadata( + hostname = "lysine.dev", + alpnIds = listOf(Protocol.HTTP_2), + ), + ), + ), + ) + assertThat(dnsEvents.take()).isEqualTo( + DnsEvent.Records( + last = true, + records = + listOf( + Dns.Record.IpAddress( + hostname = "lysine.dev", + address = InetAddress.getByName("10.20.30.40"), + ), + ), + ), + ) + } + /** An HTTPS error is received before the IPv4 results, but the error is delivered last. */ @Test fun httpsFailureIsDeliveredAfterIpv6AndIpv4Records() { @@ -426,7 +480,7 @@ class DnsOverHttpsTest( ) val call = dns.newCall(Dns.Request("lysine.dev")) - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( @@ -474,7 +528,7 @@ class DnsOverHttpsTest( ) val call = dns.newCall(Dns.Request("lysine.dev")) - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( @@ -507,7 +561,7 @@ class DnsOverHttpsTest( ) val call = dns.newCall(Dns.Request("lysine.dev")) - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( @@ -530,7 +584,7 @@ class DnsOverHttpsTest( dns = buildLocalhost(bootstrapClient, includeIPv6 = true, includeHttps = true) val call = dns.newCall(Dns.Request("lysine.dev")) - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( @@ -549,7 +603,7 @@ class DnsOverHttpsTest( val call = dns.newCall(Dns.Request("lysine.dev")) call.cancel() assertThat(call.isCanceled()).isTrue() - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isInstanceOf() } @@ -568,7 +622,7 @@ class DnsOverHttpsTest( chain.proceed(chain.request()) } - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isInstanceOf() } @@ -601,7 +655,7 @@ class DnsOverHttpsTest( } } - val dnsEvents = call.execute() + val dnsEvents = call.toEventsQueue() assertThat(dnsEvents.take()).isEqualTo( DnsEvent.Records( last = false, diff --git a/okhttp-testing-support/src/main/kotlin/okhttp3/FakeDns.kt b/okhttp-testing-support/src/main/kotlin/okhttp3/FakeDns.kt index 526e970fa4a1..7cb0d737d73d 100644 --- a/okhttp-testing-support/src/main/kotlin/okhttp3/FakeDns.kt +++ b/okhttp-testing-support/src/main/kotlin/okhttp3/FakeDns.kt @@ -17,6 +17,7 @@ package okhttp3 import assertk.assertThat import assertk.assertions.containsExactly +import java.io.IOException import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress @@ -36,13 +37,16 @@ import okhttp3.dnsoverhttps.internal.ResourceRecord import okhttp3.dnsoverhttps.internal.TYPE_A import okhttp3.dnsoverhttps.internal.TYPE_AAAA import okhttp3.dnsoverhttps.internal.TYPE_HTTPS +import okhttp3.internal.concurrent.TaskRunner import okio.Buffer import okio.ByteString.Companion.decodeBase64 /** * Handles DNS calls using in-memory records. */ -class FakeDns : Dns { +class FakeDns( + private val taskRunner: TaskRunner = TaskRunner.INSTANCE, +) : Dns { private val data = ConcurrentHashMap>() var extraHeaders: Headers = Headers.headersOf() @@ -112,6 +116,8 @@ class FakeDns : Dns { ) } + override fun newCall(request: Dns.Request): Dns.Call = FakeDnsCall(request) + @Throws(UnknownHostException::class) override fun lookup(hostname: String): List { requests.put(Request.FunctionCall(hostname)) @@ -216,6 +222,104 @@ class FakeDns : Dns { } } + /** + * Deliver each kind of DNS record in a separate callback, to most accurately simulate what a real + * DNS server does. + */ + private inner class FakeDnsCall( + override val request: Dns.Request, + ) : Dns.Call { + @Volatile private var canceled = false + private var executed = false + private val taskQueue = taskRunner.newQueue() + + override fun cancel() { + canceled = true + } + + override fun isCanceled() = canceled + + override fun enqueue(callback: Dns.Callback) { + check(!executed) + executed = true + + requests.put(Request.FunctionCall(request.hostname)) + + if (canceled) { + taskQueue.execute("${request.hostname} dns") { + callback.onFailure( + call = this, + e = IOException("canceled"), + ) + } + return + } + + val resourceRecords = data[request.hostname] ?: listOf() + if (resourceRecords.isEmpty()) { + taskQueue.execute("${request.hostname} dns") { + callback.onRecords( + call = this, + last = true, + records = listOf(), + ) + } + return + } + + val serviceMetadataRecords = mutableListOf() + val ipv6Records = mutableListOf() + val ipv4Records = mutableListOf() + for (resourceRecord in resourceRecords) { + when (resourceRecord) { + is ResourceRecord.Https -> { + serviceMetadataRecords += + Dns.Record.ServiceMetadata( + hostname = resourceRecord.targetName.takeIf { it != "" } ?: request.hostname, + port = resourceRecord.port, + alpnIds = resourceRecord.alpnIds?.map { Protocol.get(it) }, + ipAddressHints = resourceRecord.ipAddressHints, + echConfigList = resourceRecord.echConfigList, + ) + } + + is ResourceRecord.IpAddress -> { + val ipAddressRecord = Dns.Record.IpAddress(request.hostname, resourceRecord.address) + when (resourceRecord.address) { + is Inet4Address -> ipv4Records += ipAddressRecord + is Inet6Address -> ipv6Records += ipAddressRecord + else -> error("unexpected address") + } + } + } + } + + taskQueue.execute("${request.hostname} dns") { + if (serviceMetadataRecords.isNotEmpty()) { + callback.onRecords( + call = this, + last = ipv6Records.isEmpty() && ipv4Records.isEmpty(), + records = serviceMetadataRecords, + ) + } + if (ipv6Records.isNotEmpty()) { + callback.onRecords( + call = this, + last = ipv4Records.isEmpty(), + records = ipv6Records, + ) + } + if (ipv4Records.isNotEmpty()) { + callback.onRecords( + call = this, + last = true, + records = ipv4Records, + ) + } + } + } + } + sealed interface Request { val hostname: String diff --git a/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsTesting.kt b/okhttp-testing-support/src/main/kotlin/okhttp3/internal/dns/DnsTesting.kt similarity index 67% rename from okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsTesting.kt rename to okhttp-testing-support/src/main/kotlin/okhttp3/internal/dns/DnsTesting.kt index 206db2c93d60..9b137e3952db 100644 --- a/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsTesting.kt +++ b/okhttp-testing-support/src/main/kotlin/okhttp3/internal/dns/DnsTesting.kt @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package okhttp3.dnsoverhttps +package okhttp3.internal.dns import java.net.InetAddress import java.net.UnknownHostException -import java.util.concurrent.BlockingDeque +import java.util.concurrent.BlockingQueue import java.util.concurrent.LinkedBlockingDeque import okhttp3.Dns +import okhttp3.dnsoverhttps.DnsOverHttps +import okhttp3.internal.concurrent.TaskRunner import okio.IOException sealed interface DnsEvent { @@ -33,7 +35,7 @@ sealed interface DnsEvent { ) : DnsEvent } -internal fun Dns.Call.execute(): BlockingDeque { +fun Dns.Call.toEventsQueue(): BlockingQueue { val result = LinkedBlockingDeque() enqueue( @@ -73,7 +75,7 @@ operator fun DnsOverHttps.invoke( EntryPoint.NewCall -> { buildList { - val dnsEvents = newCall(Dns.Request(hostname)).execute() + val dnsEvents = newCall(Dns.Request(hostname)).toEventsQueue() while (true) { when (val dnsEvent = dnsEvents.take()) { is DnsEvent.Failure -> { @@ -98,6 +100,33 @@ operator fun DnsOverHttps.invoke( } } +/** + * Force this instance to use the lookup API or ([LookupDnsCall]), or the call API (and definitely + * not [LookupDnsCall]). This is for tests that want to defeat OkHttp's implementation detection, + * so we can exercise all code paths. + */ +fun Dns.forceEntryPoint(entryPoint: EntryPoint): Dns { + return when (entryPoint) { + EntryPoint.Lookup -> { + object : Dns by this { + override fun newCall(request: Dns.Request) = LookupDnsCall(TaskRunner.INSTANCE, this, request) + } + } + + EntryPoint.NewCall -> { + object : Dns by this { + override fun newCall(request: Dns.Request): Dns.Call { + val result = this@forceEntryPoint.newCall(request) + check(result !is LookupDnsCall) { + "unexpected call implementation on ${this@forceEntryPoint}" + } + return result + } + } + } + } +} + enum class EntryPoint { Lookup, NewCall, diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dns.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dns.kt index 1010c02dfcd0..51ea99b695fb 100644 --- a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dns.kt +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dns.kt @@ -15,13 +15,13 @@ */ package okhttp3 +import java.io.IOException import java.net.InetAddress import java.net.UnknownHostException import okhttp3.internal.concurrent.TaskRunner import okhttp3.internal.dns.LookupDnsCall import okhttp3.internal.toCanonicalHost import okio.ByteString -import okio.IOException /** * Loads IP addresses and service metadata for a hostname. diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteSelector.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteSelector.kt index 385a8dc7665d..0bba12759fb4 100644 --- a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteSelector.kt +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/RouteSelector.kt @@ -22,9 +22,12 @@ import java.net.Proxy import java.net.SocketException import java.net.UnknownHostException import okhttp3.Address +import okhttp3.Dns import okhttp3.HttpUrl import okhttp3.Route import okhttp3.internal.canParseAsIpAddress +import okhttp3.internal.dns.LookupDnsCall +import okhttp3.internal.dns.execute import okhttp3.internal.immutableListOf import okhttp3.internal.toImmutableList @@ -185,30 +188,71 @@ class RouteSelector internal constructor( } } + // TODO: switch RouteSelector to be async. + + /** + * Use the new async [Dns.Call] API, but call it *synchronously* until we change this class to + * itself be asynchronous. + * + * To save threads and context switching, this currently falls back to the synchronous API if the + * DNS implementation is itself synchronous. When everything is async we won't do that anymore. + */ private fun dnsLookup( proxy: Proxy, socketHost: String, socketPort: Int, ): List { - call.eventListener.dnsStart(call, socketHost) + call.eventListener.dnsStart( + call = call, + domainName = socketHost, + ) + + val dnsRequest = Dns.Request(socketHost) + val result = + when (val dnsCall = address.dns.newCall(dnsRequest)) { + is LookupDnsCall -> { + val inetAddresses = address.dns.lookup(socketHost) + inetAddresses.map { inetAddress -> + Route( + address, + proxy, + InetSocketAddress(inetAddress, socketPort), + ) + } + } + + else -> { + val records = dnsCall.execute() + + val hostnameToServiceMetadata = + records + .filterIsInstance() + .associateBy { it.hostname } + + records + .filterIsInstance() + .map { record -> + val serviceMetadata = hostnameToServiceMetadata[record.hostname] + Route( + address = address, + proxy = proxy, + socketAddress = InetSocketAddress(record.address, socketPort), + echConfigList = serviceMetadata?.echConfigList, + ) + } + } + } - // TODO: switch to newCall() API to get ECH. - // TODO: switch to newCall() API to make this async. - val inetAddresses = address.dns.lookup(socketHost) - if (inetAddresses.isEmpty()) { + if (result.isEmpty()) { throw UnknownHostException("${address.dns} returned no addresses for $socketHost") } - val result = - inetAddresses.map { inetAddress -> - Route( - address, - proxy, - InetSocketAddress(inetAddress, socketPort), - ) - } + call.eventListener.dnsEnd( + call = call, + domainName = socketHost, + inetAddressList = result.map { it.socketAddress.address }, + ) - call.eventListener.dnsEnd(call, socketHost, inetAddresses) return result } diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-ExecuteDns.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-ExecuteDns.kt new file mode 100644 index 000000000000..c71f81b29f0c --- /dev/null +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-ExecuteDns.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026 OkHttp Authors + * + * 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. + */ +@file:Suppress("ktlint:standard:filename") + +package okhttp3.internal.dns + +import java.util.concurrent.LinkedBlockingQueue +import okhttp3.Dns +import okhttp3.internal.OkHttpInternalApi + +/** + * Call our asynchronous API synchronously. + * + * This is intended to be transitional only; doing it this way needlessly blocks the caller's + * thread. + */ +@OkHttpInternalApi +fun Dns.Call.execute(): List { + val queue = LinkedBlockingQueue>>() + + enqueue( + object : Dns.Callback { + val allRecords = mutableListOf() + + override fun onRecords( + call: Dns.Call, + last: Boolean, + records: List, + ) { + allRecords += records + if (last) { + queue.put(Result.success(allRecords)) + } + } + + override fun onFailure( + call: Dns.Call, + e: okio.IOException, + ) { + queue.put(Result.failure(e)) + } + }, + ) + + return queue.take().getOrThrow() +} diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-LookupDnsCall.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-LookupDnsCall.kt index c78fbeca6025..a76cbcca9bd9 100644 --- a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-LookupDnsCall.kt +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/dns/-LookupDnsCall.kt @@ -33,7 +33,8 @@ import okhttp3.internal.testAndSet * When canceled, the callback is immediately notified but the in-flight call is run to completion * and discarded. */ -internal class LookupDnsCall( +@OkHttpInternalApi +class LookupDnsCall( private val taskRunner: TaskRunner, private val delegate: Dns, override val request: Dns.Request, diff --git a/okhttp/src/jvmTest/kotlin/okhttp3/ConnectionCoalescingTest.kt b/okhttp/src/jvmTest/kotlin/okhttp3/ConnectionCoalescingTest.kt index 67587aeffc57..345d71d0e5c3 100644 --- a/okhttp/src/jvmTest/kotlin/okhttp3/ConnectionCoalescingTest.kt +++ b/okhttp/src/jvmTest/kotlin/okhttp3/ConnectionCoalescingTest.kt @@ -84,7 +84,7 @@ class ConnectionCoalescingTest { .addSubjectAlternativeName("*.wildcard.com") .addSubjectAlternativeName("differentdns.com") .build() - serverIps = Dns.SYSTEM.lookup(server.hostName) + serverIps = listOf(server.socketAddress.address) dns[server.hostName] = serverIps dns["san.com"] = serverIps dns["nonsan.com"] = serverIps diff --git a/okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RouteSelectorTest.kt b/okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RouteSelectorTest.kt index 266c82c14721..164c4d31a043 100644 --- a/okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RouteSelectorTest.kt +++ b/okhttp/src/jvmTest/kotlin/okhttp3/internal/connection/RouteSelectorTest.kt @@ -15,6 +15,7 @@ */ package okhttp3.internal.connection +import app.cash.burst.Burst import assertk.assertThat import assertk.assertions.containsExactly import assertk.assertions.isEqualTo @@ -36,16 +37,24 @@ import okhttp3.OkHttpClientTestRule import okhttp3.Request import okhttp3.Route import okhttp3.TestValueFactory +import okhttp3.dnsoverhttps.internal.ResourceRecord import okhttp3.internal.connection.RouteSelector.Companion.socketHost +import okhttp3.internal.dns.EntryPoint +import okhttp3.internal.dns.forceEntryPoint import okhttp3.internal.http.RecordingProxySelector import okhttp3.testing.PlatformRule +import okio.ByteString import okio.ByteString.Companion.encodeUtf8 import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -class RouteSelectorTest { +@Burst +class RouteSelectorTest( + private val entryPoint: EntryPoint = EntryPoint.Lookup, +) { @RegisterExtension val platform = PlatformRule() @@ -56,9 +65,10 @@ class RouteSelectorTest { private val proxySelector = RecordingProxySelector() private val uriHost = "hosta" private val uriPort = 1003 + private val echConfigList = "this is an encrypted client hello".encodeUtf8() private val factory = TestValueFactory().apply { - this.dns = this@RouteSelectorTest.dns + this.dns = this@RouteSelectorTest.dns.forceEntryPoint(entryPoint) this.proxySelector = this@RouteSelectorTest.proxySelector this.uriHost = this@RouteSelectorTest.uriHost this.uriPort = this@RouteSelectorTest.uriPort @@ -99,6 +109,30 @@ class RouteSelectorTest { } } + @Test fun routeReturnsEncryptedClientHelloData() { + assumeTrue(entryPoint == EntryPoint.NewCall) + + val address = factory.newAddress() + val routeSelector = newRouteSelector(address) + assertThat(routeSelector.hasNext()).isTrue() + dns[uriHost] = + listOf( + ResourceRecord.IpAddress( + name = uriHost, + timeToLive = 5, + address = dns.allocate(1).single(), + ), + ResourceRecord.Https( + name = uriHost, + timeToLive = 5, + echConfigList = echConfigList, + ), + ) + val selection = routeSelector.next() + assertRoute(selection.next(), address, Proxy.NO_PROXY, dns[uriHost][0], uriPort, echConfigList) + dns.assertRequests(uriHost) + } + @Test fun singleRouteReturnsFailedRoute() { val address = factory.newAddress() var routeSelector = newRouteSelector(address) @@ -433,16 +467,16 @@ class RouteSelectorTest { fastFallback = false, ) assertThat(routeSelector.hasNext()).isTrue() - val (ipv4_1, ipv4_2) = dns.allocate(2) val (ipv6_1, ipv6_2) = dns.allocateIpv6(2) - dns[uriHost] = listOf(ipv4_1, ipv4_2, ipv6_1, ipv6_2) + val (ipv4_1, ipv4_2) = dns.allocate(2) + dns[uriHost] = listOf(ipv6_1, ipv6_2, ipv4_1, ipv4_2) val selection = routeSelector.next() assertThat(selection.routes.map { it.socketAddress.address }).containsExactly( - ipv4_1, - ipv4_2, ipv6_1, ipv6_2, + ipv4_1, + ipv4_2, ) } @@ -457,9 +491,9 @@ class RouteSelectorTest { fastFallback = true, ) assertThat(routeSelector.hasNext()).isTrue() - val (ipv4_1, ipv4_2) = dns.allocate(2) val (ipv6_1, ipv6_2) = dns.allocateIpv6(2) - dns[uriHost] = listOf(ipv4_1, ipv4_2, ipv6_1, ipv6_2) + val (ipv4_1, ipv4_2) = dns.allocate(2) + dns[uriHost] = listOf(ipv6_1, ipv6_2, ipv4_1, ipv4_2) val selection = routeSelector.next() assertThat(selection.routes.map { it.socketAddress.address }).containsExactly( @@ -574,11 +608,13 @@ class RouteSelectorTest { proxy: Proxy, socketAddress: InetAddress, socketPort: Int, + echConfigList: ByteString? = null, ) { assertThat(route.address).isEqualTo(address) assertThat(route.proxy).isEqualTo(proxy) assertThat(route.socketAddress.address).isEqualTo(socketAddress) assertThat(route.socketAddress.port).isEqualTo(socketPort) + assertThat(route.echConfigList).isEqualTo(echConfigList) } private fun newRouteSelector(