Skip to content

Commit bb311ad

Browse files
authored
fix: improve proxy deactivation (#83)
- Add more logs - Check proxy configuration before applying it - Rename getGlobalProxyValue function to getCurrentWifiProxyConfig - Add additional checks on adb command response when retrieving the current wifi proxy conf
1 parent ee6d020 commit bb311ad

4 files changed

Lines changed: 84 additions & 28 deletions

File tree

src/plugin.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Application } from 'express';
44
import { CliArg, ISessionCapability, MockConfig, RecordConfig, RequestInfo, ReplayConfig, SniffConfig } from './types';
55
import { DefaultPluginArgs, IPluginArgs } from './interfaces';
66
import _ from 'lodash';
7-
import { configureWifiProxy, isRealDevice, getGlobalProxyValue, getAdbReverseTunnels } from './utils/adb';
7+
import { configureWifiProxy, isRealDevice, getCurrentWifiProxyConfig, getAdbReverseTunnels } from './utils/adb';
88
import { cleanUpProxyServer, sanitizeMockConfig, setupProxyServer } from './utils/proxy';
99
import proxyCache from './proxy-cache';
1010
import logger from './logger';
@@ -107,8 +107,8 @@ export class AppiumInterceptorPlugin extends BasePlugin {
107107
return response;
108108
}
109109
const realDevice = await isRealDevice(adb, deviceUDID);
110-
const currentGlobalProxy = await getGlobalProxyValue(adb, deviceUDID)
111-
const proxy = await setupProxyServer(sessionId, deviceUDID, realDevice, certDirectory, currentGlobalProxy);
110+
const currentWifiProxyConfig = await getCurrentWifiProxyConfig(adb, deviceUDID)
111+
const proxy = await setupProxyServer(sessionId, deviceUDID, realDevice, certDirectory, currentWifiProxyConfig);
112112
await configureWifiProxy(adb, deviceUDID, realDevice, proxy.options);
113113
proxyCache.add(sessionId, proxy);
114114
}

src/proxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface ProxyOptions {
2929
certificatePath: string;
3030
port: number;
3131
ip: string;
32-
previousGlobalProxy?: ProxyOptions;
32+
previousConfig?: ProxyOptions;
3333
}
3434

3535
export class Proxy {
@@ -83,7 +83,7 @@ export class Proxy {
8383
}
8484

8585
public get previousGlobalProxy(): ProxyOptions | undefined {
86-
return this.options.previousGlobalProxy ?? undefined
86+
return this.options.previousConfig ?? undefined
8787
}
8888

8989
public async start(): Promise<boolean> {

src/utils/adb.ts

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ADB from 'appium-adb';
22
import { Proxy, ProxyOptions } from '../proxy';
3+
import logger from '../logger';
34

45
export type ADBInstance = ADB;
56
export type UDID = string;
@@ -25,17 +26,42 @@ export async function isRealDevice(adb: ADBInstance, udid: UDID): Promise<boolea
2526
return property !== 'emulator';
2627
}
2728

29+
/**
30+
* Configures the global HTTP proxy settings for Wi-Fi traffic on the target Android device via ADB.
31+
* If a valid proxy configuration is provided:
32+
* 1. It sets up an 'adb reverse' tunnel for real devices to ensure the device can reach the host-side proxy.
33+
* 2. It sets the 'http_proxy' global setting to 'IP:PORT'.
34+
* If the configuration is invalid or missing, it sets the 'http_proxy' to ':0' (which disables the proxy).
35+
*
36+
* @param adb - The ADB instance established by Appium.
37+
* @param udid - The Unique Device Identifier (UDID) of the Android device or emulator.
38+
* @param isRealDevice - Boolean indicating if the target is a physical device (requires adb reverse).
39+
* @param proxyConfig - Optional configuration object containing the IP and port for the proxy.
40+
* @returns A Promise resolving to the output of the final ADB shell command.
41+
* @throws {Error} Throws an error if any ADB command execution fails.
42+
*/
2843
export async function configureWifiProxy(
2944
adb: ADBInstance,
3045
udid: UDID,
31-
realDevice: boolean,
32-
proxy?: ProxyOptions
46+
isRealDevice: boolean,
47+
proxyConfig?: ProxyOptions
3348
): Promise<string> {
49+
logger.info(`configureWifiProxy(udid=${udid}, isRealDevice=${isRealDevice}, proxyConfig=${JSON.stringify(proxyConfig)})`)
3450
try {
35-
const host = proxy ? `${proxy.ip}:${proxy.port}` : ':0';
51+
const isConfigValid = proxyConfig
52+
&& proxyConfig.ip
53+
&& proxyConfig.ip.trim().length > 0
54+
&& !isNaN(proxyConfig.port)
55+
&& proxyConfig.port > 0;
3656

37-
if (realDevice && proxy) {
38-
await adbExecWithDevice(adb, udid, ['reverse', `tcp:${proxy.port}`, `tcp:${proxy.port}`]);
57+
if (!isConfigValid) {
58+
logger.warn(`Invalid proxy config: ${JSON.stringify(proxyConfig)}. Proxy will be disabled for udid ${udid}.`);
59+
}
60+
61+
const host = isConfigValid ? `${proxyConfig.ip}:${proxyConfig.port}` : ':0';
62+
63+
if (isRealDevice && isConfigValid) {
64+
await adbExecWithDevice(adb, udid, ['reverse', `tcp:${proxyConfig.port}`, `tcp:${proxyConfig.port}`]);
3965
}
4066

4167
return await adbExecWithDevice(adb, udid, [
@@ -51,36 +77,66 @@ export async function configureWifiProxy(
5177
}
5278
}
5379

54-
export async function getGlobalProxyValue(
80+
/**
81+
* Retrieves the current global HTTP proxy settings from the target Android device via ADB.
82+
* The function checks the 'http_proxy' setting in the 'global' namespace of the Android system settings.
83+
*
84+
* @param adb - The ADB instance established by Appium.
85+
* @param udid - The Unique Device Identifier (UDID) of the Android device or emulator.
86+
* @returns A Promise resolving to an object containing the IP and port of the proxy
87+
* ({ ip: string, port: number }), or undefined if no proxy is configured,
88+
* or if the configuration is invalid (e.g., malformed port).
89+
* @throws {Error} Throws an error if the ADB command execution fails.
90+
*/
91+
export async function getCurrentWifiProxyConfig(
5592
adb: ADBInstance,
5693
udid: UDID
57-
): Promise<ProxyOptions> {
94+
): Promise<ProxyOptions | undefined> {
95+
logger.info(`getCurrentWifiProxyConfig(udid=${udid})`);
5896
try {
59-
const proxy = await adbExecWithDevice(adb, udid, [
97+
// Execute ADB command to get the current global HTTP proxy setting
98+
const proxySettingsCommandResult = await adbExecWithDevice(adb, udid, [
6099
'shell',
61100
'settings',
62101
'get',
63102
'global',
64-
'http_proxy'
65-
])
103+
'http_proxy',
104+
]);
66105

67-
if(proxy == ":0" || proxy == "null") {
68-
return {
69-
port: 0
70-
} as ProxyOptions
106+
// ADB returns ":0" or "null" when the proxy is disabled.
107+
if (!proxySettingsCommandResult || proxySettingsCommandResult === ':0' || proxySettingsCommandResult === 'null') {
108+
logger.info(`No active proxy for udid ${udid}.`);
109+
return undefined;
71110
}
72111

73-
const [ip, portStr] = proxy.split(":");
112+
// Ensure the format is IP:PORT (must contain at least one ':').
113+
if (!proxySettingsCommandResult.includes(':')) {
114+
logger.warn(`Invalid proxy settings format detected for udid ${udid}: '${proxySettingsCommandResult}'.`);
115+
return undefined;
116+
}
117+
118+
// Split the string into IP and port
119+
const [ip, portStr] = proxySettingsCommandResult.split(':', 2);
74120
const port = Number(portStr);
75121

76-
return {
77-
ip: ip,
78-
port: port
79-
} as ProxyOptions
122+
// Validate IP and port values.
123+
// IP should not be empty after trimming, and port must be a valid number greater than 0.
124+
if (!ip.trim() || isNaN(port) || port <= 0) {
125+
logger.warn(`Invalid proxy settings detected for udid ${udid}: (ip=${ip}, port=${port})`);
126+
return undefined;
127+
}
128+
129+
const proxyOptions: ProxyOptions = {
130+
ip: ip.trim(),
131+
port: port,
132+
} as ProxyOptions;
133+
134+
logger.info(`Found active proxy for udid ${udid}: ${JSON.stringify(proxyOptions)}`);
135+
return proxyOptions;
80136

81137
} catch (error: any) {
82-
throw new Error(`Error get global proxy value ${udid}: ${error.message}`);
83-
}
138+
throw new Error(`Error getting wifi proxy settings for ${udid}: ${error.message}`);
139+
}
84140
}
85141

86142
/**

src/utils/proxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ export async function setupProxyServer(
113113
deviceUDID: string,
114114
isRealDevice: boolean,
115115
certDirectory: string,
116-
currentGlobalProxy?: ProxyOptions
116+
currentWifiProxyConfig?: ProxyOptions
117117
) {
118118
const certificatePath = prepareCertificate(sessionId, certDirectory);
119119
const port = await getPort();
120120
const _ip = isRealDevice ? 'localhost' : ip.address('public', 'ipv4');
121-
const proxy = new Proxy({ deviceUDID, sessionId, certificatePath, port, ip: _ip, previousGlobalProxy: currentGlobalProxy });
121+
const proxy = new Proxy({ deviceUDID, sessionId, certificatePath, port, ip: _ip, previousConfig: currentWifiProxyConfig });
122122
await proxy.start();
123123
if (!proxy.isStarted()) {
124124
throw new Error('Unable to start the proxy server');

0 commit comments

Comments
 (0)