roster-server 2.1.34 → 2.2.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/index.js +65 -35
- package/package.json +1 -1
- package/test/roster-server.test.js +9 -0
package/index.js
CHANGED
|
@@ -8,6 +8,8 @@ const Greenlock = require('./vendor/greenlock-express/greenlock-express.js');
|
|
|
8
8
|
const GreenlockShim = require('./vendor/greenlock-express/greenlock-shim.js');
|
|
9
9
|
const log = require('lemonlog')('roster');
|
|
10
10
|
|
|
11
|
+
const isBunRuntime = typeof Bun !== 'undefined' || (typeof process !== 'undefined' && process.release?.name === 'bun');
|
|
12
|
+
|
|
11
13
|
// CRC32 implementation for deterministic port assignment
|
|
12
14
|
function crc32(str) {
|
|
13
15
|
const crcTable = [];
|
|
@@ -234,9 +236,13 @@ class Roster {
|
|
|
234
236
|
this.disableWildcard = options.disableWildcard !== undefined
|
|
235
237
|
? parseBooleanFlag(options.disableWildcard, false)
|
|
236
238
|
: parseBooleanFlag(process.env.ROSTER_DISABLE_WILDCARD, false);
|
|
239
|
+
const combineDefault = isBunRuntime;
|
|
237
240
|
this.combineWildcardCerts = options.combineWildcardCerts !== undefined
|
|
238
|
-
? parseBooleanFlag(options.combineWildcardCerts,
|
|
239
|
-
: parseBooleanFlag(process.env.ROSTER_COMBINE_WILDCARD_CERTS,
|
|
241
|
+
? parseBooleanFlag(options.combineWildcardCerts, combineDefault)
|
|
242
|
+
: parseBooleanFlag(process.env.ROSTER_COMBINE_WILDCARD_CERTS, combineDefault);
|
|
243
|
+
if (isBunRuntime && this.combineWildcardCerts) {
|
|
244
|
+
log.info('Bun runtime detected: combined wildcard certificates enabled by default (SNI bypass)');
|
|
245
|
+
}
|
|
240
246
|
|
|
241
247
|
const port = options.port === undefined ? 443 : options.port;
|
|
242
248
|
if (port === 80 && !this.local) {
|
|
@@ -775,38 +781,7 @@ class Roster {
|
|
|
775
781
|
}
|
|
776
782
|
}
|
|
777
783
|
|
|
778
|
-
const
|
|
779
|
-
if (isBunRuntime && this.wildcardZones.size > 0) {
|
|
780
|
-
const retryDelayMs = Number.isFinite(Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_RETRY_MS))
|
|
781
|
-
? Math.max(1000, Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_RETRY_MS))
|
|
782
|
-
: 30000;
|
|
783
|
-
const maxAttempts = Number.isFinite(Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_MAX_ATTEMPTS))
|
|
784
|
-
? Math.max(0, Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_MAX_ATTEMPTS))
|
|
785
|
-
: 0; // 0 = retry forever
|
|
786
|
-
|
|
787
|
-
for (const zone of this.wildcardZones) {
|
|
788
|
-
const bootstrapHost = `bun-bootstrap.${zone}`;
|
|
789
|
-
const attemptPrewarm = async (attempt = 1) => {
|
|
790
|
-
try {
|
|
791
|
-
log.warn(`⚠️ Bun runtime detected: prewarming wildcard certificate via ${bootstrapHost} (attempt ${attempt})`);
|
|
792
|
-
await greenlockRuntime.get({ servername: bootstrapHost });
|
|
793
|
-
log.info(`✅ Bun wildcard prewarm succeeded for ${zone} on attempt ${attempt}`);
|
|
794
|
-
} catch (error) {
|
|
795
|
-
log.warn(`⚠️ Bun wildcard prewarm failed for ${zone} (attempt ${attempt}): ${error?.message || error}`);
|
|
796
|
-
if (maxAttempts > 0 && attempt >= maxAttempts) {
|
|
797
|
-
log.warn(`⚠️ Bun wildcard prewarm stopped for ${zone} after ${attempt} attempts`);
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
setTimeout(() => {
|
|
801
|
-
attemptPrewarm(attempt + 1).catch(() => {});
|
|
802
|
-
}, retryDelayMs);
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
|
|
806
|
-
// Background prewarm + retries so HTTPS startup is not blocked by DNS propagation timing.
|
|
807
|
-
attemptPrewarm().catch(() => {});
|
|
808
|
-
}
|
|
809
|
-
}
|
|
784
|
+
const bunTlsHotReloadHandlers = [];
|
|
810
785
|
|
|
811
786
|
// Create dispatcher for each port
|
|
812
787
|
const createDispatcher = (portData) => {
|
|
@@ -963,7 +938,7 @@ class Roster {
|
|
|
963
938
|
const primaryDomain = Object.keys(portData.virtualServers)[0];
|
|
964
939
|
// Under Bun, avoid glx.httpsServer fallback (may serve invalid TLS on :443).
|
|
965
940
|
// Require concrete PEM files and create native https server directly.
|
|
966
|
-
|
|
941
|
+
let defaultPems = await ensureBunDefaultPems(primaryDomain);
|
|
967
942
|
httpsServer = https.createServer({
|
|
968
943
|
...tlsOpts,
|
|
969
944
|
key: defaultPems.key,
|
|
@@ -977,6 +952,21 @@ class Roster {
|
|
|
977
952
|
.catch(callback);
|
|
978
953
|
}
|
|
979
954
|
}, dispatcher);
|
|
955
|
+
const reloadBunDefaultTls = async (servername, reason) => {
|
|
956
|
+
const nextPems = await issueAndReloadPemsForServername(servername);
|
|
957
|
+
if (!nextPems) return false;
|
|
958
|
+
defaultPems = nextPems;
|
|
959
|
+
if (typeof httpsServer.setSecureContext === 'function') {
|
|
960
|
+
try {
|
|
961
|
+
httpsServer.setSecureContext({ key: defaultPems.key, cert: defaultPems.cert });
|
|
962
|
+
log.info(`🔄 Bun TLS default certificate reloaded on port ${portNum} (${reason})`);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
log.warn(`⚠️ Failed to hot-reload Bun TLS context on port ${portNum}: ${error?.message || error}`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return true;
|
|
968
|
+
};
|
|
969
|
+
bunTlsHotReloadHandlers.push(reloadBunDefaultTls);
|
|
980
970
|
log.warn(`⚠️ Bun runtime detected: using file-based TLS with SNI for ${primaryDomain} on port ${portNum}`);
|
|
981
971
|
} else {
|
|
982
972
|
httpsServer = glx.httpsServer(tlsOpts, dispatcher);
|
|
@@ -1036,11 +1026,51 @@ class Roster {
|
|
|
1036
1026
|
});
|
|
1037
1027
|
}
|
|
1038
1028
|
}
|
|
1029
|
+
|
|
1030
|
+
if (isBunRuntime && !this.combineWildcardCerts && this.wildcardZones.size > 0 && bunTlsHotReloadHandlers.length > 0) {
|
|
1031
|
+
const retryDelayMs = Number.isFinite(Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_RETRY_MS))
|
|
1032
|
+
? Math.max(1000, Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_RETRY_MS))
|
|
1033
|
+
: 30000;
|
|
1034
|
+
const maxAttempts = Number.isFinite(Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_MAX_ATTEMPTS))
|
|
1035
|
+
? Math.max(0, Number(process.env.ROSTER_BUN_WILDCARD_PREWARM_MAX_ATTEMPTS))
|
|
1036
|
+
: 0; // 0 = retry forever
|
|
1037
|
+
|
|
1038
|
+
for (const zone of this.wildcardZones) {
|
|
1039
|
+
const bootstrapHost = `bun-bootstrap.${zone}`;
|
|
1040
|
+
const attemptPrewarm = async (attempt = 1) => {
|
|
1041
|
+
try {
|
|
1042
|
+
log.warn(`⚠️ Bun runtime detected: prewarming wildcard certificate via ${bootstrapHost} (attempt ${attempt})`);
|
|
1043
|
+
let reloaded = false;
|
|
1044
|
+
for (const reloadTls of bunTlsHotReloadHandlers) {
|
|
1045
|
+
// Trigger issuance + immediately hot-reload default TLS context when ready.
|
|
1046
|
+
reloaded = (await reloadTls(bootstrapHost, `prewarm ${bootstrapHost} attempt ${attempt}`)) || reloaded;
|
|
1047
|
+
}
|
|
1048
|
+
if (!reloaded) {
|
|
1049
|
+
throw new Error(`No certificate could be loaded for ${bootstrapHost}`);
|
|
1050
|
+
}
|
|
1051
|
+
log.info(`✅ Bun wildcard prewarm succeeded for ${zone} on attempt ${attempt}`);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
log.warn(`⚠️ Bun wildcard prewarm failed for ${zone} (attempt ${attempt}): ${error?.message || error}`);
|
|
1054
|
+
if (maxAttempts > 0 && attempt >= maxAttempts) {
|
|
1055
|
+
log.warn(`⚠️ Bun wildcard prewarm stopped for ${zone} after ${attempt} attempts`);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
setTimeout(() => {
|
|
1059
|
+
attemptPrewarm(attempt + 1).catch(() => {});
|
|
1060
|
+
}, retryDelayMs);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
// Background prewarm + retries so HTTPS startup is not blocked by DNS propagation timing.
|
|
1065
|
+
attemptPrewarm().catch(() => {});
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1039
1068
|
});
|
|
1040
1069
|
}
|
|
1041
1070
|
}
|
|
1042
1071
|
|
|
1043
1072
|
module.exports = Roster;
|
|
1073
|
+
module.exports.isBunRuntime = isBunRuntime;
|
|
1044
1074
|
module.exports.wildcardRoot = wildcardRoot;
|
|
1045
1075
|
module.exports.hostMatchesWildcard = hostMatchesWildcard;
|
|
1046
1076
|
module.exports.wildcardSubjectForHost = wildcardSubjectForHost;
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ const http = require('http');
|
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const Roster = require('../index.js');
|
|
10
10
|
const {
|
|
11
|
+
isBunRuntime,
|
|
11
12
|
wildcardRoot,
|
|
12
13
|
hostMatchesWildcard,
|
|
13
14
|
wildcardSubjectForHost,
|
|
@@ -324,6 +325,14 @@ describe('Roster', () => {
|
|
|
324
325
|
else process.env.ROSTER_COMBINE_WILDCARD_CERTS = previous;
|
|
325
326
|
}
|
|
326
327
|
});
|
|
328
|
+
it('defaults combineWildcardCerts based on isBunRuntime', () => {
|
|
329
|
+
const roster = new Roster({ local: false });
|
|
330
|
+
assert.strictEqual(roster.combineWildcardCerts, isBunRuntime);
|
|
331
|
+
});
|
|
332
|
+
it('explicit combineWildcardCerts=false overrides Bun default', () => {
|
|
333
|
+
const roster = new Roster({ local: false, combineWildcardCerts: false });
|
|
334
|
+
assert.strictEqual(roster.combineWildcardCerts, false);
|
|
335
|
+
});
|
|
327
336
|
});
|
|
328
337
|
|
|
329
338
|
describe('register (normal domain)', () => {
|