aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-17 19:08:52 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-17 19:08:52 +0200
commit84946b1fef64ee619f1c161d57ef4afdc533eae2 (patch)
tree55d13c6536b5cc2bdb8754eb0d82b6339f01072b /packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js
parent7e01b7e8c2ce2ef9fce8b6c053c3b7210b64f9bf (diff)
downloadmeshbay-84946b1fef64ee619f1c161d57ef4afdc533eae2.tar.gz
fix(spa): search hears a refused local network from the connection
On a Pixel, Chrome still lists the refused address when search gives up, so nothing tells it from a silent node. The connection given up on now runs to its own verdict and the refusal is reported then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js49
1 files changed, 34 insertions, 15 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js b/packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js
index 8a616c2a..2961420b 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/connection-pool.js
@@ -61,11 +61,15 @@ const SEARCH_MAX_MS = 30000;
*
* So does a node this device refuses to reach on the local network (Android
* 17, Chrome 155: transport.js localNetworkRefused): another node of the group
- * may be elsewhere. Search gives up long before connect() would name it, so it
- * is read from the connection when giving up, and it is what is thrown when
- * no node answered: the page says what to change rather than "unreachable".
+ * may be elsewhere. connect() names it only once ICE gives up, about thirty
+ * seconds, and until then Chrome lists the refused address like any other,
+ * its checks "sent": measured on a Pixel, nothing tells it from a node that
+ * does not answer when search gives up. So a connection given up on is left
+ * to reach its own verdict, out of the walk and its places, and
+ * `onLateRefusal` hears the refusal when it comes.
*/
-async function connectToGroup(hubBase, groupId, token, bundleKey, username, userId) {
+async function connectToGroup(hubBase, groupId, token, bundleKey, username, userId,
+ onLateRefusal = null) {
const nodesData = await hubFetch(`/v1/groups/${groupId}/nodes`, { token });
if (!nodesData.nodes || !nodesData.nodes.length) return null;
@@ -81,11 +85,12 @@ async function connectToGroup(hubBase, groupId, token, bundleKey, username, user
clearTimeout(capTimer);
transport.onConnectProgress = null;
};
+ const connecting = transport.connect(
+ n.node_id, live, groupId, null, null, bundleKey,
+ username, userId, null, undefined, undefined, n.pk_node);
try {
const ack = await Promise.race([
- transport.connect(
- n.node_id, live, groupId, null, null, bundleKey,
- username, userId, null, undefined, undefined, n.pk_node),
+ connecting,
new Promise((_, reject) => {
const giveUp = () => reject(new Error('Connection timeout'));
stallTimer = setTimeout(giveUp, SEARCH_STALL_MS);
@@ -103,13 +108,23 @@ async function connectToGroup(hubBase, groupId, token, bundleKey, username, user
} catch (e) {
stopTimers();
lastErr = e;
- if (!e.reason && await transport.localNetworkRefused()) {
- lastErr = new Error('This device refused to reach the local network');
- lastErr.reason = 'local_network_blocked';
+ if (e.reason === 'local_network_blocked') lanErr = e;
+ if (e.reason) {
+ try { transport.close(); } catch {}
+ } else {
+ // Given up on by search, not by the connection: closed once it ends.
+ connecting.then(() => {
+ try { transport.close(); } catch {}
+ }, (err) => {
+ try { transport.close(); } catch {}
+ if (err && err.reason === 'local_network_blocked' && onLateRefusal) {
+ onLateRefusal(groupId);
+ }
+ });
+ }
+ if (e.reason && e.reason !== 'not_hosted' && e.reason !== 'local_network_blocked') {
+ throw e;
}
- try { transport.close(); } catch {}
- if (lastErr.reason === 'local_network_blocked') { lanErr = lastErr; continue; }
- if (e.reason && e.reason !== 'not_hosted') throw e;
}
}
throw (lanErr || lastErr || new Error('no node served this group'));
@@ -128,7 +143,7 @@ async function connectToGroup(hubBase, groupId, token, bundleKey, username, user
* phone ran into the hub's per-account ceilings with five groups.
*/
class ConnectionPool {
- constructor(hubBase, onEvict) {
+ constructor(hubBase, onEvict, onLanRefused) {
this._hubBase = hubBase;
this._connections = new Map();
this._connecting = new Map();
@@ -136,6 +151,9 @@ class ConnectionPool {
// (eviction, close or closeAll). SearchPage uses it to drop its own record
// so it never hands a tile a `_tRef` pointing at a transport closed here.
this._onEvict = onEvict || (() => {});
+ // Called with a groupId when a node search gave up on turns out to be one
+ // this device refuses on the local network (connectToGroup).
+ this._onLanRefused = onLanRefused || (() => {});
// Negotiations under way, and those waiting for one of the places.
this._active = 0;
this._waiting = [];
@@ -233,7 +251,8 @@ class ConnectionPool {
async _doConnect(groupId, token, bundleKey, username, userId) {
const found = await connectToGroup(
- this._hubBase, groupId, token, bundleKey, username, userId);
+ this._hubBase, groupId, token, bundleKey, username, userId,
+ (id) => { if (!this._closed) this._onLanRefused(id); });
if (!found) throw new Error('offline');
const { transport, ack } = found;