@@ -30,18 +30,20 @@ import kotlinx.coroutines.isActive
3030import kotlinx.coroutines.launch
3131import kotlinx.coroutines.selects.onTimeout
3232import kotlinx.coroutines.selects.select
33- import okhttp3.OkHttpClient
33+ import java.net.SocketTimeoutException
3434import java.net.URI
3535import java.net.URL
3636import kotlin.coroutines.cancellation.CancellationException
3737import kotlin.time.Duration.Companion.seconds
38+ import kotlin.time.TimeSource
3839import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as DropDownMenu
3940import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as dropDownFactory
4041
42+ private val POLL_INTERVAL = 5 .seconds
43+
4144@OptIn(ExperimentalCoroutinesApi ::class )
4245class CoderRemoteProvider (
4346 private val context : CoderToolboxContext ,
44- private val httpClient : OkHttpClient ,
4547) : RemoteProvider(" Coder" ) {
4648 // Current polling job.
4749 private var pollJob: Job ? = null
@@ -66,7 +68,7 @@ class CoderRemoteProvider(
6668 private var firstRun = true
6769 private val isInitialized: MutableStateFlow <Boolean > = MutableStateFlow (false )
6870 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)
7072 override val environments: MutableStateFlow <LoadableState <List <RemoteProviderEnvironment >>> = MutableStateFlow (
7173 LoadableState .Value (emptyList())
7274 )
@@ -77,6 +79,7 @@ class CoderRemoteProvider(
7779 * first time).
7880 */
7981 private fun poll (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 the 5 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.
0 commit comments