summaryrefslogtreecommitdiffhomepage
path: root/app/lib
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-15 11:31:59 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-15 11:31:59 -0300
commit232ef95ae4918d7fd699c0a9e87b3ca4371d5224 (patch)
tree1a148b537ecf0122033f4522cf4cf1acd512d82d /app/lib
parent8471c7bfd8ffdf9b0452d00cf7826660be41b569 (diff)
parent9b340b1c62c9bd94365245f8d29b62262f70089c (diff)
downloadmullvadvpn-232ef95ae4918d7fd699c0a9e87b3ca4371d5224.tar.xz
mullvadvpn-232ef95ae4918d7fd699c0a9e87b3ca4371d5224.zip
Merge branch 'backend-error-types'
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/backend.js135
-rw-r--r--app/lib/jsonrpc-ws-ipc.js29
2 files changed, 91 insertions, 73 deletions
diff --git a/app/lib/backend.js b/app/lib/backend.js
index c9f9969af3..c7b4ca53ec 100644
--- a/app/lib/backend.js
+++ b/app/lib/backend.js
@@ -2,6 +2,7 @@
import { log } from '../lib/platform';
import { IpcFacade, RealIpc } from './ipc-facade';
+import { JsonRpcError, TimeOutError } from './jsonrpc-ws-ipc';
import accountActions from '../redux/account/actions';
import connectionActions from '../redux/connection/actions';
import settingsActions from '../redux/settings/actions';
@@ -11,64 +12,65 @@ import type { ReduxStore } from '../redux/store';
import type { AccountToken, BackendState, RelaySettingsUpdate } from './ipc-facade';
import type { ConnectionState } from '../redux/connection/reducers';
-export type ErrorType =
- | 'NO_CREDIT'
- | 'NO_INTERNET'
- | 'NO_DAEMON'
- | 'INVALID_ACCOUNT'
- | 'NO_ACCOUNT'
- | 'COMMUNICATION_FAILURE'
- | 'UNKNOWN_ERROR';
+export class NoCreditError extends Error {
+ constructor() {
+ super("Account doesn't have enough credit available for connection");
+ }
-export class BackendError extends Error {
- type: ErrorType;
- title: string;
- message: string;
- cause: ?Error;
+ get userFriendlyTitle(): string {
+ return 'Out of time';
+ }
- constructor(type: ErrorType, cause?: Error) {
- super('');
- this.type = type;
- this.title = BackendError.localizedTitle(type);
- this.message = BackendError.localizedMessage(type, cause);
- this.cause = cause;
+ get userFriendlyMessage(): string {
+ return 'Buy more time, so you can continue using the internet securely';
}
+}
- static localizedTitle(type: ErrorType): string {
- switch (type) {
- case 'NO_CREDIT':
- return 'Out of time';
- case 'NO_INTERNET':
- return 'Offline';
- default:
- return 'Something went wrong';
- }
+export class NoInternetError extends Error {
+ constructor() {
+ super('Internet connectivity is currently unavailable');
}
- static localizedMessage(type: ErrorType, cause: ?Error): string {
- // TODO: since instanceof now works, BackendError can be replaced by a set
- // of specific error types
- switch (type) {
- case 'NO_CREDIT':
- return 'Buy more time, so you can continue using the internet securely';
- case 'NO_INTERNET':
- return 'Your internet connection will be secured when you get back online';
- case 'INVALID_ACCOUNT':
- return 'Invalid account number';
- case 'NO_ACCOUNT':
- return 'No account was set';
- case 'NO_DAEMON':
- return 'Could not connect to the Mullvad daemon';
- case 'COMMUNICATION_FAILURE':
- return 'api.mullvad.net is blocked, please check your firewall';
- case 'UNKNOWN_ERROR': {
- const message = cause ? ', ' + cause.message : '';
+ get userFriendlyTitle(): string {
+ return 'Offline';
+ }
- return 'An unknown error occurred' + message;
- }
- default:
- return '';
- }
+ get userFriendlyMessage(): string {
+ return 'Your internet connection will be secured when you get back online';
+ }
+}
+
+export class NoDaemonError extends Error {
+ constructor() {
+ super('Could not connect to Mullvad daemon');
+ }
+}
+
+export class InvalidAccountError extends Error {
+ constructor() {
+ super('Invalid account number');
+ }
+}
+
+export class NoAccountError extends Error {
+ constructor() {
+ super('No account was set');
+ }
+}
+
+export class CommunicationError extends Error {
+ constructor() {
+ super('api.mullvad.net is blocked, please check your firewall');
+ }
+}
+
+export class UnknownError extends Error {
+ constructor(cause: string) {
+ super(`An unknown error occurred, ${cause}`);
+ }
+
+ get userFriendlyTitle(): string {
+ return 'Something went wrong';
}
}
@@ -186,35 +188,30 @@ export class Backend {
} catch (e) {
log.error('Failed to log in,', e.message);
- const err = this._rpcErrorToBackendError(e);
- this._store.dispatch(accountActions.loginFailed(err));
+ const error = this._rpcErrorToBackendError(e);
+ this._store.dispatch(accountActions.loginFailed(error));
}
}
_rpcErrorToBackendError(e) {
- if (e instanceof BackendError) {
- return e;
- }
-
- const isJsonRpcError = e.hasOwnProperty('code');
- if (isJsonRpcError) {
+ if (e instanceof JsonRpcError) {
switch (e.code) {
case -200: // Account doesn't exist
- return new BackendError('INVALID_ACCOUNT');
+ return new InvalidAccountError();
case -32603: // Internal error
// We treat all internal backend errors as the user cannot reach
// api.mullvad.net. This is not always true of course, but it is
// true so often that we choose to disregard the other edge cases
// for now.
- return new BackendError('COMMUNICATION_FAILURE');
+ return new CommunicationError();
}
+ } else if (e instanceof TimeOutError) {
+ return new CommunicationError();
+ } else if (e instanceof NoDaemonError) {
+ return e;
}
- if (e.name === 'TimeOutError') {
- return new BackendError('COMMUNICATION_FAILURE');
- }
-
- return new BackendError('UNKNOWN_ERROR', e);
+ return new UnknownError(e.message);
}
async autologin() {
@@ -227,7 +224,7 @@ export class Backend {
const accountToken = await this._ipc.getAccount();
if (!accountToken) {
- throw new BackendError('NO_ACCOUNT');
+ throw new NoAccountError();
}
log.debug('The backend had an account number stored: ', accountToken);
@@ -365,7 +362,7 @@ export class Backend {
const accountToken = await this._ipc.getAccount();
if (!accountToken) {
- throw new BackendError('NO_ACCOUNT');
+ throw new NoAccountError();
}
const accountData = await ipc.getAccountData(accountToken);
@@ -531,7 +528,7 @@ export class Backend {
}
return this._authenticationPromise;
} else {
- return Promise.reject(new BackendError('NO_DAEMON'));
+ return Promise.reject(new NoDaemonError());
}
}
diff --git a/app/lib/jsonrpc-ws-ipc.js b/app/lib/jsonrpc-ws-ipc.js
index ff73f4cc88..84dec96c77 100644
--- a/app/lib/jsonrpc-ws-ipc.js
+++ b/app/lib/jsonrpc-ws-ipc.js
@@ -11,11 +11,12 @@ export type UnansweredRequest = {
message: Object,
};
-export type JsonRpcError = {
+export type JsonRpcErrorResponse = {
type: 'error',
payload: {
id: string,
error: {
+ code: number,
message: string,
},
},
@@ -37,7 +38,26 @@ export type JsonRpcSuccess = {
result: mixed,
},
};
-export type JsonRpcMessage = JsonRpcError | JsonRpcNotification | JsonRpcSuccess;
+export type JsonRpcMessage = JsonRpcErrorResponse | JsonRpcNotification | JsonRpcSuccess;
+
+export class JsonRpcError extends Error {
+ _code: number;
+ _details: string;
+
+ constructor(code: number, details: string) {
+ super(`Remote JSON-RPC error ${code}: ${details}`);
+ this._code = code;
+ this._details = details;
+ }
+
+ get code(): number {
+ return this._code;
+ }
+
+ get details(): string {
+ return this._details;
+ }
+}
export class TimeOutError extends Error {
jsonRpcMessage: Object;
@@ -200,7 +220,7 @@ export default class Ipc {
}
}
- _onReply(message: JsonRpcError | JsonRpcSuccess) {
+ _onReply(message: JsonRpcErrorResponse | JsonRpcSuccess) {
const id = message.payload.id;
const request = this._unansweredRequests.get(id);
this._unansweredRequests.delete(id);
@@ -215,7 +235,8 @@ export default class Ipc {
clearTimeout(request.timerId);
if (message.type === 'error') {
- request.reject(message.payload.error);
+ const error = message.payload.error;
+ request.reject(new JsonRpcError(error.code, error.message));
} else {
const reply = message.payload.result;
request.resolve(reply);