@@ -20,6 +20,7 @@ import com.intellij.openapi.extensions.PluginId
20
20
import com.intellij.openapi.util.SystemInfo
21
21
import okhttp3.OkHttpClient
22
22
import okhttp3.logging.HttpLoggingInterceptor
23
+ import org.zeroturnaround.exec.ProcessExecutor
23
24
import retrofit2.Retrofit
24
25
import retrofit2.converter.gson.GsonConverterFactory
25
26
import java.net.HttpURLConnection.HTTP_CREATED
@@ -41,16 +42,16 @@ class CoderRestClientService {
41
42
*
42
43
* @throws [AuthenticationResponseException] if authentication failed.
43
44
*/
44
- fun initClientSession (url : URL , token : String ): User {
45
- client = CoderRestClient (url, token)
45
+ fun initClientSession (url : URL , token : String , headerCommand : String? ): User {
46
+ client = CoderRestClient (url, token, headerCommand )
46
47
me = client.me()
47
48
buildVersion = client.buildInfo().version
48
49
isReady = true
49
50
return me
50
51
}
51
52
}
52
53
53
- class CoderRestClient (var url : URL , var token : String ) {
54
+ class CoderRestClient (var url : URL , var token : String , var headerCommand : String? ) {
54
55
private var httpClient: OkHttpClient
55
56
private var retroRestClient: CoderV2RestFacade
56
57
@@ -61,6 +62,16 @@ class CoderRestClient(var url: URL, var token: String) {
61
62
httpClient = OkHttpClient .Builder ()
62
63
.addInterceptor { it.proceed(it.request().newBuilder().addHeader(" Coder-Session-Token" , token).build()) }
63
64
.addInterceptor { it.proceed(it.request().newBuilder().addHeader(" User-Agent" , " Coder Gateway/${pluginVersion.version} (${SystemInfo .getOsNameAndVersion()} ; ${SystemInfo .OS_ARCH } )" ).build()) }
65
+ .addInterceptor {
66
+ var request = it.request()
67
+ val headers = getHeaders(url, headerCommand)
68
+ if (headers.size > 0 ) {
69
+ val builder = request.newBuilder()
70
+ headers.forEach { h -> builder.addHeader(h.key, h.value) }
71
+ request = builder.build()
72
+ }
73
+ it.proceed(request)
74
+ }
64
75
// this should always be last if we want to see previous interceptors logged
65
76
.addInterceptor(HttpLoggingInterceptor ().apply { setLevel(HttpLoggingInterceptor .Level .BASIC ) })
66
77
.build()
@@ -141,4 +152,45 @@ class CoderRestClient(var url: URL, var token: String) {
141
152
142
153
return buildResponse.body()!!
143
154
}
155
+
156
+ companion object {
157
+ private val newlineRegex = " \r ?\n " .toRegex()
158
+ private val endingNewlineRegex = " \r ?\n $" .toRegex()
159
+
160
+ // TODO: This really only needs to be a private function, but
161
+ // unfortunately it is not possible to test the client because it fails
162
+ // on the plugin manager core call and I do not know how to fix it. So,
163
+ // for now make this static and test it directly instead.
164
+ @JvmStatic
165
+ fun getHeaders (url : URL , headerCommand : String? ): Map <String , String > {
166
+ if (headerCommand.isNullOrBlank()) {
167
+ return emptyMap()
168
+ }
169
+ val (shell, caller) = when (getOS()) {
170
+ OS .WINDOWS -> Pair (" cmd.exe" , " /c" )
171
+ else -> Pair (" sh" , " -c" )
172
+ }
173
+ return ProcessExecutor ()
174
+ .command(shell, caller, headerCommand)
175
+ .environment(" CODER_URL" , url.toString())
176
+ .exitValues(0 )
177
+ .readOutput(true )
178
+ .execute()
179
+ .outputUTF8()
180
+ .replaceFirst(endingNewlineRegex, " " )
181
+ .split(newlineRegex)
182
+ .associate {
183
+ // Header names cannot be blank or contain whitespace and
184
+ // the Coder CLI requires that there be an equals sign (the
185
+ // value can be blank though). The second case is taken
186
+ // care of by the destructure here, as it will throw if
187
+ // there are not enough parts.
188
+ val (name, value) = it.split(" =" , limit= 2 )
189
+ if (name.contains(" " ) || name == " " ) {
190
+ throw Exception (" \" $name \" is not a valid header name" )
191
+ }
192
+ name to value
193
+ }
194
+ }
195
+ }
144
196
}
0 commit comments