unplugin-https-reverse-proxy 4.1.0-beta.3 → 4.1.0-beta.5

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/dist/nuxt.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  vitePrintUrls
3
- } from "./chunk-HBXMM25C.js";
3
+ } from "./chunk-A3KSOGWY.js";
4
4
  import {
5
5
  consola
6
6
  } from "./chunk-EY3HXBJ4.js";
@@ -29,6 +29,7 @@ var import_node_process3 = __toESM(require("process"), 1);
29
29
  var import_node_buffer = require("buffer");
30
30
  var import_node_fs2 = require("fs");
31
31
  var import_node_net3 = require("net");
32
+ var import_node_path2 = require("path");
32
33
  var import_node_process2 = __toESM(require("process"), 1);
33
34
 
34
35
  // src/privileged/server.ts
@@ -215,6 +216,28 @@ function createHelperRuntime(deps) {
215
216
  let teardown;
216
217
  let generation = 0;
217
218
  let caddy;
219
+ const hostOwners = /* @__PURE__ */ new Map();
220
+ const hostId = (ip, host) => `${ip} ${host.toLowerCase()}`;
221
+ function addHostOwner(client, ip, host) {
222
+ const id = hostId(ip, host);
223
+ let owners = hostOwners.get(id);
224
+ if (!owners) {
225
+ owners = /* @__PURE__ */ new Set();
226
+ hostOwners.set(id, owners);
227
+ }
228
+ owners.add(client ?? deps);
229
+ }
230
+ function releaseHostOwner(client, ip, host) {
231
+ const id = hostId(ip, host);
232
+ const owners = hostOwners.get(id);
233
+ if (!owners)
234
+ return true;
235
+ owners.delete(client ?? deps);
236
+ if (owners.size > 0)
237
+ return false;
238
+ hostOwners.delete(id);
239
+ return true;
240
+ }
218
241
  const broadcast = (event) => {
219
242
  const line = serializeMessage(event);
220
243
  for (const client of clients) {
@@ -273,7 +296,7 @@ function createHelperRuntime(deps) {
273
296
  return false;
274
297
  }
275
298
  }
276
- async function handle(request) {
299
+ async function handle(request, client) {
277
300
  try {
278
301
  switch (request.method) {
279
302
  case "ping":
@@ -285,6 +308,7 @@ function createHelperRuntime(deps) {
285
308
  const edited = addHost(deps.readHosts(), ip, host);
286
309
  if (edited.changed)
287
310
  deps.writeHosts(edited.content);
311
+ addHostOwner(client, ip, host);
288
312
  await flushDnsBestEffort(deps);
289
313
  return { id: request.id, ok: true };
290
314
  }
@@ -292,6 +316,9 @@ function createHelperRuntime(deps) {
292
316
  const { ip, host } = request.params;
293
317
  if (!isSafeIp(ip) || !isSafeHost(host))
294
318
  return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
319
+ const last = releaseHostOwner(client, ip, host);
320
+ if (!last)
321
+ return { id: request.id, ok: true, result: { kept: true } };
295
322
  const edited = removeHost(deps.readHosts(), ip, host);
296
323
  if (edited.changed)
297
324
  deps.writeHosts(edited.content);
@@ -302,14 +329,14 @@ function createHelperRuntime(deps) {
302
329
  const { port } = request.params;
303
330
  if (!isAllowedFreePort(port))
304
331
  return { id: request.id, ok: false, error: { message: `port ${port} is not releasable` } };
305
- const killed = await deps.freePort(port);
332
+ const killed = await deps.freePort(port, caddy == null ? void 0 : caddy.pid);
306
333
  return { id: request.id, ok: true, result: { killed } };
307
334
  }
308
335
  case "caddy.start": {
309
- if (caddy)
310
- await stopCaddy();
336
+ if (caddy && caddy.running !== false)
337
+ return { id: request.id, ok: true, result: { pid: caddy.pid, started: false } };
311
338
  caddy = deps.startCaddy(broadcast);
312
- return { id: request.id, ok: true, result: { pid: caddy.pid } };
339
+ return { id: request.id, ok: true, result: { pid: caddy.pid, started: true } };
313
340
  }
314
341
  case "caddy.stop": {
315
342
  await stopCaddy();
@@ -335,6 +362,21 @@ function createHelperRuntime(deps) {
335
362
  },
336
363
  async detach(client) {
337
364
  clients.delete(client);
365
+ for (const [id, owners] of [...hostOwners]) {
366
+ if (!owners.delete(client) || owners.size > 0)
367
+ continue;
368
+ hostOwners.delete(id);
369
+ const [ip, host] = id.split(" ");
370
+ try {
371
+ const edited = removeHost(deps.readHosts(), ip, host);
372
+ if (edited.changed) {
373
+ deps.writeHosts(edited.content);
374
+ await flushDnsBestEffort(deps);
375
+ }
376
+ } catch (error) {
377
+ deps.log(`hosts cleanup failed: ${error.message}`);
378
+ }
379
+ }
338
380
  if (clients.size === 0)
339
381
  await dispose();
340
382
  },
@@ -408,11 +450,11 @@ function createNodeHelperDeps(options) {
408
450
  }
409
451
  throw lastError ?? new Error("no DNS cache flush command for this platform");
410
452
  },
411
- freePort: async (port) => {
453
+ freePort: async (port, keepPid) => {
412
454
  const pids = (0, import_node_os.platform)() === "win32" ? parseWindowsListeningPids((await execFileAsync("netstat", ["-ano"])).stdout, port) : parseListeningPids((await execFileQuiet("lsof", [`-ti:${port}`, "-sTCP:LISTEN"])).stdout);
413
455
  const killed = [];
414
456
  for (const pid of pids) {
415
- if (pid === import_node_process.default.pid)
457
+ if (pid === import_node_process.default.pid || keepPid !== void 0 && pid === keepPid)
416
458
  continue;
417
459
  try {
418
460
  import_node_process.default.kill(pid, "SIGTERM");
@@ -431,16 +473,24 @@ function createNodeHelperDeps(options) {
431
473
  storageOwner: options.storageOwner,
432
474
  startCaddy: (emit) => {
433
475
  var _a, _b;
434
- const child = (0, import_node_child_process.spawn)(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath], {
476
+ const child = (0, import_node_child_process.spawn)(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath, "--watch"], {
435
477
  cwd: options.caddyCwd ?? import_node_process.default.cwd(),
436
478
  env: { ...import_node_process.default.env, ...options.caddyEnv },
437
479
  stdio: ["ignore", "pipe", "pipe"]
438
480
  });
481
+ const handle = {
482
+ pid: child.pid,
483
+ kill: (signal) => child.kill(signal),
484
+ running: true
485
+ };
439
486
  (_a = child.stdout) == null ? void 0 : _a.on("data", (data) => emit({ type: "event", kind: "caddy.stdout", data: data.toString() }));
440
487
  (_b = child.stderr) == null ? void 0 : _b.on("data", (data) => emit({ type: "event", kind: "caddy.stderr", data: data.toString() }));
441
- child.on("exit", (code, signal) => emit({ type: "event", kind: "caddy.exit", code, signal }));
442
- caddy = child;
443
- return caddy;
488
+ child.on("exit", (code, signal) => {
489
+ handle.running = false;
490
+ emit({ type: "event", kind: "caddy.exit", code, signal });
491
+ });
492
+ caddy = handle;
493
+ return handle;
444
494
  },
445
495
  stopCaddy: async () => {
446
496
  const current = caddy;
@@ -474,7 +524,7 @@ function attachSocket(socket, runtime, log) {
474
524
  log(`invalid request: ${error.message}`);
475
525
  continue;
476
526
  }
477
- void runtime.handle(request).then((response) => {
527
+ void runtime.handle(request, socket).then((response) => {
478
528
  if (!socket.destroyed)
479
529
  socket.write(serializeMessage(response));
480
530
  });
@@ -490,6 +540,7 @@ function attachSocket(socket, runtime, log) {
490
540
 
491
541
  // src/privileged/bootstrap.ts
492
542
  var HANDSHAKE_TIMEOUT_MS = 12e4;
543
+ var DEFAULT_IDLE_TIMEOUT_MS = 3e4;
493
544
  async function startHelper(options) {
494
545
  const { socketPath, token } = options;
495
546
  const log = options.log ?? ((message) => import_node_process2.default.stdout.write(`[uhrp-helper] ${message}
@@ -512,12 +563,25 @@ async function startHelper(options) {
512
563
  } catch {
513
564
  }
514
565
  const connections = /* @__PURE__ */ new Set();
566
+ const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
567
+ let idleTimer;
568
+ const stopIdleTimer = () => {
569
+ if (idleTimer) {
570
+ clearTimeout(idleTimer);
571
+ idleTimer = void 0;
572
+ }
573
+ };
515
574
  const server = (0, import_node_net3.createServer)((socket) => {
516
575
  var _a;
517
576
  let buffer = "";
518
577
  let authenticated = false;
578
+ stopIdleTimer();
519
579
  connections.add(socket);
520
- socket.on("close", () => connections.delete(socket));
580
+ socket.on("close", () => {
581
+ connections.delete(socket);
582
+ if (connections.size === 0)
583
+ scheduleIdleExit();
584
+ });
521
585
  const timer = setTimeout(() => {
522
586
  if (!authenticated)
523
587
  socket.destroy();
@@ -559,10 +623,27 @@ async function startHelper(options) {
559
623
  socket.on("error", () => {
560
624
  });
561
625
  });
626
+ function scheduleIdleExit() {
627
+ var _a;
628
+ if (idleTimer || connections.size > 0)
629
+ return;
630
+ idleTimer = setTimeout(() => {
631
+ var _a2, _b;
632
+ idleTimer = void 0;
633
+ if (connections.size > 0)
634
+ return;
635
+ log("no clients left; shutting down");
636
+ removeOwnSocket(socketPath, token);
637
+ server.close(() => import_node_process2.default.exit(0));
638
+ (_b = (_a2 = setTimeout(() => import_node_process2.default.exit(0), 1e3)).unref) == null ? void 0 : _b.call(_a2);
639
+ }, idleTimeoutMs);
640
+ (_a = idleTimer.unref) == null ? void 0 : _a.call(idleTimer);
641
+ }
562
642
  await new Promise((resolve, reject) => {
563
643
  server.once("error", reject);
564
644
  server.listen(socketPath, () => resolve());
565
645
  });
646
+ scheduleIdleExit();
566
647
  try {
567
648
  if (typeof options.ownerUid === "number") {
568
649
  (0, import_node_fs2.chownSync)(socketPath, options.ownerUid, options.ownerGid ?? options.ownerUid);
@@ -585,6 +666,22 @@ async function startHelper(options) {
585
666
  }
586
667
  };
587
668
  }
669
+ function removeOwnSocket(socketPath, token) {
670
+ if (import_node_process2.default.platform === "win32")
671
+ return;
672
+ try {
673
+ const tokensDir = (0, import_node_path2.dirname)(socketPath);
674
+ const tokenFile = (0, import_node_path2.join)(tokensDir, "helper.token");
675
+ const current = (0, import_node_fs2.existsSync)(tokenFile) ? (0, import_node_fs2.readFileSync)(tokenFile, "utf8").trim() : void 0;
676
+ if (current === token)
677
+ (0, import_node_fs2.rmSync)(tokenFile, { force: true });
678
+ } catch {
679
+ }
680
+ try {
681
+ (0, import_node_fs2.rmSync)(socketPath, { force: true });
682
+ } catch {
683
+ }
684
+ }
588
685
 
589
686
  // src/privileged-helper.ts
590
687
  function parseArgs(argv) {
@@ -7,8 +7,9 @@ import process3 from "node:process";
7
7
 
8
8
  // src/privileged/bootstrap.ts
9
9
  import { Buffer } from "node:buffer";
10
- import { chmodSync, chownSync, rmSync } from "node:fs";
10
+ import { chmodSync, chownSync, existsSync, readFileSync as readFileSync2, rmSync } from "node:fs";
11
11
  import { createServer } from "node:net";
12
+ import { dirname, join as join2 } from "node:path";
12
13
  import process2 from "node:process";
13
14
 
14
15
  // src/privileged/server.ts
@@ -189,6 +190,28 @@ function createHelperRuntime(deps) {
189
190
  let teardown;
190
191
  let generation = 0;
191
192
  let caddy;
193
+ const hostOwners = /* @__PURE__ */ new Map();
194
+ const hostId = (ip, host) => `${ip} ${host.toLowerCase()}`;
195
+ function addHostOwner(client, ip, host) {
196
+ const id = hostId(ip, host);
197
+ let owners = hostOwners.get(id);
198
+ if (!owners) {
199
+ owners = /* @__PURE__ */ new Set();
200
+ hostOwners.set(id, owners);
201
+ }
202
+ owners.add(client ?? deps);
203
+ }
204
+ function releaseHostOwner(client, ip, host) {
205
+ const id = hostId(ip, host);
206
+ const owners = hostOwners.get(id);
207
+ if (!owners)
208
+ return true;
209
+ owners.delete(client ?? deps);
210
+ if (owners.size > 0)
211
+ return false;
212
+ hostOwners.delete(id);
213
+ return true;
214
+ }
192
215
  const broadcast = (event) => {
193
216
  const line = serializeMessage(event);
194
217
  for (const client of clients) {
@@ -247,7 +270,7 @@ function createHelperRuntime(deps) {
247
270
  return false;
248
271
  }
249
272
  }
250
- async function handle(request) {
273
+ async function handle(request, client) {
251
274
  try {
252
275
  switch (request.method) {
253
276
  case "ping":
@@ -259,6 +282,7 @@ function createHelperRuntime(deps) {
259
282
  const edited = addHost(deps.readHosts(), ip, host);
260
283
  if (edited.changed)
261
284
  deps.writeHosts(edited.content);
285
+ addHostOwner(client, ip, host);
262
286
  await flushDnsBestEffort(deps);
263
287
  return { id: request.id, ok: true };
264
288
  }
@@ -266,6 +290,9 @@ function createHelperRuntime(deps) {
266
290
  const { ip, host } = request.params;
267
291
  if (!isSafeIp(ip) || !isSafeHost(host))
268
292
  return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
293
+ const last = releaseHostOwner(client, ip, host);
294
+ if (!last)
295
+ return { id: request.id, ok: true, result: { kept: true } };
269
296
  const edited = removeHost(deps.readHosts(), ip, host);
270
297
  if (edited.changed)
271
298
  deps.writeHosts(edited.content);
@@ -276,14 +303,14 @@ function createHelperRuntime(deps) {
276
303
  const { port } = request.params;
277
304
  if (!isAllowedFreePort(port))
278
305
  return { id: request.id, ok: false, error: { message: `port ${port} is not releasable` } };
279
- const killed = await deps.freePort(port);
306
+ const killed = await deps.freePort(port, caddy == null ? void 0 : caddy.pid);
280
307
  return { id: request.id, ok: true, result: { killed } };
281
308
  }
282
309
  case "caddy.start": {
283
- if (caddy)
284
- await stopCaddy();
310
+ if (caddy && caddy.running !== false)
311
+ return { id: request.id, ok: true, result: { pid: caddy.pid, started: false } };
285
312
  caddy = deps.startCaddy(broadcast);
286
- return { id: request.id, ok: true, result: { pid: caddy.pid } };
313
+ return { id: request.id, ok: true, result: { pid: caddy.pid, started: true } };
287
314
  }
288
315
  case "caddy.stop": {
289
316
  await stopCaddy();
@@ -309,6 +336,21 @@ function createHelperRuntime(deps) {
309
336
  },
310
337
  async detach(client) {
311
338
  clients.delete(client);
339
+ for (const [id, owners] of [...hostOwners]) {
340
+ if (!owners.delete(client) || owners.size > 0)
341
+ continue;
342
+ hostOwners.delete(id);
343
+ const [ip, host] = id.split(" ");
344
+ try {
345
+ const edited = removeHost(deps.readHosts(), ip, host);
346
+ if (edited.changed) {
347
+ deps.writeHosts(edited.content);
348
+ await flushDnsBestEffort(deps);
349
+ }
350
+ } catch (error) {
351
+ deps.log(`hosts cleanup failed: ${error.message}`);
352
+ }
353
+ }
312
354
  if (clients.size === 0)
313
355
  await dispose();
314
356
  },
@@ -382,11 +424,11 @@ function createNodeHelperDeps(options) {
382
424
  }
383
425
  throw lastError ?? new Error("no DNS cache flush command for this platform");
384
426
  },
385
- freePort: async (port) => {
427
+ freePort: async (port, keepPid) => {
386
428
  const pids = platform() === "win32" ? parseWindowsListeningPids((await execFileAsync("netstat", ["-ano"])).stdout, port) : parseListeningPids((await execFileQuiet("lsof", [`-ti:${port}`, "-sTCP:LISTEN"])).stdout);
387
429
  const killed = [];
388
430
  for (const pid of pids) {
389
- if (pid === process.pid)
431
+ if (pid === process.pid || keepPid !== void 0 && pid === keepPid)
390
432
  continue;
391
433
  try {
392
434
  process.kill(pid, "SIGTERM");
@@ -405,16 +447,24 @@ function createNodeHelperDeps(options) {
405
447
  storageOwner: options.storageOwner,
406
448
  startCaddy: (emit) => {
407
449
  var _a, _b;
408
- const child = spawn(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath], {
450
+ const child = spawn(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath, "--watch"], {
409
451
  cwd: options.caddyCwd ?? process.cwd(),
410
452
  env: { ...process.env, ...options.caddyEnv },
411
453
  stdio: ["ignore", "pipe", "pipe"]
412
454
  });
455
+ const handle = {
456
+ pid: child.pid,
457
+ kill: (signal) => child.kill(signal),
458
+ running: true
459
+ };
413
460
  (_a = child.stdout) == null ? void 0 : _a.on("data", (data) => emit({ type: "event", kind: "caddy.stdout", data: data.toString() }));
414
461
  (_b = child.stderr) == null ? void 0 : _b.on("data", (data) => emit({ type: "event", kind: "caddy.stderr", data: data.toString() }));
415
- child.on("exit", (code, signal) => emit({ type: "event", kind: "caddy.exit", code, signal }));
416
- caddy = child;
417
- return caddy;
462
+ child.on("exit", (code, signal) => {
463
+ handle.running = false;
464
+ emit({ type: "event", kind: "caddy.exit", code, signal });
465
+ });
466
+ caddy = handle;
467
+ return handle;
418
468
  },
419
469
  stopCaddy: async () => {
420
470
  const current = caddy;
@@ -448,7 +498,7 @@ function attachSocket(socket, runtime, log) {
448
498
  log(`invalid request: ${error.message}`);
449
499
  continue;
450
500
  }
451
- void runtime.handle(request).then((response) => {
501
+ void runtime.handle(request, socket).then((response) => {
452
502
  if (!socket.destroyed)
453
503
  socket.write(serializeMessage(response));
454
504
  });
@@ -464,6 +514,7 @@ function attachSocket(socket, runtime, log) {
464
514
 
465
515
  // src/privileged/bootstrap.ts
466
516
  var HANDSHAKE_TIMEOUT_MS = 12e4;
517
+ var DEFAULT_IDLE_TIMEOUT_MS = 3e4;
467
518
  async function startHelper(options) {
468
519
  const { socketPath, token } = options;
469
520
  const log = options.log ?? ((message) => process2.stdout.write(`[uhrp-helper] ${message}
@@ -486,12 +537,25 @@ async function startHelper(options) {
486
537
  } catch {
487
538
  }
488
539
  const connections = /* @__PURE__ */ new Set();
540
+ const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
541
+ let idleTimer;
542
+ const stopIdleTimer = () => {
543
+ if (idleTimer) {
544
+ clearTimeout(idleTimer);
545
+ idleTimer = void 0;
546
+ }
547
+ };
489
548
  const server = createServer((socket) => {
490
549
  var _a;
491
550
  let buffer = "";
492
551
  let authenticated = false;
552
+ stopIdleTimer();
493
553
  connections.add(socket);
494
- socket.on("close", () => connections.delete(socket));
554
+ socket.on("close", () => {
555
+ connections.delete(socket);
556
+ if (connections.size === 0)
557
+ scheduleIdleExit();
558
+ });
495
559
  const timer = setTimeout(() => {
496
560
  if (!authenticated)
497
561
  socket.destroy();
@@ -533,10 +597,27 @@ async function startHelper(options) {
533
597
  socket.on("error", () => {
534
598
  });
535
599
  });
600
+ function scheduleIdleExit() {
601
+ var _a;
602
+ if (idleTimer || connections.size > 0)
603
+ return;
604
+ idleTimer = setTimeout(() => {
605
+ var _a2, _b;
606
+ idleTimer = void 0;
607
+ if (connections.size > 0)
608
+ return;
609
+ log("no clients left; shutting down");
610
+ removeOwnSocket(socketPath, token);
611
+ server.close(() => process2.exit(0));
612
+ (_b = (_a2 = setTimeout(() => process2.exit(0), 1e3)).unref) == null ? void 0 : _b.call(_a2);
613
+ }, idleTimeoutMs);
614
+ (_a = idleTimer.unref) == null ? void 0 : _a.call(idleTimer);
615
+ }
536
616
  await new Promise((resolve, reject) => {
537
617
  server.once("error", reject);
538
618
  server.listen(socketPath, () => resolve());
539
619
  });
620
+ scheduleIdleExit();
540
621
  try {
541
622
  if (typeof options.ownerUid === "number") {
542
623
  chownSync(socketPath, options.ownerUid, options.ownerGid ?? options.ownerUid);
@@ -559,6 +640,22 @@ async function startHelper(options) {
559
640
  }
560
641
  };
561
642
  }
643
+ function removeOwnSocket(socketPath, token) {
644
+ if (process2.platform === "win32")
645
+ return;
646
+ try {
647
+ const tokensDir = dirname(socketPath);
648
+ const tokenFile = join2(tokensDir, "helper.token");
649
+ const current = existsSync(tokenFile) ? readFileSync2(tokenFile, "utf8").trim() : void 0;
650
+ if (current === token)
651
+ rmSync(tokenFile, { force: true });
652
+ } catch {
653
+ }
654
+ try {
655
+ rmSync(socketPath, { force: true });
656
+ } catch {
657
+ }
658
+ }
562
659
 
563
660
  // src/privileged-helper.ts
564
661
  function parseArgs(argv) {