@@ -30,18 +30,20 @@ import kotlinx.coroutines.isActive
30
30
import kotlinx.coroutines.launch
31
31
import kotlinx.coroutines.selects.onTimeout
32
32
import kotlinx.coroutines.selects.select
33
- import okhttp3.OkHttpClient
33
+ import java.net.SocketTimeoutException
34
34
import java.net.URI
35
35
import java.net.URL
36
36
import kotlin.coroutines.cancellation.CancellationException
37
37
import kotlin.time.Duration.Companion.seconds
38
+ import kotlin.time.TimeSource
38
39
import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as DropDownMenu
39
40
import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as dropDownFactory
40
41
42
+ private val POLL_INTERVAL = 5 .seconds
43
+
41
44
@OptIn(ExperimentalCoroutinesApi ::class )
42
45
class CoderRemoteProvider (
43
46
private val context : CoderToolboxContext ,
44
- private val httpClient : OkHttpClient ,
45
47
) : RemoteProvider(" Coder" ) {
46
48
// Current polling job.
47
49
private var pollJob: Job ? = null
@@ -66,7 +68,7 @@ class CoderRemoteProvider(
66
68
private var firstRun = true
67
69
private val isInitialized: MutableStateFlow <Boolean > = MutableStateFlow (false )
68
70
private var coderHeaderPage = NewEnvironmentPage (context, context.i18n.pnotr(getDeploymentURL()?.first ? : " " ))
69
- private val linkHandler = CoderProtocolHandler (context, httpClient, dialogUi, isInitialized)
71
+ private val linkHandler = CoderProtocolHandler (context, dialogUi, isInitialized)
70
72
override val environments: MutableStateFlow <LoadableState <List <RemoteProviderEnvironment >>> = MutableStateFlow (
71
73
LoadableState .Value (emptyList())
72
74
)
@@ -77,6 +79,7 @@ class CoderRemoteProvider(
77
79
* first time).
78
80
*/
79
81
private fun poll (client : CoderRestClient , cli : CoderCLIManager ): Job = context.cs.launch {
82
+ var lastPollTime = TimeSource .Monotonic .markNow()
80
83
while (isActive) {
81
84
try {
82
85
context.logger.debug(" Fetching workspace agents from ${client.url} " )
@@ -134,16 +137,28 @@ class CoderRemoteProvider(
134
137
} catch (_: CancellationException ) {
135
138
context.logger.debug(" ${client.url} polling loop canceled" )
136
139
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
+ }
137
151
} catch (ex: Exception ) {
138
- context.logger.info (ex, " workspace polling error encountered" )
152
+ context.logger.error (ex, " workspace polling error encountered" )
139
153
pollError = ex
140
154
logout()
141
155
break
142
156
}
157
+
143
158
// TODO: Listening on a web socket might be better?
144
159
select<Unit > {
145
- onTimeout(5 .seconds ) {
146
- context.logger.trace(" workspace poller waked up by the 5 seconds timeout" )
160
+ onTimeout(POLL_INTERVAL ) {
161
+ context.logger.trace(" workspace poller waked up by the $POLL_INTERVAL timeout" )
147
162
}
148
163
triggerSshConfig.onReceive { shouldTrigger ->
149
164
if (shouldTrigger) {
@@ -152,6 +167,7 @@ class CoderRemoteProvider(
152
167
}
153
168
}
154
169
}
170
+ lastPollTime = TimeSource .Monotonic .markNow()
155
171
}
156
172
}
157
173
@@ -329,7 +345,6 @@ class CoderRemoteProvider(
329
345
context,
330
346
deploymentURL,
331
347
token,
332
- httpClient,
333
348
::goToEnvironmentsPage,
334
349
) { client, cli ->
335
350
// Store the URL and token for use next time.
0 commit comments