Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4ca9190

Browse files
authored
fix: socket connection timeout (#53)
Context:- okhttp uses an HTTP/2 connection to the coder rest api in order toresolves the workspaces.- HTTP/2 uses a single TCP connection for multiple requests(multiplexing). If the connection is idle, the http server can closethat connection, with client side ending in a socket timeout if itdoesn't detect the drop in time.- similarly on the client side, if the OS goes into sleep mode, theconnection might have been interrupted. HTTP/2 doesn't always detectthis quickly, leading to stale streams when Toolbox wakes up.Implementation:- we could try to force the client to use HTTP/1 which creates a TCPconnection for each request, but from my testing it seems thatconfiguring a retry strategy when a client attempts to reuse a TCPconnection that has unexpectedly closed plus detecting large gapsbetween the last poll time and socket timeout time allows us to resetthe client and create fresh TCP connections.-resolves#13
1 parente6af3ca commit4ca9190

File tree

5 files changed

+32
-21
lines changed

5 files changed

+32
-21
lines changed

‎src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,20 @@ import kotlinx.coroutines.isActive
3030
importkotlinx.coroutines.launch
3131
importkotlinx.coroutines.selects.onTimeout
3232
importkotlinx.coroutines.selects.select
33-
importokhttp3.OkHttpClient
33+
importjava.net.SocketTimeoutException
3434
importjava.net.URI
3535
importjava.net.URL
3636
importkotlin.coroutines.cancellation.CancellationException
3737
importkotlin.time.Duration.Companion.seconds
38+
importkotlin.time.TimeSource
3839
importcom.jetbrains.toolbox.api.ui.components.AccountDropdownFieldasDropDownMenu
3940
importcom.jetbrains.toolbox.api.ui.components.AccountDropdownFieldasdropDownFactory
4041

42+
privatevalPOLL_INTERVAL=5.seconds
43+
4144
@OptIn(ExperimentalCoroutinesApi::class)
4245
classCoderRemoteProvider(
4346
privatevalcontext:CoderToolboxContext,
44-
privatevalhttpClient:OkHttpClient,
4547
) : RemoteProvider("Coder") {
4648
// Current polling job.
4749
privatevar pollJob:Job?=null
@@ -66,7 +68,7 @@ class CoderRemoteProvider(
6668
privatevar firstRun=true
6769
privateval isInitialized:MutableStateFlow<Boolean>=MutableStateFlow(false)
6870
privatevar coderHeaderPage=NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first?:""))
69-
privateval linkHandler=CoderProtocolHandler(context,httpClient,dialogUi, isInitialized)
71+
privateval linkHandler=CoderProtocolHandler(context, dialogUi, isInitialized)
7072
overrideval environments:MutableStateFlow<LoadableState<List<RemoteProviderEnvironment>>>=MutableStateFlow(
7173
LoadableState.Value(emptyList())
7274
)
@@ -77,6 +79,7 @@ class CoderRemoteProvider(
7779
* first time).
7880
*/
7981
privatefunpoll(client:CoderRestClient,cli:CoderCLIManager):Job= context.cs.launch {
82+
var lastPollTime=TimeSource.Monotonic.markNow()
8083
while (isActive) {
8184
try {
8285
context.logger.debug("Fetching workspace agents from${client.url}")
@@ -134,16 +137,28 @@ class CoderRemoteProvider(
134137
}catch (_:CancellationException) {
135138
context.logger.debug("${client.url} polling loop canceled")
136139
break
140+
}catch (ex:SocketTimeoutException) {
141+
val elapsed= lastPollTime.elapsedNow()
142+
if (elapsed>POLL_INTERVAL*2) {
143+
context.logger.info("wake-up from an OS sleep was detected, going to re-initialize the http client...")
144+
client.setupSession()
145+
}else {
146+
context.logger.error(ex,"workspace polling error encountered")
147+
pollError= ex
148+
logout()
149+
break
150+
}
137151
}catch (ex:Exception) {
138-
context.logger.info(ex,"workspace polling error encountered")
152+
context.logger.error(ex,"workspace polling error encountered")
139153
pollError= ex
140154
logout()
141155
break
142156
}
157+
143158
// TODO: Listening on a web socket might be better?
144159
select<Unit> {
145-
onTimeout(5.seconds) {
146-
context.logger.trace("workspace poller waked up by the5 seconds timeout")
160+
onTimeout(POLL_INTERVAL) {
161+
context.logger.trace("workspace poller waked up by the$POLL_INTERVAL timeout")
147162
}
148163
triggerSshConfig.onReceive { shouldTrigger->
149164
if (shouldTrigger) {
@@ -152,6 +167,7 @@ class CoderRemoteProvider(
152167
}
153168
}
154169
}
170+
lastPollTime=TimeSource.Monotonic.markNow()
155171
}
156172
}
157173

@@ -329,7 +345,6 @@ class CoderRemoteProvider(
329345
context,
330346
deploymentURL,
331347
token,
332-
httpClient,
333348
::goToEnvironmentsPage,
334349
) { client, cli->
335350
// Store the URL and token for use next time.

‎src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
1515
importcom.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
1616
importcom.jetbrains.toolbox.api.ui.ToolboxUi
1717
importkotlinx.coroutines.CoroutineScope
18-
importokhttp3.OkHttpClient
1918

2019
/**
2120
* Entry point into the extension.
@@ -35,8 +34,7 @@ class CoderToolboxExtension : RemoteDevExtension {
3534
serviceLocator.getService(LocalizableStringFactory::class.java),
3635
CoderSettingsStore(serviceLocator.getService(PluginSettingsStore::class.java),Environment(), logger),
3736
CoderSecretsStore(serviceLocator.getService(PluginSecretStore::class.java)),
38-
),
39-
OkHttpClient(),
37+
)
4038
)
4139
}
4240
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,19 @@ open class CoderRestClient(
5353
valtoken:String?,
5454
privatevalproxyValues:ProxyValues? =null,
5555
privatevalpluginVersion:String ="development",
56-
existingHttpClient:OkHttpClient? =null,
5756
) {
5857
privateval settings= context.settingsStore.readOnly()
59-
privateval httpClient:OkHttpClient
60-
privateval retroRestClient:CoderV2RestFacade
58+
privatelateinitvar httpClient:OkHttpClient
59+
privatelateinitvar retroRestClient:CoderV2RestFacade
6160

6261
lateinitvar me:User
6362
lateinitvar buildVersion:String
6463

6564
init {
65+
setupSession()
66+
}
67+
68+
funsetupSession() {
6669
val moshi=
6770
Moshi.Builder()
6871
.add(ArchConverter())
@@ -73,7 +76,7 @@ open class CoderRestClient(
7376

7477
val socketFactory= coderSocketFactory(settings.tls)
7578
val trustManagers= coderTrustManagers(settings.tls.caPath)
76-
var builder=existingHttpClient?.newBuilder()?:OkHttpClient.Builder()
79+
var builder=OkHttpClient.Builder()
7780

7881
if (proxyValues!=null) {
7982
builder=
@@ -103,6 +106,7 @@ open class CoderRestClient(
103106
builder
104107
.sslSocketFactory(socketFactory, trustManagers[0]asX509TrustManager)
105108
.hostnameVerifier(CoderHostnameVerifier(settings.tls.altHostname))
109+
.retryOnConnectionFailure(true)
106110
.addInterceptor {
107111
it.proceed(
108112
it.request().newBuilder().addHeader(

‎src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.StateFlow
1616
importkotlinx.coroutines.flow.first
1717
importkotlinx.coroutines.launch
1818
importkotlinx.coroutines.time.withTimeout
19-
importokhttp3.OkHttpClient
2019
importjava.net.HttpURLConnection
2120
importjava.net.URI
2221
importjava.net.URL
@@ -26,7 +25,6 @@ import kotlin.time.toJavaDuration
2625

2726
openclassCoderProtocolHandler(
2827
privatevalcontext:CoderToolboxContext,
29-
privatevalhttpClient:OkHttpClient?,
3028
privatevaldialogUi:DialogUi,
3129
privatevalisInitialized:StateFlow<Boolean>,
3230
) {
@@ -230,8 +228,7 @@ open class CoderProtocolHandler(
230228
deploymentURL.toURL(),
231229
token,
232230
proxyValues=null,// TODO - not sure the above comment applies as we are creating our own http client
233-
PluginManager.pluginInfo.version,
234-
httpClient
231+
PluginManager.pluginInfo.version
235232
)
236233
client.authenticate()
237234
return client

‎src/main/kotlin/com/coder/toolbox/views/ConnectPage.kt‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import kotlinx.coroutines.Job
1414
importkotlinx.coroutines.flow.MutableStateFlow
1515
importkotlinx.coroutines.flow.StateFlow
1616
importkotlinx.coroutines.launch
17-
importokhttp3.OkHttpClient
1817
importjava.net.URL
1918

2019
/**
@@ -24,7 +23,6 @@ class ConnectPage(
2423
privatevalcontext:CoderToolboxContext,
2524
privatevalurl:URL,
2625
privatevaltoken:String?,
27-
privatevalhttpClient:OkHttpClient,
2826
privatevalonCancel: ()->Unit,
2927
privatevalonConnect: (
3028
client:CoderRestClient,
@@ -95,7 +93,6 @@ class ConnectPage(
9593
token,
9694
proxyValues=null,
9795
PluginManager.pluginInfo.version,
98-
httpClient
9996
)
10097
client.authenticate()
10198
updateStatus(context.i18n.ptrl("Checking Coder binary..."), error=null)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp