Merge pull request #325 from vector-im/bwindels/connectionerror-initial-sync

Fix handling connection error during initial sync
This commit is contained in:
Bruno Windels 2021-04-09 20:00:03 +02:00 committed by GitHub
commit 3cf86999a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 17 additions and 21 deletions

View file

@ -21,7 +21,6 @@ import {Reconnector, ConnectionStatus} from "./net/Reconnector.js";
import {ExponentialRetryDelay} from "./net/ExponentialRetryDelay.js"; import {ExponentialRetryDelay} from "./net/ExponentialRetryDelay.js";
import {MediaRepository} from "./net/MediaRepository.js"; import {MediaRepository} from "./net/MediaRepository.js";
import {RequestScheduler} from "./net/RequestScheduler.js"; import {RequestScheduler} from "./net/RequestScheduler.js";
import {HomeServerError, ConnectionError, AbortError} from "./error.js";
import {Sync, SyncStatus} from "./Sync.js"; import {Sync, SyncStatus} from "./Sync.js";
import {Session} from "./Session.js"; import {Session} from "./Session.js";
@ -109,7 +108,7 @@ export class SessionContainer {
let sessionInfo; let sessionInfo;
try { try {
const request = this._platform.request; const request = this._platform.request;
const hsApi = new HomeServerApi({homeServer, request, createTimeout: clock.createTimeout}); const hsApi = new HomeServerApi({homeServer, request});
const loginData = await hsApi.passwordLogin(username, password, "Hydrogen", {log}).response(); const loginData = await hsApi.passwordLogin(username, password, "Hydrogen", {log}).response();
const sessionId = this.createNewSessionId(); const sessionId = this.createNewSessionId();
sessionInfo = { sessionInfo = {
@ -124,7 +123,7 @@ export class SessionContainer {
await this._platform.sessionInfoStorage.add(sessionInfo); await this._platform.sessionInfoStorage.add(sessionInfo);
} catch (err) { } catch (err) {
this._error = err; this._error = err;
if (err instanceof HomeServerError) { if (err.name === "HomeServerError") {
if (err.errcode === "M_FORBIDDEN") { if (err.errcode === "M_FORBIDDEN") {
this._loginFailure = LoginFailure.Credentials; this._loginFailure = LoginFailure.Credentials;
} else { } else {
@ -132,7 +131,7 @@ export class SessionContainer {
} }
log.set("loginFailure", this._loginFailure); log.set("loginFailure", this._loginFailure);
this._status.set(LoadStatus.LoginFailed); this._status.set(LoadStatus.LoginFailed);
} else if (err instanceof ConnectionError) { } else if (err.name === "ConnectionError") {
this._loginFailure = LoginFailure.Connection; this._loginFailure = LoginFailure.Connection;
this._status.set(LoadStatus.LoginFailed); this._status.set(LoadStatus.LoginFailed);
} else { } else {
@ -169,7 +168,6 @@ export class SessionContainer {
accessToken: sessionInfo.accessToken, accessToken: sessionInfo.accessToken,
request: this._platform.request, request: this._platform.request,
reconnector: this._reconnector, reconnector: this._reconnector,
createTimeout: clock.createTimeout
}); });
this._sessionId = sessionInfo.id; this._sessionId = sessionInfo.id;
this._storage = await this._platform.storageFactory.create(sessionInfo.id); this._storage = await this._platform.storageFactory.create(sessionInfo.id);
@ -235,27 +233,26 @@ export class SessionContainer {
} }
async _waitForFirstSync() { async _waitForFirstSync() {
try {
this._sync.start(); this._sync.start();
this._status.set(LoadStatus.FirstSync); this._status.set(LoadStatus.FirstSync);
} catch (err) { // only transition into Ready once the first sync has succeeded
// swallow ConnectionError here and continue, this._waitForFirstSyncHandle = this._sync.status.waitFor(s => {
if (s === SyncStatus.Stopped) {
// keep waiting if there is a ConnectionError
// as the reconnector above will call // as the reconnector above will call
// sync.start again to retry in this case // sync.start again to retry in this case
if (!(err instanceof ConnectionError)) { return this._sync.error?.name !== "ConnectionError";
throw err;
} }
} return s === SyncStatus.Syncing;
// only transition into Ready once the first sync has succeeded });
this._waitForFirstSyncHandle = this._sync.status.waitFor(s => s === SyncStatus.Syncing || s === SyncStatus.Stopped);
try { try {
await this._waitForFirstSyncHandle.promise; await this._waitForFirstSyncHandle.promise;
if (this._sync.status.get() === SyncStatus.Stopped) { if (this._sync.status.get() === SyncStatus.Stopped && this._sync.error) {
throw this._sync.error; throw this._sync.error;
} }
} catch (err) { } catch (err) {
// if dispose is called from stop, bail out // if dispose is called from stop, bail out
if (err instanceof AbortError) { if (err.name === "AbortError") {
return; return;
} }
throw err; throw err;

View file

@ -19,13 +19,12 @@ import {encodeQueryParams, encodeBody} from "./common.js";
import {HomeServerRequest} from "./HomeServerRequest.js"; import {HomeServerRequest} from "./HomeServerRequest.js";
export class HomeServerApi { export class HomeServerApi {
constructor({homeServer, accessToken, request, createTimeout, reconnector}) { constructor({homeServer, accessToken, request, reconnector}) {
// store these both in a closure somehow so it's harder to get at in case of XSS? // store these both in a closure somehow so it's harder to get at in case of XSS?
// one could change the homeserver as well so the token gets sent there, so both must be protected from read/write // one could change the homeserver as well so the token gets sent there, so both must be protected from read/write
this._homeserver = homeServer; this._homeserver = homeServer;
this._accessToken = accessToken; this._accessToken = accessToken;
this._requestFn = request; this._requestFn = request;
this._createTimeout = createTimeout;
this._reconnector = reconnector; this._reconnector = reconnector;
} }