diff options
| author | Erik Larkö <erik@mullvad.net> | 2017-05-28 08:11:57 +0200 |
|---|---|---|
| committer | Erik Larkö <erik@mullvad.net> | 2017-05-28 08:11:57 +0200 |
| commit | f6cc8959a01bb245095f959f6c7ff0be1867e2fa (patch) | |
| tree | 0354b0a582d4e80e95ee2b89b41dca6505845f56 /app/lib | |
| parent | e4e5a8dbe22d2c4be7540e8d70861990907a0b27 (diff) | |
| parent | 28d66c1afe80cb9fed366a50f9cf5051a15d78dc (diff) | |
| download | mullvadvpn-f6cc8959a01bb245095f959f6c7ff0be1867e2fa.tar.xz mullvadvpn-f6cc8959a01bb245095f959f6c7ff0be1867e2fa.zip | |
Merge branch 'ipc-facade'
Diffstat (limited to 'app/lib')
| -rw-r--r-- | app/lib/backend.js | 44 | ||||
| -rw-r--r-- | app/lib/ipc-facade.js | 71 | ||||
| -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; } |
