Skip to content

Commit 2fa2070

Browse files
authored
Change HTTP client to OkHttp (#68)
* Update HTTP client to OkHttp - Replace URL and HttpsURLConnection with OkHttpClient - Update dependencies and gradle configuration * Update OkHttp MockWebServer dependency - Update `okhttp3-mockwebserver` to use `mockwebserver3` - Add exclusion for `META-INF/versions/*/OSGI-INF/MANIFEST.MF` in packaging resources
1 parent a1a8920 commit 2fa2070

File tree

4 files changed

+61
-33
lines changed

4 files changed

+61
-33
lines changed

gradle/libs.versions.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ dokka = "2.0.0"
1313
junit = "4.13.2"
1414
kotlin = "2.2.0"
1515
mockk = "1.14.4"
16-
okhttp3 = "5.1.0"
16+
okhttp = "5.3.0"
1717

1818
[libraries]
1919
androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
@@ -36,7 +36,8 @@ conscrypt = { module = "org.conscrypt:conscrypt-android", version.ref = "conscry
3636
junit = { module = "junit:junit", version.ref = "junit" }
3737
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
3838
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
39-
okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp3" }
39+
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
40+
okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver3", version.ref = "okhttp" }
4041

4142
[plugins]
4243
android-application = { id = "com.android.application", version.ref = "agp" }

lib/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ android {
6767
packaging {
6868
resources {
6969
excludes.add("META-INF/*.md")
70+
excludes.add("META-INF/versions/*/OSGI-INF/MANIFEST.MF")
7071
}
7172
}
7273

sample-app/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ android {
4343
dependencies {
4444
implementation(libs.androidx.core)
4545
implementation(libs.androidx.lifecycle.runtime)
46-
4746
implementation(libs.androidx.appcompat)
48-
4947
implementation(libs.androidx.activityCompose)
48+
5049
implementation(platform(libs.compose.bom))
5150
implementation(libs.compose.material3)
5251
implementation(libs.compose.ui.base)
5352
implementation(libs.compose.ui.graphics)
5453
debugImplementation(libs.compose.ui.toolingPreview)
5554
implementation(libs.compose.runtime.livedata)
5655

56+
implementation(libs.okhttp)
57+
5758
implementation(project(":lib"))
5859
}

sample-app/src/main/java/at/bitfire/cert4android/demo/MainActivity.kt

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package at.bitfire.cert4android.demo
33
import android.Manifest
44
import android.annotation.SuppressLint
55
import android.app.Application
6+
import android.content.Context
67
import android.content.pm.PackageManager
7-
import android.net.SSLCertificateSocketFactory
88
import android.os.Build
99
import android.os.Bundle
1010
import android.util.Log
@@ -37,10 +37,12 @@ import at.bitfire.cert4android.ThemeManager
3737
import kotlinx.coroutines.Dispatchers
3838
import kotlinx.coroutines.flow.MutableStateFlow
3939
import kotlinx.coroutines.launch
40+
import okhttp3.OkHttpClient
41+
import okhttp3.Request
42+
import okhttp3.internal.tls.OkHostnameVerifier
4043
import org.apache.http.conn.ssl.AllowAllHostnameVerifier
41-
import org.apache.http.conn.ssl.StrictHostnameVerifier
42-
import java.net.URL
4344
import javax.net.ssl.HttpsURLConnection
45+
import javax.net.ssl.SSLContext
4446

4547
class MainActivity : ComponentActivity() {
4648

@@ -66,15 +68,15 @@ class MainActivity : ComponentActivity() {
6668
}
6769

6870
Button(onClick = {
69-
model.testAccess("https://www.github.com")
71+
model.testAccess("https://icloud.com")
7072
}, modifier = Modifier.padding(top = 16.dp)) {
71-
Text("Access normal URL with trusted system certs")
73+
Text("Access icloud.com with trusted system certs")
7274
}
7375

7476
Button(onClick = {
75-
model.testAccess("https://www.github.com", trustSystemCerts = false)
77+
model.testAccess("https://icloud.com", trustSystemCerts = false)
7678
}, modifier = Modifier.padding(top = 16.dp)) {
77-
Text("Access normal URL with distrusted system certs")
79+
Text("Access icloud.com with distrusted system certs")
7880
}
7981

8082
Button(onClick = {
@@ -125,11 +127,8 @@ class MainActivity : ComponentActivity() {
125127

126128
class Model(application: Application): AndroidViewModel(application) {
127129

128-
companion object {
129-
130-
const val TAG = "cert4android.sample-app"
131-
132-
}
130+
val context: Context
131+
get() = getApplication()
133132

134133
val appInForeground = MutableStateFlow(true)
135134
val resultMessage = MutableLiveData<String>()
@@ -141,7 +140,7 @@ class MainActivity : ComponentActivity() {
141140
}
142141

143142
fun reset() = viewModelScope.launch(Dispatchers.IO) {
144-
CustomCertStore.getInstance(getApplication()).clearUserDecisions()
143+
CustomCertStore.getInstance(context).clearUserDecisions()
145144
}
146145

147146
fun setInForeground(foreground: Boolean) {
@@ -150,31 +149,57 @@ class MainActivity : ComponentActivity() {
150149

151150
fun testAccess(url: String, trustSystemCerts: Boolean = true) = viewModelScope.launch(Dispatchers.IO) {
152151
try {
153-
val urlConn = URL(url).openConnection() as HttpsURLConnection
152+
val client = buildClient(trustSystemCerts)
154153

155-
// set cert4android TrustManager and HostnameVerifier
156-
val certMgr = CustomCertManager(
157-
getApplication(),
158-
trustSystemCerts = trustSystemCerts,
159-
appInForeground = appInForeground
154+
// access sample URL
155+
val call = client.newCall(
156+
Request.Builder()
157+
.get()
158+
.url(url)
159+
.build()
160160
)
161-
urlConn.hostnameVerifier = certMgr.HostnameVerifier(StrictHostnameVerifier())
162-
urlConn.sslSocketFactory = object : SSLCertificateSocketFactory(/* handshakeTimeoutMillis = */ 1000) {
163-
init {
164-
setTrustManagers(arrayOf(certMgr))
165-
}
161+
call.execute().use { response ->
162+
// log result
163+
Log.i(TAG, "testAccess(): HTTP ${response.code}")
164+
resultMessage.postValue("${response.code} ${response.message}")
166165
}
167-
168-
// access sample URL
169-
Log.i(TAG, "testAccess(): HTTP ${urlConn.responseCode}")
170-
resultMessage.postValue("${urlConn.responseCode} ${urlConn.responseMessage}")
171-
urlConn.inputStream.close()
172166
} catch (e: Exception) {
173167
resultMessage.postValue("testAccess() ERROR: ${e.message}")
174168
Log.w(TAG, "testAccess(): ERROR: ${e.message}")
175169
}
176170
}
177171

172+
fun buildClient(trustSystemCerts: Boolean): OkHttpClient {
173+
val builder = OkHttpClient.Builder()
174+
175+
// set cert4android TrustManager and HostnameVerifier
176+
val certManager = CustomCertManager(
177+
context,
178+
trustSystemCerts = trustSystemCerts,
179+
appInForeground = appInForeground
180+
)
181+
182+
val sslContext = SSLContext.getInstance("TLS")
183+
sslContext.init(
184+
/* km = */ null,
185+
/* tm = */ arrayOf(certManager),
186+
/* random = */ null
187+
)
188+
builder
189+
.sslSocketFactory(sslContext.socketFactory, certManager)
190+
.hostnameVerifier(certManager.HostnameVerifier(OkHostnameVerifier))
191+
192+
// add cache
193+
//builder.cache(Cache(context.cacheDir, 10 * 1024 * 1024))
194+
195+
return builder.build()
196+
}
197+
198+
}
199+
200+
201+
companion object {
202+
const val TAG = "cert4android.sample-app"
178203
}
179204

180205
}

0 commit comments

Comments
 (0)