@theia/remote 1.72.0-next.59 → 1.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/electron-node/remote-auto-shutdown-service.d.ts +29 -0
- package/lib/electron-node/remote-auto-shutdown-service.d.ts.map +1 -0
- package/lib/electron-node/remote-auto-shutdown-service.js +109 -0
- package/lib/electron-node/remote-auto-shutdown-service.js.map +1 -0
- package/lib/electron-node/remote-auto-shutdown-service.spec.d.ts +2 -0
- package/lib/electron-node/remote-auto-shutdown-service.spec.d.ts.map +1 -0
- package/lib/electron-node/remote-auto-shutdown-service.spec.js +169 -0
- package/lib/electron-node/remote-auto-shutdown-service.spec.js.map +1 -0
- package/lib/electron-node/remote-backend-module.d.ts.map +1 -1
- package/lib/electron-node/remote-backend-module.js +9 -0
- package/lib/electron-node/remote-backend-module.js.map +1 -1
- package/lib/electron-node/remote-types.d.ts +1 -1
- package/lib/electron-node/remote-types.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/electron-node/remote-auto-shutdown-service.spec.ts +207 -0
- package/src/electron-node/remote-auto-shutdown-service.ts +120 -0
- package/src/electron-node/remote-backend-module.ts +11 -0
- package/src/electron-node/remote-types.ts +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ILogger } from '@theia/core';
|
|
2
|
+
import { MaybePromise } from '@theia/core/lib/common/types';
|
|
3
|
+
import { MessagingListenerContribution } from '@theia/core/lib/node/messaging/messaging-listeners';
|
|
4
|
+
import { CliContribution } from '@theia/core/lib/node';
|
|
5
|
+
import { Arguments, Argv } from '@theia/core/shared/yargs';
|
|
6
|
+
import * as http from 'http';
|
|
7
|
+
import { Socket } from 'socket.io';
|
|
8
|
+
export declare const REMOTE_AUTO_SHUTDOWN = "remote-auto-shutdown";
|
|
9
|
+
export declare const REMOTE_AUTO_SHUTDOWN_TIMEOUT = "remote-auto-shutdown-timeout";
|
|
10
|
+
/**
|
|
11
|
+
* Tracks active frontend WebSocket connections on a remote backend.
|
|
12
|
+
* When auto-shutdown is enabled and all connections close, starts a
|
|
13
|
+
* timer and calls `process.exit(0)` if no new connection arrives
|
|
14
|
+
* before the timeout.
|
|
15
|
+
*/
|
|
16
|
+
export declare class RemoteAutoShutdownService implements MessagingListenerContribution, CliContribution {
|
|
17
|
+
protected readonly logger: ILogger;
|
|
18
|
+
protected enabled: boolean;
|
|
19
|
+
protected shutdownTimeout: number;
|
|
20
|
+
protected activeConnections: number;
|
|
21
|
+
protected shutdownTimer: ReturnType<typeof setTimeout> | undefined;
|
|
22
|
+
configure(conf: Argv): void;
|
|
23
|
+
setArguments(args: Arguments): void;
|
|
24
|
+
onDidWebSocketUpgrade(request: http.IncomingMessage, socket: Socket): MaybePromise<void>;
|
|
25
|
+
protected scheduleShutdown(): void;
|
|
26
|
+
protected tryShutdown(): void;
|
|
27
|
+
protected cancelShutdown(): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=remote-auto-shutdown-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auto-shutdown-service.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-auto-shutdown-service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAC3D,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAK3E;;;;;GAKG;AACH,qBACa,yBAA0B,YAAW,6BAA6B,EAAE,eAAe;IAG5F,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnC,SAAS,CAAC,OAAO,UAAS;IAC1B,SAAS,CAAC,eAAe,SAA4B;IACrD,SAAS,CAAC,iBAAiB,SAAK;IAChC,SAAS,CAAC,aAAa,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,SAAS,CAAC;IAEnE,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAa3B,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAanC,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC;IAgBxF,SAAS,CAAC,gBAAgB,IAAI,IAAI;IASlC,SAAS,CAAC,WAAW,IAAI,IAAI;IAa7B,SAAS,CAAC,cAAc,IAAI,IAAI;CAOnC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.RemoteAutoShutdownService = exports.REMOTE_AUTO_SHUTDOWN_TIMEOUT = exports.REMOTE_AUTO_SHUTDOWN = void 0;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
21
|
+
const core_1 = require("@theia/core");
|
|
22
|
+
exports.REMOTE_AUTO_SHUTDOWN = 'remote-auto-shutdown';
|
|
23
|
+
exports.REMOTE_AUTO_SHUTDOWN_TIMEOUT = 'remote-auto-shutdown-timeout';
|
|
24
|
+
/** Default timeout: 5 minutes (same as VS Code). */
|
|
25
|
+
const DEFAULT_SHUTDOWN_TIMEOUT = 5 * 60 * 1000;
|
|
26
|
+
/**
|
|
27
|
+
* Tracks active frontend WebSocket connections on a remote backend.
|
|
28
|
+
* When auto-shutdown is enabled and all connections close, starts a
|
|
29
|
+
* timer and calls `process.exit(0)` if no new connection arrives
|
|
30
|
+
* before the timeout.
|
|
31
|
+
*/
|
|
32
|
+
let RemoteAutoShutdownService = class RemoteAutoShutdownService {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.enabled = false;
|
|
35
|
+
this.shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT;
|
|
36
|
+
this.activeConnections = 0;
|
|
37
|
+
}
|
|
38
|
+
configure(conf) {
|
|
39
|
+
conf.option(exports.REMOTE_AUTO_SHUTDOWN, {
|
|
40
|
+
description: 'Automatically shut down the remote backend when all frontend connections close',
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
default: false
|
|
43
|
+
});
|
|
44
|
+
conf.option(exports.REMOTE_AUTO_SHUTDOWN_TIMEOUT, {
|
|
45
|
+
description: 'Timeout in milliseconds before auto-shutdown after the last connection closes',
|
|
46
|
+
type: 'number',
|
|
47
|
+
default: DEFAULT_SHUTDOWN_TIMEOUT
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
setArguments(args) {
|
|
51
|
+
this.enabled = Boolean(args[exports.REMOTE_AUTO_SHUTDOWN]);
|
|
52
|
+
const timeout = Number(args[exports.REMOTE_AUTO_SHUTDOWN_TIMEOUT]);
|
|
53
|
+
if (!isNaN(timeout) && timeout > 0) {
|
|
54
|
+
this.shutdownTimeout = timeout;
|
|
55
|
+
}
|
|
56
|
+
if (this.enabled) {
|
|
57
|
+
// Start the initial timer — if no frontend connects within the timeout, shut down
|
|
58
|
+
this.scheduleShutdown();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
onDidWebSocketUpgrade(request, socket) {
|
|
62
|
+
this.activeConnections++;
|
|
63
|
+
this.logger.debug(`RemoteAutoShutdown: connection opened (active: ${this.activeConnections})`);
|
|
64
|
+
this.cancelShutdown();
|
|
65
|
+
socket.on('disconnect', () => {
|
|
66
|
+
this.activeConnections = Math.max(0, this.activeConnections - 1);
|
|
67
|
+
this.logger.debug(`RemoteAutoShutdown: connection closed (active: ${this.activeConnections})`);
|
|
68
|
+
if (this.activeConnections === 0 && this.enabled) {
|
|
69
|
+
this.scheduleShutdown();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
scheduleShutdown() {
|
|
74
|
+
this.cancelShutdown();
|
|
75
|
+
this.logger.debug(`RemoteAutoShutdown: scheduling shutdown in ${this.shutdownTimeout}ms`);
|
|
76
|
+
this.shutdownTimer = setTimeout(() => {
|
|
77
|
+
this.shutdownTimer = undefined;
|
|
78
|
+
this.tryShutdown();
|
|
79
|
+
}, this.shutdownTimeout);
|
|
80
|
+
}
|
|
81
|
+
tryShutdown() {
|
|
82
|
+
if (this.activeConnections > 0) {
|
|
83
|
+
this.logger.debug('RemoteAutoShutdown: consumer connected before shutdown, aborting');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.logger.info('RemoteAutoShutdown: no active connections, shutting down');
|
|
87
|
+
// process.exit triggers the 'exit' event, which BackendApplication handles
|
|
88
|
+
// by calling onStop() on all contributions. Theia's onStop() contract is
|
|
89
|
+
// synchronous-only (returns void, not MaybePromise<void>), so process.exit
|
|
90
|
+
// does not skip any cleanup — there is no async shutdown mechanism to miss.
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
cancelShutdown() {
|
|
94
|
+
if (this.shutdownTimer) {
|
|
95
|
+
this.logger.debug('RemoteAutoShutdown: cancelling shutdown timer');
|
|
96
|
+
clearTimeout(this.shutdownTimer);
|
|
97
|
+
this.shutdownTimer = undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
exports.RemoteAutoShutdownService = RemoteAutoShutdownService;
|
|
102
|
+
tslib_1.__decorate([
|
|
103
|
+
(0, inversify_1.inject)(core_1.ILogger),
|
|
104
|
+
tslib_1.__metadata("design:type", Object)
|
|
105
|
+
], RemoteAutoShutdownService.prototype, "logger", void 0);
|
|
106
|
+
exports.RemoteAutoShutdownService = RemoteAutoShutdownService = tslib_1.__decorate([
|
|
107
|
+
(0, inversify_1.injectable)()
|
|
108
|
+
], RemoteAutoShutdownService);
|
|
109
|
+
//# sourceMappingURL=remote-auto-shutdown-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auto-shutdown-service.js","sourceRoot":"","sources":["../../src/electron-node/remote-auto-shutdown-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auto-shutdown-service.spec.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-auto-shutdown-service.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const chai_1 = require("chai");
|
|
19
|
+
const remote_auto_shutdown_service_1 = require("./remote-auto-shutdown-service");
|
|
20
|
+
const events_1 = require("events");
|
|
21
|
+
class MockSocket extends events_1.EventEmitter {
|
|
22
|
+
disconnect() {
|
|
23
|
+
this.emit('disconnect');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function createMockLogger() {
|
|
27
|
+
return {
|
|
28
|
+
debug: () => { },
|
|
29
|
+
info: () => { },
|
|
30
|
+
warn: () => { },
|
|
31
|
+
error: () => { },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Exposes protected state for assertions and replaces `tryShutdown`
|
|
36
|
+
* with a flag so tests don't need real timers or `process.exit` stubs.
|
|
37
|
+
*/
|
|
38
|
+
class TestableAutoShutdownService extends remote_auto_shutdown_service_1.RemoteAutoShutdownService {
|
|
39
|
+
constructor() {
|
|
40
|
+
super();
|
|
41
|
+
this.shutdownRequested = false;
|
|
42
|
+
this.logger = createMockLogger();
|
|
43
|
+
}
|
|
44
|
+
get isEnabled() { return this.enabled; }
|
|
45
|
+
get connections() { return this.activeConnections; }
|
|
46
|
+
get hasScheduledTimer() { return this.shutdownTimer !== undefined; }
|
|
47
|
+
tryShutdown() {
|
|
48
|
+
this.shutdownRequested = true;
|
|
49
|
+
}
|
|
50
|
+
/** Manually fire the scheduled timer callback (if any). */
|
|
51
|
+
fireTimer() {
|
|
52
|
+
if (this.shutdownTimer) {
|
|
53
|
+
clearTimeout(this.shutdownTimer);
|
|
54
|
+
this.shutdownTimer = undefined;
|
|
55
|
+
this.tryShutdown();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Cancel any pending timer without triggering shutdown. */
|
|
59
|
+
cancelTimer() {
|
|
60
|
+
this.cancelShutdown();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function enableAutoShutdown(service, timeout = 60000) {
|
|
64
|
+
service.setArguments({
|
|
65
|
+
'remote-auto-shutdown': true,
|
|
66
|
+
'remote-auto-shutdown-timeout': timeout,
|
|
67
|
+
_: [],
|
|
68
|
+
'$0': ''
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function connectSocket(service) {
|
|
72
|
+
const socket = new MockSocket();
|
|
73
|
+
service.onDidWebSocketUpgrade({}, socket);
|
|
74
|
+
return socket;
|
|
75
|
+
}
|
|
76
|
+
describe('RemoteAutoShutdownService', () => {
|
|
77
|
+
const services = [];
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
for (const service of services) {
|
|
80
|
+
service.cancelTimer();
|
|
81
|
+
}
|
|
82
|
+
services.length = 0;
|
|
83
|
+
});
|
|
84
|
+
function createService() {
|
|
85
|
+
const service = new TestableAutoShutdownService();
|
|
86
|
+
services.push(service);
|
|
87
|
+
return service;
|
|
88
|
+
}
|
|
89
|
+
it('should not be enabled by default', () => {
|
|
90
|
+
const service = createService();
|
|
91
|
+
(0, chai_1.expect)(service.isEnabled).to.equal(false);
|
|
92
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(false);
|
|
93
|
+
});
|
|
94
|
+
it('should schedule initial timer when enabled', () => {
|
|
95
|
+
const service = createService();
|
|
96
|
+
enableAutoShutdown(service);
|
|
97
|
+
(0, chai_1.expect)(service.isEnabled).to.equal(true);
|
|
98
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
99
|
+
});
|
|
100
|
+
it('should cancel timer when a connection arrives', () => {
|
|
101
|
+
const service = createService();
|
|
102
|
+
enableAutoShutdown(service);
|
|
103
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
104
|
+
connectSocket(service);
|
|
105
|
+
(0, chai_1.expect)(service.connections).to.equal(1);
|
|
106
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(false);
|
|
107
|
+
});
|
|
108
|
+
it('should schedule timer when last connection disconnects', () => {
|
|
109
|
+
const service = createService();
|
|
110
|
+
enableAutoShutdown(service);
|
|
111
|
+
const socket = connectSocket(service);
|
|
112
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(false);
|
|
113
|
+
socket.disconnect();
|
|
114
|
+
(0, chai_1.expect)(service.connections).to.equal(0);
|
|
115
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
116
|
+
});
|
|
117
|
+
it('should not schedule timer while connections remain', () => {
|
|
118
|
+
const service = createService();
|
|
119
|
+
enableAutoShutdown(service);
|
|
120
|
+
const socket1 = connectSocket(service);
|
|
121
|
+
const socket2 = connectSocket(service);
|
|
122
|
+
(0, chai_1.expect)(service.connections).to.equal(2);
|
|
123
|
+
socket1.disconnect();
|
|
124
|
+
(0, chai_1.expect)(service.connections).to.equal(1);
|
|
125
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(false);
|
|
126
|
+
socket2.disconnect();
|
|
127
|
+
(0, chai_1.expect)(service.connections).to.equal(0);
|
|
128
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
129
|
+
});
|
|
130
|
+
it('should cancel and reschedule timer when new connection arrives during countdown', () => {
|
|
131
|
+
const service = createService();
|
|
132
|
+
enableAutoShutdown(service);
|
|
133
|
+
const socket1 = connectSocket(service);
|
|
134
|
+
socket1.disconnect();
|
|
135
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
136
|
+
// New connection cancels the timer
|
|
137
|
+
const socket2 = connectSocket(service);
|
|
138
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(false);
|
|
139
|
+
(0, chai_1.expect)(service.shutdownRequested).to.equal(false);
|
|
140
|
+
socket2.disconnect();
|
|
141
|
+
(0, chai_1.expect)(service.hasScheduledTimer).to.equal(true);
|
|
142
|
+
});
|
|
143
|
+
it('should request shutdown when timer fires with no connections', () => {
|
|
144
|
+
const service = createService();
|
|
145
|
+
enableAutoShutdown(service);
|
|
146
|
+
const socket = connectSocket(service);
|
|
147
|
+
socket.disconnect();
|
|
148
|
+
(0, chai_1.expect)(service.shutdownRequested).to.equal(false);
|
|
149
|
+
service.fireTimer();
|
|
150
|
+
(0, chai_1.expect)(service.shutdownRequested).to.equal(true);
|
|
151
|
+
});
|
|
152
|
+
it('should not request shutdown when timer fires but a connection exists', () => {
|
|
153
|
+
const service = createService();
|
|
154
|
+
enableAutoShutdown(service);
|
|
155
|
+
// Initial timer is scheduled
|
|
156
|
+
connectSocket(service);
|
|
157
|
+
// Timer was cancelled by the connection, but let's test tryShutdown directly
|
|
158
|
+
service.fireTimer(); // no-op since timer was cancelled
|
|
159
|
+
(0, chai_1.expect)(service.shutdownRequested).to.equal(false);
|
|
160
|
+
});
|
|
161
|
+
it('should request shutdown on initial timer if no connection ever arrives', () => {
|
|
162
|
+
const service = createService();
|
|
163
|
+
enableAutoShutdown(service);
|
|
164
|
+
// Nobody connects, timer fires
|
|
165
|
+
service.fireTimer();
|
|
166
|
+
(0, chai_1.expect)(service.shutdownRequested).to.equal(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=remote-auto-shutdown-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auto-shutdown-service.spec.js","sourceRoot":"","sources":["../../src/electron-node/remote-auto-shutdown-service.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-backend-module.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-backend-module.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"remote-backend-module.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-backend-module.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA+B/D,eAAO,MAAM,sBAAsB,iBAQjC,CAAC;;AAEH,wBA0CG"}
|
|
@@ -18,6 +18,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.remoteConnectionModule = void 0;
|
|
19
19
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
20
20
|
const node_1 = require("@theia/core/lib/node");
|
|
21
|
+
const messaging_listeners_1 = require("@theia/core/lib/node/messaging/messaging-listeners");
|
|
22
|
+
const remote_auto_shutdown_service_1 = require("./remote-auto-shutdown-service");
|
|
23
|
+
const remote_cli_contribution_1 = require("@theia/core/lib/node/remote/remote-cli-contribution");
|
|
21
24
|
const remote_connection_service_1 = require("./remote-connection-service");
|
|
22
25
|
const remote_proxy_server_provider_1 = require("./remote-proxy-server-provider");
|
|
23
26
|
const remote_connection_socket_provider_1 = require("./remote-connection-socket-provider");
|
|
@@ -79,5 +82,11 @@ exports.default = new inversify_1.ContainerModule((bind, _unbind, _isBound, rebi
|
|
|
79
82
|
bind(node_1.CliContribution).toService(backend_remote_service_impl_1.BackendRemoteServiceImpl);
|
|
80
83
|
bind(ssh_identity_file_collector_1.SSHIdentityFileCollector).toSelf().inSingletonScope();
|
|
81
84
|
(0, remote_preferences_1.bindRemotePreferences)(bind);
|
|
85
|
+
bind(remote_auto_shutdown_service_1.RemoteAutoShutdownService).toSelf().inSingletonScope();
|
|
86
|
+
bind(node_1.CliContribution).toService(remote_auto_shutdown_service_1.RemoteAutoShutdownService);
|
|
87
|
+
bind(messaging_listeners_1.MessagingListenerContribution).toService(remote_auto_shutdown_service_1.RemoteAutoShutdownService);
|
|
88
|
+
bind(remote_cli_contribution_1.RemoteCliContribution).toConstantValue({
|
|
89
|
+
enhanceArgs: () => ['--remote-auto-shutdown']
|
|
90
|
+
});
|
|
82
91
|
});
|
|
83
92
|
//# sourceMappingURL=remote-backend-module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-backend-module.js","sourceRoot":"","sources":["../../src/electron-node/remote-backend-module.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"remote-backend-module.js","sourceRoot":"","sources":["../../src/electron-node/remote-backend-module.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ export interface RemoteConnection extends Disposable {
|
|
|
35
35
|
/**
|
|
36
36
|
* copy files from local to remote
|
|
37
37
|
*/
|
|
38
|
-
copy(localPath: string
|
|
38
|
+
copy(localPath: string, remotePath: string): Promise<void>;
|
|
39
39
|
/**
|
|
40
40
|
* used for disposing when theia is shutting down
|
|
41
41
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-types.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-types.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,QAAQ,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;AAE3E,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpD;;OAEG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE3F;;;OAGG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE5H;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"remote-types.d.ts","sourceRoot":"","sources":["../../src/electron-node/remote-types.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,QAAQ,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;AAE3E,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpD;;OAEG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE3F;;;OAGG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE5H;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D;;OAEG;IACH,WAAW,CAAC,IAAI,IAAI,CAAC;CACxB"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/remote",
|
|
3
|
-
"version": "1.72.0
|
|
3
|
+
"version": "1.72.0",
|
|
4
4
|
"description": "Theia - Remote",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.72.0
|
|
7
|
-
"@theia/filesystem": "1.72.0
|
|
8
|
-
"@theia/userstorage": "1.72.0
|
|
9
|
-
"@theia/variable-resolver": "1.72.0
|
|
10
|
-
"@theia/workspace": "1.72.0
|
|
6
|
+
"@theia/core": "1.72.0",
|
|
7
|
+
"@theia/filesystem": "1.72.0",
|
|
8
|
+
"@theia/userstorage": "1.72.0",
|
|
9
|
+
"@theia/variable-resolver": "1.72.0",
|
|
10
|
+
"@theia/workspace": "1.72.0",
|
|
11
11
|
"archiver": "^5.3.2",
|
|
12
12
|
"decompress": "^4.2.1",
|
|
13
13
|
"decompress-tar": "^4.1.1",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"watch": "theiaext watch"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@theia/ext-scripts": "1.
|
|
59
|
+
"@theia/ext-scripts": "1.72.0",
|
|
60
60
|
"@types/archiver": "^5.3.4",
|
|
61
61
|
"@types/decompress": "^4.2.7",
|
|
62
62
|
"@types/express-http-proxy": "^1.6.7",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"nyc": {
|
|
68
68
|
"extends": "../../configs/nyc.json"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "5bc20bc672aa732fb7e05234cf63c1b514868896"
|
|
71
71
|
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { RemoteAutoShutdownService } from './remote-auto-shutdown-service';
|
|
19
|
+
import { ILogger } from '@theia/core';
|
|
20
|
+
import { Arguments } from '@theia/core/shared/yargs';
|
|
21
|
+
import * as http from 'http';
|
|
22
|
+
import { EventEmitter } from 'events';
|
|
23
|
+
import { Socket } from 'socket.io';
|
|
24
|
+
|
|
25
|
+
class MockSocket extends EventEmitter {
|
|
26
|
+
disconnect(): void {
|
|
27
|
+
this.emit('disconnect');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createMockLogger(): ILogger {
|
|
32
|
+
return {
|
|
33
|
+
debug: () => { },
|
|
34
|
+
info: () => { },
|
|
35
|
+
warn: () => { },
|
|
36
|
+
error: () => { },
|
|
37
|
+
} as unknown as ILogger;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Exposes protected state for assertions and replaces `tryShutdown`
|
|
42
|
+
* with a flag so tests don't need real timers or `process.exit` stubs.
|
|
43
|
+
*/
|
|
44
|
+
class TestableAutoShutdownService extends RemoteAutoShutdownService {
|
|
45
|
+
public shutdownRequested = false;
|
|
46
|
+
protected override readonly logger = createMockLogger();
|
|
47
|
+
|
|
48
|
+
constructor() {
|
|
49
|
+
super();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get isEnabled(): boolean { return this.enabled; }
|
|
53
|
+
get connections(): number { return this.activeConnections; }
|
|
54
|
+
get hasScheduledTimer(): boolean { return this.shutdownTimer !== undefined; }
|
|
55
|
+
|
|
56
|
+
protected override tryShutdown(): void {
|
|
57
|
+
this.shutdownRequested = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Manually fire the scheduled timer callback (if any). */
|
|
61
|
+
fireTimer(): void {
|
|
62
|
+
if (this.shutdownTimer) {
|
|
63
|
+
clearTimeout(this.shutdownTimer);
|
|
64
|
+
this.shutdownTimer = undefined;
|
|
65
|
+
this.tryShutdown();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Cancel any pending timer without triggering shutdown. */
|
|
70
|
+
cancelTimer(): void {
|
|
71
|
+
this.cancelShutdown();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function enableAutoShutdown(service: TestableAutoShutdownService, timeout: number = 60000): void {
|
|
76
|
+
service.setArguments({
|
|
77
|
+
'remote-auto-shutdown': true,
|
|
78
|
+
'remote-auto-shutdown-timeout': timeout,
|
|
79
|
+
_: [],
|
|
80
|
+
'$0': ''
|
|
81
|
+
} as unknown as Arguments);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function connectSocket(service: TestableAutoShutdownService): MockSocket {
|
|
85
|
+
const socket = new MockSocket();
|
|
86
|
+
service.onDidWebSocketUpgrade({} as http.IncomingMessage, socket as unknown as Socket);
|
|
87
|
+
return socket;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe('RemoteAutoShutdownService', () => {
|
|
91
|
+
|
|
92
|
+
const services: TestableAutoShutdownService[] = [];
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
for (const service of services) {
|
|
96
|
+
service.cancelTimer();
|
|
97
|
+
}
|
|
98
|
+
services.length = 0;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function createService(): TestableAutoShutdownService {
|
|
102
|
+
const service = new TestableAutoShutdownService();
|
|
103
|
+
services.push(service);
|
|
104
|
+
return service;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
it('should not be enabled by default', () => {
|
|
108
|
+
const service = createService();
|
|
109
|
+
expect(service.isEnabled).to.equal(false);
|
|
110
|
+
expect(service.hasScheduledTimer).to.equal(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should schedule initial timer when enabled', () => {
|
|
114
|
+
const service = createService();
|
|
115
|
+
enableAutoShutdown(service);
|
|
116
|
+
expect(service.isEnabled).to.equal(true);
|
|
117
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should cancel timer when a connection arrives', () => {
|
|
121
|
+
const service = createService();
|
|
122
|
+
enableAutoShutdown(service);
|
|
123
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
124
|
+
|
|
125
|
+
connectSocket(service);
|
|
126
|
+
expect(service.connections).to.equal(1);
|
|
127
|
+
expect(service.hasScheduledTimer).to.equal(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should schedule timer when last connection disconnects', () => {
|
|
131
|
+
const service = createService();
|
|
132
|
+
enableAutoShutdown(service);
|
|
133
|
+
|
|
134
|
+
const socket = connectSocket(service);
|
|
135
|
+
expect(service.hasScheduledTimer).to.equal(false);
|
|
136
|
+
|
|
137
|
+
socket.disconnect();
|
|
138
|
+
expect(service.connections).to.equal(0);
|
|
139
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should not schedule timer while connections remain', () => {
|
|
143
|
+
const service = createService();
|
|
144
|
+
enableAutoShutdown(service);
|
|
145
|
+
|
|
146
|
+
const socket1 = connectSocket(service);
|
|
147
|
+
const socket2 = connectSocket(service);
|
|
148
|
+
expect(service.connections).to.equal(2);
|
|
149
|
+
|
|
150
|
+
socket1.disconnect();
|
|
151
|
+
expect(service.connections).to.equal(1);
|
|
152
|
+
expect(service.hasScheduledTimer).to.equal(false);
|
|
153
|
+
|
|
154
|
+
socket2.disconnect();
|
|
155
|
+
expect(service.connections).to.equal(0);
|
|
156
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should cancel and reschedule timer when new connection arrives during countdown', () => {
|
|
160
|
+
const service = createService();
|
|
161
|
+
enableAutoShutdown(service);
|
|
162
|
+
|
|
163
|
+
const socket1 = connectSocket(service);
|
|
164
|
+
socket1.disconnect();
|
|
165
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
166
|
+
|
|
167
|
+
// New connection cancels the timer
|
|
168
|
+
const socket2 = connectSocket(service);
|
|
169
|
+
expect(service.hasScheduledTimer).to.equal(false);
|
|
170
|
+
expect(service.shutdownRequested).to.equal(false);
|
|
171
|
+
|
|
172
|
+
socket2.disconnect();
|
|
173
|
+
expect(service.hasScheduledTimer).to.equal(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should request shutdown when timer fires with no connections', () => {
|
|
177
|
+
const service = createService();
|
|
178
|
+
enableAutoShutdown(service);
|
|
179
|
+
|
|
180
|
+
const socket = connectSocket(service);
|
|
181
|
+
socket.disconnect();
|
|
182
|
+
expect(service.shutdownRequested).to.equal(false);
|
|
183
|
+
|
|
184
|
+
service.fireTimer();
|
|
185
|
+
expect(service.shutdownRequested).to.equal(true);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should not request shutdown when timer fires but a connection exists', () => {
|
|
189
|
+
const service = createService();
|
|
190
|
+
enableAutoShutdown(service);
|
|
191
|
+
|
|
192
|
+
// Initial timer is scheduled
|
|
193
|
+
connectSocket(service);
|
|
194
|
+
// Timer was cancelled by the connection, but let's test tryShutdown directly
|
|
195
|
+
service.fireTimer(); // no-op since timer was cancelled
|
|
196
|
+
expect(service.shutdownRequested).to.equal(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should request shutdown on initial timer if no connection ever arrives', () => {
|
|
200
|
+
const service = createService();
|
|
201
|
+
enableAutoShutdown(service);
|
|
202
|
+
|
|
203
|
+
// Nobody connects, timer fires
|
|
204
|
+
service.fireTimer();
|
|
205
|
+
expect(service.shutdownRequested).to.equal(true);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { ILogger } from '@theia/core';
|
|
19
|
+
import { MaybePromise } from '@theia/core/lib/common/types';
|
|
20
|
+
import { MessagingListenerContribution } from '@theia/core/lib/node/messaging/messaging-listeners';
|
|
21
|
+
import { CliContribution } from '@theia/core/lib/node';
|
|
22
|
+
import { Arguments, Argv } from '@theia/core/shared/yargs';
|
|
23
|
+
import * as http from 'http';
|
|
24
|
+
import { Socket } from 'socket.io';
|
|
25
|
+
|
|
26
|
+
export const REMOTE_AUTO_SHUTDOWN = 'remote-auto-shutdown';
|
|
27
|
+
export const REMOTE_AUTO_SHUTDOWN_TIMEOUT = 'remote-auto-shutdown-timeout';
|
|
28
|
+
|
|
29
|
+
/** Default timeout: 5 minutes (same as VS Code). */
|
|
30
|
+
const DEFAULT_SHUTDOWN_TIMEOUT = 5 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Tracks active frontend WebSocket connections on a remote backend.
|
|
34
|
+
* When auto-shutdown is enabled and all connections close, starts a
|
|
35
|
+
* timer and calls `process.exit(0)` if no new connection arrives
|
|
36
|
+
* before the timeout.
|
|
37
|
+
*/
|
|
38
|
+
@injectable()
|
|
39
|
+
export class RemoteAutoShutdownService implements MessagingListenerContribution, CliContribution {
|
|
40
|
+
|
|
41
|
+
@inject(ILogger)
|
|
42
|
+
protected readonly logger: ILogger;
|
|
43
|
+
|
|
44
|
+
protected enabled = false;
|
|
45
|
+
protected shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT;
|
|
46
|
+
protected activeConnections = 0;
|
|
47
|
+
protected shutdownTimer: ReturnType<typeof setTimeout> | undefined;
|
|
48
|
+
|
|
49
|
+
configure(conf: Argv): void {
|
|
50
|
+
conf.option(REMOTE_AUTO_SHUTDOWN, {
|
|
51
|
+
description: 'Automatically shut down the remote backend when all frontend connections close',
|
|
52
|
+
type: 'boolean',
|
|
53
|
+
default: false
|
|
54
|
+
});
|
|
55
|
+
conf.option(REMOTE_AUTO_SHUTDOWN_TIMEOUT, {
|
|
56
|
+
description: 'Timeout in milliseconds before auto-shutdown after the last connection closes',
|
|
57
|
+
type: 'number',
|
|
58
|
+
default: DEFAULT_SHUTDOWN_TIMEOUT
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setArguments(args: Arguments): void {
|
|
63
|
+
this.enabled = Boolean(args[REMOTE_AUTO_SHUTDOWN]);
|
|
64
|
+
const timeout = Number(args[REMOTE_AUTO_SHUTDOWN_TIMEOUT]);
|
|
65
|
+
if (!isNaN(timeout) && timeout > 0) {
|
|
66
|
+
this.shutdownTimeout = timeout;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.enabled) {
|
|
70
|
+
// Start the initial timer — if no frontend connects within the timeout, shut down
|
|
71
|
+
this.scheduleShutdown();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onDidWebSocketUpgrade(request: http.IncomingMessage, socket: Socket): MaybePromise<void> {
|
|
76
|
+
this.activeConnections++;
|
|
77
|
+
this.logger.debug(`RemoteAutoShutdown: connection opened (active: ${this.activeConnections})`);
|
|
78
|
+
|
|
79
|
+
this.cancelShutdown();
|
|
80
|
+
|
|
81
|
+
socket.on('disconnect', () => {
|
|
82
|
+
this.activeConnections = Math.max(0, this.activeConnections - 1);
|
|
83
|
+
this.logger.debug(`RemoteAutoShutdown: connection closed (active: ${this.activeConnections})`);
|
|
84
|
+
|
|
85
|
+
if (this.activeConnections === 0 && this.enabled) {
|
|
86
|
+
this.scheduleShutdown();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected scheduleShutdown(): void {
|
|
92
|
+
this.cancelShutdown();
|
|
93
|
+
this.logger.debug(`RemoteAutoShutdown: scheduling shutdown in ${this.shutdownTimeout}ms`);
|
|
94
|
+
this.shutdownTimer = setTimeout(() => {
|
|
95
|
+
this.shutdownTimer = undefined;
|
|
96
|
+
this.tryShutdown();
|
|
97
|
+
}, this.shutdownTimeout);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
protected tryShutdown(): void {
|
|
101
|
+
if (this.activeConnections > 0) {
|
|
102
|
+
this.logger.debug('RemoteAutoShutdown: consumer connected before shutdown, aborting');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.logger.info('RemoteAutoShutdown: no active connections, shutting down');
|
|
106
|
+
// process.exit triggers the 'exit' event, which BackendApplication handles
|
|
107
|
+
// by calling onStop() on all contributions. Theia's onStop() contract is
|
|
108
|
+
// synchronous-only (returns void, not MaybePromise<void>), so process.exit
|
|
109
|
+
// does not skip any cleanup — there is no async shutdown mechanism to miss.
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected cancelShutdown(): void {
|
|
114
|
+
if (this.shutdownTimer) {
|
|
115
|
+
this.logger.debug('RemoteAutoShutdown: cancelling shutdown timer');
|
|
116
|
+
clearTimeout(this.shutdownTimer);
|
|
117
|
+
this.shutdownTimer = undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
18
18
|
import { BackendApplicationContribution, CliContribution } from '@theia/core/lib/node';
|
|
19
|
+
import { MessagingListenerContribution } from '@theia/core/lib/node/messaging/messaging-listeners';
|
|
20
|
+
import { RemoteAutoShutdownService } from './remote-auto-shutdown-service';
|
|
21
|
+
import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution';
|
|
19
22
|
import { RemoteConnectionService } from './remote-connection-service';
|
|
20
23
|
import { RemoteProxyServerProvider } from './remote-proxy-server-provider';
|
|
21
24
|
import { RemoteConnectionSocketProvider } from './remote-connection-socket-provider';
|
|
@@ -86,4 +89,12 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
86
89
|
|
|
87
90
|
bind(SSHIdentityFileCollector).toSelf().inSingletonScope();
|
|
88
91
|
bindRemotePreferences(bind);
|
|
92
|
+
|
|
93
|
+
bind(RemoteAutoShutdownService).toSelf().inSingletonScope();
|
|
94
|
+
bind(CliContribution).toService(RemoteAutoShutdownService);
|
|
95
|
+
bind(MessagingListenerContribution).toService(RemoteAutoShutdownService);
|
|
96
|
+
|
|
97
|
+
bind(RemoteCliContribution).toConstantValue({
|
|
98
|
+
enhanceArgs: () => ['--remote-auto-shutdown']
|
|
99
|
+
});
|
|
89
100
|
});
|
|
@@ -60,7 +60,7 @@ export interface RemoteConnection extends Disposable {
|
|
|
60
60
|
/**
|
|
61
61
|
* copy files from local to remote
|
|
62
62
|
*/
|
|
63
|
-
copy(localPath: string
|
|
63
|
+
copy(localPath: string, remotePath: string): Promise<void>;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* used for disposing when theia is shutting down
|