Skip to content

Commit

Permalink
make the webview api ping timeout configurable, defaults to 15 sec
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimiry committed Dec 1, 2018
1 parent 2ed2268 commit 1bd0ad1
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/electron-main/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const INITIAL_STORES: Readonly<{
timeouts: {
// "fetchingRateLimiting" values need to be taking into the account defining the "fetching" timeout
fetching: ONE_SECOND_MS * 60 * 60, // 60 minutes
webViewApiPing: ONE_SECOND_MS * 15,
},
};
},
Expand Down
5 changes: 5 additions & 0 deletions src/electron-main/storage-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const CONFIG_UPGRADES: Record<string, (config: Config) => void> = {
config.timeouts = INITIAL_STORES.config().timeouts;
}
},
"2.0.0-beta.8": (config) => {
if (typeof config.timeouts.webViewApiPing === "undefined") {
config.timeouts.webViewApiPing = INITIAL_STORES.config().timeouts.webViewApiPing;
}
},
};

const SETTINGS_UPGRADES: Record<string, (settings: Settings) => void> = {
Expand Down
11 changes: 9 additions & 2 deletions src/electron-preload/webview/protonmail/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {PROTONMAIL_IPC_WEBVIEW_API, ProtonmailApi, ProtonmailNotificationOutput}
import {StatusCodeError} from "src/shared/model/error";
import {Unpacked} from "src/shared/types";
import {angularJsHttpResponseTypeGuard, isUpsertOperationType, preprocessError} from "./lib/uilt";
import {asyncDelay, curryFunctionMembers, isEntityUpdatesPatchNotEmpty} from "src/shared/util";
import {buildContact, buildFolder, buildMail} from "./lib/database";
import {
buildDbPatchRetryPipeline,
Expand All @@ -27,7 +28,6 @@ import {
waitElements,
} from "src/electron-preload/webview/util";
import {buildLoggerBundle} from "src/electron-preload/util";
import {curryFunctionMembers, isEntityUpdatesPatchNotEmpty} from "src/shared/util";

const _logger = curryFunctionMembers(WEBVIEW_LOGGERS.protonmail, "[api]");
const twoFactorCodeElementId = "twoFactorCode";
Expand Down Expand Up @@ -84,7 +84,14 @@ const endpoints: ProtonmailApi = {
logger.info();

if (!isLoggedIn()) {
throw new Error("protonmail:buildDbPatch(): user is supposed to be logged-in");
// TODO handle switching from built-in webclient to remote and back more properly
// the account state keeps the "signed-in" state despite of page still being reloded
// so we need to reset "signed-in" state with "account.entryUrl" value change
await asyncDelay(ONE_SECOND_MS * 5);

if (!isLoggedIn()) {
throw new Error("protonmail:buildDbPatch(): user is supposed to be logged-in");
}
}

if (!input.metadata || !input.metadata.latestEventId) {
Expand Down
1 change: 1 addition & 0 deletions src/shared/model/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface Config extends BaseConfig, Partial<StoreModel.StoreEntity> {
};
timeouts: {
fetching: number;
webViewApiPing: number;
};
}

Expand Down
37 changes: 24 additions & 13 deletions src/web/src/app/_core/electron.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import {Injectable} from "@angular/core";
import {Model} from "pubsub-to-stream-api";
import {concat, concatMap, delay, retryWhen, takeWhile} from "rxjs/operators";
import {Store, select} from "@ngrx/store";
import {concat, concatMap, delay, retryWhen, switchMap, take, takeWhile} from "rxjs/operators";
import {from, of, throwError} from "rxjs";

import {AccountType} from "src/shared/model/account";
import {IPC_MAIN_API} from "src/shared/api/main";
import {ONE_SECOND_MS} from "src/shared/constants";
import {OptionsSelectors} from "src/web/src/app/store/selectors";
import {PROTONMAIL_IPC_WEBVIEW_API} from "src/shared/api/webview/protonmail";
import {State} from "src/web/src/app/store/reducers/options";
import {TUTANOTA_IPC_WEBVIEW_API} from "src/shared/api/webview/tutanota";
import {WebViewApi} from "src/shared/api/webview/common";
import {getZoneNameBoundWebLogger} from "src/web/src/util";
Expand All @@ -17,11 +20,13 @@ const logger = getZoneNameBoundWebLogger("[accounts.effects]");

@Injectable()
export class ElectronService {
readonly webViewPingIntervalMs = ONE_SECOND_MS / 2;
readonly webViewPingTimeoutMs = ONE_SECOND_MS * 20;
readonly apiCallTimeoutMs = ONE_SECOND_MS * 15;
readonly defaultCommonApiCallTimeoutMs = ONE_SECOND_MS * 15;
readonly webViewApiPingIntervalMs = ONE_SECOND_MS / 2;
readonly timeouts$ = this.store.pipe(select(OptionsSelectors.CONFIG.timeouts));

constructor() {}
constructor(
private store: Store<State>,
) {}

webViewClient<T extends AccountType>(webView: Electron.WebviewTag, type: T, options?: CallOptions) {
// TODO TS: get rid of "as any"
Expand All @@ -31,13 +36,19 @@ export class ElectronService {
// TODO consider removing "ping" API
// TODO it's sufficient to "ping" API initialization only once since there is no dynamic api de-registration enabled
const pingStart = Number(new Date());
const ping$ = from(apiClient("ping", {timeoutMs: 1})({zoneName: logger.zoneName()}).pipe(
retryWhen((errors) => errors.pipe(
takeWhile(() => (Number(new Date()) - pingStart) < this.webViewPingTimeoutMs),
delay(this.webViewPingIntervalMs),
concat(throwError(new Error(`Failed to wait for "webview:${type}" service provider initialization`))),
)),
).toPromise());
const ping$ = this.timeouts$.pipe(
take(1),
// tslint:disable-next-line:ban
switchMap((timeouts) => {
return from(apiClient("ping", {timeoutMs: 1})({zoneName: logger.zoneName()}).pipe(
retryWhen((errors) => errors.pipe(
takeWhile(() => (Number(new Date()) - pingStart) < timeouts.webViewApiPing),
delay(this.webViewApiPingIntervalMs),
concat(throwError(new Error(`Failed to wait for "webview:${type}" service provider initialization`))),
)),
).toPromise());
}),
);

return ping$.pipe(
concatMap(() => of(apiClient)),
Expand All @@ -53,7 +64,7 @@ export class ElectronService {

private buildApiCallOptions(options: CallOptions = {}): Model.CallOptions {
return Object.assign(
{timeoutMs: this.apiCallTimeoutMs, notificationWrapper: Zone.current.run.bind(Zone.current)},
{timeoutMs: this.defaultCommonApiCallTimeoutMs, notificationWrapper: Zone.current.run.bind(Zone.current)},
options,
);
}
Expand Down

0 comments on commit 1bd0ad1

Please sign in to comment.