summaryrefslogtreecommitdiffhomepage
path: root/app/lib
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-08-08 16:53:23 +0200
committerAndrej Mihajlov <and@mullvad.net>2018-08-08 16:53:23 +0200
commitac98e48c68eadfdd7250eccd38aa492e6f830744 (patch)
tree05bfd7a9f05456220f11f40329093b127a5b20a2 /app/lib
parentd53fda746465c0bb6525371b9d780cbfac90f942 (diff)
parenta8f2831cd50e98b1d126b8c3169adcf1ef75b8a2 (diff)
downloadmullvadvpn-ac98e48c68eadfdd7250eccd38aa492e6f830744.tar.xz
mullvadvpn-ac98e48c68eadfdd7250eccd38aa492e6f830744.zip
Merge branch 'remove-auto-login'
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/daemon-rpc.js2
-rw-r--r--app/lib/jsonrpc-transport.js117
-rw-r--r--app/lib/reconnection-backoff.js8
-rw-r--r--app/lib/window-state-observer.js64
4 files changed, 124 insertions, 67 deletions
diff --git a/app/lib/daemon-rpc.js b/app/lib/daemon-rpc.js
index fb3915e068..163ea170d0 100644
--- a/app/lib/daemon-rpc.js
+++ b/app/lib/daemon-rpc.js
@@ -327,7 +327,7 @@ export class DaemonRpc implements DaemonRpcProtocol {
const validatedObject = validate(RelaySettingsSchema, response);
/* $FlowFixMe:
- There is no way to express the constraints with string literals, i.e:
+ There is no way to express constraints with string literals, i.e:
RelaySettingsSchema constraint:
oneOf(string, object)
diff --git a/app/lib/jsonrpc-transport.js b/app/lib/jsonrpc-transport.js
index 2fa98df988..cbf1e7dce8 100644
--- a/app/lib/jsonrpc-transport.js
+++ b/app/lib/jsonrpc-transport.js
@@ -123,7 +123,7 @@ const DEFAULT_TIMEOUT_MILLIS = 5000;
export default class JsonRpcTransport extends EventEmitter {
_unansweredRequests: Map<string, UnansweredRequest> = new Map();
_subscriptions: Map<string | number, (mixed) => void> = new Map();
- _websocket: ?WebSocket;
+ _webSocket: ?WebSocket;
_websocketFactory: (string) => WebSocket;
constructor(websocketFactory: ?(string) => WebSocket) {
@@ -133,46 +133,60 @@ export default class JsonRpcTransport extends EventEmitter {
}
/// Connect websocket
- connect(connectionString: string) {
- this.disconnect();
+ connect(connectionString: string): Promise<void> {
+ return new Promise((resolve, reject) => {
+ this.disconnect();
- log.info('Connecting to websocket', connectionString);
+ log.info('Connecting to websocket', connectionString);
- const websocket = this._websocketFactory(connectionString);
+ const webSocket = this._websocketFactory(connectionString);
- websocket.onopen = () => {
- log.info('Websocket is connected');
- this.emit('open');
- };
+ // A flag used to determine if Promise was resolved.
+ let isPromiseResolved = false;
- websocket.onmessage = (event) => {
- const data = event.data;
- if (typeof data === 'string') {
- this._onMessage(data);
- } else {
- log.error('Got invalid reply from the server', event);
- }
- };
+ webSocket.onopen = () => {
+ log.info('Websocket is connected');
+ this.emit('open');
+
+ // Resolve the Promise
+ resolve();
+ isPromiseResolved = true;
+ };
+
+ webSocket.onmessage = (event) => {
+ const data = event.data;
+ if (typeof data === 'string') {
+ this._onMessage(data);
+ } else {
+ log.error('Got invalid reply from the server', event);
+ }
+ };
- websocket.onclose = (event) => {
- log.info(`The websocket connection closed with code: ${event.code}`);
+ webSocket.onclose = (event) => {
+ log.info(`The websocket connection closed with code: ${event.code}`);
- // Remove all subscriptions since they are connection based
- this._subscriptions.clear();
+ // Remove all subscriptions since they are connection based
+ this._subscriptions.clear();
- // 1000 is a code used for normal connection closure.
- const connectionError = event.code === 1000 ? null : new ConnectionError(event.code);
+ // 1000 is a code used for normal connection closure.
+ const connectionError = event.code === 1000 ? null : new ConnectionError(event.code);
- this.emit('close', connectionError);
- };
+ this.emit('close', connectionError);
- this._websocket = websocket;
+ // Prevent rejecting a previously resolved Promise.
+ if (!isPromiseResolved) {
+ reject(connectionError);
+ }
+ };
+
+ this._webSocket = webSocket;
+ });
}
disconnect() {
- if (this._websocket) {
- this._websocket.close();
- this._websocket = null;
+ if (this._webSocket) {
+ this._webSocket.close();
+ this._webSocket = null;
}
}
@@ -195,19 +209,14 @@ export default class JsonRpcTransport extends EventEmitter {
}
}
- async send(
- action: string,
- data: mixed,
- timeout: number = DEFAULT_TIMEOUT_MILLIS,
- ): Promise<mixed> {
- let socket: WebSocket;
- try {
- socket = await this._getWebSocket();
- } catch (error) {
- throw error;
- }
-
+ send(action: string, data: mixed, timeout: number = DEFAULT_TIMEOUT_MILLIS): Promise<mixed> {
return new Promise((resolve, reject) => {
+ const webSocket = this._webSocket;
+ if (!webSocket) {
+ reject(new Error('Websocket is not connected.'));
+ return;
+ }
+
const id = uuid.v4();
const payload = this._prepareParams(data);
const timerId = setTimeout(() => this._onTimeout(id), timeout);
@@ -221,9 +230,14 @@ export default class JsonRpcTransport extends EventEmitter {
try {
log.silly('Sending message', id, action);
- socket.send(JSON.stringify(message));
+ webSocket.send(JSON.stringify(message));
} catch (error) {
log.error(`Failed sending RPC message "${action}": ${error.message}`);
+
+ // clean up on error
+ this._unansweredRequests.delete(id);
+ clearTimeout(timerId);
+
throw error;
}
});
@@ -245,25 +259,6 @@ export default class JsonRpcTransport extends EventEmitter {
}
}
- _getWebSocket(): Promise<WebSocket> {
- if (this._websocket && this._websocket.readyState === 1) {
- return Promise.resolve(this._websocket);
- } else {
- return new Promise((resolve, reject) => {
- log.debug('Waiting for websocket to connect');
-
- this.once('open', () => {
- const ws = this._websocket;
- if (ws) {
- resolve(ws);
- } else {
- reject(new Error('Internal error'));
- }
- });
- });
- }
- }
-
_onTimeout(requestId) {
const request = this._unansweredRequests.get(requestId);
diff --git a/app/lib/reconnection-backoff.js b/app/lib/reconnection-backoff.js
index 1cd8203dac..d777e86e57 100644
--- a/app/lib/reconnection-backoff.js
+++ b/app/lib/reconnection-backoff.js
@@ -1,13 +1,11 @@
+// @flow
+
/*
* Used to calculate the time to wait before reconnecting to the daemon.
* It uses a linear backoff function that goes from 500ms to 3000ms.
*/
export default class ReconnectionBackoff {
- _attempt: number;
-
- constructor() {
- this._attempt = 0;
- }
+ _attempt = 0;
attempt(handler: () => void) {
setTimeout(handler, this._getIncreasedBackoff());
diff --git a/app/lib/window-state-observer.js b/app/lib/window-state-observer.js
new file mode 100644
index 0000000000..67252d8f6a
--- /dev/null
+++ b/app/lib/window-state-observer.js
@@ -0,0 +1,64 @@
+// @flow
+
+import { remote } from 'electron';
+
+type EventListener = () => void;
+
+// Tiny helper for detecting the window state.
+export default class WindowStateObserver {
+ _onShow: ?EventListener;
+ _onHide: ?EventListener;
+
+ get onShow() {
+ return this._onShow;
+ }
+
+ set onShow(listener: ?EventListener) {
+ const currentWindow = remote.getCurrentWindow();
+ const oldListener = this._onShow;
+ if (oldListener) {
+ currentWindow.removeListener('show', oldListener);
+ }
+
+ if (listener) {
+ currentWindow.addListener('show', listener);
+ }
+
+ this._onShow = listener;
+ }
+
+ get onHide() {
+ return this._onHide;
+ }
+
+ set onHide(listener: ?EventListener) {
+ const currentWindow = remote.getCurrentWindow();
+ const oldListener = this._onHide;
+ if (oldListener) {
+ currentWindow.removeListener('hide', oldListener);
+ }
+
+ if (listener) {
+ currentWindow.addListener('hide', listener);
+ }
+
+ this._onHide = listener;
+ }
+
+ constructor() {
+ // Because BrowserWindow persists between page reloads,
+ // it's important to release event handlers when that happens.
+ window.addEventListener('beforeunload', this._onBeforeUnload);
+ }
+
+ _onBeforeUnload = () => {
+ this.dispose();
+ };
+
+ dispose() {
+ this.onShow = null;
+ this.onHide = null;
+
+ window.removeEventListener('beforeunload', this._onBeforeUnload);
+ }
+}