summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-09-13 12:51:32 +0300
committerAndrej Mihajlov <and@mullvad.net>2018-09-13 12:51:32 +0300
commit32be6c60e8a742214c3f092b31721b181902a73f (patch)
tree9096afa3473b558426bd1409edef3d33ad9e8f55
parentae9a73f4c9a9f66b26f384061891ee451dae52fe (diff)
parent4f304822baa38caa5b8f9d07b58d4183dd9f93ea (diff)
downloadmullvadvpn-32be6c60e8a742214c3f092b31721b181902a73f.tar.xz
mullvadvpn-32be6c60e8a742214c3f092b31721b181902a73f.zip
Merge branch 'new-settings-rpc'
-rw-r--r--gui/packages/desktop/src/renderer/app.js269
-rw-r--r--gui/packages/desktop/src/renderer/components/SelectLocation.js55
-rw-r--r--gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js5
-rw-r--r--gui/packages/desktop/src/renderer/containers/ConnectPage.js2
-rw-r--r--gui/packages/desktop/src/renderer/containers/SelectLocationPage.js9
-rw-r--r--gui/packages/desktop/src/renderer/lib/daemon-rpc.js159
-rw-r--r--gui/packages/desktop/src/renderer/lib/jsonrpc-client.js8
-rw-r--r--gui/packages/desktop/src/renderer/lib/relay-settings-builder.js2
-rw-r--r--gui/packages/desktop/src/renderer/lib/subscription-proxy/base-proxy.js118
-rw-r--r--gui/packages/desktop/src/renderer/lib/subscription-proxy/settings-proxy.js15
-rw-r--r--gui/packages/desktop/src/renderer/lib/subscription-proxy/tunnel-state-proxy.js18
-rw-r--r--gui/packages/desktop/src/renderer/redux/settings/reducers.js2
-rw-r--r--gui/packages/desktop/test/components/SelectLocation.spec.js78
-rw-r--r--gui/packages/desktop/test/relay-settings-builder.spec.js2
-rw-r--r--mullvad-daemon/src/main.rs41
-rw-r--r--mullvad-daemon/src/management_interface.rs81
16 files changed, 448 insertions, 416 deletions
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js
index 66154bcc9d..6b310613da 100644
--- a/gui/packages/desktop/src/renderer/app.js
+++ b/gui/packages/desktop/src/renderer/app.js
@@ -14,10 +14,9 @@ import { createMemoryHistory } from 'history';
import makeRoutes from './routes';
import ReconnectionBackoff from './lib/reconnection-backoff';
-import { DaemonRpc, ConnectionObserver, SubscriptionListener } from './lib/daemon-rpc';
+import { DaemonRpc, ConnectionObserver } from './lib/daemon-rpc';
import NotificationController from './lib/notification-controller';
import setShutdownHandler from './lib/shutdown-handler';
-import { NoAccountError } from './errors';
import configureStore from './redux/store';
import accountActions from './redux/account/actions';
@@ -26,11 +25,16 @@ import settingsActions from './redux/settings/actions';
import versionActions from './redux/version/actions';
import windowActions from './redux/window/actions';
+import SettingsProxy from './lib/subscription-proxy/settings-proxy';
+import TunnelStateProxy from './lib/subscription-proxy/tunnel-state-proxy';
+
import type { WindowShapeParameters } from '../main/window-controller';
import type {
AccountToken,
+ Settings,
TunnelStateTransition,
RelaySettingsUpdate,
+ RelaySettings,
TunnelState,
DaemonRpcProtocol,
AccountData,
@@ -56,9 +60,13 @@ export default class AppRenderer {
_accountDataCache = new AccountDataCache((accountToken) => {
return this._daemonRpc.getAccountData(accountToken);
});
+ _tunnelStateProxy = new TunnelStateProxy(this._daemonRpc, (tunnelState) => {
+ this._setTunnelState(tunnelState);
+ });
+ _settingsProxy = new SettingsProxy(this._daemonRpc, (settings) => {
+ this._setSettings(settings);
+ });
_connectedToDaemon = false;
- _accountToken: ?AccountToken;
- _tunnelState: ?TunnelStateTransition;
constructor() {
const store = configureStore(null, this._memoryHistory);
@@ -129,9 +137,6 @@ export default class AppRenderer {
const accountData = await this._daemonRpc.getAccountData(accountToken);
await this._daemonRpc.setAccount(accountToken);
- // reset the account token with the one that we just sent to daemon
- this._accountToken = accountToken;
-
actions.account.updateAccountExpiry(accountData.expiry);
actions.account.loginSuccessful();
@@ -156,64 +161,12 @@ export default class AppRenderer {
}
}
- async _restoreSession() {
- const actions = this._reduxActions;
- const history = this._memoryHistory;
-
- log.debug('Restoring session');
-
- const accountToken = await this._daemonRpc.getAccount();
-
- // save the account token received after connecting to daemon
- this._accountToken = accountToken;
-
- if (accountToken) {
- log.debug(`Got account token: ${accountToken}`);
- actions.account.updateAccountToken(accountToken);
- actions.account.loginSuccessful();
-
- // take user to main view if user is still at launch screen `/`
- if (history.location.pathname === '/') {
- actions.history.replace('/connect');
- } else {
- // TODO: Reinvent the navigation back in history to make sure that user does not end up on
- // the restricted screen due to changes in daemon's state.
- for (const entry of history.entries) {
- if (entry.pathname === '/') {
- entry.pathname = '/connect';
- }
- }
- }
- } else {
- log.debug('No account set, showing login view.');
-
- // show window when account is not set
- ipcRenderer.send('show-window');
-
- // take user to `/login` screen if user is at launch screen `/`
- if (history.location.pathname === '/') {
- actions.history.replace('/login');
- } else {
- // TODO: Reinvent the navigation back in history to make sure that user does not end up on
- // the restricted screen due to changes in daemon's state.
- for (const entry of history.entries) {
- if (!entry.pathname.startsWith('/settings')) {
- entry.pathname = '/login';
- }
- }
- }
- }
- }
-
async logout() {
const actions = this._reduxActions;
try {
await this._daemonRpc.setAccount(null);
- // reset the account token after log out
- this._accountToken = null;
-
await this._fetchAccountHistory();
actions.account.loggedOut();
@@ -229,11 +182,10 @@ export default class AppRenderer {
async connectTunnel() {
const actions = this._reduxActions;
+ const tunnelState = await this._tunnelStateProxy.fetch();
+
// connect only if tunnel is disconnected or blocked.
- if (
- this._tunnelState &&
- (this._tunnelState.state === 'disconnected' || this._tunnelState.state === 'blocked')
- ) {
+ if (tunnelState.state === 'disconnected' || tunnelState.state === 'blocked') {
// switch to connecting state immediately to prevent a lag that may be caused by RPC
// communication delay
actions.connection.connecting();
@@ -250,11 +202,8 @@ export default class AppRenderer {
return this._daemonRpc.updateRelaySettings(relaySettings);
}
- async fetchRelaySettings() {
+ _setRelaySettings(relaySettings: RelaySettings) {
const actions = this._reduxActions;
- const relaySettings = await this._daemonRpc.getRelaySettings();
-
- log.debug('Got relay settings from daemon', JSON.stringify(relaySettings));
if (relaySettings.normal) {
const payload = {};
@@ -280,17 +229,17 @@ export default class AppRenderer {
actions.settings.updateRelay({
normal: payload,
});
- } else if (relaySettings.custom_tunnel_endpoint) {
- const custom_tunnel_endpoint = relaySettings.custom_tunnel_endpoint;
+ } else if (relaySettings.customTunnelEndpoint) {
+ const customTunnelEndpoint = relaySettings.customTunnelEndpoint;
const {
host,
tunnel: {
openvpn: { port, protocol },
},
- } = custom_tunnel_endpoint;
+ } = customTunnelEndpoint;
actions.settings.updateRelay({
- custom_tunnel_endpoint: {
+ customTunnelEndpoint: {
host,
port,
protocol,
@@ -301,18 +250,16 @@ export default class AppRenderer {
async updateAccountExpiry() {
const actions = this._reduxActions;
+ const settings = await this._settingsProxy.fetch();
const accountDataCache = this._accountDataCache;
- try {
- const accountToken = this._accountToken;
- if (accountToken) {
- const accountData = await accountDataCache.fetch(accountToken);
+ if (settings && settings.accountToken) {
+ try {
+ const accountData = await accountDataCache.fetch(settings.accountToken);
actions.account.updateAccountExpiry(accountData.expiry);
- } else {
- throw new NoAccountError();
+ } catch (error) {
+ log.error(`Failed to update account expiry: ${error.message}`);
}
- } catch (e) {
- log.error(`Failed to update account expiry: ${e.message}`);
}
}
@@ -387,32 +334,6 @@ export default class AppRenderer {
actions.settings.updateAutoConnect(autoConnect);
}
- async _fetchAllowLan() {
- const actions = this._reduxActions;
- const allowLan = await this._daemonRpc.getAllowLan();
- actions.settings.updateAllowLan(allowLan);
- }
-
- async _fetchAutoConnect() {
- const actions = this._reduxActions;
- const autoConnect = await this._daemonRpc.getAutoConnect();
- actions.settings.updateAutoConnect(autoConnect);
- }
-
- async _fetchTunnelState() {
- const state = await this._daemonRpc.getState();
-
- log.debug(`Got state: ${JSON.stringify(state)}`);
-
- this._onChangeTunnelState(state);
- }
-
- async _fetchTunnelOptions() {
- const actions = this._reduxActions;
- const tunnelOptions = await this._daemonRpc.getTunnelOptions();
- actions.settings.updateEnableIpv6(tunnelOptions.enableIpv6);
- }
-
async _fetchVersionInfo() {
const actions = this._reduxActions;
const latestVersionInfo = await this._daemonRpc.getVersionInfo();
@@ -429,32 +350,62 @@ export default class AppRenderer {
// reset the reconnect backoff when connection established.
this._reconnectBackoff.reset();
- // attempt to restore the session
try {
- await this._restoreSession();
+ await this._runPrimaryApplicationFlow();
+ } catch (error) {
+ log.error(`An error was raised when running the primary application flow: ${error.message}`);
+ }
+ }
+
+ async _runPrimaryApplicationFlow() {
+ // fetch initial state and subscribe for changes
+ try {
+ await this._settingsProxy.fetch();
+ } catch (error) {
+ log.error(`Cannot fetch the initial settings: ${error.message}`);
+ }
+
+ try {
+ await this._tunnelStateProxy.fetch();
+ } catch (error) {
+ log.error(`Cannot fetch the initial tunnel state: ${error.message}`);
+ }
+
+ // fetch the rest of data
+ try {
+ await this._fetchRelayLocations();
} catch (error) {
- log.error(`Failed to restore session: ${error.message}`);
+ log.error(`Cannot fetch the relay locations: ${error.message}`);
}
- // make sure to re-subscribe to state notifications when connection is re-established.
try {
- await this._subscribeStateListener();
+ await this._fetchAccountHistory();
+ } catch (error) {
+ log.error(`Cannot fetch the account history: ${error.message}`);
+ }
+
+ // set the appropriate start view
+ await this._setStartView();
+
+ try {
+ await this._fetchLocation();
} catch (error) {
- log.error(`Cannot subscribe for RPC notifications: ${error.message}`);
+ log.error(`Cannot fetch the location: ${error.message}`);
}
- // fetch initial state
try {
- await this._fetchInitialState();
+ await this._fetchVersionInfo();
} catch (error) {
- log.error(`Cannot fetch initial state: ${error.message}`);
+ log.error(`Cannot fetch the version information: ${error.message}`);
}
// auto connect the tunnel on startup
// note: disabled when developing
if (process.env.NODE_ENV !== 'development') {
+ const settings = await this._settingsProxy.fetch();
+
// only connect if account is set in the daemon
- if (this._accountToken) {
+ if (settings.accountToken) {
try {
log.debug('Autoconnect the tunnel');
await this.connectTunnel();
@@ -469,6 +420,51 @@ export default class AppRenderer {
}
}
+ async _setStartView() {
+ const actions = this._reduxActions;
+ const history = this._memoryHistory;
+ const settings = await this._settingsProxy.fetch();
+ const accountToken = settings.accountToken;
+
+ if (accountToken) {
+ log.debug(`Account token is set. Showing the tunnel view.`);
+
+ actions.account.updateAccountToken(accountToken);
+ actions.account.loginSuccessful();
+
+ // take user to main view if user is still at launch screen `/`
+ if (history.location.pathname === '/') {
+ actions.history.replace('/connect');
+ } else {
+ // TODO: Reinvent the navigation back in history to make sure that user does not end up on
+ // the restricted screen due to changes in daemon's state.
+ for (const entry of history.entries) {
+ if (entry.pathname === '/') {
+ entry.pathname = '/connect';
+ }
+ }
+ }
+ } else {
+ log.debug('No account set, showing login view.');
+
+ // show window when account is not set
+ ipcRenderer.send('show-window');
+
+ // take user to `/login` screen if user is at launch screen `/`
+ if (history.location.pathname === '/') {
+ actions.history.replace('/login');
+ } else {
+ // TODO: Reinvent the navigation back in history to make sure that user does not end up on
+ // the restricted screen due to changes in daemon's state.
+ for (const entry of history.entries) {
+ if (!entry.pathname.startsWith('/settings')) {
+ entry.pathname = '/login';
+ }
+ }
+ }
+ }
+ }
+
_onCloseConnection(error: ?Error) {
const actions = this._reduxActions;
@@ -499,37 +495,8 @@ export default class AppRenderer {
this._connectedToDaemon = false;
}
- _subscribeStateListener(): Promise<void> {
- const listener = new SubscriptionListener(
- (newState: TunnelStateTransition) => {
- log.debug(`Got state update: '${JSON.stringify(newState)}'`);
-
- this._onChangeTunnelState(newState);
- },
- (error: Error) => {
- log.error(`Failed to deserialize the new state: ${error.message}`);
- },
- );
-
- return this._daemonRpc.subscribeStateListener(listener);
- }
-
- _fetchInitialState() {
- return Promise.all([
- this._fetchTunnelState(),
- this.fetchRelaySettings(),
- this._fetchRelayLocations(),
- this._fetchAllowLan(),
- this._fetchAutoConnect(),
- this._fetchLocation(),
- this._fetchAccountHistory(),
- this._fetchTunnelOptions(),
- this._fetchVersionInfo(),
- ]);
- }
-
- _onChangeTunnelState(tunnelState: TunnelStateTransition) {
- this._tunnelState = tunnelState;
+ _setTunnelState(tunnelState: TunnelStateTransition) {
+ log.debug(`Tunnel state: ${tunnelState.state}`);
this._updateConnectionStatus(tunnelState);
this._updateUserLocation(tunnelState.state);
@@ -537,6 +504,16 @@ export default class AppRenderer {
this._showNotification(tunnelState.state);
}
+ _setSettings(newSettings: Settings) {
+ const reduxSettings = this._reduxActions.settings;
+
+ reduxSettings.updateAllowLan(newSettings.allowLan);
+ reduxSettings.updateAutoConnect(newSettings.autoConnect);
+ reduxSettings.updateEnableIpv6(newSettings.tunnelOptions.enableIpv6);
+
+ this._setRelaySettings(newSettings.relaySettings);
+ }
+
async _updateUserLocation(tunnelState: TunnelState) {
if (tunnelState === 'connecting' || tunnelState === 'disconnected') {
try {
diff --git a/gui/packages/desktop/src/renderer/components/SelectLocation.js b/gui/packages/desktop/src/renderer/components/SelectLocation.js
index e9c9852ec2..f84cadd6f3 100644
--- a/gui/packages/desktop/src/renderer/components/SelectLocation.js
+++ b/gui/packages/desktop/src/renderer/components/SelectLocation.js
@@ -2,7 +2,7 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
-import { View } from 'reactxp';
+import { View, Component } from 'reactxp';
import { Accordion } from '@mullvad/components';
import { Layout, Container } from './Layout';
import CustomScrollbars from './CustomScrollbars';
@@ -12,15 +12,16 @@ import * as Cell from './Cell';
import styles from './SelectLocationStyles';
import type {
- SettingsReduxState,
+ RelaySettingsRedux,
RelayLocationRedux,
RelayLocationCityRedux,
RelayLocationRelayRedux,
} from '../redux/settings/reducers';
import type { RelayLocation } from '../lib/daemon-rpc';
-export type SelectLocationProps = {
- settings: SettingsReduxState,
+type Props = {
+ relaySettings: RelaySettingsRedux,
+ relayLocations: Array<RelayLocationRedux>,
onClose: () => void,
onSelect: (location: RelayLocation) => void,
};
@@ -29,19 +30,19 @@ type State = {
expanded: Array<string>,
};
-export default class SelectLocation extends React.Component<SelectLocationProps, State> {
- _selectedCell: ?Cell.CellButton;
- _scrollView: ?CustomScrollbars;
+export default class SelectLocation extends Component<Props, State> {
+ _selectedCellRef = React.createRef();
+ _scrollViewRef = React.createRef();
state = {
expanded: [],
};
- constructor(props: SelectLocationProps, context?: any) {
- super(props, context);
+ constructor(props: Props) {
+ super(props);
// set initially expanded country based on relaySettings
- const relaySettings = this.props.settings.relaySettings;
+ const relaySettings = this.props.relaySettings;
if (relaySettings.normal) {
const { location } = relaySettings.normal;
if (location === 'any') {
@@ -64,8 +65,8 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
componentDidMount() {
// restore scroll to selected cell
- const cell = this._selectedCell;
- const scrollView = this._scrollView;
+ const cell = this._selectedCellRef.current;
+ const scrollView = this._scrollViewRef.current;
if (scrollView && cell) {
// eslint-disable-next-line react/no-find-dom-node
@@ -90,7 +91,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
<HeaderTitle>Select location</HeaderTitle>
</SettingsHeader>
- <CustomScrollbars autoHide={true} ref={(ref) => (this._scrollView = ref)}>
+ <CustomScrollbars autoHide={true} ref={this._scrollViewRef}>
<View style={styles.content}>
<SettingsHeader style={styles.subtitle_header}>
<HeaderSubTitle>
@@ -99,7 +100,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
</HeaderSubTitle>
</SettingsHeader>
- {this.props.settings.relayLocations.map((relayCountry) => {
+ {this.props.relayLocations.map((relayCountry) => {
return this._renderCountry(relayCountry);
})}
</View>
@@ -112,7 +113,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
}
_isSelected(selectedLocation: RelayLocation) {
- const { relaySettings } = this.props.settings;
+ const relaySettings = this.props.relaySettings;
if (relaySettings.normal) {
const otherLocation = relaySettings.normal.location;
@@ -173,11 +174,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
_renderCountry(relayCountry: RelayLocationRedux) {
const isSelected = this._isSelected({ country: relayCountry.code });
- const onRef = isSelected
- ? (element) => {
- this._selectedCell = element;
- }
- : undefined;
+ const cellRef = isSelected ? this._selectedCellRef : undefined;
// either expanded by user or when the city selected within the country
const isExpanded = this.state.expanded.includes(relayCountry.code);
@@ -206,7 +203,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
onPress={handleSelect}
disabled={!relayCountry.hasActiveRelays}
testName="country"
- ref={onRef}>
+ ref={cellRef}>
{this._relayStatusIndicator(relayCountry.hasActiveRelays, isSelected)}
<Cell.Label>{relayCountry.name}</Cell.Label>
@@ -238,11 +235,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
const isSelected = this._isSelected(relayLocation);
- const onRef = isSelected
- ? (element) => {
- this._selectedCell = element;
- }
- : undefined;
+ const cellRef = isSelected ? this._selectedCellRef : undefined;
// either expanded by user or when the city or a relay from the city is selected
const isExpanded = this.state.expanded.includes(expandedCode);
@@ -267,7 +260,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
cellHoverStyle={isSelected ? styles.sub_cell__selected : null}
style={isSelected ? styles.sub_cell__selected : styles.sub_cell}
testName="city"
- ref={onRef}>
+ ref={cellRef}>
{this._relayStatusIndicator(relayCity.hasActiveRelays, isSelected)}
<Cell.Label>{relayCity.name}</Cell.Label>
@@ -298,11 +291,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
const isSelected = this._isSelected(relayLocation);
- const onRef = isSelected
- ? (element) => {
- this._selectedCell = element;
- }
- : undefined;
+ const cellRef = isSelected ? this._selectedCellRef : undefined;
const handleSelect = !isSelected
? () => {
@@ -317,7 +306,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
cellHoverStyle={isSelected ? styles.sub_sub_cell__selected : null}
style={isSelected ? styles.sub_sub_cell__selected : styles.sub_sub_cell}
testName="relay"
- ref={onRef}>
+ ref={cellRef}>
{this._relayStatusIndicator(true, isSelected)}
<Cell.Label>{relay.hostname}</Cell.Label>
diff --git a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js
index a5f0c4aaea..35e50b3653 100644
--- a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js
+++ b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js
@@ -24,8 +24,8 @@ const mapRelaySettingsToProtocolAndPort = (relaySettings: RelaySettingsRedux) =>
protocol: protocol === 'any' ? 'Automatic' : protocol,
port: port === 'any' ? 'Automatic' : port,
};
- } else if (relaySettings.custom_tunnel_endpoint) {
- const { protocol, port } = relaySettings.custom_tunnel_endpoint;
+ } else if (relaySettings.customTunnelEndpoint) {
+ const { protocol, port } = relaySettings.customTunnelEndpoint;
return { protocol, port };
} else {
throw new Error('Unknown type of relay settings.');
@@ -56,7 +56,6 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) =>
try {
await props.app.updateRelaySettings(relayUpdate);
- await props.app.fetchRelaySettings();
} catch (e) {
log.error('Failed to update relay settings', e.message);
}
diff --git a/gui/packages/desktop/src/renderer/containers/ConnectPage.js b/gui/packages/desktop/src/renderer/containers/ConnectPage.js
index 828d92959e..5edb4447cc 100644
--- a/gui/packages/desktop/src/renderer/containers/ConnectPage.js
+++ b/gui/packages/desktop/src/renderer/containers/ConnectPage.js
@@ -48,7 +48,7 @@ function getRelayName(
}
return 'Unknown';
- } else if (relaySettings.custom_tunnel_endpoint) {
+ } else if (relaySettings.customTunnelEndpoint) {
return 'Custom';
} else {
throw new Error('Unsupported relay settings.');
diff --git a/gui/packages/desktop/src/renderer/containers/SelectLocationPage.js b/gui/packages/desktop/src/renderer/containers/SelectLocationPage.js
index c4a09f0e6f..80f4f64b74 100644
--- a/gui/packages/desktop/src/renderer/containers/SelectLocationPage.js
+++ b/gui/packages/desktop/src/renderer/containers/SelectLocationPage.js
@@ -11,23 +11,24 @@ import type { ReduxState, ReduxDispatch } from '../redux/store';
import type { SharedRouteProps } from '../routes';
const mapStateToProps = (state: ReduxState) => ({
- settings: state.settings,
+ relaySettings: state.settings.relaySettings,
+ relayLocations: state.settings.relayLocations,
});
const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) => {
const history = bindActionCreators({ goBack }, dispatch);
return {
onClose: () => history.goBack(),
onSelect: async (relayLocation) => {
+ // dismiss the view first
+ history.goBack();
+
try {
const relayUpdate = RelaySettingsBuilder.normal()
.location.fromRaw(relayLocation)
.build();
await props.app.updateRelaySettings(relayUpdate);
- await props.app.fetchRelaySettings();
await props.app.connectTunnel();
-
- history.goBack();
} catch (e) {
log.error(`Failed to select server: ${e.message}`);
}
diff --git a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js
index fcfcf6744a..b884002403 100644
--- a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js
+++ b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js
@@ -96,7 +96,7 @@ export type RelaySettings =
normal: RelaySettingsNormal<TunnelConstraints<OpenVpnConstraints>>,
|}
| {|
- custom_tunnel_endpoint: RelaySettingsCustom,
+ customTunnelEndpoint: RelaySettingsCustom,
|};
// types describing the partial update of RelaySettings
@@ -108,7 +108,7 @@ export type RelaySettingsUpdate =
normal: RelaySettingsNormalUpdate,
|}
| {|
- custom_tunnel_endpoint: RelaySettingsCustom,
+ customTunnelEndpoint: RelaySettingsCustom,
|};
const constraint = <T>(constraintValue: SchemaNode<T>) => {
@@ -179,9 +179,9 @@ export type RelayListCity = {
export type RelayListHostname = {
hostname: string,
- ipv4_addr_in: string,
- ipv4_addr_exit: string,
- include_in_country: boolean,
+ ipv4AddrIn: string,
+ ipv4AddrExit: string,
+ includeInCountry: boolean,
weight: number,
};
@@ -213,6 +213,9 @@ const RelayListSchema = object({
export type TunnelOptions = {
enableIpv6: boolean,
+ openvpn: {
+ mssfix: ?number,
+ },
};
const TunnelOptionsSchema = object({
@@ -294,26 +297,39 @@ export class SubscriptionListener<T> {
}
}
+export type Settings = {
+ accountToken: AccountToken,
+ allowLan: boolean,
+ autoConnect: boolean,
+ relaySettings: RelaySettings,
+ tunnelOptions: TunnelOptions,
+};
+
+const SettingsSchema = object({
+ account_token: maybe(string),
+ allow_lan: boolean,
+ auto_connect: boolean,
+ relay_settings: RelaySettingsSchema,
+ tunnel_options: TunnelOptionsSchema,
+});
+
export interface DaemonRpcProtocol {
connect({ path: string }): void;
disconnect(): void;
getAccountData(AccountToken): Promise<AccountData>;
getRelayLocations(): Promise<RelayList>;
- getAccount(): Promise<?AccountToken>;
setAccount(accountToken: ?AccountToken): Promise<void>;
updateRelaySettings(RelaySettingsUpdate): Promise<void>;
- getRelaySettings(): Promise<RelaySettings>;
setAllowLan(boolean): Promise<void>;
- getAllowLan(): Promise<boolean>;
setEnableIpv6(boolean): Promise<void>;
- getTunnelOptions(): Promise<TunnelOptions>;
setAutoConnect(boolean): Promise<void>;
- getAutoConnect(): Promise<boolean>;
connectTunnel(): Promise<void>;
disconnectTunnel(): Promise<void>;
getLocation(): Promise<Location>;
getState(): Promise<TunnelStateTransition>;
+ getSettings(): Promise<Settings>;
subscribeStateListener(listener: SubscriptionListener<TunnelStateTransition>): Promise<void>;
+ subscribeSettingsListener(listener: SubscriptionListener<Settings>): Promise<void>;
addConnectionObserver(observer: ConnectionObserver): void;
removeConnectionObserver(observer: ConnectionObserver): void;
getAccountHistory(): Promise<Array<AccountToken>>;
@@ -386,94 +402,32 @@ export class DaemonRpc implements DaemonRpcProtocol {
async getRelayLocations(): Promise<RelayList> {
const response = await this._transport.send('get_relay_locations');
try {
- return validate(RelayListSchema, response);
+ return camelCaseObjectKeys(validate(RelayListSchema, response));
} catch (error) {
throw new ResponseParseError('Invalid response from get_relay_locations', error);
}
}
- async getAccount(): Promise<?AccountToken> {
- const response = await this._transport.send('get_account');
- if (response === null || typeof response === 'string') {
- return response;
- } else {
- throw new ResponseParseError('Invalid response from get_account', null);
- }
- }
-
async setAccount(accountToken: ?AccountToken): Promise<void> {
await this._transport.send('set_account', accountToken);
}
async updateRelaySettings(relaySettings: RelaySettingsUpdate): Promise<void> {
- await this._transport.send('update_relay_settings', [relaySettings]);
- }
-
- async getRelaySettings(): Promise<RelaySettings> {
- const response = await this._transport.send('get_relay_settings');
- try {
- const validatedObject = validate(RelaySettingsSchema, response);
-
- /* $FlowFixMe:
- There is no way to express constraints with string literals, i.e:
-
- RelaySettingsSchema constraint:
- oneOf(string, object)
-
- RelaySettings constraint:
- 'any' | object
-
- These two are incompatible so we simply enforce the type for now.
- */
- return ((validatedObject: any): RelaySettings);
- } catch (e) {
- throw new ResponseParseError('Invalid response from get_relay_settings', e);
- }
+ await this._transport.send('update_relay_settings', [underscoreObjectKeys(relaySettings)]);
}
async setAllowLan(allowLan: boolean): Promise<void> {
await this._transport.send('set_allow_lan', [allowLan]);
}
- async getAllowLan(): Promise<boolean> {
- const response = await this._transport.send('get_allow_lan');
- if (typeof response === 'boolean') {
- return response;
- } else {
- throw new ResponseParseError('Invalid response from get_allow_lan', null);
- }
- }
-
async setEnableIpv6(enableIpv6: boolean): Promise<void> {
await this._transport.send('set_enable_ipv6', [enableIpv6]);
}
- async getTunnelOptions(): Promise<TunnelOptions> {
- const response = await this._transport.send('get_tunnel_options');
- try {
- const validatedObject = validate(TunnelOptionsSchema, response);
-
- return {
- enableIpv6: validatedObject.enable_ipv6,
- };
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_tunnel_options', error);
- }
- }
-
async setAutoConnect(autoConnect: boolean): Promise<void> {
await this._transport.send('set_auto_connect', [autoConnect]);
}
- async getAutoConnect(): Promise<boolean> {
- const response = await this._transport.send('get_auto_connect');
- if (typeof response === 'boolean') {
- return response;
- } else {
- throw new ResponseParseError('Invalid response from get_auto_connect', null);
- }
- }
-
async connectTunnel(): Promise<void> {
await this._transport.send('connect');
}
@@ -500,10 +454,19 @@ export class DaemonRpc implements DaemonRpcProtocol {
}
}
+ async getSettings(): Promise<Settings> {
+ const response = await this._transport.send('get_settings');
+ try {
+ return camelCaseObjectKeys(validate(SettingsSchema, response));
+ } catch (error) {
+ throw new ResponseParseError('Invalid response from get_settings', error);
+ }
+ }
+
subscribeStateListener(listener: SubscriptionListener<TunnelStateTransition>): Promise<void> {
return this._transport.subscribe('new_state', (payload) => {
try {
- const newState = validate(TunnelStateTransitionSchema, payload);
+ const newState = camelCaseObjectKeys(validate(TunnelStateTransitionSchema, payload));
listener._onEvent(newState);
} catch (error) {
listener._onError(new ResponseParseError('Invalid payload from new_state', error));
@@ -511,6 +474,17 @@ export class DaemonRpc implements DaemonRpcProtocol {
});
}
+ subscribeSettingsListener(listener: SubscriptionListener<Settings>): Promise<void> {
+ return this._transport.subscribe('settings', (payload) => {
+ try {
+ const newSettings = camelCaseObjectKeys(validate(SettingsSchema, payload));
+ listener._onEvent(newSettings);
+ } catch (error) {
+ listener._onError(new ResponseParseError('Invalid payload from settings', error));
+ }
+ });
+ }
+
async getAccountHistory(): Promise<Array<AccountToken>> {
const response = await this._transport.send('get_account_history');
try {
@@ -549,3 +523,38 @@ export class DaemonRpc implements DaemonRpcProtocol {
}
}
}
+
+function underscoreToCamelCase(str: string): string {
+ return str.replace(/_([a-z])/gi, (matches) => matches[1].toUpperCase());
+}
+
+function camelCaseToUnderscore(str: string): string {
+ return str
+ .replace(/[a-z0-9][A-Z]/g, (matches) => `${matches[0]}_${matches[1].toLowerCase()}`)
+ .toLowerCase();
+}
+
+function camelCaseObjectKeys(object: Object) {
+ return transformObjectKeys(object, underscoreToCamelCase);
+}
+
+function underscoreObjectKeys(object: Object) {
+ return transformObjectKeys(object, camelCaseToUnderscore);
+}
+
+function transformObjectKeys(object: Object, keyTransformer: (string) => string) {
+ for (const sourceKey of Object.keys(object)) {
+ const targetKey = keyTransformer(sourceKey);
+ const sourceValue = object[sourceKey];
+
+ object[targetKey] =
+ sourceValue !== null && typeof sourceValue === 'object'
+ ? transformObjectKeys(sourceValue, keyTransformer)
+ : sourceValue;
+
+ if (sourceKey !== targetKey) {
+ delete object[sourceKey];
+ }
+ }
+ return object;
+}
diff --git a/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js b/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js
index 9cafade681..0f51ca57cb 100644
--- a/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js
+++ b/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js
@@ -184,7 +184,7 @@ export default class JsonRpcClient<T> extends EventEmitter {
}
async subscribe(event: string, listener: (mixed) => void): Promise<*> {
- log.silly(`Adding a listener to ${event}`);
+ log.silly(`Adding a listener for ${event}`);
try {
const subscriptionId = await this.send(`${event}_subscribe`);
@@ -206,7 +206,7 @@ export default class JsonRpcClient<T> extends EventEmitter {
return new Promise((resolve, reject) => {
const transport = this._transport;
if (!transport) {
- reject(new Error('Websocket is not connected.'));
+ reject(new Error('RPC client transport is not connected.'));
return;
}
@@ -288,10 +288,10 @@ export default class JsonRpcClient<T> extends EventEmitter {
const listener = this._subscriptions.get(subscriptionId);
if (listener) {
- log.silly('Got notification', message.payload.method, message.payload.params.result);
+ log.silly(`Got notification for ${message.payload.method}`);
listener(message.payload.params.result);
} else {
- log.warn('Got notification for', message.payload.method, 'but no one is listening for it');
+ log.warn(`Got notification for ${message.payload.method} but no one is listening for it`);
}
}
diff --git a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js
index 5065a485f6..dc865472d5 100644
--- a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js
+++ b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js
@@ -161,7 +161,7 @@ class CustomRelaySettingsBuilder {
build(): RelaySettingsUpdate {
return {
- custom_tunnel_endpoint: this._payload,
+ customTunnelEndpoint: this._payload,
};
}
diff --git a/gui/packages/desktop/src/renderer/lib/subscription-proxy/base-proxy.js b/gui/packages/desktop/src/renderer/lib/subscription-proxy/base-proxy.js
new file mode 100644
index 0000000000..b3ebfd2595
--- /dev/null
+++ b/gui/packages/desktop/src/renderer/lib/subscription-proxy/base-proxy.js
@@ -0,0 +1,118 @@
+// @flow
+
+import log from 'electron-log';
+import { ConnectionObserver, SubscriptionListener, ResponseParseError } from '../daemon-rpc';
+import type { DaemonRpcProtocol } from '../daemon-rpc';
+
+export default class BaseSubscriptionProxy<T> {
+ _rpc: DaemonRpcProtocol;
+ _connectionObserver = new ConnectionObserver(
+ () => {},
+ () => {
+ this._didDisconnectFromDaemon();
+ },
+ );
+
+ _isSubscribed = false;
+ _executingPromise: ?Promise<T>;
+
+ _value: ?T;
+ _onUpdate: (T) => void;
+
+ constructor(rpc: DaemonRpcProtocol, onUpdate: (T) => void) {
+ this._rpc = rpc;
+ this._onUpdate = onUpdate;
+
+ rpc.addConnectionObserver(this._connectionObserver);
+ }
+
+ async fetch(): Promise<T> {
+ // return the cached promise if there is an ongoing fetch
+ if (this._executingPromise) {
+ return this._executingPromise;
+ }
+
+ // return the value if it's available
+ if (this._value) {
+ return this._value;
+ }
+
+ // subscribe if needed and fetch the initial state.
+ const fetchPromise = this._subscribeAndFetchValue();
+
+ // cache the fetch promise
+ this._executingPromise = fetchPromise;
+
+ try {
+ const value = await fetchPromise;
+
+ // cache the initial value
+ this._value = value;
+
+ // notify the delegate upon initial fetch
+ this._onUpdate(value);
+
+ return value;
+ } catch (error) {
+ throw error;
+ } finally {
+ // unset the cached fetch promise
+ this._executingPromise = null;
+ }
+ }
+
+ async _subscribeAndFetchValue(): Promise<T> {
+ if (!this._isSubscribed) {
+ await this._subscribeValueListener();
+ this._isSubscribed = true;
+ }
+
+ // request the initial value
+ return await this.constructor.requestValue(this._rpc);
+ }
+
+ static subscribeValueListener(
+ _rpc: DaemonRpcProtocol,
+ _listener: SubscriptionListener<T>,
+ ): Promise<void> {
+ throw new Error(
+ `Override static ${this.constructor.name}.subscribeValueListener() in subclasses`,
+ );
+ }
+
+ static requestValue(_rpc: DaemonRpcProtocol): Promise<T> {
+ throw new Error(`Override static ${this.constructor.name}.requestValue() in subclasses`);
+ }
+
+ _subscribeValueListener(): Promise<void> {
+ const listener = new SubscriptionListener(
+ (value: T) => {
+ this._didReceiveUpdate(value);
+ },
+ (error: Error) => {
+ let reason = '';
+
+ if (error instanceof ResponseParseError) {
+ const validationError = error.validationError;
+ if (validationError) {
+ reason = ` Reason: ${validationError.message}`;
+ }
+ }
+
+ log.error(`Failed to deserialize the payload: ${error.message}.${reason}`);
+ },
+ );
+ return this.constructor.subscribeValueListener(this._rpc, listener);
+ }
+
+ _didReceiveUpdate(updatedValue: T) {
+ this._value = updatedValue;
+ this._onUpdate(updatedValue);
+ }
+
+ _didDisconnectFromDaemon() {
+ this._isSubscribed = false;
+ this._executingPromise = null;
+ this._value = null;
+ }
+}
diff --git a/gui/packages/desktop/src/renderer/lib/subscription-proxy/settings-proxy.js b/gui/packages/desktop/src/renderer/lib/subscription-proxy/settings-proxy.js
new file mode 100644
index 0000000000..6dc3e7e9b7
--- /dev/null
+++ b/gui/packages/desktop/src/renderer/lib/subscription-proxy/settings-proxy.js
@@ -0,0 +1,15 @@
+// @flow
+
+import BaseSubscriptionProxy from './base-proxy';
+import { SubscriptionListener } from '../daemon-rpc';
+import type { DaemonRpcProtocol, Settings } from '../daemon-rpc';
+
+export default class SettingsProxy extends BaseSubscriptionProxy<Settings> {
+ static subscribeValueListener(rpc: DaemonRpcProtocol, listener: SubscriptionListener<Settings>) {
+ return rpc.subscribeSettingsListener(listener);
+ }
+
+ static requestValue(rpc: DaemonRpcProtocol): Promise<Settings> {
+ return rpc.getSettings();
+ }
+}
diff --git a/gui/packages/desktop/src/renderer/lib/subscription-proxy/tunnel-state-proxy.js b/gui/packages/desktop/src/renderer/lib/subscription-proxy/tunnel-state-proxy.js
new file mode 100644
index 0000000000..520885fe98
--- /dev/null
+++ b/gui/packages/desktop/src/renderer/lib/subscription-proxy/tunnel-state-proxy.js
@@ -0,0 +1,18 @@
+// @flow
+
+import BaseSubscriptionProxy from './base-proxy';
+import { SubscriptionListener } from '../daemon-rpc';
+import type { DaemonRpcProtocol, TunnelStateTransition } from '../daemon-rpc';
+
+export default class TunnelStateProxy extends BaseSubscriptionProxy<TunnelStateTransition> {
+ static subscribeValueListener(
+ rpc: DaemonRpcProtocol,
+ listener: SubscriptionListener<TunnelStateTransition>,
+ ) {
+ return rpc.subscribeStateListener(listener);
+ }
+
+ static requestValue(rpc: DaemonRpcProtocol): Promise<TunnelStateTransition> {
+ return rpc.getState();
+ }
+}
diff --git a/gui/packages/desktop/src/renderer/redux/settings/reducers.js b/gui/packages/desktop/src/renderer/redux/settings/reducers.js
index 096dede5c2..e9b2ff61b2 100644
--- a/gui/packages/desktop/src/renderer/redux/settings/reducers.js
+++ b/gui/packages/desktop/src/renderer/redux/settings/reducers.js
@@ -12,7 +12,7 @@ export type RelaySettingsRedux =
},
|}
| {|
- custom_tunnel_endpoint: {
+ customTunnelEndpoint: {
host: string,
port: number,
protocol: RelayProtocol,
diff --git a/gui/packages/desktop/test/components/SelectLocation.spec.js b/gui/packages/desktop/test/components/SelectLocation.spec.js
index 3d35da0edd..f4aaf83ba1 100644
--- a/gui/packages/desktop/test/components/SelectLocation.spec.js
+++ b/gui/packages/desktop/test/components/SelectLocation.spec.js
@@ -5,11 +5,8 @@ import { shallow } from 'enzyme';
import { CloseBarItem } from '../../src/renderer/components/NavigationBar';
import SelectLocation from '../../src/renderer/components/SelectLocation';
-import type { SettingsReduxState } from '../../src/renderer/redux/settings/reducers';
-import type { SelectLocationProps } from '../../src/renderer/components/SelectLocation';
-
describe('components/SelectLocation', () => {
- const state: SettingsReduxState = {
+ const defaultProps = {
relaySettings: {
normal: {
location: 'any',
@@ -37,6 +34,13 @@ describe('components/SelectLocation', () => {
includeInCountry: true,
weight: 1,
},
+ {
+ hostname: 'fake2.mullvad.net',
+ ipv4AddrIn: '192.168.0.101',
+ ipv4AddrExit: '192.168.1.101',
+ includeInCountry: true,
+ weight: 1,
+ },
],
},
{
@@ -58,39 +62,24 @@ describe('components/SelectLocation', () => {
],
},
],
- autoConnect: false,
- allowLan: false,
- enableIpv6: true,
- };
-
- const makeProps = (
- state: SettingsReduxState,
- mergeProps: $Shape<SelectLocationProps>,
- ): SelectLocationProps => {
- const defaultProps: SelectLocationProps = {
- settings: state,
- onClose: () => {},
- onSelect: (_server) => {},
- };
- return Object.assign({}, defaultProps, mergeProps);
- };
-
- const render = (props: SelectLocationProps) => {
- return shallow(<SelectLocation {...props} />);
};
it('should call close callback', (done) => {
- const props = makeProps(state, {
+ const props = {
+ ...defaultProps,
onClose: () => done(),
- });
- const component = render(props)
+ onSelect: () => {},
+ };
+ const component = shallow(<SelectLocation {...props} />)
.find(CloseBarItem)
.dive();
component.simulate('press');
});
it('should call select callback for country', (done) => {
- const props = makeProps(state, {
+ const props = {
+ ...defaultProps,
+ onClose: () => {},
onSelect: (location) => {
try {
expect(location).to.deep.equal({
@@ -101,14 +90,17 @@ describe('components/SelectLocation', () => {
done(e);
}
},
- });
- const elements = getComponent(render(props), 'country');
+ };
+ const component = shallow(<SelectLocation {...props} />);
+ const elements = getComponent(component, 'country');
expect(elements).to.have.length(1);
elements.at(0).simulate('press');
});
it('should call select callback for city', (done) => {
- const props = makeProps(state, {
+ const props = {
+ ...defaultProps,
+ onClose: () => {},
onSelect: (location) => {
try {
expect(location).to.deep.equal({
@@ -119,8 +111,30 @@ describe('components/SelectLocation', () => {
done(e);
}
},
- });
- const elements = getComponent(render(props), 'city');
+ };
+ const component = shallow(<SelectLocation {...props} />);
+ const elements = getComponent(component, 'city');
+ expect(elements).to.have.length(2);
+ elements.at(0).simulate('press');
+ });
+
+ it('should call select callback for relay', (done) => {
+ const props = {
+ ...defaultProps,
+ onClose: () => {},
+ onSelect: (location) => {
+ try {
+ expect(location).to.deep.equal({
+ hostname: ['se', 'mma', 'fake1.mullvad.net'],
+ });
+ done();
+ } catch (e) {
+ done(e);
+ }
+ },
+ };
+ const component = shallow(<SelectLocation {...props} />);
+ const elements = getComponent(component, 'relay');
expect(elements).to.have.length(2);
elements.at(0).simulate('press');
});
diff --git a/gui/packages/desktop/test/relay-settings-builder.spec.js b/gui/packages/desktop/test/relay-settings-builder.spec.js
index 3230861e0a..56126adca5 100644
--- a/gui/packages/desktop/test/relay-settings-builder.spec.js
+++ b/gui/packages/desktop/test/relay-settings-builder.spec.js
@@ -132,7 +132,7 @@ describe('Relay settings builder', () => {
})
.build(),
).to.deep.equal({
- custom_tunnel_endpoint: {
+ customTunnelEndpoint: {
host: 'se2.mullvad.net',
tunnel: {
openvpn: {
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index 1bcdeb8dd2..8eb1a0348a 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -79,7 +79,7 @@ use std::{mem, thread};
use talpid_core::mpsc::IntoSender;
use talpid_core::tunnel_state_machine::{self, TunnelCommand, TunnelParameters};
-use talpid_types::net::{TunnelEndpoint, TunnelOptions};
+use talpid_types::net::TunnelEndpoint;
use talpid_types::tunnel::{BlockReason, TunnelStateTransition};
@@ -350,17 +350,12 @@ impl Daemon {
GetAccountData(tx, account_token) => self.on_get_account_data(tx, account_token),
GetRelayLocations(tx) => self.on_get_relay_locations(tx),
SetAccount(tx, account_token) => self.on_set_account(tx, account_token),
- GetAccount(tx) => self.on_get_account(tx),
UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update),
SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan),
- GetAllowLan(tx) => self.on_get_allow_lan(tx),
SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect),
- GetAutoConnect(tx) => self.on_get_auto_connect(tx),
SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg),
SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6),
- GetTunnelOptions(tx) => self.on_get_tunnel_options(tx),
GetSettings(tx) => self.on_get_settings(tx),
- GetRelaySettings(tx) => self.on_get_relay_settings(tx),
GetVersionInfo(tx) => self.on_get_version_info(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
Shutdown => self.handle_trigger_shutdown_event(),
@@ -471,17 +466,14 @@ impl Daemon {
Self::oneshot_send(tx, current_version, "get_current_version response");
}
- fn on_get_account(&self, tx: OneshotSender<Option<String>>) {
- Self::oneshot_send(tx, self.settings.get_account_token(), "current account")
- }
-
fn on_update_relay_settings(&mut self, tx: OneshotSender<()>, update: RelaySettingsUpdate) {
let save_result = self.settings.update_relay_settings(update);
match save_result.chain_err(|| "Unable to save settings") {
- Ok(changed) => {
+ Ok(settings_changed) => {
Self::oneshot_send(tx, (), "update_relay_settings response");
-
- if changed {
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
info!("Initiating tunnel restart because the relay settings changed");
self.reconnect_tunnel();
}
@@ -490,29 +482,21 @@ impl Daemon {
}
}
- fn on_get_relay_settings(&self, tx: OneshotSender<RelaySettings>) {
- Self::oneshot_send(tx, self.settings.get_relay_settings(), "relay settings")
- }
-
fn on_set_allow_lan(&mut self, tx: OneshotSender<()>, allow_lan: bool) {
let save_result = self.settings.set_allow_lan(allow_lan);
match save_result.chain_err(|| "Unable to save settings") {
Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set_allow_lan response");
if settings_changed {
self.management_interface_broadcaster
.notify_settings(&self.settings);
self.send_tunnel_command(TunnelCommand::AllowLan(allow_lan));
}
- Self::oneshot_send(tx, (), "set_allow_lan response");
}
Err(e) => error!("{}", e.display_chain()),
}
}
- fn on_get_allow_lan(&self, tx: OneshotSender<bool>) {
- Self::oneshot_send(tx, self.settings.get_allow_lan(), "allow lan")
- }
-
fn on_set_auto_connect(&mut self, tx: OneshotSender<()>, auto_connect: bool) {
let save_result = self.settings.set_auto_connect(auto_connect);
match save_result.chain_err(|| "Unable to save settings") {
@@ -527,14 +511,6 @@ impl Daemon {
}
}
- fn on_get_auto_connect(&self, tx: OneshotSender<bool>) {
- Self::oneshot_send(
- tx,
- self.settings.get_auto_connect(),
- "get auto-connect response",
- )
- }
-
fn on_set_openvpn_mssfix(&mut self, tx: OneshotSender<()>, mssfix_arg: Option<u16>) {
let save_result = self.settings.set_openvpn_mssfix(mssfix_arg);
match save_result.chain_err(|| "Unable to save settings") {
@@ -565,11 +541,6 @@ impl Daemon {
}
}
- fn on_get_tunnel_options(&self, tx: OneshotSender<TunnelOptions>) {
- let tunnel_options = self.settings.get_tunnel_options().clone();
- Self::oneshot_send(tx, tunnel_options, "get_tunnel_options response");
- }
-
fn on_get_settings(&self, tx: OneshotSender<Settings>) {
Self::oneshot_send(tx, self.settings.clone(), "get_settings response");
}
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 2e534baf52..db5c3fedc1 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -10,7 +10,7 @@ use mullvad_types::account::{AccountData, AccountToken};
use mullvad_types::location::GeoIpLocation;
use mullvad_paths;
-use mullvad_types::relay_constraints::{RelaySettings, RelaySettingsUpdate};
+use mullvad_types::relay_constraints::RelaySettingsUpdate;
use mullvad_types::relay_list::RelayList;
use mullvad_types::settings::Settings;
use mullvad_types::states::TargetState;
@@ -25,7 +25,6 @@ use std::sync::{Arc, Mutex, RwLock};
use talpid_core::mpsc::IntoSender;
use talpid_ipc;
-use talpid_types::net::TunnelOptions;
use talpid_types::tunnel::TunnelStateTransition;
use uuid;
@@ -53,10 +52,6 @@ build_rpc_trait! {
#[rpc(meta, name = "set_account")]
fn set_account(&self, Self::Metadata, Option<AccountToken>) -> BoxFuture<(), Error>;
- /// Get which account is configured.
- #[rpc(meta, name = "get_account")]
- fn get_account(&self, Self::Metadata) -> BoxFuture<Option<AccountToken>, Error>;
-
/// Update constraints put on the type of tunnel connection to use
#[rpc(meta, name = "update_relay_settings")]
fn update_relay_settings(
@@ -64,29 +59,14 @@ build_rpc_trait! {
Self::Metadata, RelaySettingsUpdate
) -> BoxFuture<(), Error>;
- /// Update constraints put on the type of tunnel connection to use
- #[rpc(meta, name = "get_relay_settings")]
- fn get_relay_settings(
- &self,
- Self::Metadata
- ) -> BoxFuture<RelaySettings, Error>;
-
/// Set if the client should allow communication with the LAN while in secured state.
#[rpc(meta, name = "set_allow_lan")]
fn set_allow_lan(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
- /// Get if the client should allow communication with the LAN while in secured state.
- #[rpc(meta, name = "get_allow_lan")]
- fn get_allow_lan(&self, Self::Metadata) -> BoxFuture<bool, Error>;
-
/// Set if the daemon should automatically establish a tunnel on start or not.
#[rpc(meta, name = "set_auto_connect")]
fn set_auto_connect(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
- /// Get if the daemon should automatically establish a tunnel on start or not.
- #[rpc(meta, name = "get_auto_connect")]
- fn get_auto_connect(&self, Self::Metadata) -> BoxFuture<bool, Error>;
-
/// Try to connect if disconnected, or do nothing if already connecting/connected.
#[rpc(meta, name = "connect")]
fn connect(&self, Self::Metadata) -> BoxFuture<(), Error>;
@@ -126,10 +106,6 @@ build_rpc_trait! {
#[rpc(meta, name = "set_enable_ipv6")]
fn set_enable_ipv6(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
- /// Gets tunnel specific options
- #[rpc(meta, name = "get_tunnel_options")]
- fn get_tunnel_options(&self, Self::Metadata) -> BoxFuture<TunnelOptions, Error>;
-
/// Returns the current daemon settings
#[rpc(meta, name = "get_settings")]
fn get_settings(&self, Self::Metadata) -> BoxFuture<Settings, Error>;
@@ -187,26 +163,16 @@ pub enum ManagementCommand {
GetRelayLocations(OneshotSender<RelayList>),
/// Set which account token to use for subsequent connection attempts.
SetAccount(OneshotSender<()>, Option<AccountToken>),
- /// Request the current account token being used.
- GetAccount(OneshotSender<Option<AccountToken>>),
/// Place constraints on the type of tunnel and relay
UpdateRelaySettings(OneshotSender<()>, RelaySettingsUpdate),
- /// Read the constraints put on the tunnel and relay
- GetRelaySettings(OneshotSender<RelaySettings>),
/// Set the allow LAN setting.
SetAllowLan(OneshotSender<()>, bool),
- /// Get the current allow LAN setting.
- GetAllowLan(OneshotSender<bool>),
/// Set the auto-connect setting.
SetAutoConnect(OneshotSender<()>, bool),
- /// Get the current auto-connect setting.
- GetAutoConnect(OneshotSender<bool>),
/// Set the mssfix argument for OpenVPN
SetOpenVpnMssfix(OneshotSender<()>, Option<u16>),
/// Set if IPv6 should be enabled in the tunnel
SetEnableIpv6(OneshotSender<()>, bool),
- /// Get the tunnel options
- GetTunnelOptions(OneshotSender<TunnelOptions>),
/// Get the daemon settings
GetSettings(OneshotSender<Settings>),
/// Get information about the currently running and latest app versions
@@ -446,15 +412,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn get_account(&self, _: Self::Metadata) -> BoxFuture<Option<AccountToken>, Error> {
- debug!("get_account");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::GetAccount(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
fn update_relay_settings(
&self,
_: Self::Metadata,
@@ -470,15 +427,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn get_relay_settings(&self, _: Self::Metadata) -> BoxFuture<RelaySettings, Error> {
- debug!("get_relay_settings");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::GetRelaySettings(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
fn set_allow_lan(&self, _: Self::Metadata, allow_lan: bool) -> BoxFuture<(), Error> {
debug!("set_allow_lan({})", allow_lan);
let (tx, rx) = sync::oneshot::channel();
@@ -488,15 +436,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn get_allow_lan(&self, _: Self::Metadata) -> BoxFuture<bool, Error> {
- debug!("get_allow_lan");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::GetAllowLan(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
fn set_auto_connect(&self, _: Self::Metadata, auto_connect: bool) -> BoxFuture<(), Error> {
debug!("set_auto_connect({})", auto_connect);
let (tx, rx) = sync::oneshot::channel();
@@ -506,15 +445,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn get_auto_connect(&self, _: Self::Metadata) -> BoxFuture<bool, Error> {
- debug!("get_auto_connect");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::GetAutoConnect(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
fn connect(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
debug!("connect");
let (tx, rx) = sync::oneshot::channel();
@@ -617,15 +547,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn get_tunnel_options(&self, _: Self::Metadata) -> BoxFuture<TunnelOptions, Error> {
- debug!("get_tunnel_options");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::GetTunnelOptions(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
fn get_settings(&self, _: Self::Metadata) -> BoxFuture<Settings, Error> {
debug!("get_settings");
let (tx, rx) = sync::oneshot::channel();