socket-function 1.2.4 → 1.2.6
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/package.json +1 -1
- package/src/webSocketServer.ts +52 -35
package/package.json
CHANGED
package/src/webSocketServer.ts
CHANGED
|
@@ -20,6 +20,12 @@ import { formatTime } from "./formatting/format";
|
|
|
20
20
|
import { getExternalIP, testTCPIsListening } from "./networking";
|
|
21
21
|
import { forwardPort } from "./forwardPort";
|
|
22
22
|
|
|
23
|
+
// When a requested port is taken and useAvailablePortIfPortInUse is set, we scan
|
|
24
|
+
// upwards from this base instead of binding a random OS-assigned port, so restarts
|
|
25
|
+
// land on predictable, consistent ports.
|
|
26
|
+
const PORT_SCAN_START = 13000;
|
|
27
|
+
const PORT_SCAN_COUNT = 10000;
|
|
28
|
+
|
|
23
29
|
export type SocketServerConfig = (
|
|
24
30
|
https.ServerOptions & {
|
|
25
31
|
key: string | Buffer;
|
|
@@ -311,52 +317,63 @@ export async function startSocketServer(
|
|
|
311
317
|
|
|
312
318
|
let port = config.port;
|
|
313
319
|
if (!SocketFunction.silent) {
|
|
314
|
-
console.log(yellow(`Trying to
|
|
320
|
+
console.log(yellow(`Trying to listen on ${host}:${port}`));
|
|
315
321
|
}
|
|
316
322
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
//
|
|
321
|
-
async function
|
|
323
|
+
// Attempts to bind realServer to a single port. Resolves true once the server is
|
|
324
|
+
// actually listening, false if the port is already in use, and rejects on any
|
|
325
|
+
// other error. After an EADDRINUSE the server is still usable, so the caller can
|
|
326
|
+
// simply retry listen() on the next candidate.
|
|
327
|
+
async function tryListen(candidatePort: number): Promise<boolean> {
|
|
322
328
|
return await new Promise<boolean>((resolve, reject) => {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
realServer.once("listening", () => {
|
|
327
|
-
resolve(false);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (config.useAvailablePortIfPortInUse && port) {
|
|
333
|
-
realServer.listen(port, host);
|
|
334
|
-
let isListening = await new Promise<boolean>((resolve, reject) => {
|
|
335
|
-
if (realServer.listening) {
|
|
336
|
-
resolve(true);
|
|
337
|
-
return;
|
|
329
|
+
function cleanup() {
|
|
330
|
+
realServer.removeListener("error", onError);
|
|
331
|
+
realServer.removeListener("listening", onListening);
|
|
338
332
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
333
|
+
function onError(e: NodeJS.ErrnoException) {
|
|
334
|
+
cleanup();
|
|
335
|
+
if (e.code === "EADDRINUSE" || e.message.includes("EADDRINUSE")) {
|
|
336
|
+
resolve(false);
|
|
342
337
|
} else {
|
|
343
338
|
reject(e);
|
|
344
339
|
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
340
|
+
}
|
|
341
|
+
function onListening() {
|
|
342
|
+
cleanup();
|
|
343
|
+
resolve(true);
|
|
344
|
+
}
|
|
345
|
+
realServer.once("error", onError);
|
|
346
|
+
realServer.once("listening", onListening);
|
|
347
|
+
realServer.listen(candidatePort, host);
|
|
349
348
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let bound = false;
|
|
352
|
+
// Honor an explicitly requested port first. A falsy port (0, the default) means
|
|
353
|
+
// "no preference" — skip straight to the scan so we land on a consistent port
|
|
354
|
+
// instead of an OS-assigned random one.
|
|
355
|
+
if (port) {
|
|
356
|
+
bound = await tryListen(port);
|
|
357
|
+
if (!bound && !config.useAvailablePortIfPortInUse) {
|
|
358
|
+
throw new Error(`Port ${port} is already in use (set useAvailablePortIfPortInUse to fall back to another port)`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Scan upwards from PORT_SCAN_START for the first free port, so restarts land on
|
|
362
|
+
// predictable ports.
|
|
363
|
+
if (!bound) {
|
|
364
|
+
for (let candidate = PORT_SCAN_START; candidate < PORT_SCAN_START + PORT_SCAN_COUNT; candidate++) {
|
|
365
|
+
if (candidate === port) continue;
|
|
366
|
+
if (await tryListen(candidate)) {
|
|
367
|
+
bound = true;
|
|
368
|
+
port = candidate;
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (!bound) {
|
|
373
|
+
throw new Error(`Could not find an available port in range ${PORT_SCAN_START}-${PORT_SCAN_START + PORT_SCAN_COUNT - 1} (requested ${config.port})`);
|
|
354
374
|
}
|
|
355
|
-
} else {
|
|
356
|
-
realServer.listen(port, host);
|
|
357
375
|
}
|
|
358
376
|
|
|
359
|
-
await listeningPromise;
|
|
360
377
|
port = (realServer.address() as net.AddressInfo).port;
|
|
361
378
|
|
|
362
379
|
if (config.autoForwardPort && config.public) {
|