Skip to content

Commit cf0042b

Browse files
authored
Extended Android tests (#5298)
Added tests related to certificate pinning, mockwebserver and cleartext blocking on legacy Android.
1 parent 609888f commit cf0042b

File tree

4 files changed

+112
-9
lines changed

4 files changed

+112
-9
lines changed

android-test/build.gradle

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
apply plugin: 'com.android.library'
22
apply plugin: 'org.jetbrains.kotlin.android'
33

4+
repositories {
5+
jcenter {
6+
// Required for a dependency of Android lint.
7+
content {
8+
includeGroup 'org.jetbrains.trove4j'
9+
}
10+
}
11+
}
12+
413
android {
514
compileOptions {
615
sourceCompatibility JavaVersion.VERSION_1_8
@@ -23,6 +32,8 @@ dependencies {
2332
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
2433
implementation project(':okhttp')
2534
testImplementation 'junit:junit:4.12'
35+
androidTestImplementation project(':mockwebserver')
36+
androidTestImplementation project(':okhttp-tls')
2637
androidTestImplementation 'com.android.support.test:runner:1.0.2'
2738
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
2839
}

android-test/src/androidTest/java/okhttp/android/test/OkHttpTest.kt

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,29 @@ package okhttp.android.test
1818
import android.os.Build
1919
import android.support.test.runner.AndroidJUnit4
2020
import okhttp3.Call
21+
import okhttp3.CertificatePinner
2122
import okhttp3.Connection
2223
import okhttp3.EventListener
2324
import okhttp3.OkHttpClient
2425
import okhttp3.Protocol
2526
import okhttp3.Request
2627
import okhttp3.TlsVersion
28+
import okhttp3.mockwebserver.MockResponse
29+
import okhttp3.mockwebserver.MockWebServer
30+
import okhttp3.tls.internal.TlsUtil.localhost
2731
import org.junit.After
2832
import org.junit.Assert.assertEquals
33+
import org.junit.Assert.assertTrue
2934
import org.junit.Assert.fail
30-
import org.junit.Assume
35+
import org.junit.Assume.assumeNoException
36+
import org.junit.Assume.assumeTrue
3137
import org.junit.Before
38+
import org.junit.Rule
3239
import org.junit.Test
3340
import org.junit.runner.RunWith
3441
import java.net.InetAddress
3542
import java.net.UnknownHostException
43+
import javax.net.ssl.SSLPeerUnverifiedException
3644

3745
/**
3846
* Run with "./gradlew :android-test:connectedCheck" and make sure ANDROID_SDK_ROOT is set.
@@ -41,6 +49,11 @@ import java.net.UnknownHostException
4149
class OkHttpTest {
4250
private lateinit var client: OkHttpClient
4351

52+
@JvmField
53+
@Rule
54+
val server = MockWebServer()
55+
private val handshakeCertificates = localhost()
56+
4457
@Before
4558
fun createClient() {
4659
client = OkHttpClient.Builder()
@@ -91,13 +104,26 @@ class OkHttpTest {
91104
assertEquals(TlsVersion.TLS_1_2, response.handshake?.tlsVersion)
92105
}
93106
assertEquals(200, response.code)
94-
assertEquals("com.android.org.conscrypt.Java8FileDescriptorSocket", socketClass)
107+
assertTrue(socketClass?.startsWith("com.android.org.conscrypt.") == true)
108+
}
109+
}
110+
111+
@Test
112+
fun testHttpRequestNotBlockedOnLegacyAndroid() {
113+
assumeTrue(Build.VERSION.SDK_INT < 23)
114+
115+
val request = Request.Builder().url("http://squareup.com/robots.txt").build()
116+
117+
val response = client.newCall(request).execute()
118+
119+
response.use {
120+
assertEquals(200, response.code)
95121
}
96122
}
97123

98124
@Test
99125
fun testHttpRequestBlocked() {
100-
Assume.assumeTrue(Build.VERSION.SDK_INT >= 23)
126+
assumeTrue(Build.VERSION.SDK_INT >= 23)
101127

102128
val request = Request.Builder().url("http://api.twitter.com/robots.txt").build()
103129

@@ -108,11 +134,75 @@ class OkHttpTest {
108134
}
109135
}
110136

137+
@Test
138+
fun testMockWebserverRequest() {
139+
enableTls()
140+
141+
server.enqueue(MockResponse().setBody("abc"))
142+
143+
val request = Request.Builder().url(server.url("/")).build()
144+
145+
val response = client.newCall(request).execute()
146+
147+
response.use {
148+
assertEquals(200, response.code)
149+
}
150+
}
151+
152+
@Test
153+
fun testCertificatePinningFailure() {
154+
enableTls()
155+
156+
val certificatePinner = CertificatePinner.Builder()
157+
.add(server.hostName, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
158+
.build()
159+
client = client.newBuilder().certificatePinner(certificatePinner).build()
160+
161+
server.enqueue(MockResponse().setBody("abc"))
162+
163+
val request = Request.Builder().url(server.url("/")).build()
164+
165+
try {
166+
client.newCall(request).execute()
167+
fail()
168+
} catch (_: SSLPeerUnverifiedException) {
169+
}
170+
}
171+
172+
@Test
173+
fun testCertificatePinningSuccess() {
174+
enableTls()
175+
176+
val certificatePinner = CertificatePinner.Builder()
177+
.add(server.hostName,
178+
CertificatePinner.pin(handshakeCertificates.trustManager.acceptedIssuers[0]))
179+
.build()
180+
client = client.newBuilder().certificatePinner(certificatePinner).build()
181+
182+
server.enqueue(MockResponse().setBody("abc"))
183+
184+
val request = Request.Builder().url(server.url("/")).build()
185+
186+
val response = client.newCall(request).execute()
187+
188+
response.use {
189+
assertEquals(200, response.code)
190+
}
191+
}
192+
193+
private fun enableTls() {
194+
client = client.newBuilder()
195+
.sslSocketFactory(
196+
handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager)
197+
.build()
198+
server.useHttps(handshakeCertificates.sslSocketFactory(), false)
199+
}
200+
111201
private fun assumeNetwork() {
112202
try {
113203
InetAddress.getByName("www.google.com")
114204
} catch (uhe: UnknownHostException) {
115-
Assume.assumeNoException(uhe)
205+
assumeNoException(uhe)
116206
}
117207
}
118208
}

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ buildscript {
4747
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
4848
classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.18"
4949
classpath 'com.diffplug.spotless:spotless-plugin-gradle:3.21.1'
50-
classpath 'com.android.tools.build:gradle:3.4.1'
50+
classpath 'com.android.tools.build:gradle:3.4.2'
5151
}
5252

5353
repositories {
@@ -146,7 +146,7 @@ subprojects { project ->
146146
def alpnBootVersion = alpnBootVersion()
147147
if (alpnBootVersion != null) {
148148
dependencies {
149-
testCompile "org.mortbay.jetty.alpn:alpn-boot:$alpnBootVersion"
149+
testImplementation "org.mortbay.jetty.alpn:alpn-boot:$alpnBootVersion"
150150
}
151151
def alpnBootJar = configurations.testCompile.find { it.name.startsWith("alpn-boot-") }
152152
test {

settings.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
rootProject.name = 'okhttp-parent'
22

33
include ':mockwebserver'
4-
if (!properties.containsKey('android.injected.invoked.from.ide') && System.getenv(
5-
'ANDROID_SDK_ROOT') != null) {
6-
// Currently incompatible with Intellij
4+
5+
if (properties.containsKey('android.injected.invoked.from.ide') ||
6+
System.getenv('ANDROID_SDK_ROOT') != null) {
7+
// Currently incompatible with Intellij, use with Android Studio and from CLI with explicit flag
78
include ':android-test'
89
}
10+
911
include ':okcurl'
1012
include ':okhttp'
1113
include ':okhttp-brotli'

0 commit comments

Comments
 (0)