You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Check TURN servers periodically, and at start of calls
Hopefully this should make our turn-credential checking code a bit more robust (and possibly fix a seconds / ms mismatch).
This commit is contained in:
@@ -61,6 +61,7 @@ import {DEHYDRATION_ALGORITHM} from "./crypto/dehydration";
|
|||||||
const SCROLLBACK_DELAY_MS = 3000;
|
const SCROLLBACK_DELAY_MS = 3000;
|
||||||
export const CRYPTO_ENABLED = isCryptoAvailable();
|
export const CRYPTO_ENABLED = isCryptoAvailable();
|
||||||
const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value
|
const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value
|
||||||
|
const TURN_CHECK_INTERVAL = 10 * 60 * 1000; // poll for turn credentials every 10 minutes
|
||||||
|
|
||||||
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
||||||
const keys = [];
|
const keys = [];
|
||||||
@@ -394,7 +395,8 @@ export function MatrixClient(opts) {
|
|||||||
this._clientWellKnownPromise = undefined;
|
this._clientWellKnownPromise = undefined;
|
||||||
|
|
||||||
this._turnServers = [];
|
this._turnServers = [];
|
||||||
this._turnServersExpiry = null;
|
this._turnServersExpiry = 0;
|
||||||
|
this._checkTurnServersIntervalID = null;
|
||||||
|
|
||||||
// The SDK doesn't really provide a clean way for events to recalculate the push
|
// The SDK doesn't really provide a clean way for events to recalculate the push
|
||||||
// actions for themselves, so we have to kinda help them out when they are encrypted.
|
// actions for themselves, so we have to kinda help them out when they are encrypted.
|
||||||
@@ -4954,6 +4956,48 @@ MatrixClient.prototype.getTurnServersExpiry = function() {
|
|||||||
return this._turnServersExpiry;
|
return this._turnServersExpiry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MatrixClient.prototype._checkTurnServers = async function() {
|
||||||
|
if (!this._supportsVoip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let credentialsGood = false;
|
||||||
|
const remainingTime = this._turnServersExpiry - Date.now();
|
||||||
|
if (remainingTime > TURN_CHECK_INTERVAL) {
|
||||||
|
logger.debug("TURN creds are valid for another " + remainingTime + " ms: not fetching new ones.");
|
||||||
|
credentialsGood = true;
|
||||||
|
} else {
|
||||||
|
logger.debug("Fetching new TURN credentials");
|
||||||
|
try {
|
||||||
|
const res = await this.turnServer();
|
||||||
|
if (res.uris) {
|
||||||
|
logger.log("Got TURN URIs: " + res.uris + " refresh in " + res.ttl + " secs");
|
||||||
|
// map the response to a format that can be fed to RTCPeerConnection
|
||||||
|
const servers = {
|
||||||
|
urls: res.uris,
|
||||||
|
username: res.username,
|
||||||
|
credential: res.password,
|
||||||
|
};
|
||||||
|
this._turnServers = [servers];
|
||||||
|
// The TTL is in seconds but we work in ms
|
||||||
|
this._turnServersExpiry = Date.now() + (res.ttl * 1000);
|
||||||
|
credentialsGood = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("Failed to get TURN URIs", err);
|
||||||
|
// If we get a 403, there's no point in looping forever.
|
||||||
|
if (err.httpStatus === 403) {
|
||||||
|
logger.info("TURN access unavailable for this account: stopping credentials checks");
|
||||||
|
if (this._checkTurnServersIntervalID !== null) global.clearInterval(this._checkTurnServersIntervalID);
|
||||||
|
this._checkTurnServersIntervalID = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// otherwise, if we failed for whatever reason, try again the next time we're called.
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentialsGood;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to allow a fallback ICE server should be used for negotiating a
|
* Set whether to allow a fallback ICE server should be used for negotiating a
|
||||||
* WebRTC connection if the homeserver doesn't provide any servers. Defaults to
|
* WebRTC connection if the homeserver doesn't provide any servers. Defaults to
|
||||||
@@ -5106,7 +5150,10 @@ MatrixClient.prototype.startClient = async function(opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// periodically poll for turn servers if we support voip
|
// periodically poll for turn servers if we support voip
|
||||||
checkTurnServers(this);
|
this._checkTurnServersIntervalID = setInterval(() => {
|
||||||
|
this._checkTurnServers();
|
||||||
|
}, TURN_CHECK_INTERVAL);
|
||||||
|
this._checkTurnServers();
|
||||||
|
|
||||||
if (this._syncApi) {
|
if (this._syncApi) {
|
||||||
// This shouldn't happen since we thought the client was not running
|
// This shouldn't happen since we thought the client was not running
|
||||||
@@ -5218,7 +5265,7 @@ MatrixClient.prototype.stopClient = function() {
|
|||||||
this._callEventHandler = null;
|
this._callEventHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.clearTimeout(this._checkTurnServersTimeoutID);
|
global.clearInterval(this._checkTurnServersIntervalID);
|
||||||
if (this._clientWellKnownIntervalID !== undefined) {
|
if (this._clientWellKnownIntervalID !== undefined) {
|
||||||
global.clearInterval(this._clientWellKnownIntervalID);
|
global.clearInterval(this._clientWellKnownIntervalID);
|
||||||
}
|
}
|
||||||
@@ -5435,42 +5482,6 @@ async function(roomId, eventId, relationType, eventType, opts = {}) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkTurnServers(client) {
|
|
||||||
if (!client._supportsVoip) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.turnServer().then(function(res) {
|
|
||||||
if (res.uris) {
|
|
||||||
logger.log("Got TURN URIs: " + res.uris + " refresh in " +
|
|
||||||
res.ttl + " secs");
|
|
||||||
// map the response to a format that can be fed to
|
|
||||||
// RTCPeerConnection
|
|
||||||
const servers = {
|
|
||||||
urls: res.uris,
|
|
||||||
username: res.username,
|
|
||||||
credential: res.password,
|
|
||||||
};
|
|
||||||
client._turnServers = [servers];
|
|
||||||
client._turnServersExpiry = Date.now() + res.ttl;
|
|
||||||
// re-fetch when we're about to reach the TTL
|
|
||||||
client._checkTurnServersTimeoutID = setTimeout(() => {
|
|
||||||
checkTurnServers(client);
|
|
||||||
}, (res.ttl || (60 * 60)) * 1000 * 0.9);
|
|
||||||
}
|
|
||||||
}, function(err) {
|
|
||||||
logger.error("Failed to get TURN URIs");
|
|
||||||
// If we get a 403, there's no point in looping forever.
|
|
||||||
if (err.httpStatus === 403) {
|
|
||||||
logger.info("TURN access unavailable for this account");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
client._checkTurnServersTimeoutID = setTimeout(function() {
|
|
||||||
checkTurnServers(client);
|
|
||||||
}, 60000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _reject(callback, reject, err) {
|
function _reject(callback, reject, err) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(err);
|
callback(err);
|
||||||
|
|||||||
@@ -527,6 +527,13 @@ export class MatrixCall extends EventEmitter {
|
|||||||
const invite = event.getContent();
|
const invite = event.getContent();
|
||||||
this.direction = CallDirection.Inbound;
|
this.direction = CallDirection.Inbound;
|
||||||
|
|
||||||
|
// make sure we have valid turn creds. Unless something's gone wrong, it should
|
||||||
|
// poll and keep the credentials valid so this should be instant.
|
||||||
|
const haveTurnCreds = await this.client._checkTurnServers();
|
||||||
|
if (!haveTurnCreds) {
|
||||||
|
logger.warn("Failed to get TURN credentials! Proceeding with call anyway...");
|
||||||
|
}
|
||||||
|
|
||||||
this.peerConn = this.createPeerConnection();
|
this.peerConn = this.createPeerConnection();
|
||||||
// we must set the party ID before await-ing on anything: the call event
|
// we must set the party ID before await-ing on anything: the call event
|
||||||
// handler will start giving us more call events (eg. candidates) so if
|
// handler will start giving us more call events (eg. candidates) so if
|
||||||
@@ -1662,6 +1669,14 @@ export class MatrixCall extends EventEmitter {
|
|||||||
this.setState(CallState.WaitLocalMedia);
|
this.setState(CallState.WaitLocalMedia);
|
||||||
this.direction = CallDirection.Outbound;
|
this.direction = CallDirection.Outbound;
|
||||||
this.config = constraints;
|
this.config = constraints;
|
||||||
|
|
||||||
|
// make sure we have valid turn creds. Unless something's gone wrong, it should
|
||||||
|
// poll and keep the credentials valid so this should be instant.
|
||||||
|
const haveTurnCreds = await this.client._checkTurnServers();
|
||||||
|
if (!haveTurnCreds) {
|
||||||
|
logger.warn("Failed to get TURN credentials! Proceeding with call anyway...");
|
||||||
|
}
|
||||||
|
|
||||||
// It would be really nice if we could start gathering candidates at this point
|
// It would be really nice if we could start gathering candidates at this point
|
||||||
// so the ICE agent could be gathering while we open our media devices: we already
|
// so the ICE agent could be gathering while we open our media devices: we already
|
||||||
// know the type of the call and therefore what tracks we want to send.
|
// know the type of the call and therefore what tracks we want to send.
|
||||||
|
|||||||
Reference in New Issue
Block a user