Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class AndroidSocketAdapterTest(
val sslSocket = socketFactory.createSocket() as SSLSocket
assertTrue(adapter.matchesSocket(sslSocket))

adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1))
adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1), null)
// not connected
assertNull(adapter.getSelectedProtocol(sslSocket))
}
Expand Down Expand Up @@ -89,7 +89,7 @@ class AndroidSocketAdapterTest(
object : DelegatingSSLSocket(context.socketFactory.createSocket() as SSLSocket) {}
assertFalse(adapter.matchesSocket(sslSocket))

adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1))
adapter.configureTlsExtensions(sslSocket, null, listOf(HTTP_2, HTTP_1_1), null)
// not connected
assertNull(adapter.getSelectedProtocol(sslSocket))
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ mockserver-client = "5.15.0"
mrjar = "0.1.1"
openjsse = "1.1.14"
org-bouncycastle = "1.84"
org-conscrypt = "2.5.2"
org-conscrypt = "2.6-SNAPSHOT"
org-junit-jupiter = "5.13.4"
playservices-safetynet = "18.1.0"
robolectric = "4.16.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,12 @@ public class MockWebServer : Closeable {
openClientSockets.add(sslSocket)

if (protocolNegotiationEnabled) {
Platform.get().configureTlsExtensions(sslSocket, null, protocols)
Platform.get().configureTlsExtensions(
sslSocket = sslSocket,
hostname = null,
protocols = protocols,
echConfigList = null,
)
}

sslSocket.startHandshake()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ class Http2Server(
true,
) as SSLSocket
sslSocket.useClientMode = false
Platform.get().configureTlsExtensions(sslSocket, null, listOf(Protocol.HTTP_2))
Platform.get().configureTlsExtensions(
sslSocket = sslSocket,
hostname = null,
protocols = listOf(Protocol.HTTP_2),
echConfigList = null,
)
sslSocket.startHandshake()
return sslSocket
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.
*/
package okhttp3.dnsoverhttps

import assertk.assertThat
import assertk.assertions.matchesPredicate
import java.net.InetAddress
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.OkHttpClientTestRule
import okhttp3.Request
import okhttp3.Response
import okhttp3.testing.PlatformRule
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension

@Tag("Remote")
class EchRemoteTest {
@RegisterExtension
val platform = PlatformRule(requiredPlatformName = PlatformRule.CONSCRYPT_PROPERTY)

@RegisterExtension
val clientTestRule = OkHttpClientTestRule()

private var client = clientTestRule.newClientBuilder()
.dns(
DnsOverHttps
.Builder()
.client(clientTestRule.newClient())
.url("https://1.1.1.1/dns-query".toHttpUrl())
.bootstrapDnsHosts(InetAddress.getByName("1.1.1.1"))
.includeHttps(true)
.build()
)
.build()

@Test
fun testHttpsRequest() {
val cloudflareBody = client.sendRequest(
Request.Builder().url("https://crypto.cloudflare.com/cdn-cgi/trace").build()
) {
it.body.string()
}
assertThat(cloudflareBody).matchesPredicate { it.contains("sni=encrypted") }

val tlsEchBody = client.sendRequest(Request.Builder().url("https://tls-ech.dev/").build()) {
it.body.string()
}
assertThat(tlsEchBody).matchesPredicate { it.contains("You are using ECH. :)") }
}

private fun <T> OkHttpClient.sendRequest(request: Request, fn: (Response) -> T): T {
val response = newCall(request).execute()

return response.use {
fn(it)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import okhttp3.internal.platform.android.ConscryptSocketAdapter
import okhttp3.internal.platform.android.DeferredSocketAdapter
import okhttp3.internal.tls.CertificateChainCleaner
import okhttp3.internal.tls.TrustRootIndex
import okio.ByteString

/** Android 10+ (API 29+). */
@SuppressSignatureCheck
Expand Down Expand Up @@ -75,11 +76,12 @@ class Android10Platform :
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
// No TLS extensions if the socket class is custom.
socketAdapters
.find { it.matchesSocket(sslSocket) }
?.configureTlsExtensions(sslSocket, hostname, protocols)
?.configureTlsExtensions(sslSocket, hostname, protocols, echConfigList)
}

override fun getSelectedProtocol(sslSocket: SSLSocket): String? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import okhttp3.internal.platform.android.StandardAndroidSocketAdapter
import okhttp3.internal.tls.BasicTrustRootIndex
import okhttp3.internal.tls.CertificateChainCleaner
import okhttp3.internal.tls.TrustRootIndex
import okio.ByteString

/** Android 5 to 9 (API 21 to 28). */
@SuppressSignatureCheck
Expand Down Expand Up @@ -93,11 +94,12 @@ class AndroidPlatform :
sslSocket: SSLSocket,
hostname: String?,
protocols: List<@JvmSuppressWildcards Protocol>,
echConfigList: ByteString?,
) {
// No TLS extensions if the socket class is custom.
socketAdapters
.find { it.matchesSocket(sslSocket) }
?.configureTlsExtensions(sslSocket, hostname, protocols)
?.configureTlsExtensions(sslSocket, hostname, protocols, echConfigList)
}

override fun getSelectedProtocol(sslSocket: SSLSocket): String? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import okhttp3.Protocol
import okhttp3.internal.SuppressSignatureCheck
import okhttp3.internal.platform.Platform
import okhttp3.internal.platform.Platform.Companion.isAndroid
import okio.ByteString

/**
* Simple non-reflection SocketAdapter for Android Q+.
Expand Down Expand Up @@ -57,6 +58,7 @@ class Android10SocketAdapter : SocketAdapter {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
try {
SSLSockets.setUseSessionTickets(sslSocket, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import javax.net.ssl.SSLSocket
import okhttp3.Protocol
import okhttp3.internal.platform.AndroidPlatform
import okhttp3.internal.platform.Platform
import okio.ByteString

/**
* Modern reflection based SocketAdapter for Conscrypt class SSLSockets.
Expand All @@ -48,6 +49,7 @@ open class AndroidSocketAdapter(
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
// No TLS extensions if the socket class is custom.
if (matchesSocket(sslSocket)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package okhttp3.internal.platform.android
import javax.net.ssl.SSLSocket
import okhttp3.Protocol
import okhttp3.internal.platform.Platform
import okio.ByteString
import org.bouncycastle.jsse.BCSSLSocket

/**
Expand All @@ -41,6 +42,7 @@ class BouncyCastleSocketAdapter : SocketAdapter {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
// No TLS extensions if the socket class is custom.
if (matchesSocket(sslSocket)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package okhttp3.internal.platform.android
import javax.net.ssl.SSLSocket
import okhttp3.Protocol
import okhttp3.internal.platform.Platform
import okio.ByteString
import org.conscrypt.Conscrypt

/**
Expand All @@ -39,6 +40,7 @@ class ConscryptSocketAdapter : SocketAdapter {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
// No TLS extensions if the socket class is custom.
if (matchesSocket(sslSocket)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package okhttp3.internal.platform.android

import javax.net.ssl.SSLSocket
import okhttp3.Protocol
import okio.ByteString

/**
* Deferred implementation of SocketAdapter that works by observing the socket
Expand All @@ -39,8 +40,9 @@ class DeferredSocketAdapter(
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
getDelegate(sslSocket)?.configureTlsExtensions(sslSocket, hostname, protocols)
getDelegate(sslSocket)?.configureTlsExtensions(sslSocket, hostname, protocols, echConfigList)
}

override fun getSelectedProtocol(sslSocket: SSLSocket): String? = getDelegate(sslSocket)?.getSelectedProtocol(sslSocket)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Protocol
import okio.ByteString

interface SocketAdapter {
fun isSupported(): Boolean
Expand All @@ -33,6 +34,7 @@ interface SocketAdapter {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
)

fun getSelectedProtocol(sslSocket: SSLSocket): String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package okhttp3.internal.connection
import java.io.IOException
import java.net.ConnectException
import java.net.HttpURLConnection
import java.net.NoRouteToHostException
import java.net.ProtocolException
import java.net.Proxy
import java.net.Socket as JavaNetSocket
Expand Down Expand Up @@ -345,7 +346,12 @@ class ConnectPlan internal constructor(
var success = false
try {
if (connectionSpec.supportsTlsExtensions) {
Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)
Platform.get().configureTlsExtensions(
sslSocket = sslSocket,
hostname = address.url.host,
protocols = address.protocols,
echConfigList = route.echConfigList
)
}

// Force handshake. This can throw!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Protocol
import okhttp3.internal.SuppressSignatureCheck
import okio.ByteString

/**
* OpenJDK 9+ and JDK8 build 252+.
Expand All @@ -35,6 +36,7 @@ open class Jdk9Platform : Platform() {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<@JvmSuppressWildcards Protocol>,
echConfigList: ByteString?,
) {
val sslParameters = sslSocket.sslParameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import okhttp3.internal.tls.BasicTrustRootIndex
import okhttp3.internal.tls.CertificateChainCleaner
import okhttp3.internal.tls.TrustRootIndex
import okio.Buffer
import okio.ByteString
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

/**
Expand Down Expand Up @@ -116,6 +117,7 @@ open class Platform {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<@JvmSuppressWildcards Protocol>,
echConfigList: ByteString?,
) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Protocol
import okio.ByteString
import org.bouncycastle.jsse.BCSSLSocket
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider

Expand Down Expand Up @@ -59,6 +60,7 @@ class BouncyCastlePlatform private constructor() : Platform() {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<@JvmSuppressWildcards Protocol>,
echConfigList: ByteString?,
) {
if (sslSocket is BCSSLSocket) {
val sslParameters = sslSocket.parameters
Expand All @@ -69,7 +71,7 @@ class BouncyCastlePlatform private constructor() : Platform() {

sslSocket.parameters = sslParameters
} else {
super.configureTlsExtensions(sslSocket, hostname, protocols)
super.configureTlsExtensions(sslSocket, hostname, protocols, echConfigList)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Protocol
import okio.ByteString
import org.conscrypt.Conscrypt
import org.conscrypt.ConscryptHostnameVerifier
import org.conscrypt.EchParameters

/**
* Platform using Conscrypt (conscrypt.org) if installed as the first Security Provider.
Expand Down Expand Up @@ -78,6 +80,7 @@ class ConscryptPlatform private constructor() : Platform() {
sslSocket: SSLSocket,
hostname: String?,
protocols: List<@JvmSuppressWildcards Protocol>,
echConfigList: ByteString?,
) {
if (Conscrypt.isConscrypt(sslSocket)) {
// Enable session tickets.
Expand All @@ -86,8 +89,11 @@ class ConscryptPlatform private constructor() : Platform() {
// Enable ALPN.
val names = alpnProtocolNames(protocols)
Conscrypt.setApplicationProtocols(sslSocket, names.toTypedArray())

// Enable ECH parameters.
Conscrypt.setEchParameters(sslSocket, EchParameters(true, echConfigList?.toByteArray()))
} else {
super.configureTlsExtensions(sslSocket, hostname, protocols)
super.configureTlsExtensions(sslSocket, hostname, protocols, echConfigList)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import java.lang.reflect.Method
import java.lang.reflect.Proxy
import javax.net.ssl.SSLSocket
import okhttp3.Protocol
import okio.ByteString

/** OpenJDK 8 with `org.mortbay.jetty.alpn:alpn-boot` in the boot class path. */
class Jdk8WithJettyBootPlatform(
Expand All @@ -34,6 +35,7 @@ class Jdk8WithJettyBootPlatform(
sslSocket: SSLSocket,
hostname: String?,
protocols: List<Protocol>,
echConfigList: ByteString?,
) {
val names = alpnProtocolNames(protocols)

Expand Down
Loading
Loading