socket-function 1.2.22 → 1.2.23

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/index.d.ts CHANGED
@@ -984,6 +984,9 @@ declare module "socket-function/src/forwardPort" {
984
984
  externalPort: number;
985
985
  internalPort: number;
986
986
  duration?: number;
987
+ /** Keep the lease alive by re-creating the mapping at 80% of `duration`, forever. On by
988
+ * default, which is what you want — an un-maintained forward silently expires. */
989
+ autoRenew?: boolean;
987
990
  }): Promise<void>;
988
991
  /** Our machine's LAN IP, as the router sees it — used to tell whether an existing port
989
992
  * mapping points at us or at a different machine on the network. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socket-function",
3
- "version": "1.2.22",
3
+ "version": "1.2.23",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "dependencies": {
@@ -19,6 +19,9 @@ export declare function forwardPort(config: {
19
19
  externalPort: number;
20
20
  internalPort: number;
21
21
  duration?: number;
22
+ /** Keep the lease alive by re-creating the mapping at 80% of `duration`, forever. On by
23
+ * default, which is what you want — an un-maintained forward silently expires. */
24
+ autoRenew?: boolean;
22
25
  }): Promise<void>;
23
26
  /** Our machine's LAN IP, as the router sees it — used to tell whether an existing port
24
27
  * mapping points at us or at a different machine on the network. */
@@ -74,36 +74,58 @@ export async function listPortMappings(): Promise<PortMapping[]> {
74
74
  throw new Error(`Failed to list port mappings, could not find a working controlURL. Last error: ${(lastError as Error)?.stack ?? lastError}`);
75
75
  }
76
76
 
77
+ // A UPnP lease expires on its own, so a forward left un-renewed silently dies partway through.
78
+ // Renew at 80% of the duration: enough margin to cover a failed attempt (we retry on the next
79
+ // tick) while the previous lease is still live, so the mapping never actually lapses.
80
+ const LEASE_RENEW_FRACTION = 0.8;
81
+
77
82
  export async function forwardPort(config: {
78
83
  externalPort: number;
79
84
  internalPort: number;
80
85
  duration?: number;
86
+ /** Keep the lease alive by re-creating the mapping at 80% of `duration`, forever. On by
87
+ * default, which is what you want — an un-maintained forward silently expires. */
88
+ autoRenew?: boolean;
81
89
  }) {
82
- try {
83
- const { externalPort, internalPort } = config;
84
- let duration = config.duration ?? timeInHour;
85
-
86
- const { internalIP, gatewayIP, controlPort, controlURLs } = await resolveGateway();
90
+ let duration = config.duration ?? timeInHour;
91
+ let autoRenew = config.autoRenew ?? true;
87
92
 
88
- for (let controlURL of controlURLs) {
89
- try {
90
- await createPortMapping({
91
- externalPort, internalPort,
92
- gatewayIP,
93
- controlPort,
94
- controlPath: controlURL,
95
- internalIP,
96
- duration,
97
- });
98
- console.log(`Port mapping created on ${gatewayIP}:${externalPort} -> ${internalIP}:${internalPort}`);
99
- return;
100
- } catch (e) {
101
- console.error(`Failed to create port mapping using controlURL ${controlURL}`, e);
93
+ async function createMapping() {
94
+ try {
95
+ const { externalPort, internalPort } = config;
96
+ const { internalIP, gatewayIP, controlPort, controlURLs } = await resolveGateway();
97
+
98
+ for (let controlURL of controlURLs) {
99
+ try {
100
+ await createPortMapping({
101
+ externalPort, internalPort,
102
+ gatewayIP,
103
+ controlPort,
104
+ controlPath: controlURL,
105
+ internalIP,
106
+ duration,
107
+ });
108
+ console.log(`Port mapping created on ${gatewayIP}:${externalPort} -> ${internalIP}:${internalPort}`);
109
+ return;
110
+ } catch (e) {
111
+ console.error(`Failed to create port mapping using controlURL ${controlURL}`, e);
112
+ }
102
113
  }
114
+ console.error("Failed to create port mapping, could not find controlURL");
115
+ } catch (e) {
116
+ console.error("Error in forwardPort", e);
103
117
  }
104
- console.error("Failed to create port mapping, could not find controlURL");
105
- } catch (e) {
106
- console.error("Error in forwardPort", e);
118
+ }
119
+
120
+ await createMapping();
121
+
122
+ if (autoRenew) {
123
+ // unref so a script that only wants a one-off forward (and then exits) isn't held open by
124
+ // the renewal timer; a running server stays alive on its own and the renewals keep firing.
125
+ let timer = setInterval(() => {
126
+ void createMapping();
127
+ }, Math.max(1, Math.floor(duration * LEASE_RENEW_FRACTION)));
128
+ timer.unref?.();
107
129
  }
108
130
  }
109
131
 
@@ -11,8 +11,8 @@ import { parseSNIExtension, parseTLSHello, SNIType } from "./tlsParsing";
11
11
  import debugbreak from "debugbreak";
12
12
  import { getNodeId } from "./nodeCache";
13
13
  import crypto from "crypto";
14
- import { Watchable, getRootDomain, timeInHour, timeInMinute } from "./misc";
15
- import { delay, runInfinitePoll } from "./batching";
14
+ import { Watchable, getRootDomain } from "./misc";
15
+ import { delay } from "./batching";
16
16
  import { magenta, red } from "./formatting/logColors";
17
17
  import { yellow } from "./formatting/logColors";
18
18
  import { green } from "./formatting/logColors";
@@ -439,12 +439,9 @@ export async function startSocketServer(
439
439
  port = (realServer.address() as net.AddressInfo).port;
440
440
 
441
441
  if (doForward) {
442
- // The mapping is claimed above; keep refreshing the lease so it doesn't expire.
443
- async function refreshForward() {
444
- await forwardPort({ externalPort: port, internalPort: port });
445
- console.log(magenta(`Refreshed port forward ${port} to our machine`));
446
- }
447
- runInfinitePoll(timeInMinute * 30, refreshForward);
442
+ // The mapping is claimed above; this establishes the maintained forward, which
443
+ // auto-renews its own lease (at 80% of the duration) so it doesn't expire.
444
+ await forwardPort({ externalPort: port, internalPort: port });
448
445
  }
449
446
 
450
447
  let nodeId = getNodeId(getCommonName(config.cert), port);