@blockcast/mmt-container 2.0.0 → 2.0.1
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/README.md +1 -1
- package/dist/browser-runtime.js +45 -12
- package/dist/browser-runtime.js.map +2 -2
- package/dist/delivery-catalog.js +3 -0
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +19 -10
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/multicast-manager.js +45 -12
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.d.ts +8 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +93 -2
- package/dist/multicast-transport.js.map +4 -4
- package/dist/no-modules.js +19 -10
- package/dist/no-modules.js.map +2 -2
- package/package.json +5 -5
- package/src/multicast-transport.ts +43 -4
|
@@ -15864,7 +15864,6 @@ _RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
|
|
|
15864
15864
|
var BCPV2_LEAD_BOUND_MS = 5e3;
|
|
15865
15865
|
var REPLAY_WINDOW = 64n;
|
|
15866
15866
|
var BUCKET_DEPTH = 64;
|
|
15867
|
-
var BUCKET_RATE_PER_MS = 1;
|
|
15868
15867
|
var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
15869
15868
|
function sameBytes(a, b) {
|
|
15870
15869
|
if (a.byteLength !== b.byteLength)
|
|
@@ -15893,6 +15892,10 @@ var BcProvenanceV2Receiver = class {
|
|
|
15893
15892
|
verifySig;
|
|
15894
15893
|
accepted;
|
|
15895
15894
|
queueLimit;
|
|
15895
|
+
/** Octets a held datagram is charged beyond its payload: the IPv4 or IPv6 plus UDP headers (step 2). */
|
|
15896
|
+
headerOctets;
|
|
15897
|
+
/** Budget refill, tokens per ms: V / 1000 (step 1). */
|
|
15898
|
+
bucketRatePerMs;
|
|
15896
15899
|
current = null;
|
|
15897
15900
|
previous = null;
|
|
15898
15901
|
graceStart = {};
|
|
@@ -15938,6 +15941,8 @@ var BcProvenanceV2Receiver = class {
|
|
|
15938
15941
|
multicast: this.maxAuthDelayMs * bandwidth / 8e3,
|
|
15939
15942
|
unicast: (this.maxAuthDelayMs + this.widenMs) * bandwidth / 8e3
|
|
15940
15943
|
};
|
|
15944
|
+
this.headerOctets = parseBcProvenanceV2Address(cfg.endpoint.groupAddress).byteLength === 16 ? 48 : 28;
|
|
15945
|
+
this.bucketRatePerMs = Math.max(1e3, 1.25 * bandwidth / (8 * 512 * 37)) / 1e3;
|
|
15941
15946
|
const t = this.now();
|
|
15942
15947
|
this.buckets = {
|
|
15943
15948
|
multicast: { tokens: BUCKET_DEPTH, last: t, dropped: 0 },
|
|
@@ -16036,7 +16041,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
16036
16041
|
arrival: now,
|
|
16037
16042
|
sent,
|
|
16038
16043
|
digest: hex(sha2562(bytes)),
|
|
16039
|
-
pairs: record.digests.map((d, i) => ({ digest: hex(d), seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
16044
|
+
pairs: record.digests.map((d, i) => ({ digest: hex(d), epoch, seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
16040
16045
|
awaitingMulticast: path === "mirror"
|
|
16041
16046
|
};
|
|
16042
16047
|
this.verified.set(id, entry);
|
|
@@ -16103,12 +16108,12 @@ var BcProvenanceV2Receiver = class {
|
|
|
16103
16108
|
digest(datagram) {
|
|
16104
16109
|
return hex(bcProvenanceV2DatagramDigest(this.cfg.endpoint, datagram));
|
|
16105
16110
|
}
|
|
16106
|
-
/** Consume the
|
|
16111
|
+
/** Consume the pending entry for `digest` lowest in (epoch, sequence) order, admitting it. */
|
|
16107
16112
|
consumePending(digest, now) {
|
|
16108
16113
|
const list = this.pending.get(digest);
|
|
16109
16114
|
if (!list?.length)
|
|
16110
16115
|
return false;
|
|
16111
|
-
list.sort((a, b) => a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0);
|
|
16116
|
+
list.sort((a, b) => a.pair.epoch - b.pair.epoch || (a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0));
|
|
16112
16117
|
const hit = list.shift();
|
|
16113
16118
|
if (list.length === 0)
|
|
16114
16119
|
this.pending.delete(digest);
|
|
@@ -16117,14 +16122,18 @@ var BcProvenanceV2Receiver = class {
|
|
|
16117
16122
|
this.admittedUntil.set(digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
16118
16123
|
return true;
|
|
16119
16124
|
}
|
|
16125
|
+
/** A held datagram's charge against its queue: payload plus IP and UDP headers. */
|
|
16126
|
+
charge(held) {
|
|
16127
|
+
return held.datagram.byteLength + this.headerOctets;
|
|
16128
|
+
}
|
|
16120
16129
|
enqueue(held, path, now) {
|
|
16121
|
-
if (this.holdBytes[path] + held
|
|
16130
|
+
if (this.holdBytes[path] + this.charge(held) > this.queueLimit[path]) {
|
|
16122
16131
|
return { verdict: "overflow", admitted: [] };
|
|
16123
16132
|
}
|
|
16124
16133
|
held.expiry = now + this.maxAuthDelayMs + (path === "unicast" ? this.widenMs : 0);
|
|
16125
16134
|
held.order = this.order++;
|
|
16126
16135
|
this.hold[path].push(held);
|
|
16127
|
-
this.holdBytes[path] += held
|
|
16136
|
+
this.holdBytes[path] += this.charge(held);
|
|
16128
16137
|
return { verdict: "held", admitted: [] };
|
|
16129
16138
|
}
|
|
16130
16139
|
/** Remove and return the oldest held datagram with `digest`, across both queues. */
|
|
@@ -16139,7 +16148,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
16139
16148
|
if (!best)
|
|
16140
16149
|
return void 0;
|
|
16141
16150
|
const [held] = this.hold[best.path].splice(best.index, 1);
|
|
16142
|
-
this.holdBytes[best.path] -= held
|
|
16151
|
+
this.holdBytes[best.path] -= this.charge(held);
|
|
16143
16152
|
return held.datagram;
|
|
16144
16153
|
}
|
|
16145
16154
|
dropAdmittedHeld() {
|
|
@@ -16147,7 +16156,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
16147
16156
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
16148
16157
|
const keep = h.digest === void 0 || !this.admittedUntil.has(h.digest);
|
|
16149
16158
|
if (!keep)
|
|
16150
|
-
this.holdBytes[path] -= h
|
|
16159
|
+
this.holdBytes[path] -= this.charge(h);
|
|
16151
16160
|
return keep;
|
|
16152
16161
|
});
|
|
16153
16162
|
}
|
|
@@ -16231,7 +16240,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
16231
16240
|
}
|
|
16232
16241
|
take(path, now) {
|
|
16233
16242
|
const b = this.buckets[path];
|
|
16234
|
-
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) *
|
|
16243
|
+
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) * this.bucketRatePerMs);
|
|
16235
16244
|
b.last = now;
|
|
16236
16245
|
if (b.tokens < 1) {
|
|
16237
16246
|
b.dropped++;
|
|
@@ -16245,7 +16254,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
16245
16254
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
16246
16255
|
if (h.expiry > now)
|
|
16247
16256
|
return true;
|
|
16248
|
-
this.holdBytes[path] -= h
|
|
16257
|
+
this.holdBytes[path] -= this.charge(h);
|
|
16249
16258
|
this.expired[path]++;
|
|
16250
16259
|
return false;
|
|
16251
16260
|
});
|
|
@@ -20863,6 +20872,9 @@ var AMTGatewayManager = class {
|
|
|
20863
20872
|
*/
|
|
20864
20873
|
async subscribe(config) {
|
|
20865
20874
|
const { key } = config;
|
|
20875
|
+
if (config.protected) {
|
|
20876
|
+
throw new Error("AMTGatewayManager refuses a protected join: it reports no per-datagram sender (multicast-02 7.2.7)");
|
|
20877
|
+
}
|
|
20866
20878
|
const keyStr = this.getKeyString(key);
|
|
20867
20879
|
if (this.subscriptions.has(keyStr)) {
|
|
20868
20880
|
console.log(`[AMT] Reusing existing subscription to ${keyStr}`);
|
|
@@ -21100,6 +21112,10 @@ var MulticastTransport = class {
|
|
|
21100
21112
|
#acceptedBytesByPacketId = /* @__PURE__ */ new Map();
|
|
21101
21113
|
#packetIds = null;
|
|
21102
21114
|
#protected = false;
|
|
21115
|
+
// A protected join's S, and the last sender spelling that matched it, so a
|
|
21116
|
+
// matching sender parses once per spelling rather than per datagram.
|
|
21117
|
+
#protectedSource = void 0;
|
|
21118
|
+
#matchedSource = void 0;
|
|
21103
21119
|
// The sender S this join's accepted packets came from, once known; see
|
|
21104
21120
|
// packetsDroppedForeignSender. Cleared by every join and by a silence
|
|
21105
21121
|
// failback, so a replacement publisher is not locked out.
|
|
@@ -21151,6 +21167,11 @@ var MulticastTransport = class {
|
|
|
21151
21167
|
return this.#declined(generation, "unavailable");
|
|
21152
21168
|
}
|
|
21153
21169
|
let failure = "invalid-request";
|
|
21170
|
+
const protectedSource = config.protected === true ? config.sourceAddress ?? config.senderAddress : void 0;
|
|
21171
|
+
if (config.protected === true && (protectedSource === void 0 || config.atsc3)) {
|
|
21172
|
+
console.warn("[MulticastTransport] protected join refused: the path reports no per-datagram sender to check against S");
|
|
21173
|
+
return this.#declined(generation, "invalid-request");
|
|
21174
|
+
}
|
|
21154
21175
|
try {
|
|
21155
21176
|
const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
21156
21177
|
console.log(
|
|
@@ -21186,7 +21207,7 @@ var MulticastTransport = class {
|
|
|
21186
21207
|
this.#subscription = subscription;
|
|
21187
21208
|
const live = () => !this.#disposed && generation === this.#joinGeneration;
|
|
21188
21209
|
const anySource = !config.sourceAddress && !config.senderAddress && !config.atsc3;
|
|
21189
|
-
const packetListener = !anySource && subscriptionDeliversPacketBatches(subscription) ? {
|
|
21210
|
+
const packetListener = !anySource && protectedSource === void 0 && subscriptionDeliversPacketBatches(subscription) ? {
|
|
21190
21211
|
type: "packets",
|
|
21191
21212
|
listener: (event) => {
|
|
21192
21213
|
if (!live()) return;
|
|
@@ -21202,6 +21223,8 @@ var MulticastTransport = class {
|
|
|
21202
21223
|
this.#packetListener = packetListener;
|
|
21203
21224
|
this.#packetIds = packetIds;
|
|
21204
21225
|
this.#protected = config.protected === true;
|
|
21226
|
+
this.#protectedSource = protectedSource;
|
|
21227
|
+
this.#matchedSource = void 0;
|
|
21205
21228
|
this.#providerFilter = {
|
|
21206
21229
|
acknowledged: packetIds,
|
|
21207
21230
|
sent: 0,
|
|
@@ -21661,13 +21684,23 @@ var MulticastTransport = class {
|
|
|
21661
21684
|
this.#delivery = void 0;
|
|
21662
21685
|
this.#packetIds = null;
|
|
21663
21686
|
this.#protected = false;
|
|
21687
|
+
this.#protectedSource = void 0;
|
|
21688
|
+
this.#matchedSource = void 0;
|
|
21664
21689
|
this.#providerFilter = null;
|
|
21665
21690
|
this.#context = void 0;
|
|
21666
21691
|
this.#withdrawnPacketIds.clear();
|
|
21667
21692
|
}
|
|
21668
21693
|
/** The ingress filter; an accepted packet is counted here, once. */
|
|
21669
21694
|
#acceptPacket(packet, sourceAddress) {
|
|
21670
|
-
const
|
|
21695
|
+
const source = this.#protectedSource;
|
|
21696
|
+
if (source !== void 0 && (sourceAddress === void 0 || sourceAddress !== this.#matchedSource)) {
|
|
21697
|
+
if (sourceAddress === void 0 || !sameIpAddress(sourceAddress, source)) {
|
|
21698
|
+
this.#stats.packetsDroppedForeignSender++;
|
|
21699
|
+
return false;
|
|
21700
|
+
}
|
|
21701
|
+
this.#matchedSource = sourceAddress;
|
|
21702
|
+
}
|
|
21703
|
+
const owner = source === void 0 ? this.#lockedTo?.() ?? this.#sender : void 0;
|
|
21671
21704
|
if (sourceAddress !== void 0 && owner !== void 0 && sourceAddress !== owner) {
|
|
21672
21705
|
this.#stats.packetsDroppedForeignSender++;
|
|
21673
21706
|
return false;
|