Skip to content

Commit c27fc2f

Browse files
authored
Merge pull request #92 from int2001/clean_quit
Cleanup websockets on Quit // V-Bump
2 parents 44bb18a + 254f779 commit c27fc2f

File tree

3 files changed

+194
-18
lines changed

3 files changed

+194
-18
lines changed

main.js

Lines changed: 111 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ let currentCAT=null;
2020
var WServer;
2121
let wsServer;
2222
let wsClients = new Set();
23+
let isShuttingDown = false;
24+
let activeConnections = new Set(); // Track active TCP connections
25+
let activeHttpRequests = new Set(); // Track active HTTP requests for cancellation
2326

2427
const DemoAdif='<call:5>DJ7NT <gridsquare:4>JO30 <mode:3>FT8 <rst_sent:3>-15 <rst_rcvd:2>33 <qso_date:8>20240110 <time_on:6>051855 <qso_date_off:8>20240110 <time_off:6>051855 <band:3>40m <freq:8>7.155783 <station_callsign:5>TE1ST <my_gridsquare:6>JO30OO <eor>';
2528

@@ -116,7 +119,8 @@ ipcMain.on("setCAT", async (event,arg) => {
116119
});
117120

118121
ipcMain.on("quit", async (event,arg) => {
119-
app.isQuitting = true;
122+
console.log('Quit requested from renderer');
123+
shutdownApplication();
120124
app.quit();
121125
event.returnValue=true;
122126
});
@@ -127,6 +131,84 @@ ipcMain.on("radio_status_update", async (event,arg) => {
127131
event.returnValue=true;
128132
});
129133

134+
function cleanupConnections() {
135+
console.log('Cleaning up active TCP connections...');
136+
137+
// Close all tracked TCP connections
138+
activeConnections.forEach(connection => {
139+
try {
140+
if (connection && !connection.destroyed) {
141+
connection.destroy();
142+
console.log('Closed TCP connection');
143+
}
144+
} catch (error) {
145+
console.error('Error closing TCP connection:', error);
146+
}
147+
});
148+
149+
// Clear the connections set
150+
activeConnections.clear();
151+
console.log('All TCP connections cleaned up');
152+
153+
// Abort all in-flight HTTP requests
154+
activeHttpRequests.forEach(request => {
155+
try {
156+
request.abort();
157+
console.log('Aborted HTTP request');
158+
} catch (error) {
159+
console.error('Error aborting HTTP request:', error);
160+
}
161+
});
162+
163+
// Clear the HTTP requests set
164+
activeHttpRequests.clear();
165+
console.log('All HTTP requests aborted');
166+
}
167+
168+
function shutdownApplication() {
169+
if (isShuttingDown) {
170+
console.log('Shutdown already in progress, ignoring duplicate request');
171+
return;
172+
}
173+
174+
isShuttingDown = true;
175+
console.log('Initiating application shutdown...');
176+
177+
try {
178+
// Signal renderer to clear timers and connections
179+
if (s_mainWindow && !s_mainWindow.isDestroyed()) {
180+
console.log('Sending cleanup signal to renderer...');
181+
s_mainWindow.webContents.send('cleanup');
182+
}
183+
184+
// Clean up TCP connections
185+
cleanupConnections();
186+
187+
// Close all servers
188+
if (WServer) {
189+
console.log('Closing UDP server...');
190+
WServer.close();
191+
}
192+
if (httpServer) {
193+
console.log('Closing HTTP server...');
194+
httpServer.close();
195+
}
196+
if (wsServer) {
197+
console.log('Closing WebSocket server and clients...');
198+
// Close all WebSocket client connections
199+
wsClients.forEach(client => {
200+
if (client.readyState === WebSocket.OPEN) {
201+
client.close();
202+
}
203+
});
204+
wsClients.clear();
205+
wsServer.close();
206+
}
207+
} catch (error) {
208+
console.error('Error during server shutdown:', error);
209+
}
210+
}
211+
130212
function show_noti(arg) {
131213
if (Notification.isSupported()) {
132214
try {
@@ -165,19 +247,13 @@ ipcMain.on("test", async (event,arg) => {
165247
});
166248

167249
app.on('before-quit', () => {
168-
console.log('Shutting down servers...');
169-
if (WServer) {
170-
WServer.close();
171-
}
172-
if (httpServer) {
173-
httpServer.close();
174-
}
250+
console.log('before-quit event triggered');
251+
shutdownApplication();
175252
});
176253

177254
process.on('SIGINT', () => {
178-
console.log('SIGINT received, closing servers...');
179-
if (WServer) WServer.close();
180-
if (httpServer) httpServer.close();
255+
console.log('SIGINT received, initiating shutdown...');
256+
shutdownApplication();
181257
process.exit(0);
182258
});
183259

@@ -209,8 +285,12 @@ if (!gotTheLock) {
209285
}
210286

211287
app.on('window-all-closed', function () {
288+
console.log('All windows closed, initiating shutdown...');
289+
if (!isShuttingDown) {
290+
shutdownApplication();
291+
}
212292
if (process.platform !== 'darwin') app.quit();
213-
app.quit();
293+
else app.quit();
214294
})
215295

216296
function normalizeTxPwr(adifdata) {
@@ -310,6 +390,9 @@ function send2wavelog(o_cfg,adif, dryrun = false) {
310390
const body = [];
311391
res.on('data', (chunk) => body.push(chunk));
312392
res.on('end', () => {
393+
// Remove request from tracking when completed
394+
activeHttpRequests.delete(req);
395+
313396
let resString = Buffer.concat(body).toString();
314397
if (rej) {
315398
if (resString.indexOf('html>')>0) {
@@ -325,19 +408,26 @@ function send2wavelog(o_cfg,adif, dryrun = false) {
325408
})
326409

327410
req.on('error', (err) => {
411+
// Remove request from tracking on error
412+
activeHttpRequests.delete(req);
328413
rej=true;
329414
req.destroy();
330415
result.resString='{"status":"failed","reason":"internet problem"}';
331416
reject(result);
332417
})
333418

334419
req.on('timeout', (err) => {
420+
// Remove request from tracking on timeout
421+
activeHttpRequests.delete(req);
335422
rej=true;
336423
req.destroy();
337424
result.resString='{"status":"failed","reason":"timeout"}';
338425
reject(result);
339426
})
340427

428+
// Track the HTTP request for cleanup
429+
activeHttpRequests.add(req);
430+
341431
req.write(postData);
342432
req.end();
343433
});
@@ -619,8 +709,15 @@ async function settrx(qrg, mode = '') {
619709
client.end();
620710
});
621711

622-
client.on("error", (err) => {});
623-
client.on("close", () => {});
712+
// Track the connection for cleanup
713+
activeConnections.add(client);
714+
715+
client.on("error", (err) => {
716+
activeConnections.delete(client);
717+
});
718+
client.on("close", () => {
719+
activeConnections.delete(client);
720+
});
624721
}
625722

626723
// Broadcast frequency/mode change to WebSocket clients

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Gateway for connecting WSJT-* and FLRig to Wavelog",
55
"keywords": [],
66
"main": "./main.js",
7-
"version": "1.1.11",
7+
"version": "1.1.12",
88
"author": "DJ7NT",
99
"scripts": {
1010
"start": "electron-forge start",

renderer.js

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
let cfg={};
1111
let active_cfg=0;
1212
let trxpoll=undefined;
13+
let utcTimeInterval=undefined;
14+
let activeConnections = new Set(); // Track active TCP connections in renderer
15+
let activeAbortControllers = new Set(); // Track active HTTP requests for cancellation
1316

1417
const {ipcRenderer} = require('electron')
1518
const net = require('net');
@@ -81,6 +84,7 @@ $(document).ready(function() {
8184
});
8285

8386
bt_quit.addEventListener('click', () => {
87+
cleanup(); // Clear all timers and connections before quit
8488
const x=ipcRenderer.sendSync("quit", '');
8589
});
8690

@@ -115,7 +119,7 @@ $(document).ready(function() {
115119
getStations();
116120
});
117121

118-
setInterval(updateUtcTime, 1000);
122+
utcTimeInterval = setInterval(updateUtcTime, 1000);
119123
window.onload = updateUtcTime;
120124

121125
$("#config-tab").on("click",function() {
@@ -139,6 +143,11 @@ $(document).ready(function() {
139143
ipcRenderer.send('get_info_result', result);
140144
});
141145

146+
// Handle cleanup request from main process
147+
ipcRenderer.on('cleanup', () => {
148+
cleanup();
149+
});
150+
142151
// Dropdown change handler
143152
$('#radio_type').change(function() {
144153
updateRadioFields();
@@ -260,6 +269,9 @@ async function get_trx() {
260269

261270
async function getInfo(which) {
262271
if (cfg.profiles[active_cfg].flrig_ena){
272+
const abortController = new AbortController();
273+
activeAbortControllers.add(abortController);
274+
263275
try {
264276
const response = await fetch(
265277
"http://"+$("#radio_host").val()+':'+$("#radio_port").val(), {
@@ -270,8 +282,10 @@ async function getInfo(which) {
270282
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
271283
},
272284
body: '<?xml version="1.0"?><methodCall><methodName>'+which+'</methodName></methodCall>',
285+
signal: abortController.signal
273286
}
274287
);
288+
275289
const data = await response.text();
276290
const parser = new DOMParser();
277291
const xmlDoc = parser.parseFromString(data, "application/xml");
@@ -292,6 +306,9 @@ async function getInfo(which) {
292306
}
293307
} catch (e) {
294308
return '';
309+
} finally {
310+
// Always clean up abort controller when done
311+
activeAbortControllers.delete(abortController);
295312
}
296313
}
297314
if (cfg.profiles[active_cfg].hamlib_ena) {
@@ -303,6 +320,10 @@ async function getInfo(which) {
303320
return new Promise((resolve, reject) => {
304321
if (commands[which]) {
305322
const client = net.createConnection({ host, port }, () => client.write(commands[which]));
323+
324+
// Track the connection for cleanup
325+
activeConnections.add(client);
326+
306327
client.on('data', (data) => {
307328
data = data.toString()
308329
if(data.startsWith("RPRT")){
@@ -312,8 +333,13 @@ async function getInfo(which) {
312333
}
313334
client.end();
314335
});
315-
client.on('error', (err) => reject());
316-
client.on("close", () => {});
336+
client.on('error', (err) => {
337+
activeConnections.delete(client);
338+
reject();
339+
});
340+
client.on("close", () => {
341+
activeConnections.delete(client);
342+
});
317343
} else {
318344
resolve(undefined);
319345
}
@@ -396,6 +422,59 @@ async function informWavelog(CAT) {
396422
return x;
397423
}
398424

425+
function cleanupConnections() {
426+
console.log('Cleaning up renderer TCP connections...');
427+
428+
// Close all tracked TCP connections
429+
activeConnections.forEach(connection => {
430+
try {
431+
if (connection && !connection.destroyed) {
432+
connection.destroy();
433+
console.log('Closed renderer TCP connection');
434+
}
435+
} catch (error) {
436+
console.error('Error closing renderer TCP connection:', error);
437+
}
438+
});
439+
440+
// Clear the connections set
441+
activeConnections.clear();
442+
console.log('All renderer TCP connections cleaned up');
443+
444+
// Abort all in-flight HTTP requests
445+
activeAbortControllers.forEach(controller => {
446+
try {
447+
controller.abort();
448+
console.log('Aborted HTTP request');
449+
} catch (error) {
450+
console.error('Error aborting HTTP request:', error);
451+
}
452+
});
453+
454+
// Clear the abort controllers set
455+
activeAbortControllers.clear();
456+
console.log('All HTTP requests aborted');
457+
}
458+
459+
function cleanup() {
460+
// Clear radio polling timeout
461+
if (trxpoll) {
462+
clearTimeout(trxpoll);
463+
trxpoll = undefined;
464+
console.log('Cleared radio polling timeout');
465+
}
466+
467+
// Clear UTC time update interval
468+
if (utcTimeInterval) {
469+
clearInterval(utcTimeInterval);
470+
utcTimeInterval = undefined;
471+
console.log('Cleared UTC time update interval');
472+
}
473+
474+
// Clean up TCP connections
475+
cleanupConnections();
476+
}
477+
399478
function updateUtcTime() {
400479
const now = new Date();
401480

0 commit comments

Comments
 (0)