aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/transport.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/transport.js28
1 files changed, 27 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
index 1a63e23..853ae75 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
@@ -44,6 +44,11 @@ const UPLOAD_BUFFER_HIGH = 1024 * 1024;
// Segments of 256 KB: 24 in flight is 6 MB, enough to keep playback fed over a
// slow link and small enough that nothing accumulates.
+// How long to collect ICE candidates before sending the offer anyway. Long
+// enough for a STUN round trip on a slow link, short enough that a STUN server
+// that never answers costs a pause rather than the whole attempt.
+const ICE_GATHER_TIMEOUT_MS = 4000;
+
const STREAM_CREDITS = 24;
function _aborted() {
@@ -155,10 +160,31 @@ class MeshBayTransport {
const offer = await this._pc.createOffer();
await this._pc.setLocalDescription(offer);
+ // Wait for candidates, but not indefinitely.
+ //
+ // This is non-trickle signaling: the offer carries its candidates, so the
+ // SDP is only sent once gathering is done. When gathering *never* finishes
+ // — a STUN server that is slow, filtered, or being resolved through a DNS
+ // that is not answering — this promise never settles, and joining a group
+ // hangs with no error and nothing on screen. Reported after exactly that,
+ // and it succeeded on a later attempt, which is the shape of a network
+ // wait rather than a refusal.
+ //
+ // Past the deadline the offer goes out with whatever has been gathered.
+ // Host candidates are already there, which is enough on a LAN — the case
+ // this project cares most about — and the reflexive ones normally arrive
+ // in well under a second when STUN is reachable at all. A partial offer
+ // that usually connects beats a promise that never returns.
await new Promise((resolve) => {
if (this._pc.iceGatheringState === 'complete') return resolve();
+ const done = () => { clearTimeout(timer); resolve(); };
+ const timer = setTimeout(() => {
+ console.warn('[MeshBay] ICE gathering did not finish in',
+ ICE_GATHER_TIMEOUT_MS, 'ms — offering what we have');
+ done();
+ }, ICE_GATHER_TIMEOUT_MS);
this._pc.onicegatheringstatechange = () => {
- if (this._pc.iceGatheringState === 'complete') resolve();
+ if (this._pc.iceGatheringState === 'complete') done();
};
});