Skip to content

Commit 5d3eef3

Browse files
committed
impl: extract OkHttpClient building logic into a reusable utility
The commit will allow later the reuse of the same http client setup (or similar because some of the interceptors will not be the same) for CLI downloading
1 parent 6ab431e commit 5d3eef3

File tree

2 files changed

+94
-73
lines changed

2 files changed

+94
-73
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.coder.toolbox.sdk
2+
3+
import com.coder.toolbox.CoderToolboxContext
4+
import com.coder.toolbox.sdk.interceptors.LoggingInterceptor
5+
import com.coder.toolbox.util.CoderHostnameVerifier
6+
import com.coder.toolbox.util.coderSocketFactory
7+
import com.coder.toolbox.util.coderTrustManagers
8+
import com.coder.toolbox.util.getArch
9+
import com.coder.toolbox.util.getHeaders
10+
import com.coder.toolbox.util.getOS
11+
import com.jetbrains.toolbox.api.remoteDev.connection.ProxyAuth
12+
import okhttp3.Credentials
13+
import okhttp3.OkHttpClient
14+
import java.net.URL
15+
import javax.net.ssl.X509TrustManager
16+
17+
object CoderHttpClientBuilder {
18+
fun build(
19+
context: CoderToolboxContext,
20+
pluginVersion: String,
21+
url: URL,
22+
token: String?,
23+
): OkHttpClient {
24+
val settings = context.settingsStore.readOnly()
25+
26+
val socketFactory = coderSocketFactory(settings.tls)
27+
val trustManagers = coderTrustManagers(settings.tls.caPath)
28+
var builder = OkHttpClient.Builder()
29+
30+
if (context.proxySettings.getProxy() != null) {
31+
context.logger.info("proxy: ${context.proxySettings.getProxy()}")
32+
builder.proxy(context.proxySettings.getProxy())
33+
} else if (context.proxySettings.getProxySelector() != null) {
34+
context.logger.info("proxy selector: ${context.proxySettings.getProxySelector()}")
35+
builder.proxySelector(context.proxySettings.getProxySelector()!!)
36+
}
37+
38+
// Note: This handles only HTTP/HTTPS proxy authentication.
39+
// SOCKS5 proxy authentication is currently not supported due to limitations described in:
40+
// https://youtrack.jetbrains.com/issue/TBX-14532/Missing-proxy-authentication-settings#focus=Comments-27-12265861.0-0
41+
builder.proxyAuthenticator { _, response ->
42+
val proxyAuth = context.proxySettings.getProxyAuth()
43+
if (proxyAuth == null || proxyAuth !is ProxyAuth.Basic) {
44+
return@proxyAuthenticator null
45+
}
46+
val credentials = Credentials.basic(proxyAuth.username, proxyAuth.password)
47+
response.request.newBuilder()
48+
.header("Proxy-Authorization", credentials)
49+
.build()
50+
}
51+
52+
if (context.settingsStore.requireTokenAuth) {
53+
if (token.isNullOrBlank()) {
54+
throw IllegalStateException("Token is required for $url deployment")
55+
}
56+
builder = builder.addInterceptor {
57+
it.proceed(
58+
it.request().newBuilder().addHeader("Coder-Session-Token", token).build()
59+
)
60+
}
61+
}
62+
63+
return builder
64+
.sslSocketFactory(socketFactory, trustManagers[0] as X509TrustManager)
65+
.hostnameVerifier(CoderHostnameVerifier(settings.tls.altHostname))
66+
.retryOnConnectionFailure(true)
67+
.addInterceptor {
68+
it.proceed(
69+
it.request().newBuilder().addHeader(
70+
"User-Agent",
71+
"Coder Toolbox/$pluginVersion (${getOS()}; ${getArch()})",
72+
).build(),
73+
)
74+
}
75+
.addInterceptor {
76+
var request = it.request()
77+
val headers = getHeaders(url, settings.headerCommand)
78+
if (headers.isNotEmpty()) {
79+
val reqBuilder = request.newBuilder()
80+
headers.forEach { h -> reqBuilder.addHeader(h.key, h.value) }
81+
request = reqBuilder.build()
82+
}
83+
it.proceed(request)
84+
}
85+
.addInterceptor(LoggingInterceptor(context))
86+
.build()
87+
}
88+
}

src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import com.coder.toolbox.sdk.convertors.LoggingConverterFactory
77
import com.coder.toolbox.sdk.convertors.OSConverter
88
import com.coder.toolbox.sdk.convertors.UUIDConverter
99
import com.coder.toolbox.sdk.ex.APIResponseException
10-
import com.coder.toolbox.sdk.interceptors.LoggingInterceptor
1110
import com.coder.toolbox.sdk.v2.CoderV2RestFacade
1211
import com.coder.toolbox.sdk.v2.models.ApiErrorResponse
1312
import com.coder.toolbox.sdk.v2.models.BuildInfo
@@ -21,23 +20,14 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceBuildReason
2120
import com.coder.toolbox.sdk.v2.models.WorkspaceResource
2221
import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
2322
import com.coder.toolbox.sdk.v2.models.WorkspaceTransition
24-
import com.coder.toolbox.util.CoderHostnameVerifier
25-
import com.coder.toolbox.util.coderSocketFactory
26-
import com.coder.toolbox.util.coderTrustManagers
27-
import com.coder.toolbox.util.getArch
28-
import com.coder.toolbox.util.getHeaders
29-
import com.coder.toolbox.util.getOS
30-
import com.jetbrains.toolbox.api.remoteDev.connection.ProxyAuth
3123
import com.squareup.moshi.Moshi
32-
import okhttp3.Credentials
3324
import okhttp3.OkHttpClient
3425
import retrofit2.Response
3526
import retrofit2.Retrofit
3627
import retrofit2.converter.moshi.MoshiConverterFactory
3728
import java.net.HttpURLConnection
3829
import java.net.URL
3930
import java.util.UUID
40-
import javax.net.ssl.X509TrustManager
4131

4232
/**
4333
* An HTTP client that can make requests to the Coder API.
@@ -50,7 +40,6 @@ open class CoderRestClient(
5040
val token: String?,
5141
private val pluginVersion: String = "development",
5242
) {
53-
private val settings = context.settingsStore.readOnly()
5443
private lateinit var moshi: Moshi
5544
private lateinit var httpClient: OkHttpClient
5645
private lateinit var retroRestClient: CoderV2RestFacade
@@ -71,68 +60,12 @@ open class CoderRestClient(
7160
.add(UUIDConverter())
7261
.build()
7362

74-
val socketFactory = coderSocketFactory(settings.tls)
75-
val trustManagers = coderTrustManagers(settings.tls.caPath)
76-
var builder = OkHttpClient.Builder()
77-
78-
if (context.proxySettings.getProxy() != null) {
79-
context.logger.info("proxy: ${context.proxySettings.getProxy()}")
80-
builder.proxy(context.proxySettings.getProxy())
81-
} else if (context.proxySettings.getProxySelector() != null) {
82-
context.logger.info("proxy selector: ${context.proxySettings.getProxySelector()}")
83-
builder.proxySelector(context.proxySettings.getProxySelector()!!)
84-
}
85-
86-
// Note: This handles only HTTP/HTTPS proxy authentication.
87-
// SOCKS5 proxy authentication is currently not supported due to limitations described in:
88-
// https://youtrack.jetbrains.com/issue/TBX-14532/Missing-proxy-authentication-settings#focus=Comments-27-12265861.0-0
89-
builder.proxyAuthenticator { _, response ->
90-
val proxyAuth = context.proxySettings.getProxyAuth()
91-
if (proxyAuth == null || proxyAuth !is ProxyAuth.Basic) {
92-
return@proxyAuthenticator null
93-
}
94-
val credentials = Credentials.basic(proxyAuth.username, proxyAuth.password)
95-
response.request.newBuilder()
96-
.header("Proxy-Authorization", credentials)
97-
.build()
98-
}
99-
100-
if (context.settingsStore.requireTokenAuth) {
101-
if (token.isNullOrBlank()) {
102-
throw IllegalStateException("Token is required for $url deployment")
103-
}
104-
builder = builder.addInterceptor {
105-
it.proceed(
106-
it.request().newBuilder().addHeader("Coder-Session-Token", token).build()
107-
)
108-
}
109-
}
110-
111-
httpClient =
112-
builder
113-
.sslSocketFactory(socketFactory, trustManagers[0] as X509TrustManager)
114-
.hostnameVerifier(CoderHostnameVerifier(settings.tls.altHostname))
115-
.retryOnConnectionFailure(true)
116-
.addInterceptor {
117-
it.proceed(
118-
it.request().newBuilder().addHeader(
119-
"User-Agent",
120-
"Coder Toolbox/$pluginVersion (${getOS()}; ${getArch()})",
121-
).build(),
122-
)
123-
}
124-
.addInterceptor {
125-
var request = it.request()
126-
val headers = getHeaders(url, settings.headerCommand)
127-
if (headers.isNotEmpty()) {
128-
val reqBuilder = request.newBuilder()
129-
headers.forEach { h -> reqBuilder.addHeader(h.key, h.value) }
130-
request = reqBuilder.build()
131-
}
132-
it.proceed(request)
133-
}
134-
.addInterceptor(LoggingInterceptor(context))
135-
.build()
63+
httpClient = CoderHttpClientBuilder.build(
64+
context,
65+
pluginVersion,
66+
url,
67+
token
68+
)
13669

13770
retroRestClient =
13871
Retrofit.Builder().baseUrl(url.toString()).client(httpClient)

0 commit comments

Comments
 (0)