@@ -474,6 +474,9 @@ export class MainServer extends Server {
474474 private readonly proxyTimeout = 5000 ;
475475
476476 private settings : Settings = { } ;
477+ private heartbeatTimer ?: NodeJS . Timeout ;
478+ private heartbeatInterval = 60000 ;
479+ private lastHeartbeat = 0 ;
477480
478481 public constructor ( options : ServerOptions , args : ParsedArgs ) {
479482 super ( options ) ;
@@ -491,6 +494,7 @@ export class MainServer extends Server {
491494 }
492495
493496 protected async handleWebSocket ( socket : net . Socket , parsedUrl : url . UrlWithParsedQuery ) : Promise < void > {
497+ this . heartbeat ( ) ;
494498 if ( ! parsedUrl . query . reconnectionToken ) {
495499 throw new Error ( "Reconnection token is missing from query parameters" ) ;
496500 }
@@ -514,6 +518,7 @@ export class MainServer extends Server {
514518 parsedUrl : url . UrlWithParsedQuery ,
515519 request : http . IncomingMessage ,
516520 ) : Promise < Response > {
521+ this . heartbeat ( ) ;
517522 switch ( base ) {
518523 case "/" : return this . getRoot ( request , parsedUrl ) ;
519524 case "/resource" :
@@ -876,4 +881,48 @@ export class MainServer extends Server {
876881 ( this . services . get ( ILogService ) as ILogService ) . warn ( error . message ) ;
877882 }
878883 }
884+
885+ /**
886+ * Return the file path for the heartbeat file.
887+ */
888+ private get heartbeatPath ( ) : string {
889+ const environment = this . services . get ( IEnvironmentService ) as IEnvironmentService ;
890+ return path . join ( environment . userDataPath , "heartbeat" ) ;
891+ }
892+
893+ /**
894+ * Return all online connections regardless of type.
895+ */
896+ private get onlineConnections ( ) : Connection [ ] {
897+ const online = < Connection [ ] > [ ] ;
898+ this . connections . forEach ( ( connections ) => {
899+ connections . forEach ( ( connection ) => {
900+ if ( typeof connection . offline === "undefined" ) {
901+ online . push ( connection ) ;
902+ }
903+ } ) ;
904+ } ) ;
905+ return online ;
906+ }
907+
908+ /**
909+ * Write to the heartbeat file if we haven't already done so within the
910+ * timeout and start or reset a timer that keeps running as long as there are
911+ * active connections. Failures are logged as warnings.
912+ */
913+ private heartbeat ( ) : void {
914+ const now = Date . now ( ) ;
915+ if ( now - this . lastHeartbeat >= this . heartbeatInterval ) {
916+ util . promisify ( fs . writeFile ) ( this . heartbeatPath , "" ) . catch ( ( error ) => {
917+ ( this . services . get ( ILogService ) as ILogService ) . warn ( error . message ) ;
918+ } ) ;
919+ this . lastHeartbeat = now ;
920+ clearTimeout ( this . heartbeatTimer ! ) ; // We can clear undefined so ! is fine.
921+ this . heartbeatTimer = setTimeout ( ( ) => {
922+ if ( this . onlineConnections . length > 0 ) {
923+ this . heartbeat ( ) ;
924+ }
925+ } , this . heartbeatInterval ) ;
926+ }
927+ }
879928}
0 commit comments