summaryrefslogtreecommitdiffhomepage
path: root/app/lib
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-05-28 08:11:57 +0200
committerErik Larkö <erik@mullvad.net>2017-05-28 08:11:57 +0200
commitf6cc8959a01bb245095f959f6c7ff0be1867e2fa (patch)
tree0354b0a582d4e80e95ee2b89b41dca6505845f56 /app/lib
parente4e5a8dbe22d2c4be7540e8d70861990907a0b27 (diff)
parent28d66c1afe80cb9fed366a50f9cf5051a15d78dc (diff)
downloadmullvadvpn-f6cc8959a01bb245095f959f6c7ff0be1867e2fa.tar.xz
mullvadvpn-f6cc8959a01bb245095f959f6c7ff0be1867e2fa.zip
Merge branch 'ipc-facade'
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/backend.js44
-rw-r--r--app/lib/ipc-facade.js71
-rw-r--r--app/lib/jsonrpc-ws-ipc.js (renamed from app/lib/ipc.js)67
3 files changed, 146 insertions, 36 deletions
diff --git a/app/lib/backend.js b/app/lib/backend.js
index d310c6f28d..ce7c6d17c3 100644
--- a/app/lib/backend.js
+++ b/app/lib/backend.js
@@ -4,7 +4,7 @@ import log from 'electron-log';
import Enum from './enum';
import EventEmitter from 'events';
import { servers } from '../config';
-import Ipc from './ipc';
+import { IpcFacade, RealIpc } from './ipc-facade';
/**
* Server info
@@ -66,7 +66,6 @@ import Ipc from './ipc';
* @event Backend.EventType.updatedIp
* @param {string} new IP address
*/
-
/**
* Updated location event
*
@@ -117,12 +116,6 @@ class BackendError extends Error {
}
-type Location = {
- latlong: Array<number>,
- city: string,
- country: string,
-};
-
/**
* Backend implementation
*
@@ -169,16 +162,16 @@ export default class Backend extends EventEmitter {
*/
static EventType = new Enum('connect', 'connecting', 'disconnect', 'login', 'logging', 'logout', 'updatedIp', 'updatedLocation', 'updatedReachability');
- _ipc: Ipc;
+ _ipc: IpcFacade;
/**
* Creates an instance of Backend.
*
* @memberOf Backend
*/
- constructor(ipc: Ipc) {
+ constructor(ipc: IpcFacade) {
super();
- this._ipc = ipc || new Ipc(undefined);
+ this._ipc = ipc || new RealIpc(undefined);
this._registerIpcListeners();
// check for network reachability
@@ -188,15 +181,15 @@ export default class Backend extends EventEmitter {
setLocation(loc: string) {
log.info('Got connection info to backend', loc);
- this._ipc = new Ipc(loc);
+ this._ipc = new RealIpc(loc);
this._registerIpcListeners();
}
sync() {
log.info('Syncing with the backend...');
- this._ipc.send('get_ip')
- .then( (ip: string) => {
+ this._ipc.getIp()
+ .then( ip => {
log.info('Got ip', ip);
this.emit(Backend.EventType.updatedIp, ip);
})
@@ -204,8 +197,8 @@ export default class Backend extends EventEmitter {
log.info('Failed syncing with the backend', e);
});
- this._ipc.send('get_location')
- .then((location: Location) => {
+ this._ipc.getLocation()
+ .then( location => {
log.info('Got location', location);
const newLocation = {
location: location.latlong,
@@ -285,14 +278,13 @@ export default class Backend extends EventEmitter {
// emit: logging in
this.emit(Backend.EventType.logging, { account }, null);
-
-
- this._ipc.send('get_account_data', account)
- .then(response => {
+ this._ipc.getAccountData(account)
+ .then( response => {
log.info('Account exists', response);
- return this._ipc.send('set_account', account)
- .then(() => response );
+ return this._ipc.setAccount(account)
+ .then( () => response );
+
}).then( accountData => {
log.info('Log in complete');
@@ -315,7 +307,7 @@ export default class Backend extends EventEmitter {
*/
logout() {
// @TODO: What does it mean for a logout to be successful or failed?
- this._ipc.send('set_account', '')
+ this._ipc.setAccount('')
.then(() => {
// emit event
this.emit(Backend.EventType.logout);
@@ -342,9 +334,9 @@ export default class Backend extends EventEmitter {
// emit: connecting
this.emit(Backend.EventType.connecting, addr);
- this._ipc.send('set_country', addr)
+ this._ipc.setCountry(addr)
.then( () => {
- return this._ipc.send('connect');
+ return this._ipc.connect();
})
.then(() => {
this.emit(Backend.EventType.connect, addr);
@@ -364,7 +356,7 @@ export default class Backend extends EventEmitter {
*/
disconnect() {
// @TODO: Failure modes
- this._ipc.send('disconnect')
+ this._ipc.disconnect()
.then(() => {
// emit: disconnect
this.emit(Backend.EventType.disconnect);
diff --git a/app/lib/ipc-facade.js b/app/lib/ipc-facade.js
new file mode 100644
index 0000000000..03fa4f961a
--- /dev/null
+++ b/app/lib/ipc-facade.js
@@ -0,0 +1,71 @@
+// @flow
+
+import JsonRpcWs from './jsonrpc-ws-ipc';
+
+export type AccountData = {paid_until: string};
+export type AccountNumber = string;
+export type Ip = string;
+export type Location = {
+ latlong: Array<Number>,
+ country: string,
+ city: string,
+};
+
+export interface IpcFacade {
+ getAccountData(AccountNumber): Promise<AccountData>,
+ setAccount(accountNumber: AccountNumber): Promise<void>,
+ setCountry(address: string): Promise<void>,
+ connect(): Promise<void>,
+ disconnect(): Promise<void>,
+ getIp(): Promise<Ip>,
+ getLocation(): Promise<Location>,
+}
+
+export class RealIpc implements IpcFacade {
+
+ _ipc: JsonRpcWs;
+
+ constructor(connectionString: ?string) {
+ this._ipc = new JsonRpcWs(connectionString);
+ }
+
+ getAccountData(accountNumber: AccountNumber): Promise<AccountData> {
+ return this._ipc.send('get_account_data', accountNumber)
+ .then(raw => {
+ // TODO: Validate here
+ return raw;
+ });
+ }
+
+ setAccount(accountNumber: AccountNumber): Promise<void> {
+ return this._ipc.send('set_account', accountNumber);
+ }
+
+ setCountry(address: string): Promise<void> {
+ return this._ipc.send('set_country', address);
+ }
+
+ connect(): Promise<void> {
+ return this._ipc.send('connect');
+ }
+
+ disconnect(): Promise<void> {
+ return this._ipc.send('disconnect');
+ }
+
+ getIp(): Promise<Ip> {
+ return this._ipc.send('get_ip')
+ .then(raw => {
+ // TODO: Validate here
+ return raw;
+ });
+ }
+
+ getLocation(): Promise<Location> {
+ return this._ipc.send('get_location')
+ .then(raw => {
+ // TODO: Validate here
+ return raw;
+ });
+ }
+}
diff --git a/app/lib/ipc.js b/app/lib/jsonrpc-ws-ipc.js
index 8471360c9c..1fdcdf6428 100644
--- a/app/lib/ipc.js
+++ b/app/lib/jsonrpc-ws-ipc.js
@@ -1,12 +1,55 @@
+// @flow
+
import jsonrpc from 'jsonrpc-lite';
import uuid from 'uuid';
import log from 'electron-log';
+export type UnansweredRequest<T, E> = {
+ resolve: (T) => void,
+ reject: (E) => void,
+ timeout: number,
+}
+
+export type JsonRpcError = {
+ type: 'error',
+ payload: {
+ id: string,
+ error: {
+ message: string,
+ }
+ }
+}
+export type JsonRpcNotification = {
+ type: 'notification',
+ payload: {
+ method: string,
+ params: {
+ subscription: string,
+ result: any,
+ }
+ }
+}
+export type JsonRpcSuccess = {
+ type: 'success',
+ payload: {
+ id: string,
+ result: any,
+ }
+}
+export type JsonRpcMessage = JsonRpcError | JsonRpcNotification | JsonRpcSuccess;
+
const DEFAULT_TIMEOUT_MILLIS = 750;
export default class Ipc {
- constructor(connectionString) {
+ _connectionString: ?string;
+ _onConnect: Array<{resolve: ()=>void}>;
+ _unansweredRequests: {[string]: UnansweredRequest<any, any>};
+ _subscriptions: {[string]: (any) => void};
+ _websocket: WebSocket;
+ _backoff: ReconnectionBackoff;
+
+ constructor(connectionString: ?string) {
this._connectionString = connectionString;
this._onConnect = [];
this._unansweredRequests = {};
@@ -16,7 +59,7 @@ export default class Ipc {
this._reconnect();
}
- on(event, listener) {
+ on(event: string, listener: (any) => void) {
// We're currently not actually using the event parameter.
// This is because we aren't sure if the backend will use
// one subscription per event or one subscription per
@@ -27,7 +70,7 @@ export default class Ipc {
.then(subscriptionId => this._subscriptions[subscriptionId] = listener);
}
- send(action, ...data) {
+ send(action: string, ...data: Array<any>): Promise<any> {
return this._getWebSocket()
.then(ws => this._send(ws, action, data))
.catch(e => {
@@ -77,7 +120,7 @@ export default class Ipc {
request.reject('The request timed out');
}
- _onMessage(message) {
+ _onMessage(message: string) {
const json = JSON.parse(message);
const c = jsonrpc.parseObject(json);
@@ -88,7 +131,7 @@ export default class Ipc {
}
}
- _onNotification(message) {
+ _onNotification(message: JsonRpcNotification) {
const subscriptionId = message.payload.params.subscription;
const listener = this._subscriptions[subscriptionId];
@@ -100,7 +143,7 @@ export default class Ipc {
}
}
- _onReply(message) {
+ _onReply(message: JsonRpcError | JsonRpcSuccess) {
const id = message.payload.id;
const request = this._unansweredRequests[id];
delete this._unansweredRequests[id];
@@ -123,10 +166,11 @@ export default class Ipc {
}
_reconnect() {
- if (!this._connectionString) return;
+ const connectionString = this._connectionString;
+ if (!connectionString) return;
- log.info('Connecting to websocket', this._connectionString);
- this._websocket = new WebSocket(this._connectionString);
+ log.info('Connecting to websocket', connectionString);
+ this._websocket = new WebSocket(connectionString);
this._websocket.onopen = () => {
log.debug('Websocket is connected');
@@ -138,7 +182,8 @@ export default class Ipc {
};
this._websocket.onmessage = (evt) => {
- this._onMessage(evt.data);
+ const data: string = (evt.data: any);
+ this._onMessage(data);
};
this._websocket.onclose = () => {
@@ -157,6 +202,8 @@ export default class Ipc {
* to 3000ms
*/
class ReconnectionBackoff {
+ _attempt: number;
+
constructor() {
this._attempt = 0;
}