Skip to content

Commit 70df926

Browse files
committed
Add headers to API requests
1 parent d4807fb commit 70df926

File tree

6 files changed

+88
-6
lines changed

6 files changed

+88
-6
lines changed

src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
150150
if (token == null) { // User aborted.
151151
throw IllegalArgumentException("Unable to connect to $deploymentURL, $TOKEN is missing")
152152
}
153-
val client = CoderRestClient(deploymentURL, token.first)
153+
val client = CoderRestClient(deploymentURL, token.first, settings.headerCommand)
154154
return try {
155155
Pair(client, client.me().username)
156156
} catch (ex: AuthenticationResponseException) {

src/main/kotlin/com/coder/gateway/sdk/CoderRestClientService.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ class CoderRestClientService {
4141
*
4242
* @throws [AuthenticationResponseException] if authentication failed.
4343
*/
44-
fun initClientSession(url: URL, token: String): User {
45-
client = CoderRestClient(url, token)
44+
fun initClientSession(url: URL, token: String, headerCommand: String?): User {
45+
client = CoderRestClient(url, token, headerCommand)
4646
me = client.me()
4747
buildVersion = client.buildInfo().version
4848
isReady = true
4949
return me
5050
}
5151
}
5252

53-
class CoderRestClient(var url: URL, var token: String) {
53+
class CoderRestClient(var url: URL, var token: String, var headerCommand: String?) {
5454
private var httpClient: OkHttpClient
5555
private var retroRestClient: CoderV2RestFacade
5656

@@ -61,6 +61,16 @@ class CoderRestClient(var url: URL, var token: String) {
6161
httpClient = OkHttpClient.Builder()
6262
.addInterceptor { it.proceed(it.request().newBuilder().addHeader("Coder-Session-Token", token).build()) }
6363
.addInterceptor { it.proceed(it.request().newBuilder().addHeader("User-Agent", "Coder Gateway/${pluginVersion.version} (${SystemInfo.getOsNameAndVersion()}; ${SystemInfo.OS_ARCH})").build()) }
64+
.addInterceptor {
65+
var request = it.request()
66+
val headers = getHeaders(headerCommand)
67+
if (headers.size > 0) {
68+
val builder = request.newBuilder()
69+
headers.forEach { builder.addHeader(it.key, it.value) }
70+
request = builder.build()
71+
}
72+
it.proceed(request)
73+
}
6474
// this should always be last if we want to see previous interceptors logged
6575
.addInterceptor(HttpLoggingInterceptor().apply { setLevel(HttpLoggingInterceptor.Level.BASIC) })
6676
.build()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.coder.gateway.sdk
2+
3+
import org.zeroturnaround.exec.ProcessExecutor
4+
5+
fun getHeaders(headerCommand: String?): Map<String, String> {
6+
val (shell, caller) = when (getOS()) {
7+
OS.WINDOWS -> Pair("cmd.exe", "/c")
8+
else -> Pair("sh", "-c")
9+
}
10+
return ProcessExecutor()
11+
.command(shell, caller, headerCommand)
12+
.exitValues(0)
13+
.readOutput(true)
14+
.execute()
15+
.outputUTF8()
16+
.split("\n")
17+
.associate {
18+
val (name, value) = it.split("=")
19+
name to value
20+
}
21+
}

src/main/kotlin/com/coder/gateway/views/CoderGatewayRecentWorkspaceConnectionsView.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.coder.gateway.sdk.toURL
1313
import com.coder.gateway.sdk.v2.models.WorkspaceStatus
1414
import com.coder.gateway.sdk.v2.models.toAgentModels
1515
import com.coder.gateway.services.CoderRecentWorkspaceConnectionsService
16+
import com.coder.gateway.services.CoderSettingsState
1617
import com.coder.gateway.toWorkspaceParams
1718
import com.intellij.icons.AllIcons
1819
import com.intellij.ide.BrowserUtil
@@ -72,6 +73,7 @@ data class DeploymentInfo(
7273
)
7374

7475
class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback: (Component) -> Unit) : GatewayRecentConnections, Disposable {
76+
private val settings: CoderSettingsState = service()
7577
private val recentConnectionsService = service<CoderRecentWorkspaceConnectionsService>()
7678
private val cs = CoroutineScope(Dispatchers.Main)
7779

@@ -259,7 +261,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
259261
deployments[dir] ?: try {
260262
val url = Path.of(dir).resolve("url").readText()
261263
val token = Path.of(dir).resolve("session").readText()
262-
DeploymentInfo(CoderRestClient(url.toURL(), token))
264+
DeploymentInfo(CoderRestClient(url.toURL(), token, settings.headerCommand))
263265
} catch (e: Exception) {
264266
logger.error("Unable to create client from $dir", e)
265267
DeploymentInfo(error = "Error trying to read $dir: ${e.message}")

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
527527
*/
528528
private fun authenticate(url: URL, token: String) {
529529
logger.info("Authenticating to $url...")
530-
clientService.initClientSession(url, token)
530+
clientService.initClientSession(url, token, settings.headerCommand)
531531

532532
try {
533533
logger.info("Checking compatibility with Coder version ${clientService.buildVersion}...")

src/test/groovy/Headers.groovy

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.coder.gateway.sdk
2+
3+
import spock.lang.Shared
4+
import spock.lang.Specification
5+
import spock.lang.Unroll
6+
7+
import java.nio.file.Files
8+
import java.nio.file.Path
9+
import java.nio.file.attribute.AclEntry
10+
import java.nio.file.attribute.AclEntryPermission
11+
import java.nio.file.attribute.AclEntryType
12+
import java.nio.file.attribute.AclFileAttributeView
13+
14+
@Unroll
15+
class HeadersTest extends Specification {
16+
def "gets headers"() {
17+
expect:
18+
getHeaders(command) == expected
19+
20+
where:
21+
command | expected
22+
"printf foo=bar'\n'baz=qux" | ["foo": "bar", "baz": "qux"]
23+
"printf foo=bar'\r\n'baz=qux" | ["foo": "bar", "baz": "qux"]
24+
"printf foo=bar'\r\n'" | ["foo": "bar"]
25+
"printf foo=bar" | ["foo": "bar"]
26+
"printf foo=bar=" | ["foo": "bar="]
27+
"printf foo=bar=baz" | ["foo": "bar=baz"]
28+
"printf foo=" | ["foo": ""]
29+
}
30+
31+
def "fails to get headers"() {
32+
when:
33+
getHeaders(command)
34+
35+
then:
36+
thrown(Exception)
37+
38+
where:
39+
command
40+
"printf foo=bar'\r\n\r\n'"
41+
"printf '\r\n'foo=bar"
42+
"printf =foo"
43+
"printf foo"
44+
"printf ' =foo'"
45+
"printf 'foo =bar'"
46+
"printf 'foo foo=bar'"
47+
"printf ''"
48+
}
49+
}

0 commit comments

Comments
 (0)