1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Merge branch 'develop' into feed

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner
2021-03-17 16:16:07 +01:00
10 changed files with 146 additions and 59 deletions

View File

@@ -1,3 +1,58 @@
Changes in [9.9.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.9.0) (2021-03-15)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.9.0-rc.1...v9.9.0)
* No changes since rc.1
Changes in [9.9.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.9.0-rc.1) (2021-03-10)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.8.0...v9.9.0-rc.1)
* Remove detailed Olm session logging
[\#1638](https://github.com/matrix-org/matrix-js-sdk/pull/1638)
* Add space summary suggested only param
[\#1637](https://github.com/matrix-org/matrix-js-sdk/pull/1637)
* Check TURN servers periodically, and at start of calls
[\#1634](https://github.com/matrix-org/matrix-js-sdk/pull/1634)
* Support sending invite reasons
[\#1624](https://github.com/matrix-org/matrix-js-sdk/pull/1624)
* Bump elliptic from 6.5.3 to 6.5.4
[\#1636](https://github.com/matrix-org/matrix-js-sdk/pull/1636)
* Add a function to get a room's MXC URI
[\#1635](https://github.com/matrix-org/matrix-js-sdk/pull/1635)
* Stop streams if the call has ended
[\#1633](https://github.com/matrix-org/matrix-js-sdk/pull/1633)
* Remove export keyword from global.d.ts
[\#1631](https://github.com/matrix-org/matrix-js-sdk/pull/1631)
* Fix IndexedDB store creation example
[\#1445](https://github.com/matrix-org/matrix-js-sdk/pull/1445)
* An attempt to cleanup how constraints are handled in calls
[\#1613](https://github.com/matrix-org/matrix-js-sdk/pull/1613)
* Extract display name patterns to constants
[\#1628](https://github.com/matrix-org/matrix-js-sdk/pull/1628)
* Bump pug-code-gen from 2.0.2 to 2.0.3
[\#1630](https://github.com/matrix-org/matrix-js-sdk/pull/1630)
* Avoid deadlocks when ensuring Olm sessions for devices
[\#1627](https://github.com/matrix-org/matrix-js-sdk/pull/1627)
* Filter out edits from other senders in history
[\#1626](https://github.com/matrix-org/matrix-js-sdk/pull/1626)
* Fix ContentHelpers export
[\#1618](https://github.com/matrix-org/matrix-js-sdk/pull/1618)
* Add logging to in progress Olm sessions
[\#1621](https://github.com/matrix-org/matrix-js-sdk/pull/1621)
* Don't ignore ICE candidates received before offer/answer
[\#1623](https://github.com/matrix-org/matrix-js-sdk/pull/1623)
* Better handling of send failures on VoIP events
[\#1622](https://github.com/matrix-org/matrix-js-sdk/pull/1622)
* Log when turn creds expire
[\#1620](https://github.com/matrix-org/matrix-js-sdk/pull/1620)
* Initial Spaces [MSC1772] support
[\#1563](https://github.com/matrix-org/matrix-js-sdk/pull/1563)
* Add logging to crypto store transactions
[\#1617](https://github.com/matrix-org/matrix-js-sdk/pull/1617)
* Room helper for getting type and checking if it is a space room
[\#1610](https://github.com/matrix-org/matrix-js-sdk/pull/1610)
Changes in [9.8.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.8.0) (2021-03-01) Changes in [9.8.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.8.0) (2021-03-01)
================================================================================================ ================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.8.0-rc.1...v9.8.0) [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.8.0-rc.1...v9.8.0)

View File

@@ -1,6 +1,6 @@
{ {
"name": "matrix-js-sdk", "name": "matrix-js-sdk",
"version": "9.8.0", "version": "9.9.0",
"description": "Matrix Client-Server SDK for Javascript", "description": "Matrix Client-Server SDK for Javascript",
"scripts": { "scripts": {
"prepublishOnly": "yarn build", "prepublishOnly": "yarn build",

View File

@@ -1518,6 +1518,21 @@ MatrixBaseApis.prototype.getDevices = function() {
); );
}; };
/**
* Gets specific device details for the logged-in user
* @param {string} device_id device to query
* @return {Promise} Resolves: result object
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixBaseApis.prototype.getDevice = function(device_id) {
const path = utils.encodeUri("/devices/$device_id", {
$device_id: device_id,
});
return this._http.authedRequest(
undefined, 'GET', path, undefined, undefined,
);
};
/** /**
* Update the given device * Update the given device
* *
@@ -2378,18 +2393,27 @@ MatrixBaseApis.prototype.reportEvent = function(roomId, eventId, score, reason)
* Fetches or paginates a summary of a space as defined by MSC2946 * Fetches or paginates a summary of a space as defined by MSC2946
* @param {string} roomId The ID of the space-room to use as the root of the summary. * @param {string} roomId The ID of the space-room to use as the root of the summary.
* @param {number?} maxRoomsPerSpace The maximum number of rooms to return per subspace. * @param {number?} maxRoomsPerSpace The maximum number of rooms to return per subspace.
* @param {boolean?} suggestedOnly Whether to only return rooms with suggested=true.
* @param {boolean?} autoJoinOnly Whether to only return rooms with auto_join=true. * @param {boolean?} autoJoinOnly Whether to only return rooms with auto_join=true.
* @param {number?} limit The maximum number of rooms to return in total. * @param {number?} limit The maximum number of rooms to return in total.
* @param {string?} batch The opaque token to paginate a previous summary request. * @param {string?} batch The opaque token to paginate a previous summary request.
* @returns {Promise} the response, with next_batch, rooms, events fields. * @returns {Promise} the response, with next_batch, rooms, events fields.
*/ */
MatrixBaseApis.prototype.getSpaceSummary = function(roomId, maxRoomsPerSpace, autoJoinOnly, limit, batch) { MatrixBaseApis.prototype.getSpaceSummary = function(
roomId,
maxRoomsPerSpace,
suggestedOnly,
autoJoinOnly,
limit,
batch,
) {
const path = utils.encodeUri("/rooms/$roomId/spaces", { const path = utils.encodeUri("/rooms/$roomId/spaces", {
$roomId: roomId, $roomId: roomId,
}); });
return this._http.authedRequest(undefined, "POST", path, null, { return this._http.authedRequest(undefined, "POST", path, null, {
max_rooms_per_space: maxRoomsPerSpace, max_rooms_per_space: maxRoomsPerSpace,
suggested_only: suggestedOnly,
auto_join_only: autoJoinOnly, auto_join_only: autoJoinOnly,
limit, limit,
batch, batch,

View File

@@ -500,19 +500,8 @@ MatrixClient.prototype.rehydrateDevice = async function() {
return; return;
} }
let getDeviceResult; const getDeviceResult = this.getDehydratedDevice();
try { if (!getDeviceResult) {
getDeviceResult = await this._http.authedRequest(
undefined,
"GET",
"/dehydrated_device",
undefined, undefined,
{
prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2",
},
);
} catch (e) {
logger.info("could not get dehydrated device", e.toString());
return; return;
} }
@@ -574,6 +563,27 @@ MatrixClient.prototype.rehydrateDevice = async function() {
} }
}; };
/**
* Get the current dehydrated device, if any
* @return {Promise} A promise of an object containing the dehydrated device
*/
MatrixClient.prototype.getDehydratedDevice = async function() {
try {
return await this._http.authedRequest(
undefined,
"GET",
"/dehydrated_device",
undefined, undefined,
{
prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2",
},
);
} catch (e) {
logger.info("could not get dehydrated device", e.toString());
return;
}
};
/** /**
* Set the dehydration key. This will also periodically dehydrate devices to * Set the dehydration key. This will also periodically dehydrate devices to
* the server. * the server.

View File

@@ -213,9 +213,8 @@ export async function ensureOlmSessionsForDevices(
// synchronous operation, as otherwise it is possible to have deadlocks // synchronous operation, as otherwise it is possible to have deadlocks
// where multiple tasks wait indefinitely on another task to update some set // where multiple tasks wait indefinitely on another task to update some set
// of common devices. // of common devices.
for (const [userId, devices] of Object.entries(devicesByUser)) { for (const [, devices] of Object.entries(devicesByUser)) {
for (const deviceInfo of devices) { for (const deviceInfo of devices) {
const deviceId = deviceInfo.deviceId;
const key = deviceInfo.getIdentityKey(); const key = deviceInfo.getIdentityKey();
if (key === olmDevice.deviceCurve25519Key) { if (key === olmDevice.deviceCurve25519Key) {
@@ -224,15 +223,12 @@ export async function ensureOlmSessionsForDevices(
continue; continue;
} }
const forWhom = `for ${key} (${userId}:${deviceId})`;
if (!olmDevice._sessionsInProgress[key]) { if (!olmDevice._sessionsInProgress[key]) {
// pre-emptively mark the session as in-progress to avoid race // pre-emptively mark the session as in-progress to avoid race
// conditions. If we find that we already have a session, then // conditions. If we find that we already have a session, then
// we'll resolve // we'll resolve
log.debug(`Marking Olm session in progress ${forWhom}`);
olmDevice._sessionsInProgress[key] = new Promise(resolve => { olmDevice._sessionsInProgress[key] = new Promise(resolve => {
resolveSession[key] = (...args) => { resolveSession[key] = (...args) => {
log.debug(`Resolved Olm session in progress ${forWhom}`);
delete olmDevice._sessionsInProgress[key]; delete olmDevice._sessionsInProgress[key];
resolve(...args); resolve(...args);
}; };
@@ -266,11 +262,9 @@ export async function ensureOlmSessionsForDevices(
} }
const forWhom = `for ${key} (${userId}:${deviceId})`; const forWhom = `for ${key} (${userId}:${deviceId})`;
log.debug(`Ensuring Olm session ${forWhom}`);
const sessionId = await olmDevice.getSessionIdForDevice( const sessionId = await olmDevice.getSessionIdForDevice(
key, resolveSession[key], log, key, resolveSession[key], log,
); );
log.debug(`Got Olm session ${sessionId} ${forWhom}`);
if (sessionId !== null && resolveSession[key]) { if (sessionId !== null && resolveSession[key]) {
// we found a session, but we had marked the session as // we found a session, but we had marked the session as
// in-progress, so resolve it now, which will unmark it and // in-progress, so resolve it now, which will unmark it and
@@ -299,18 +293,6 @@ export async function ensureOlmSessionsForDevices(
const oneTimeKeyAlgorithm = "signed_curve25519"; const oneTimeKeyAlgorithm = "signed_curve25519";
let res; let res;
let taskDetail = `one-time keys for ${devicesWithoutSession.length} devices`; let taskDetail = `one-time keys for ${devicesWithoutSession.length} devices`;
// If your homeserver takes a nap here and never replies, this process
// would hang indefinitely. While that's easily fixed by setting a
// timeout on this request, let's first log whether that's the root
// cause we're seeing in practice.
// See also https://github.com/vector-im/element-web/issues/16194
let otkTimeoutLogger;
// XXX: Perhaps there should be a default timeout?
if (otkTimeout) {
otkTimeoutLogger = setTimeout(() => {
log.error(`Homeserver never replied while claiming ${taskDetail}`);
}, otkTimeout);
}
try { try {
log.debug(`Claiming ${taskDetail}`); log.debug(`Claiming ${taskDetail}`);
res = await baseApis.claimOneTimeKeys( res = await baseApis.claimOneTimeKeys(
@@ -323,8 +305,6 @@ export async function ensureOlmSessionsForDevices(
} }
log.log(`Failed to claim ${taskDetail}`, e, devicesWithoutSession); log.log(`Failed to claim ${taskDetail}`, e, devicesWithoutSession);
throw e; throw e;
} finally {
clearTimeout(otkTimeoutLogger);
} }
if (failedServers && "failures" in res) { if (failedServers && "failures" in res) {

View File

@@ -20,6 +20,7 @@ import {logger} from '../../logger';
import * as utils from "../../utils"; import * as utils from "../../utils";
export const VERSION = 9; export const VERSION = 9;
const PROFILE_TRANSACTIONS = false;
/** /**
* Implementation of a CryptoStore which is backed by an existing * Implementation of a CryptoStore which is backed by an existing
@@ -759,13 +760,18 @@ export class Backend {
} }
doTxn(mode, stores, func, log = logger) { doTxn(mode, stores, func, log = logger) {
let startTime;
let description;
if (PROFILE_TRANSACTIONS) {
const txnId = this._nextTxnId++; const txnId = this._nextTxnId++;
const startTime = Date.now(); startTime = Date.now();
const description = `${mode} crypto store transaction ${txnId} in ${stores}`; description = `${mode} crypto store transaction ${txnId} in ${stores}`;
log.debug(`Starting ${description}`); log.debug(`Starting ${description}`);
}
const txn = this._db.transaction(stores, mode); const txn = this._db.transaction(stores, mode);
const promise = promiseifyTxn(txn); const promise = promiseifyTxn(txn);
const result = func(txn); const result = func(txn);
if (PROFILE_TRANSACTIONS) {
promise.then(() => { promise.then(() => {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
log.debug(`Finished ${description}, took ${elapsedTime} ms`); log.debug(`Finished ${description}, took ${elapsedTime} ms`);
@@ -773,6 +779,7 @@ export class Backend {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
log.error(`Failed ${description}, took ${elapsedTime} ms`); log.error(`Failed ${description}, took ${elapsedTime} ms`);
}); });
}
return promise.then(() => { return promise.then(() => {
return result; return result;
}); });

View File

@@ -179,7 +179,9 @@ export class ToDeviceChannel {
const isAcceptingEvent = type === START_TYPE || type === READY_TYPE; const isAcceptingEvent = type === START_TYPE || type === READY_TYPE;
// the request has picked a ready or start event, tell the other devices about it // the request has picked a ready or start event, tell the other devices about it
if (isAcceptingEvent && !wasStarted && isStarted && this._deviceId) { if (isAcceptingEvent && !wasStarted && isStarted && this._deviceId) {
const nonChosenDevices = this._devices.filter(d => d !== this._deviceId); const nonChosenDevices = this._devices.filter(
d => d !== this._deviceId && d !== this._client.getDeviceId(),
);
if (nonChosenDevices.length) { if (nonChosenDevices.length) {
const message = this.completeContent({ const message = this.completeContent({
code: "m.accepted", code: "m.accepted",

View File

@@ -271,10 +271,10 @@ MatrixHttpApi.prototype = {
xhr.timeout_timer = callbacks.setTimeout(timeout_fn, 30000); xhr.timeout_timer = callbacks.setTimeout(timeout_fn, 30000);
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
let resp;
switch (xhr.readyState) { switch (xhr.readyState) {
case global.XMLHttpRequest.DONE: case global.XMLHttpRequest.DONE:
callbacks.clearTimeout(xhr.timeout_timer); callbacks.clearTimeout(xhr.timeout_timer);
var resp;
try { try {
if (xhr.status === 0) { if (xhr.status === 0) {
throw new AbortError(); throw new AbortError();

View File

@@ -503,6 +503,7 @@ export class MatrixCall extends EventEmitter {
this.chooseOpponent(event); this.chooseOpponent(event);
try { try {
await this.peerConn.setRemoteDescription(invite.offer); await this.peerConn.setRemoteDescription(invite.offer);
await this.addBufferedIceCandidates();
} catch (e) { } catch (e) {
logger.debug("Failed to set remote description", e); logger.debug("Failed to set remote description", e);
this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false); this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false);
@@ -920,7 +921,7 @@ export class MatrixCall extends EventEmitter {
private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent) => { private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent) => {
if (event.candidate) { if (event.candidate) {
logger.debug( logger.debug(
"Got local ICE " + event.candidate.sdpMid + " candidate: " + "Call " + this.callId + " got local ICE " + event.candidate.sdpMid + " candidate: " +
event.candidate.candidate, event.candidate.candidate,
); );
@@ -954,7 +955,7 @@ export class MatrixCall extends EventEmitter {
} }
}; };
onRemoteIceCandidatesReceived(ev: MatrixEvent) { async onRemoteIceCandidatesReceived(ev: MatrixEvent) {
if (this.callHasEnded()) { if (this.callHasEnded()) {
//debuglog("Ignoring remote ICE candidate because call has ended"); //debuglog("Ignoring remote ICE candidate because call has ended");
return; return;
@@ -986,7 +987,7 @@ export class MatrixCall extends EventEmitter {
return; return;
} }
this.addIceCandidates(cands); await this.addIceCandidates(cands);
} }
/** /**
@@ -994,7 +995,10 @@ export class MatrixCall extends EventEmitter {
* @param {Object} msg * @param {Object} msg
*/ */
async onAnswerReceived(event: MatrixEvent) { async onAnswerReceived(event: MatrixEvent) {
logger.debug(`Got answer for call ID ${this.callId} from party ID ${event.getContent().party_id}`);
if (this.callHasEnded()) { if (this.callHasEnded()) {
logger.debug(`Ignoring answer because call ID ${this.callId} has ended`);
return; return;
} }
@@ -1007,6 +1011,7 @@ export class MatrixCall extends EventEmitter {
} }
this.chooseOpponent(event); this.chooseOpponent(event);
await this.addBufferedIceCandidates();
this.setState(CallState.Connecting); this.setState(CallState.Connecting);
@@ -1624,6 +1629,8 @@ export class MatrixCall extends EventEmitter {
// I choo-choo-choose you // I choo-choo-choose you
const msg = ev.getContent(); const msg = ev.getContent();
logger.debug(`Choosing party ID ${msg.party_id} for call ID ${this.callId}`);
this.opponentVersion = msg.version; this.opponentVersion = msg.version;
if (this.opponentVersion === 0) { if (this.opponentVersion === 0) {
// set to null to indicate that we've chosen an opponent, but because // set to null to indicate that we've chosen an opponent, but because
@@ -1637,30 +1644,32 @@ export class MatrixCall extends EventEmitter {
} }
this.opponentCaps = msg.capabilities || {}; this.opponentCaps = msg.capabilities || {};
this.opponentMember = ev.sender; this.opponentMember = ev.sender;
}
private async addBufferedIceCandidates() {
const bufferedCands = this.remoteCandidateBuffer.get(this.opponentPartyId); const bufferedCands = this.remoteCandidateBuffer.get(this.opponentPartyId);
if (bufferedCands) { if (bufferedCands) {
logger.info(`Adding ${bufferedCands.length} buffered candidates for opponent ${this.opponentPartyId}`); logger.info(`Adding ${bufferedCands.length} buffered candidates for opponent ${this.opponentPartyId}`);
this.addIceCandidates(bufferedCands); await this.addIceCandidates(bufferedCands);
} }
this.remoteCandidateBuffer = null; this.remoteCandidateBuffer = null;
} }
private addIceCandidates(cands: RTCIceCandidate[]) { private async addIceCandidates(cands: RTCIceCandidate[]) {
for (const cand of cands) { for (const cand of cands) {
if ( if (
(cand.sdpMid === null || cand.sdpMid === undefined) && (cand.sdpMid === null || cand.sdpMid === undefined) &&
(cand.sdpMLineIndex === null || cand.sdpMLineIndex === undefined) (cand.sdpMLineIndex === null || cand.sdpMLineIndex === undefined)
) { ) {
logger.debug("Ignoring remote ICE candidate with no sdpMid or sdpMLineIndex"); logger.debug("Ignoring remote ICE candidate with no sdpMid or sdpMLineIndex");
return; continue;
} }
logger.debug("Got remote ICE " + cand.sdpMid + " candidate: " + cand.candidate); logger.debug("Call " + this.callId + " got remote ICE " + cand.sdpMid + " candidate: " + cand.candidate);
try { try {
this.peerConn.addIceCandidate(cand); await this.peerConn.addIceCandidate(cand);
} catch (err) { } catch (err) {
if (!this.ignoreOffer) { if (!this.ignoreOffer) {
logger.info("Failed to add remore ICE candidate", err); logger.info("Failed to add remote ICE candidate", err);
} }
} }
} }