diff --git a/.circleci/config.yml b/.circleci/config.yml index bb042a791..aa1f3bfe5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -140,6 +140,20 @@ jobs: - runtests: platform: jdk8alpn + testopenjsse: + docker: + - image: circleci/openjdk:8u171-jdk + + environment: + JVM_OPTS: -Xmx1g + TERM: dumb + + steps: + - checkout + + - runtests: + platform: openjsse + testjdk11: docker: - image: circleci/openjdk:11.0.3-jdk-stretch @@ -205,6 +219,11 @@ workflows: filters: branches: only: master + - testopenjsse: + filters: + branches: + ignore: + - gh-pages - testjdk11: filters: branches: @@ -236,6 +255,9 @@ workflows: - testjdk8alpn: requires: - compile + - testopenjsse: + requires: + - compile - testjdk11: requires: - compile diff --git a/build.gradle b/build.gradle index 22f122ecc..ca2d2bf06 100644 --- a/build.gradle +++ b/build.gradle @@ -159,6 +159,10 @@ subprojects { project -> dependencies { testRuntime "org.conscrypt:conscrypt-openjdk-uber:${versions.conscrypt}" } + } else if (platform == "openjsse") { + dependencies { + testRuntime deps.openjsse + } } dependencies { diff --git a/okhttp-testing-support/build.gradle b/okhttp-testing-support/build.gradle index b2611277a..3efe88254 100644 --- a/okhttp-testing-support/build.gradle +++ b/okhttp-testing-support/build.gradle @@ -3,6 +3,7 @@ dependencies { api deps.junit api deps.assertj api deps.conscrypt + api deps.openjsse compileOnly deps.jsr305 } diff --git a/okhttp-testing-support/src/main/java/okhttp3/TestUtil.java b/okhttp-testing-support/src/main/java/okhttp3/TestUtil.java index 8ce86ae1e..8a6bc3443 100644 --- a/okhttp-testing-support/src/main/java/okhttp3/TestUtil.java +++ b/okhttp-testing-support/src/main/java/okhttp3/TestUtil.java @@ -15,12 +15,16 @@ */ package okhttp3; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import okhttp3.internal.http2.Header; +import static org.junit.Assume.assumeNoException; + public final class TestUtil { public static final InetSocketAddress UNREACHABLE_ADDRESS = new InetSocketAddress("198.51.100.1", 8080); @@ -52,4 +56,12 @@ public final class TestUtil { Thread.sleep(100); System.runFinalization(); } + + public static void assumeNetwork() { + try { + InetAddress.getByName("www.google.com"); + } catch (UnknownHostException uhe) { + assumeNoException(uhe); + } + } } diff --git a/okhttp-testing-support/src/main/java/okhttp3/testing/PlatformRule.kt b/okhttp-testing-support/src/main/java/okhttp3/testing/PlatformRule.kt index be0213609..49f98011c 100644 --- a/okhttp-testing-support/src/main/java/okhttp3/testing/PlatformRule.kt +++ b/okhttp-testing-support/src/main/java/okhttp3/testing/PlatformRule.kt @@ -18,6 +18,7 @@ package okhttp3.testing import okhttp3.internal.platform.ConscryptPlatform import okhttp3.internal.platform.Jdk8WithJettyBootPlatform import okhttp3.internal.platform.Jdk9Platform +import okhttp3.internal.platform.OpenJSSEPlatform import okhttp3.internal.platform.Platform import org.conscrypt.Conscrypt import org.hamcrest.BaseMatcher @@ -34,6 +35,7 @@ import org.junit.Assume.assumeTrue import org.junit.AssumptionViolatedException import org.junit.rules.TestRule import org.junit.runners.model.Statement +import org.openjsse.net.ssl.OpenJSSE import java.security.Security /** @@ -57,6 +59,8 @@ open class PlatformRule @JvmOverloads constructor( try { setupPlatform() + System.err.println("Running with ${Platform.get().javaClass.simpleName}") + base.evaluate() } catch (e: AssumptionViolatedException) { throw e @@ -95,6 +99,10 @@ open class PlatformRule @JvmOverloads constructor( expectFailure(platformMatches(CONSCRYPT_PROPERTY)) } + fun expectFailureOnOpenJSSEPlatform() { + expectFailure(platformMatches(OPENJSSE_PROPERTY)) + } + fun expectFailureFromJdkVersion(majorVersion: Int) { expectFailure(fromMajor(majorVersion)) } @@ -118,7 +126,7 @@ open class PlatformRule @JvmOverloads constructor( fun fromMajor(version: Int): Matcher { return object : TypeSafeMatcher() { - override fun describeTo(description: org.hamcrest.Description) { + override fun describeTo(description: Description) { description.appendText("JDK with version from $version") } @@ -171,6 +179,11 @@ open class PlatformRule @JvmOverloads constructor( JDK9_PROPERTY)) } + fun assumeOpenJSSE() { + assumeThat(getPlatformSystemProperty(), equalTo( + OPENJSSE_PROPERTY)) + } + fun assumeJdk8() { assumeThat(getPlatformSystemProperty(), equalTo( JDK8_PROPERTY)) @@ -206,6 +219,11 @@ open class PlatformRule @JvmOverloads constructor( JDK8_ALPN_PROPERTY)) } + fun assumeNotOpenJSSE() { + assumeThat(getPlatformSystemProperty(), not( + OPENJSSE_PROPERTY)) + } + fun assumeNotHttp2Support() { assumeThat(getPlatformSystemProperty(), equalTo( JDK8_PROPERTY)) @@ -221,6 +239,7 @@ open class PlatformRule @JvmOverloads constructor( const val JDK9_PROPERTY = "jdk9" const val JDK8_ALPN_PROPERTY = "jdk8alpn" const val JDK8_PROPERTY = "jdk8" + const val OPENJSSE_PROPERTY = "openjsse" init { if (getPlatformSystemProperty() == CONSCRYPT_PROPERTY && Security.getProviders()[0].name != "Conscrypt") { @@ -238,7 +257,15 @@ open class PlatformRule @JvmOverloads constructor( if (isAlpnBootEnabled()) { System.err.println("Warning: ALPN Boot enabled unintentionally") } + } else if (getPlatformSystemProperty() == OPENJSSE_PROPERTY && Security.getProviders()[0].name != "OpenJSSE") { + if (!OpenJSSEPlatform.isSupported) { + System.err.println("Warning: OpenJSSE not available") + } + + Security.insertProviderAt(OpenJSSE(), 1) } + + Platform.resetForTests() } @JvmStatic @@ -249,6 +276,7 @@ open class PlatformRule @JvmOverloads constructor( if (property == null) { property = when (Platform.get()) { is ConscryptPlatform -> CONSCRYPT_PROPERTY + is OpenJSSEPlatform -> OPENJSSE_PROPERTY is Jdk8WithJettyBootPlatform -> CONSCRYPT_PROPERTY is Jdk9Platform -> JDK9_PROPERTY else -> JDK8_PROPERTY @@ -261,6 +289,9 @@ open class PlatformRule @JvmOverloads constructor( @JvmStatic fun conscrypt() = PlatformRule(CONSCRYPT_PROPERTY) + @JvmStatic + fun openjsse() = PlatformRule(OPENJSSE_PROPERTY) + @JvmStatic fun jdk9() = PlatformRule(JDK9_PROPERTY) diff --git a/okhttp/build.gradle b/okhttp/build.gradle index 1e8a45025..087ca5b6f 100644 --- a/okhttp/build.gradle +++ b/okhttp/build.gradle @@ -26,6 +26,7 @@ dependencies { api deps.okio api deps.kotlinStdlib compileOnly deps.conscrypt + compileOnly deps.openjsse compileOnly deps.android compileOnly deps.jsr305 compileOnly deps.animalSniffer diff --git a/okhttp/src/main/java/okhttp3/internal/Util.kt b/okhttp/src/main/java/okhttp3/internal/Util.kt index bd37b1e63..d40f16c4d 100644 --- a/okhttp/src/main/java/okhttp3/internal/Util.kt +++ b/okhttp/src/main/java/okhttp3/internal/Util.kt @@ -542,3 +542,27 @@ inline fun Any.notify() = (this as Object).notify() @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") inline fun Any.notifyAll() = (this as Object).notifyAll() + +fun readFieldOrNull(instance: Any, fieldType: Class, fieldName: String): T? { + var c: Class<*> = instance.javaClass + while (c != Any::class.java) { + try { + val field = c.getDeclaredField(fieldName) + field.isAccessible = true + val value = field.get(instance) + return if (!fieldType.isInstance(value)) null else fieldType.cast(value) + } catch (_: NoSuchFieldException) { + } + + c = c.superclass + } + + // Didn't find the field we wanted. As a last gasp attempt, + // try to find the value on a delegate. + if (fieldName != "delegate") { + val delegate = readFieldOrNull(instance, Any::class.java, "delegate") + if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName) + } + + return null +} diff --git a/okhttp/src/main/java/okhttp3/internal/platform/AndroidPlatform.kt b/okhttp/src/main/java/okhttp3/internal/platform/AndroidPlatform.kt index d992b69d6..331850b07 100644 --- a/okhttp/src/main/java/okhttp3/internal/platform/AndroidPlatform.kt +++ b/okhttp/src/main/java/okhttp3/internal/platform/AndroidPlatform.kt @@ -15,7 +15,6 @@ */ package okhttp3.internal.platform -import android.annotation.SuppressLint import android.os.Build import okhttp3.Protocol import okhttp3.internal.platform.android.CloseGuard @@ -225,7 +224,6 @@ class AndroidPlatform : Platform() { } companion object { - @SuppressLint("PrivateApi") val isSupported: Boolean = try { // Trigger an early exception over a fatal error, prefer a RuntimeException over Error. Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl") diff --git a/okhttp/src/main/java/okhttp3/internal/platform/ConscryptPlatform.kt b/okhttp/src/main/java/okhttp3/internal/platform/ConscryptPlatform.kt index 22670dee6..a5f9911c2 100644 --- a/okhttp/src/main/java/okhttp3/internal/platform/ConscryptPlatform.kt +++ b/okhttp/src/main/java/okhttp3/internal/platform/ConscryptPlatform.kt @@ -16,6 +16,7 @@ package okhttp3.internal.platform import okhttp3.Protocol +import okhttp3.internal.readFieldOrNull import org.conscrypt.Conscrypt import java.security.Provider import javax.net.ssl.SSLContext @@ -29,13 +30,9 @@ import javax.net.ssl.X509TrustManager * Requires org.conscrypt:conscrypt-openjdk-uber >= 2.1.0 on the classpath. */ class ConscryptPlatform private constructor() : Platform() { - private val provider: Provider - get() { - // n.b. We should consider defaulting to OpenJDK 11 trust manager - // https://groups.google.com/forum/#!topic/conscrypt/3vYzbesjOb4 - - return Conscrypt.newProviderBuilder().provideTrustManager(true).build() - } + // n.b. We should consider defaulting to OpenJDK 11 trust manager + // https://groups.google.com/forum/#!topic/conscrypt/3vYzbesjOb4 + private val provider: Provider = Conscrypt.newProviderBuilder().provideTrustManager(true).build() // See release notes https://groups.google.com/forum/#!forum/conscrypt // for version differences diff --git a/okhttp/src/main/java/okhttp3/internal/platform/OpenJSSEPlatform.kt b/okhttp/src/main/java/okhttp3/internal/platform/OpenJSSEPlatform.kt new file mode 100644 index 000000000..bf80f2212 --- /dev/null +++ b/okhttp/src/main/java/okhttp3/internal/platform/OpenJSSEPlatform.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 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.internal.platform + +import okhttp3.Protocol +import java.security.KeyStore +import java.security.Provider +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager + +/** + * Platform using OpenJSSE (https://github.com/openjsse/openjsse) if installed as the first + * Security Provider. + * + * Requires org.openjsse:openjsse >= 1.1.0 on the classpath. + */ +class OpenJSSEPlatform private constructor() : Platform() { + private val provider: Provider = org.openjsse.net.ssl.OpenJSSE() + + // Selects TLSv1.3 so we are specific about our intended version ranges (not just 1.3) + // and because it's a common pattern for VMs to have differences between supported and + // defaulted versions for TLS based on what is requested. + override fun newSSLContext(): SSLContext = + SSLContext.getInstance("TLSv1.3", provider) + + override fun platformTrustManager(): X509TrustManager { + val factory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm(), provider) + factory.init(null as KeyStore?) + val trustManagers = factory.trustManagers!! + check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + "Unexpected default trust managers: ${trustManagers.contentToString()}" + } + return trustManagers[0] as X509TrustManager + } + + public override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = + throw UnsupportedOperationException( + "clientBuilder.sslSocketFactory(SSLSocketFactory) not supported with OpenJSSE") + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) { + val sslParameters = sslSocket.sslParameters + + if (sslParameters is org.openjsse.javax.net.ssl.SSLParameters) { + // Enable ALPN. + val names = alpnProtocolNames(protocols) + sslParameters.applicationProtocols = names.toTypedArray() + + sslSocket.sslParameters = sslParameters + } + } else { + super.configureTlsExtensions(sslSocket, hostname, protocols) + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) { + when (val protocol = sslSocket.applicationProtocol) { + // Handles both un-configured and none selected. + null, "" -> null + else -> protocol + } + } else { + super.getSelectedProtocol(sslSocket) + } + + companion object { + val isSupported: Boolean = try { + // Trigger an early exception over a fatal error, prefer a RuntimeException over Error. + Class.forName("org.openjsse.net.ssl.OpenJSSE") + + true + } catch (_: ClassNotFoundException) { + false + } + + fun buildIfSupported(): OpenJSSEPlatform? = if (isSupported) OpenJSSEPlatform() else null + } +} diff --git a/okhttp/src/main/java/okhttp3/internal/platform/Platform.kt b/okhttp/src/main/java/okhttp3/internal/platform/Platform.kt index fda0ed010..99607dfa3 100644 --- a/okhttp/src/main/java/okhttp3/internal/platform/Platform.kt +++ b/okhttp/src/main/java/okhttp3/internal/platform/Platform.kt @@ -18,6 +18,7 @@ package okhttp3.internal.platform import okhttp3.OkHttpClient import okhttp3.Protocol +import okhttp3.internal.readFieldOrNull import okhttp3.internal.tls.BasicCertificateChainCleaner import okhttp3.internal.tls.BasicTrustRootIndex import okhttp3.internal.tls.CertificateChainCleaner @@ -193,12 +194,18 @@ open class Platform { fun alpnProtocolNames(protocols: List) = protocols.filter { it != Protocol.HTTP_1_0 }.map { it.toString() } - val isConscryptPreferred: Boolean + private val isConscryptPreferred: Boolean get() { val preferredProvider = Security.getProviders()[0].name return "Conscrypt" == preferredProvider } + private val isOpenJSSEPreferred: Boolean + get() { + val preferredProvider = Security.getProviders()[0].name + return "OpenJSSE" == preferredProvider + } + /** Attempt to match the host runtime to a capable Platform implementation. */ private fun findPlatform(): Platform { val android = AndroidPlatform.buildIfSupported() @@ -215,6 +222,14 @@ open class Platform { } } + if (isOpenJSSEPreferred) { + val openJSSE = OpenJSSEPlatform.buildIfSupported() + + if (openJSSE != null) { + return openJSSE + } + } + val jdk9 = Jdk9Platform.buildIfSupported() if (jdk9 != null) { @@ -239,29 +254,5 @@ open class Platform { } return result.readByteArray() } - - fun readFieldOrNull(instance: Any, fieldType: Class, fieldName: String): T? { - var c: Class<*> = instance.javaClass - while (c != Any::class.java) { - try { - val field = c.getDeclaredField(fieldName) - field.isAccessible = true - val value = field.get(instance) - return if (!fieldType.isInstance(value)) null else fieldType.cast(value) - } catch (_: NoSuchFieldException) { - } - - c = c.superclass - } - - // Didn't find the field we wanted. As a last gasp attempt, - // try to find the value on a delegate. - if (fieldName != "delegate") { - val delegate = readFieldOrNull(instance, Any::class.java, "delegate") - if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName) - } - - return null - } } } diff --git a/okhttp/src/main/java/okhttp3/internal/platform/android/StandardAndroidSocketAdapter.kt b/okhttp/src/main/java/okhttp3/internal/platform/android/StandardAndroidSocketAdapter.kt index bb66604ff..4974b2308 100644 --- a/okhttp/src/main/java/okhttp3/internal/platform/android/StandardAndroidSocketAdapter.kt +++ b/okhttp/src/main/java/okhttp3/internal/platform/android/StandardAndroidSocketAdapter.kt @@ -16,6 +16,7 @@ package okhttp3.internal.platform.android import okhttp3.internal.platform.Platform +import okhttp3.internal.readFieldOrNull import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -35,11 +36,11 @@ class StandardAndroidSocketAdapter( override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? { val context: Any? = - Platform.readFieldOrNull(sslSocketFactory, paramClass, + readFieldOrNull(sslSocketFactory, paramClass, "sslParameters") - val x509TrustManager = Platform.readFieldOrNull( + val x509TrustManager = readFieldOrNull( context!!, X509TrustManager::class.java, "x509TrustManager") - return x509TrustManager ?: Platform.readFieldOrNull(context, + return x509TrustManager ?: readFieldOrNull(context, X509TrustManager::class.java, "trustManager") } diff --git a/okhttp/src/test/java/okhttp3/CacheTest.java b/okhttp/src/test/java/okhttp3/CacheTest.java index 02fb43cd4..0a9ab2029 100644 --- a/okhttp/src/test/java/okhttp3/CacheTest.java +++ b/okhttp/src/test/java/okhttp3/CacheTest.java @@ -40,6 +40,7 @@ import okhttp3.internal.platform.Platform; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import okhttp3.testing.PlatformRule; import okhttp3.tls.HandshakeCertificates; import okio.Buffer; import okio.BufferedSink; @@ -65,6 +66,7 @@ public final class CacheTest { @Rule public MockWebServer server2 = new MockWebServer(); @Rule public InMemoryFileSystem fileSystem = new InMemoryFileSystem(); @Rule public final OkHttpClientTestRule clientTestRule = new OkHttpClientTestRule(); + @Rule public final PlatformRule platform = new PlatformRule(); private final HandshakeCertificates handshakeCertificates = localhost(); private OkHttpClient client; @@ -72,6 +74,8 @@ public final class CacheTest { private final CookieManager cookieManager = new CookieManager(); @Before public void setUp() throws Exception { + platform.assumeNotOpenJSSE(); + server.setProtocolNegotiationEnabled(false); cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem); client = clientTestRule.newClientBuilder() @@ -82,7 +86,10 @@ public final class CacheTest { @After public void tearDown() throws Exception { ResponseCache.setDefault(null); - cache.delete(); + + if (cache != null) { + cache.delete(); + } } /** diff --git a/okhttp/src/test/java/okhttp3/CallTest.java b/okhttp/src/test/java/okhttp3/CallTest.java index 2d95ed4db..cfe907a53 100644 --- a/okhttp/src/test/java/okhttp3/CallTest.java +++ b/okhttp/src/test/java/okhttp3/CallTest.java @@ -117,6 +117,8 @@ public final class CallTest { private Logger logger = Logger.getLogger(OkHttpClient.class.getName()); @Before public void setUp() { + platform.assumeNotOpenJSSE(); + logger.addHandler(logHandler); client = clientTestRule.newClientBuilder() .eventListener(listener) diff --git a/okhttp/src/test/java/okhttp3/ConscryptTest.kt b/okhttp/src/test/java/okhttp3/ConscryptTest.kt index 547e822cd..47381de1d 100644 --- a/okhttp/src/test/java/okhttp3/ConscryptTest.kt +++ b/okhttp/src/test/java/okhttp3/ConscryptTest.kt @@ -15,6 +15,7 @@ */ package okhttp3 +import okhttp3.TestUtil.assumeNetwork import okhttp3.internal.platform.ConscryptPlatform import okhttp3.internal.platform.Platform import okhttp3.testing.PlatformRule @@ -22,13 +23,10 @@ import org.assertj.core.api.Assertions.assertThat import org.conscrypt.Conscrypt import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue -import org.junit.Assume import org.junit.Before import org.junit.Ignore import org.junit.Rule import org.junit.Test -import java.net.InetAddress -import java.net.UnknownHostException class ConscryptTest { @Suppress("RedundantVisibilityModifier") @@ -49,14 +47,6 @@ class ConscryptTest { assertThat(Conscrypt.isConscrypt(Platform.get().platformTrustManager())).isTrue() } - private fun assumeNetwork() { - try { - InetAddress.getByName("www.google.com") - } catch (uhe: UnknownHostException) { - Assume.assumeNoException(uhe) - } - } - @Test @Ignore fun testMozilla() { diff --git a/okhttp/src/test/java/okhttp3/DuplexTest.java b/okhttp/src/test/java/okhttp3/DuplexTest.java index c5c64caf4..7453035a9 100644 --- a/okhttp/src/test/java/okhttp3/DuplexTest.java +++ b/okhttp/src/test/java/okhttp3/DuplexTest.java @@ -54,6 +54,7 @@ public final class DuplexTest { private OkHttpClient client; @Before public void setUp() { + platform.assumeNotOpenJSSE(); platform.assumeHttp2Support(); client = clientTestRule.newClientBuilder() .eventListener(listener) diff --git a/okhttp/src/test/java/okhttp3/EventListenerTest.java b/okhttp/src/test/java/okhttp3/EventListenerTest.java index 915a6ffe8..d5f23f603 100644 --- a/okhttp/src/test/java/okhttp3/EventListenerTest.java +++ b/okhttp/src/test/java/okhttp3/EventListenerTest.java @@ -86,6 +86,8 @@ public final class EventListenerTest { private SocksProxy socksProxy; @Before public void setUp() { + platform.assumeNotOpenJSSE(); + client = clientTestRule.newClientBuilder() .eventListener(listener) .build(); diff --git a/okhttp/src/test/java/okhttp3/OpenJSSETest.kt b/okhttp/src/test/java/okhttp3/OpenJSSETest.kt index 7b1943c3f..a99243576 100644 --- a/okhttp/src/test/java/okhttp3/OpenJSSETest.kt +++ b/okhttp/src/test/java/okhttp3/OpenJSSETest.kt @@ -15,23 +15,22 @@ */ package okhttp3 -import okhttp3.Protocol.HTTP_2 +import okhttp3.TestUtil.assumeNetwork +import okhttp3.internal.platform.OpenJSSEPlatform import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.testing.PlatformRule import okhttp3.tls.HandshakeCertificates import okhttp3.tls.HeldCertificate import org.assertj.core.api.Assertions.assertThat -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.openjsse.net.ssl.OpenJSSE import org.openjsse.sun.security.ssl.SSLSocketFactoryImpl import org.openjsse.sun.security.ssl.SSLSocketImpl import java.net.InetAddress -import java.security.Security class OpenJSSETest { @JvmField @Rule var platform = PlatformRule() @@ -41,17 +40,11 @@ class OpenJSSETest { @Before fun setUp() { - platform.assumeJdk8() + platform.assumeOpenJSSE() - Security.insertProviderAt(OpenJSSE(), 1) client = clientTestRule.newClient() } - @After - fun cleanup() { - Security.removeProvider("OpenJSSE") - } - @Test fun testTlsv13Works() { enableTls() @@ -65,7 +58,7 @@ class OpenJSSETest { response.use { assertEquals(200, response.code) assertEquals(TlsVersion.TLS_1_3, response.handshake?.tlsVersion) - assertEquals(Protocol.HTTP_1_1, response.protocol) + assertEquals(Protocol.HTTP_2, response.protocol) assertThat(response.exchange?.connection()?.socket()).isInstanceOf(SSLSocketImpl::class.java) } @@ -76,14 +69,31 @@ class OpenJSSETest { val factory = SSLSocketFactoryImpl() val s = factory.createSocket() as SSLSocketImpl - // A Public API available is available to use in a custom Platform - s.setHandshakeApplicationProtocolSelector { _, _ -> HTTP_2.toString() } assertEquals(listOf("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"), s.enabledProtocols.toList()) } + @Test + @Ignore + fun testMozilla() { + assumeNetwork() + + val request = Request.Builder().url("https://mozilla.org/robots.txt").build() + + client.newCall(request).execute().use { + assertThat(it.protocol).isEqualTo(Protocol.HTTP_2) + assertThat(it.handshake!!.tlsVersion).isEqualTo(TlsVersion.TLS_1_3) + } + } + + @Test + fun testBuildIfSupported() { + val actual = OpenJSSEPlatform.buildIfSupported() + assertThat(actual).isNotNull + } + private fun enableTls() { // Generate a self-signed cert for the server to serve and the client to trust. - // can't use localhost with a non OpenJSSE trust manager + // can't use TlsUtil.localhost with a non OpenJSSE trust manager val heldCertificate = HeldCertificate.Builder() .commonName("localhost") .addSubjectAlternativeName(InetAddress.getByName("localhost").canonicalHostName) diff --git a/okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java b/okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java index 0ed99bff2..7a05f6b43 100644 --- a/okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java +++ b/okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java @@ -129,6 +129,8 @@ public final class HttpOverHttp2Test { } @Before public void setUp() { + platform.assumeNotOpenJSSE(); + if (protocol == Protocol.HTTP_2) { platform.assumeHttp2Support(); server.useHttps(handshakeCertificates.sslSocketFactory(), false); diff --git a/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java b/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java index 9b121a918..25a3c66d1 100644 --- a/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java +++ b/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java @@ -65,6 +65,8 @@ public final class ClientAuthTest { @Before public void setUp() { + platform.assumeNotOpenJSSE(); + serverRootCa = new HeldCertificate.Builder() .serialNumber(1L) .certificateAuthority(1) diff --git a/okhttp/src/test/java/okhttp3/internal/ws/WebSocketHttpTest.java b/okhttp/src/test/java/okhttp3/internal/ws/WebSocketHttpTest.java index 6d09aa150..2006f235e 100644 --- a/okhttp/src/test/java/okhttp3/internal/ws/WebSocketHttpTest.java +++ b/okhttp/src/test/java/okhttp3/internal/ws/WebSocketHttpTest.java @@ -42,6 +42,7 @@ import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okhttp3.mockwebserver.SocketPolicy; import okhttp3.testing.Flaky; +import okhttp3.testing.PlatformRule; import okhttp3.tls.HandshakeCertificates; import okio.Buffer; import okio.ByteString; @@ -65,6 +66,7 @@ public final class WebSocketHttpTest { @Rule public final MockWebServer webServer = new MockWebServer(); @Rule public final OkHttpClientTestRule clientTestRule = new OkHttpClientTestRule(); + @Rule public final PlatformRule platform = new PlatformRule(); private final HandshakeCertificates handshakeCertificates = localhost(); private final WebSocketRecorder clientListener = new WebSocketRecorder("client"); @@ -73,6 +75,8 @@ public final class WebSocketHttpTest { private OkHttpClient client; @Before public void setUp() { + platform.assumeNotOpenJSSE(); + client = clientTestRule.newClientBuilder() .writeTimeout(500, TimeUnit.MILLISECONDS) .readTimeout(500, TimeUnit.MILLISECONDS)