Skip to content

Commit 4ae1b1a

Browse files
feat(client): add https config options
1 parent e9e1c40 commit 4ae1b1a

File tree

4 files changed

+183
-4
lines changed

4 files changed

+183
-4
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,28 @@ FinchClient client = FinchOkHttpClient.builder()
445445
.build();
446446
```
447447

448+
### HTTPS
449+
450+
> [!NOTE]
451+
> Most applications should not call these methods, and instead use the system defaults. The defaults include
452+
> special optimizations that can be lost if the implementations are modified.
453+
454+
To configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods:
455+
456+
```java
457+
import com.tryfinch.api.client.FinchClient;
458+
import com.tryfinch.api.client.okhttp.FinchOkHttpClient;
459+
460+
FinchClient client = FinchOkHttpClient.builder()
461+
.fromEnv()
462+
// If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa.
463+
.sslSocketFactory(yourSSLSocketFactory)
464+
.trustManager(yourTrustManager)
465+
.hostnameVerifier(yourHostnameVerifier)
466+
.accessToken("My Access Token")
467+
.build();
468+
```
469+
448470
### Custom HTTP client
449471

450472
The SDK consists of three artifacts:

finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import java.time.Clock
1515
import java.time.Duration
1616
import java.util.Optional
1717
import java.util.concurrent.Executor
18+
import javax.net.ssl.HostnameVerifier
19+
import javax.net.ssl.SSLSocketFactory
20+
import javax.net.ssl.X509TrustManager
1821
import kotlin.jvm.optionals.getOrNull
1922

2023
class FinchOkHttpClient private constructor() {
@@ -32,8 +35,62 @@ class FinchOkHttpClient private constructor() {
3235

3336
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
3437
private var proxy: Proxy? = null
38+
private var sslSocketFactory: SSLSocketFactory? = null
39+
private var trustManager: X509TrustManager? = null
40+
private var hostnameVerifier: HostnameVerifier? = null
3541

36-
fun proxy(proxy: Proxy) = apply { this.proxy = proxy }
42+
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
43+
44+
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
45+
fun proxy(proxy: Optional<Proxy>) = proxy(proxy.getOrNull())
46+
47+
/**
48+
* The socket factory used to secure HTTPS connections.
49+
*
50+
* If this is set, then [trustManager] must also be set.
51+
*
52+
* If unset, then the system default is used. Most applications should not call this method,
53+
* and instead use the system default. The default include special optimizations that can be
54+
* lost if the implementation is modified.
55+
*/
56+
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
57+
this.sslSocketFactory = sslSocketFactory
58+
}
59+
60+
/** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */
61+
fun sslSocketFactory(sslSocketFactory: Optional<SSLSocketFactory>) =
62+
sslSocketFactory(sslSocketFactory.getOrNull())
63+
64+
/**
65+
* The trust manager used to secure HTTPS connections.
66+
*
67+
* If this is set, then [sslSocketFactory] must also be set.
68+
*
69+
* If unset, then the system default is used. Most applications should not call this method,
70+
* and instead use the system default. The default include special optimizations that can be
71+
* lost if the implementation is modified.
72+
*/
73+
fun trustManager(trustManager: X509TrustManager?) = apply {
74+
this.trustManager = trustManager
75+
}
76+
77+
/** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */
78+
fun trustManager(trustManager: Optional<X509TrustManager>) =
79+
trustManager(trustManager.getOrNull())
80+
81+
/**
82+
* The verifier used to confirm that response certificates apply to requested hostnames for
83+
* HTTPS connections.
84+
*
85+
* If unset, then a default hostname verifier is used.
86+
*/
87+
fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
88+
this.hostnameVerifier = hostnameVerifier
89+
}
90+
91+
/** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */
92+
fun hostnameVerifier(hostnameVerifier: Optional<HostnameVerifier>) =
93+
hostnameVerifier(hostnameVerifier.getOrNull())
3794

3895
/**
3996
* Whether to throw an exception if any of the Jackson versions detected at runtime are
@@ -190,7 +247,13 @@ class FinchOkHttpClient private constructor() {
190247
FinchClientImpl(
191248
clientOptions
192249
.httpClient(
193-
OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build()
250+
OkHttpClient.builder()
251+
.timeout(clientOptions.timeout())
252+
.proxy(proxy)
253+
.sslSocketFactory(sslSocketFactory)
254+
.trustManager(trustManager)
255+
.hostnameVerifier(hostnameVerifier)
256+
.build()
194257
)
195258
.build()
196259
)

finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import java.time.Clock
1515
import java.time.Duration
1616
import java.util.Optional
1717
import java.util.concurrent.Executor
18+
import javax.net.ssl.HostnameVerifier
19+
import javax.net.ssl.SSLSocketFactory
20+
import javax.net.ssl.X509TrustManager
1821
import kotlin.jvm.optionals.getOrNull
1922

2023
class FinchOkHttpClientAsync private constructor() {
@@ -32,8 +35,62 @@ class FinchOkHttpClientAsync private constructor() {
3235

3336
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
3437
private var proxy: Proxy? = null
38+
private var sslSocketFactory: SSLSocketFactory? = null
39+
private var trustManager: X509TrustManager? = null
40+
private var hostnameVerifier: HostnameVerifier? = null
3541

36-
fun proxy(proxy: Proxy) = apply { this.proxy = proxy }
42+
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
43+
44+
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
45+
fun proxy(proxy: Optional<Proxy>) = proxy(proxy.getOrNull())
46+
47+
/**
48+
* The socket factory used to secure HTTPS connections.
49+
*
50+
* If this is set, then [trustManager] must also be set.
51+
*
52+
* If unset, then the system default is used. Most applications should not call this method,
53+
* and instead use the system default. The default include special optimizations that can be
54+
* lost if the implementation is modified.
55+
*/
56+
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
57+
this.sslSocketFactory = sslSocketFactory
58+
}
59+
60+
/** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */
61+
fun sslSocketFactory(sslSocketFactory: Optional<SSLSocketFactory>) =
62+
sslSocketFactory(sslSocketFactory.getOrNull())
63+
64+
/**
65+
* The trust manager used to secure HTTPS connections.
66+
*
67+
* If this is set, then [sslSocketFactory] must also be set.
68+
*
69+
* If unset, then the system default is used. Most applications should not call this method,
70+
* and instead use the system default. The default include special optimizations that can be
71+
* lost if the implementation is modified.
72+
*/
73+
fun trustManager(trustManager: X509TrustManager?) = apply {
74+
this.trustManager = trustManager
75+
}
76+
77+
/** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */
78+
fun trustManager(trustManager: Optional<X509TrustManager>) =
79+
trustManager(trustManager.getOrNull())
80+
81+
/**
82+
* The verifier used to confirm that response certificates apply to requested hostnames for
83+
* HTTPS connections.
84+
*
85+
* If unset, then a default hostname verifier is used.
86+
*/
87+
fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
88+
this.hostnameVerifier = hostnameVerifier
89+
}
90+
91+
/** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */
92+
fun hostnameVerifier(hostnameVerifier: Optional<HostnameVerifier>) =
93+
hostnameVerifier(hostnameVerifier.getOrNull())
3794

3895
/**
3996
* Whether to throw an exception if any of the Jackson versions detected at runtime are
@@ -190,7 +247,13 @@ class FinchOkHttpClientAsync private constructor() {
190247
FinchClientAsyncImpl(
191248
clientOptions
192249
.httpClient(
193-
OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build()
250+
OkHttpClient.builder()
251+
.timeout(clientOptions.timeout())
252+
.proxy(proxy)
253+
.sslSocketFactory(sslSocketFactory)
254+
.trustManager(trustManager)
255+
.hostnameVerifier(hostnameVerifier)
256+
.build()
194257
)
195258
.build()
196259
)

finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import java.io.InputStream
1414
import java.net.Proxy
1515
import java.time.Duration
1616
import java.util.concurrent.CompletableFuture
17+
import javax.net.ssl.HostnameVerifier
18+
import javax.net.ssl.SSLSocketFactory
19+
import javax.net.ssl.X509TrustManager
1720
import okhttp3.Call
1821
import okhttp3.Callback
1922
import okhttp3.HttpUrl.Companion.toHttpUrl
@@ -189,13 +192,28 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
189192

190193
private var timeout: Timeout = Timeout.default()
191194
private var proxy: Proxy? = null
195+
private var sslSocketFactory: SSLSocketFactory? = null
196+
private var trustManager: X509TrustManager? = null
197+
private var hostnameVerifier: HostnameVerifier? = null
192198

193199
fun timeout(timeout: Timeout) = apply { this.timeout = timeout }
194200

195201
fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build())
196202

197203
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
198204

205+
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
206+
this.sslSocketFactory = sslSocketFactory
207+
}
208+
209+
fun trustManager(trustManager: X509TrustManager?) = apply {
210+
this.trustManager = trustManager
211+
}
212+
213+
fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
214+
this.hostnameVerifier = hostnameVerifier
215+
}
216+
199217
fun build(): OkHttpClient =
200218
OkHttpClient(
201219
okhttp3.OkHttpClient.Builder()
@@ -204,6 +222,19 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
204222
.writeTimeout(timeout.write())
205223
.callTimeout(timeout.request())
206224
.proxy(proxy)
225+
.apply {
226+
val sslSocketFactory = sslSocketFactory
227+
val trustManager = trustManager
228+
if (sslSocketFactory != null && trustManager != null) {
229+
sslSocketFactory(sslSocketFactory, trustManager)
230+
} else {
231+
check((sslSocketFactory != null) == (trustManager != null)) {
232+
"Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set"
233+
}
234+
}
235+
236+
hostnameVerifier?.let(::hostnameVerifier)
237+
}
207238
.build()
208239
.apply {
209240
// We usually make all our requests to the same host so it makes sense to

0 commit comments

Comments
 (0)