1
0
mirror of https://github.com/square/okhttp.git synced 2025-07-29 17:41:17 +03:00

Convert more tests from Java to Kotlin (#8155)

This commit is contained in:
Jesse Wilson
2023-12-23 03:26:25 -07:00
committed by GitHub
parent 1561bbaeae
commit 9724956cb4
87 changed files with 6628 additions and 7249 deletions

View File

@ -1,265 +0,0 @@
/*
* Copyright (C) 2018 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.dnsoverhttps;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import mockwebserver3.MockResponse;
import mockwebserver3.MockWebServer;
import mockwebserver3.RecordedRequest;
import mockwebserver3.junit5.internal.MockWebServerExtension;
import okhttp3.Cache;
import okhttp3.Dns;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.testing.PlatformRule;
import okio.Buffer;
import okio.ByteString;
import okio.FileSystem;
import okio.Path;
import okio.fakefilesystem.FakeFileSystem;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
@ExtendWith(MockWebServerExtension.class)
@Tag("Slowish")
public class DnsOverHttpsTest {
@RegisterExtension public final PlatformRule platform = new PlatformRule();
private MockWebServer server;
private Dns dns;
private final FileSystem cacheFs = new FakeFileSystem();
private final OkHttpClient bootstrapClient = new OkHttpClient.Builder()
.protocols(asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build();
@BeforeEach public void setUp(MockWebServer server) {
this.server = server;
server.setProtocols(bootstrapClient.protocols());
dns = buildLocalhost(bootstrapClient, false);
}
@Test public void getOne() throws Exception {
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
+ "0010000003b00049df00112"));
List<InetAddress> result = dns.lookup("google.com");
assertThat(result).isEqualTo(singletonList(address("157.240.1.18")));
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
}
@Test public void getIpv6() throws Exception {
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
+ "0010000003b00049df00112"));
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c00050001"
+ "00000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c0"
+ "0010000003b00102a032880f0290011faceb00c00000002"));
dns = buildLocalhost(bootstrapClient, true);
List<InetAddress> result = dns.lookup("google.com");
assertThat(result.size()).isEqualTo(2);
assertThat(result).contains(address("157.240.1.18"));
assertThat(result).contains(address("2a03:2880:f029:11:face:b00c:0:2"));
RecordedRequest request1 = server.takeRequest();
assertThat(request1.getMethod()).isEqualTo("GET");
RecordedRequest request2 = server.takeRequest();
assertThat(request2.getMethod()).isEqualTo("GET");
assertThat(asList(request1.getPath(), request2.getPath())).containsExactlyInAnyOrder(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ",
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ");
}
@Test public void failure() throws Exception {
server.enqueue(dnsResponse(
"0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b00060001"
+ "000007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e657"
+ "4c01b5adb12c100000e10000003840012750000000e10"));
try {
dns.lookup("google.com");
fail();
} catch (UnknownHostException uhe) {
uhe.printStackTrace();
assertThat(uhe.getMessage()).isEqualTo("google.com: NXDOMAIN");
}
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
}
@Test public void failOnExcessiveResponse() {
char[] array = new char[128 * 1024 + 2];
Arrays.fill(array, '0');
server.enqueue(dnsResponse(new String(array)));
try {
dns.lookup("google.com");
fail();
} catch (IOException ioe) {
assertThat(ioe.getMessage()).isEqualTo("google.com");
Throwable cause = ioe.getCause();
assertThat(cause).isInstanceOf(IOException.class);
assertThat(cause).hasMessage("response size exceeds limit (65536 bytes): 65537 bytes");
}
}
@Test public void failOnBadResponse() {
server.enqueue(dnsResponse("00"));
try {
dns.lookup("google.com");
fail();
} catch (IOException ioe) {
assertThat(ioe).hasMessage("google.com");
assertThat(ioe.getCause()).isInstanceOf(EOFException.class);
}
}
// TODO GET preferred order - with tests to confirm this
// 1. successful fresh cached GET response
// 2. unsuccessful (404, 500) fresh cached GET response
// 3. successful network response
// 4. successful stale cached GET response
// 5. unsuccessful response
// TODO how closely to follow POST rules on caching?
@Test public void usesCache() throws Exception {
Cache cache = new Cache(Path.get("cache"), 100 * 1024, cacheFs);
OkHttpClient cachedClient = bootstrapClient.newBuilder().cache(cache).build();
DnsOverHttps cachedDns = buildLocalhost(cachedClient, false);
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
+ "0010000003b00049df00112")
.newBuilder()
.setHeader("cache-control", "private, max-age=298")
.build());
List<InetAddress> result = cachedDns.lookup("google.com");
assertThat(result).containsExactly(address("157.240.1.18"));
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
result = cachedDns.lookup("google.com");
assertThat(result).isEqualTo(singletonList(address("157.240.1.18")));
}
@Test public void usesCacheOnlyIfFresh() throws Exception {
Cache cache = new Cache(new File("./target/DnsOverHttpsTest.cache"), 100 * 1024);
OkHttpClient cachedClient = bootstrapClient.newBuilder().cache(cache).build();
DnsOverHttps cachedDns = buildLocalhost(cachedClient, false);
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
+ "0010000003b00049df00112")
.newBuilder()
.setHeader("cache-control", "max-age=1")
.build());
List<InetAddress> result = cachedDns.lookup("google.com");
assertThat(result).containsExactly(address("157.240.1.18"));
RecordedRequest recordedRequest = server.takeRequest(0, TimeUnit.SECONDS);
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
Thread.sleep(2000);
server.enqueue(dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
+ "0010000003b00049df00112")
.newBuilder()
.setHeader("cache-control", "max-age=1")
.build());
result = cachedDns.lookup("google.com");
assertThat(result).isEqualTo(singletonList(address("157.240.1.18")));
recordedRequest = server.takeRequest(0, TimeUnit.SECONDS);
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
}
private MockResponse dnsResponse(String s) {
return new MockResponse.Builder()
.body(new Buffer().write(ByteString.decodeHex(s)))
.addHeader("content-type", "application/dns-message")
.addHeader("content-length", s.length() / 2)
.build();
}
private DnsOverHttps buildLocalhost(OkHttpClient bootstrapClient, boolean includeIPv6) {
HttpUrl url = server.url("/lookup?ct");
return new DnsOverHttps.Builder().client(bootstrapClient)
.includeIPv6(includeIPv6)
.resolvePrivateAddresses(true)
.url(url)
.build();
}
private static InetAddress address(String host) {
try {
return InetAddress.getByName(host);
} catch (UnknownHostException e) {
// impossible for IP addresses
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2018 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.dnsoverhttps
import java.io.EOFException
import java.io.File
import java.io.IOException
import java.net.InetAddress
import java.net.UnknownHostException
import java.util.Arrays
import java.util.concurrent.TimeUnit
import mockwebserver3.MockResponse
import mockwebserver3.MockWebServer
import mockwebserver3.junit5.internal.MockWebServerExtension
import okhttp3.Cache
import okhttp3.Dns
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.testing.PlatformRule
import okio.Buffer
import okio.ByteString.Companion.decodeHex
import okio.Path.Companion.toPath
import okio.fakefilesystem.FakeFileSystem
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.fail
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.extension.RegisterExtension
@ExtendWith(MockWebServerExtension::class)
@Tag("Slowish")
class DnsOverHttpsTest {
@RegisterExtension
val platform = PlatformRule()
private lateinit var server: MockWebServer
private lateinit var dns: Dns
private val cacheFs = FakeFileSystem()
private val bootstrapClient = OkHttpClient.Builder()
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build()
@BeforeEach
fun setUp(server: MockWebServer) {
this.server = server
server.protocols = bootstrapClient.protocols
dns = buildLocalhost(bootstrapClient, false)
}
@Test
fun getOne() {
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010" +
"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000" +
"0003b00049df00112"
)
)
val result = dns.lookup("google.com")
assertThat(result).isEqualTo(listOf(address("157.240.1.18")))
val recordedRequest = server.takeRequest()
assertThat(recordedRequest.method).isEqualTo("GET")
assertThat(recordedRequest.path)
.isEqualTo("/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ")
}
@Test
fun getIpv6() {
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c0005000" +
"100000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c0420001000" +
"10000003b00049df00112"
)
)
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c0005000" +
"100000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c000" +
"10000003b00102a032880f0290011faceb00c00000002"
)
)
dns = buildLocalhost(bootstrapClient, true)
val result = dns.lookup("google.com")
assertThat(result.size).isEqualTo(2)
assertThat(result).contains(address("157.240.1.18"))
assertThat(result).contains(address("2a03:2880:f029:11:face:b00c:0:2"))
val request1 = server.takeRequest()
assertThat(request1.method).isEqualTo("GET")
val request2 = server.takeRequest()
assertThat(request2.method).isEqualTo("GET")
assertThat(Arrays.asList(request1.path, request2.path))
.containsExactlyInAnyOrder(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ",
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ"
)
}
@Test
fun failure() {
server.enqueue(
dnsResponse(
"0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b00060001000" +
"007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e6574c01b5adb1" +
"2c100000e10000003840012750000000e10"
)
)
try {
dns.lookup("google.com")
fail<Any>()
} catch (uhe: UnknownHostException) {
assertThat(uhe.message).isEqualTo("google.com: NXDOMAIN")
}
val recordedRequest = server.takeRequest()
assertThat(recordedRequest.method).isEqualTo("GET")
assertThat(recordedRequest.path)
.isEqualTo("/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ")
}
@Test
fun failOnExcessiveResponse() {
val array = CharArray(128 * 1024 + 2) { '0' }
server.enqueue(dnsResponse(String(array)))
try {
dns.lookup("google.com")
fail<Any>()
} catch (ioe: IOException) {
assertThat(ioe.message).isEqualTo("google.com")
val cause = ioe.cause
assertThat(cause).isInstanceOf(IOException::class.java)
assertThat(cause).hasMessage("response size exceeds limit (65536 bytes): 65537 bytes")
}
}
@Test
fun failOnBadResponse() {
server.enqueue(dnsResponse("00"))
try {
dns.lookup("google.com")
fail<Any>()
} catch (ioe: IOException) {
assertThat(ioe).hasMessage("google.com")
assertThat(ioe.cause).isInstanceOf(EOFException::class.java)
}
}
// TODO GET preferred order - with tests to confirm this
// 1. successful fresh cached GET response
// 2. unsuccessful (404, 500) fresh cached GET response
// 3. successful network response
// 4. successful stale cached GET response
// 5. unsuccessful response
// TODO how closely to follow POST rules on caching?
@Test
fun usesCache() {
val cache = Cache("cache".toPath(), (100 * 1024).toLong(), cacheFs)
val cachedClient = bootstrapClient.newBuilder().cache(cache).build()
val cachedDns = buildLocalhost(cachedClient, false)
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010" +
"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000" +
"0003b00049df00112"
)
.newBuilder()
.setHeader("cache-control", "private, max-age=298")
.build()
)
var result = cachedDns.lookup("google.com")
assertThat(result).containsExactly(address("157.240.1.18"))
val recordedRequest = server.takeRequest()
assertThat(recordedRequest.method).isEqualTo("GET")
assertThat(recordedRequest.path)
.isEqualTo("/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ")
result = cachedDns.lookup("google.com")
assertThat(result).isEqualTo(listOf(address("157.240.1.18")))
}
@Test
fun usesCacheOnlyIfFresh() {
val cache = Cache(File("./target/DnsOverHttpsTest.cache"), 100 * 1024L)
val cachedClient = bootstrapClient.newBuilder().cache(cache).build()
val cachedDns = buildLocalhost(cachedClient, false)
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010" +
"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000" +
"0003b00049df00112"
)
.newBuilder()
.setHeader("cache-control", "max-age=1")
.build()
)
var result = cachedDns.lookup("google.com")
assertThat(result).containsExactly(address("157.240.1.18"))
var recordedRequest = server.takeRequest(0, TimeUnit.SECONDS)
assertThat(recordedRequest!!.method).isEqualTo("GET")
assertThat(recordedRequest.path).isEqualTo(
"/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ"
)
Thread.sleep(2000)
server.enqueue(
dnsResponse(
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c000500010" +
"0000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010001000" +
"0003b00049df00112"
)
.newBuilder()
.setHeader("cache-control", "max-age=1")
.build()
)
result = cachedDns.lookup("google.com")
assertThat(result).isEqualTo(listOf(address("157.240.1.18")))
recordedRequest = server.takeRequest(0, TimeUnit.SECONDS)
assertThat(recordedRequest!!.method).isEqualTo("GET")
assertThat(recordedRequest.path)
.isEqualTo("/lookup?ct&dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ")
}
private fun dnsResponse(s: String): MockResponse {
return MockResponse.Builder()
.body(Buffer().write(s.decodeHex()))
.addHeader("content-type", "application/dns-message")
.addHeader("content-length", s.length / 2)
.build()
}
private fun buildLocalhost(bootstrapClient: OkHttpClient, includeIPv6: Boolean): DnsOverHttps {
val url = server.url("/lookup?ct")
return DnsOverHttps.Builder().client(bootstrapClient)
.includeIPv6(includeIPv6)
.resolvePrivateAddresses(true)
.url(url)
.build()
}
companion object {
private fun address(host: String) = InetAddress.getByName(host)
}
}

View File

@ -1,83 +0,0 @@
/*
* Copyright (C) 2018 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.dnsoverhttps;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import okio.ByteString;
import org.junit.jupiter.api.Test;
import static okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_A;
import static okhttp3.dnsoverhttps.DnsRecordCodec.TYPE_AAAA;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
public class DnsRecordCodecTest {
@Test public void testGoogleDotComEncoding() {
String encoded = encodeQuery("google.com", TYPE_A);
assertThat(encoded).isEqualTo("AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ");
}
private String encodeQuery(String host, int type) {
return DnsRecordCodec.INSTANCE.encodeQuery(host, type).base64Url().replace("=", "");
}
@Test public void testGoogleDotComEncodingWithIPv6() {
String encoded = encodeQuery("google.com", TYPE_AAAA);
assertThat(encoded).isEqualTo("AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ");
}
@Test public void testGoogleDotComDecodingFromCloudflare() throws Exception {
List<InetAddress> encoded = DnsRecordCodec.INSTANCE.decodeAnswers("test.com",
ByteString.decodeHex("00008180000100010000000006676f6f676c6503636f6d0000010001c00c000100010"
+ "00000430004d83ad54e"));
assertThat(encoded).containsExactly(InetAddress.getByName("216.58.213.78"));
}
@Test public void testGoogleDotComDecodingFromGoogle() throws Exception {
List<InetAddress> decoded = DnsRecordCodec.INSTANCE.decodeAnswers("test.com",
ByteString.decodeHex("0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010"
+ "001c00c0005000100000a6d000603617069c012c0300005000100000cde000c04737461720463313072c"
+ "012c042000100010000003b00049df00112"));
assertThat(decoded).containsExactly(InetAddress.getByName("157.240.1.18"));
}
@Test public void testGoogleDotComDecodingFromGoogleIPv6() throws Exception {
List<InetAddress> decoded = DnsRecordCodec.INSTANCE.decodeAnswers("test.com",
ByteString.decodeHex("0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0"
+ "001c00c0005000100000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c"
+ "012c042001c00010000003b00102a032880f0290011faceb00c00000002"));
assertThat(decoded).containsExactly(InetAddress.getByName("2a03:2880:f029:11:face:b00c:0:2"));
}
@Test public void testGoogleDotComDecodingNxdomainFailure() throws Exception {
try {
DnsRecordCodec.INSTANCE.decodeAnswers("sdflkhfsdlkjdf.ee", ByteString.decodeHex("000081830001"
+ "0000000100000e7364666c6b686673646c6b6a64660265650000010001c01b00060001000007070038026e"
+ "7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e6574c01b5adb12c100000e1000"
+ "0003840012750000000e10"));
fail("");
} catch (UnknownHostException uhe) {
assertThat(uhe.getMessage()).isEqualTo("sdflkhfsdlkjdf.ee: NXDOMAIN");
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2018 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.
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package okhttp3.dnsoverhttps
import java.net.InetAddress
import java.net.UnknownHostException
import okhttp3.AsyncDns.Companion.TYPE_A
import okhttp3.AsyncDns.Companion.TYPE_AAAA
import okhttp3.dnsoverhttps.DnsRecordCodec.decodeAnswers
import okio.ByteString.Companion.decodeHex
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.fail
import org.junit.jupiter.api.Test
class DnsRecordCodecTest {
@Test
fun testGoogleDotComEncoding() {
val encoded = encodeQuery("google.com", TYPE_A)
assertThat(encoded).isEqualTo("AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ")
}
private fun encodeQuery(host: String, type: Int): String {
return DnsRecordCodec.encodeQuery(host, type).base64Url().replace("=", "")
}
@Test
fun testGoogleDotComEncodingWithIPv6() {
val encoded = encodeQuery("google.com", TYPE_AAAA)
assertThat(encoded).isEqualTo("AAABAAABAAAAAAAABmdvb2dsZQNjb20AABwAAQ")
}
@Test
fun testGoogleDotComDecodingFromCloudflare() {
val encoded = decodeAnswers(
hostname = "test.com",
byteString = ("00008180000100010000000006676f6f676c6503636f6d0000010001c00c0001000100000043" +
"0004d83ad54e").decodeHex()
)
assertThat(encoded).containsExactly(InetAddress.getByName("216.58.213.78"))
}
@Test
fun testGoogleDotComDecodingFromGoogle() {
val decoded = decodeAnswers(
hostname = "test.com",
byteString = ("0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c" +
"0005000100000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c0420001" +
"00010000003b00049df00112").decodeHex()
)
assertThat(decoded).containsExactly(InetAddress.getByName("157.240.1.18"))
}
@Test
fun testGoogleDotComDecodingFromGoogleIPv6() {
val decoded = decodeAnswers(
hostname = "test.com",
byteString = ("0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c" +
"0005000100000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c" +
"00010000003b00102a032880f0290011faceb00c00000002").decodeHex()
)
assertThat(decoded)
.containsExactly(InetAddress.getByName("2a03:2880:f029:11:face:b00c:0:2"))
}
@Test
fun testGoogleDotComDecodingNxdomainFailure() {
try {
decodeAnswers(
hostname = "sdflkhfsdlkjdf.ee",
byteString = ("0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b" +
"00060001000007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e65" +
"74c01b5adb12c100000e10000003840012750000000e10").decodeHex()
)
fail<Any>("")
} catch (uhe: UnknownHostException) {
assertThat(uhe.message).isEqualTo("sdflkhfsdlkjdf.ee: NXDOMAIN")
}
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright (C) 2018 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.dnsoverhttps;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
/**
* Temporary registry of known DNS over HTTPS providers.
*
* https://github.com/curl/curl/wiki/DNS-over-HTTPS
*/
public class DohProviders {
static DnsOverHttps buildGoogle(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://dns.google/dns-query"))
.bootstrapDnsHosts(getByIp("8.8.4.4"), getByIp("8.8.8.8"))
.build();
}
static DnsOverHttps buildGooglePost(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://dns.google/dns-query"))
.bootstrapDnsHosts(getByIp("8.8.4.4"), getByIp("8.8.8.8"))
.post(true)
.build();
}
static DnsOverHttps buildCloudflareIp(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://1.1.1.1/dns-query"))
.includeIPv6(false)
.build();
}
static DnsOverHttps buildCloudflare(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://1.1.1.1/dns-query"))
.bootstrapDnsHosts(getByIp("1.1.1.1"), getByIp("1.0.0.1"))
.includeIPv6(false)
.build();
}
static DnsOverHttps buildCloudflarePost(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://cloudflare-dns.com/dns-query"))
.bootstrapDnsHosts(getByIp("1.1.1.1"), getByIp("1.0.0.1"))
.includeIPv6(false)
.post(true)
.build();
}
static DnsOverHttps buildCleanBrowsing(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://doh.cleanbrowsing.org/doh/family-filter/"))
.includeIPv6(false)
.build();
}
static DnsOverHttps buildChantra(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://dns.dnsoverhttps.net/dns-query"))
.includeIPv6(false)
.build();
}
static DnsOverHttps buildCryptoSx(OkHttpClient bootstrapClient) {
return new DnsOverHttps.Builder().client(bootstrapClient)
.url(HttpUrl.get("https://doh.crypto.sx/dns-query"))
.includeIPv6(false)
.build();
}
public static List<DnsOverHttps> providers(OkHttpClient client, boolean http2Only,
boolean workingOnly, boolean getOnly) {
List<DnsOverHttps> result = new ArrayList<>();
result.add(buildGoogle(client));
if (!getOnly) {
result.add(buildGooglePost(client));
}
result.add(buildCloudflare(client));
result.add(buildCloudflareIp(client));
if (!getOnly) {
result.add(buildCloudflarePost(client));
}
if (!workingOnly) {
//result.add(buildCleanBrowsing(client)); // timeouts
result.add(buildCryptoSx(client)); // 521 - server down
}
result.add(buildChantra(client));
return result;
}
private static InetAddress getByIp(String host) {
try {
return InetAddress.getByName(host);
} catch (UnknownHostException e) {
// unlikely
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2018 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.dnsoverhttps
import java.net.InetAddress
import java.net.UnknownHostException
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
/**
* Temporary registry of known DNS over HTTPS providers.
*
* https://github.com/curl/curl/wiki/DNS-over-HTTPS
*/
object DohProviders {
private fun buildGoogle(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://dns.google/dns-query".toHttpUrl())
.bootstrapDnsHosts(getByIp("8.8.4.4"), getByIp("8.8.8.8"))
.build()
}
private fun buildGooglePost(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://dns.google/dns-query".toHttpUrl())
.bootstrapDnsHosts(getByIp("8.8.4.4"), getByIp("8.8.8.8"))
.post(true)
.build()
}
private fun buildCloudflareIp(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://1.1.1.1/dns-query".toHttpUrl())
.includeIPv6(false)
.build()
}
private fun buildCloudflare(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://1.1.1.1/dns-query".toHttpUrl())
.bootstrapDnsHosts(getByIp("1.1.1.1"), getByIp("1.0.0.1"))
.includeIPv6(false)
.build()
}
private fun buildCloudflarePost(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://cloudflare-dns.com/dns-query".toHttpUrl())
.bootstrapDnsHosts(getByIp("1.1.1.1"), getByIp("1.0.0.1"))
.includeIPv6(false)
.post(true)
.build()
}
fun buildCleanBrowsing(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://doh.cleanbrowsing.org/doh/family-filter/".toHttpUrl())
.includeIPv6(false)
.build()
}
private fun buildChantra(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://dns.dnsoverhttps.net/dns-query".toHttpUrl())
.includeIPv6(false)
.build()
}
private fun buildCryptoSx(bootstrapClient: OkHttpClient): DnsOverHttps {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url("https://doh.crypto.sx/dns-query".toHttpUrl())
.includeIPv6(false)
.build()
}
@JvmStatic
fun providers(
client: OkHttpClient,
http2Only: Boolean,
workingOnly: Boolean,
getOnly: Boolean,
): List<DnsOverHttps> {
return buildList {
add(buildGoogle(client))
if (!getOnly) {
add(buildGooglePost(client))
}
add(buildCloudflare(client))
add(buildCloudflareIp(client))
if (!getOnly) {
add(buildCloudflarePost(client))
}
if (!workingOnly) {
// result += buildCleanBrowsing(client); // timeouts
add(buildCryptoSx(client)) // 521 - server down
}
add(buildChantra(client))
}
}
private fun getByIp(host: String): InetAddress {
return try {
InetAddress.getByName(host)
} catch (e: UnknownHostException) {
// unlikely
throw RuntimeException(e)
}
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (C) 2018 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.dnsoverhttps;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Security;
import java.util.Collections;
import java.util.List;
import okhttp3.Cache;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import static java.util.Arrays.asList;
public class TestDohMain {
public static void main(String[] args) throws IOException {
Security.insertProviderAt(new org.conscrypt.OpenSSLProvider(), 1);
OkHttpClient bootstrapClient = new OkHttpClient.Builder().build();
List<String> names = asList("google.com", "graph.facebook.com", "sdflkhfsdlkjdf.ee");
try {
System.out.println("uncached\n********\n");
List<DnsOverHttps> dnsProviders =
DohProviders.providers(bootstrapClient, false, false, false);
runBatch(dnsProviders, names);
Cache dnsCache =
new Cache(new File("./target/TestDohMain.cache." + System.currentTimeMillis()),
10 * 1024 * 1024);
System.out.println("Bad targets\n***********\n");
HttpUrl url = HttpUrl.get("https://dns.cloudflare.com/.not-so-well-known/run-dmc-query");
List<DnsOverHttps> badProviders = Collections.singletonList(
new DnsOverHttps.Builder().client(bootstrapClient).url(url).post(true).build());
runBatch(badProviders, names);
System.out.println("cached first run\n****************\n");
names = asList("google.com", "graph.facebook.com");
bootstrapClient = bootstrapClient.newBuilder().cache(dnsCache).build();
dnsProviders = DohProviders.providers(bootstrapClient, true, true, true);
runBatch(dnsProviders, names);
System.out.println("cached second run\n*****************\n");
dnsProviders = DohProviders.providers(bootstrapClient, true, true, true);
runBatch(dnsProviders, names);
} finally {
bootstrapClient.connectionPool().evictAll();
bootstrapClient.dispatcher().executorService().shutdownNow();
Cache cache = bootstrapClient.cache();
if (cache != null) {
cache.close();
}
}
}
private static void runBatch(List<DnsOverHttps> dnsProviders, List<String> names) {
long time = System.currentTimeMillis();
for (DnsOverHttps dns : dnsProviders) {
System.out.println("Testing " + dns.url());
for (String host : names) {
System.out.print(host + ": ");
System.out.flush();
try {
List<InetAddress> results = dns.lookup(host);
System.out.println(results);
} catch (UnknownHostException uhe) {
Throwable e = uhe;
while (e != null) {
System.out.println(e);
e = e.getCause();
}
}
}
System.out.println();
}
time = System.currentTimeMillis() - time;
System.out.println("Time: " + (((double) time) / 1000) + " seconds\n");
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2018 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.dnsoverhttps
import java.io.File
import java.net.UnknownHostException
import java.security.Security
import okhttp3.Cache
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.dnsoverhttps.DohProviders.providers
import org.conscrypt.OpenSSLProvider
private fun runBatch(dnsProviders: List<DnsOverHttps>, names: List<String>) {
var time = System.currentTimeMillis()
for (dns in dnsProviders) {
println("Testing ${dns.url}")
for (host in names) {
print("$host: ")
System.out.flush()
try {
val results = dns.lookup(host)
println(results)
} catch (uhe: UnknownHostException) {
var e: Throwable? = uhe
while (e != null) {
println(e)
e = e.cause
}
}
}
println()
}
time = System.currentTimeMillis() - time
println("Time: ${time.toDouble() / 1000} seconds\n")
}
fun main() {
Security.insertProviderAt(OpenSSLProvider(), 1)
var bootstrapClient = OkHttpClient()
var names = listOf("google.com", "graph.facebook.com", "sdflkhfsdlkjdf.ee")
try {
println("uncached\n********\n")
var dnsProviders = providers(
client = bootstrapClient,
http2Only = false,
workingOnly = false,
getOnly = false,
)
runBatch(dnsProviders, names)
val dnsCache = Cache(
directory = File("./target/TestDohMain.cache.${System.currentTimeMillis()}"),
maxSize = 10L * 1024 * 1024
)
println("Bad targets\n***********\n")
val url = "https://dns.cloudflare.com/.not-so-well-known/run-dmc-query".toHttpUrl()
val badProviders = listOf(
DnsOverHttps.Builder()
.client(bootstrapClient)
.url(url)
.post(true)
.build()
)
runBatch(badProviders, names)
println("cached first run\n****************\n")
names = listOf("google.com", "graph.facebook.com")
bootstrapClient = bootstrapClient.newBuilder()
.cache(dnsCache)
.build()
dnsProviders = providers(
client = bootstrapClient,
http2Only = true,
workingOnly = true,
getOnly = true,
)
runBatch(dnsProviders, names)
println("cached second run\n*****************\n")
dnsProviders = providers(
client = bootstrapClient,
http2Only = true,
workingOnly = true,
getOnly = true,
)
runBatch(dnsProviders, names)
} finally {
bootstrapClient.connectionPool.evictAll()
bootstrapClient.dispatcher.executorService.shutdownNow()
bootstrapClient.cache?.close()
}
}