Skip to content

Commit

Permalink
feat: remove using iproxy (appium#996)
Browse files Browse the repository at this point in the history
  • Loading branch information
umutuzgur authored Jul 2, 2019
1 parent 268e9d5 commit 41c5eb6
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 127 deletions.
19 changes: 7 additions & 12 deletions lib/commands/recordscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class ScreenRecorder {
const localPort = this.opts.remotePort;
const {protocol, hostname} = url.parse(this.opts.remoteUrl);
if (this.opts.usePortForwarding) {
await this.startIproxy(localPort);
this.startIproxy(localPort);
}

const args = [
Expand Down Expand Up @@ -108,10 +108,10 @@ class ScreenRecorder {
}, timeoutMs);
}

async startIproxy (localPort) {
this.iproxy = new iProxy(this.udid, localPort, this.opts.remotePort, false);
startIproxy (localPort) {
this.iproxy = new iProxy(this.udid, localPort, this.opts.remotePort);
try {
await this.iproxy.start();
this.iproxy.start();
} catch (err) {
log.warn(`Cannot start iproxy. Assuming it is already forwarding the remote port ${this.opts.remotePort} to ${localPort} ` +
`for the device ${this.udid}. Set the custom value to 'mjpegServerPort' capability if this is an undesired behavior. ` +
Expand All @@ -120,18 +120,13 @@ class ScreenRecorder {
}
}

async stopIproxy () {
stopIproxy () {
if (!this.iproxy) {
return;
}

const quitPromise = this.iproxy.quit();
this.iproxy.quit();
this.iproxy = null;
try {
await quitPromise;
} catch (err) {
log.warn(`Cannot stop iproxy. Original error: ${err.message}`);
}
}

async interrupt (force = false) {
Expand All @@ -155,7 +150,7 @@ class ScreenRecorder {
}

if (this.opts.usePortForwarding) {
await this.stopIproxy();
this.stopIproxy();
}

return result;
Expand Down
112 changes: 29 additions & 83 deletions lib/wda/iproxy.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,44 @@
import log from '../logger';
import { killProcess } from './utils';
import B from 'bluebird';
import net from 'net';
import { logger } from 'appium-support';
import { SubProcess } from 'teen_process';
import { checkPortStatus } from 'portscanner';
import { waitForCondition } from 'asyncbox';
import { utilities } from 'appium-ios-device';


const IPROXY_STARTUP_TIMEOUT = 5000;

const iproxyLog = logger.getLogger('iProxy');
const log = logger.getLogger('iProxy');

class iProxy {
constructor (udid, localport, deviceport, detached = true) {
this.expectIProxyErrors = true;
constructor (udid, localport, deviceport) {
this.localport = parseInt(localport, 10);
this.deviceport = parseInt(deviceport, 10);
this.udid = udid;
this.iproxy = new SubProcess('iproxy', [localport, deviceport, udid], {
detached,
stdio: ['ignore', 'pipe', 'pipe'],
});
this.serverSocket = null;
}

async start () {
log.debug(`Starting iproxy to forward traffic from local port ${this.localport} ` +
`to device port ${this.deviceport} over USB for the device ${this.udid}`);
this.expectIProxyErrors = true;

return await new B((resolve, reject) => {
this.iproxy.on('exit', (code) => {
log.debug(`iproxy exited with code '${code}'`);
if (code) {
return reject(new Error(`iproxy exited with code '${code}'`));
}
});
this.iproxy.on('output', (stdout, stderr) => {
// do nothing if we expect errors
if (this.expectIProxyErrors) {
return;
}

let out = stdout || stderr;
for (let line of out.split('\n')) {
if (!line.length) {
continue;
}

if (line.includes('Resource temporarily unavailable')) {
// this generally happens when WDA does not respond,
// so print a more useful message
log.debug('Connection to WDA timed out');
} else {
iproxyLog.debug(line);
}
}
});

return (async () => {
try {
if ((await checkPortStatus(this.localport, '127.0.0.1')) === 'open') {
throw new Error(`The port #${this.localport} is occupied by an other app. ` +
`You can customize its value by setting the 'wdaLocalPort' capability.`);
}
await this.iproxy.start(0);
try {
await waitForCondition(async () => {
try {
return (await checkPortStatus(this.localport, '127.0.0.1')) === 'open';
} catch (ign) {
return false;
}
}, {
waitMs: IPROXY_STARTUP_TIMEOUT,
intervalMs: 300,
});
log.debug(`iProxy is running and is listening on port #${this.localport}`);
} catch (e) {
log.warn(`The local port ${this.localport} is still closed after ${IPROXY_STARTUP_TIMEOUT}ms. ` +
`Continuing anyway`);
}
resolve();
} catch (err) {
log.error(`Error starting iproxy: '${err.message}'`);
reject(new Error('Unable to start iproxy. Make sure libusbmuxd is installed and ' +
'PATH contains the folder, where the binary is located.'));
}
})();
start () {
if (this.serverSocket) {
return;
}
this.serverSocket = net.createServer(async (connection) => {
try {
const socket = await utilities.connectPort(this.udid, this.deviceport);
socket.on('close', connection.destroy);
socket.on('error', log.error);
connection.on('close', socket.destroy);
connection.on('error', log.error);
connection.pipe(socket);
socket.pipe(connection);
} catch (e) {
log.warn(e.message);
connection.destroy();
}
});
this.serverSocket.listen(this.localport);
}

async quit () {
await killProcess('iproxy', this.iproxy);
this.expectIProxyErrors = true;
quit () {
if (!this.serverSocket) {
return;
}
this.serverSocket.close();
this.serverSocket = null;
}
}

Expand Down
46 changes: 15 additions & 31 deletions lib/wda/webdriveragent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import { fs, util } from 'appium-support';
import log from '../logger';
import { NoSessionProxy } from './no-session-proxy';
import { getWDAUpgradeTimestamp, CARTHAGE_ROOT } from './utils';
import {
resetXCTestProcesses, getPIDsListeningOnPort, isLocalHost,
getPIDsUsingPattern } from '../utils';
import { resetXCTestProcesses, getPIDsListeningOnPort, isLocalHost } from '../utils';
import XcodeBuild from './xcodebuild';
import iProxy from './iproxy';
import { exec } from 'teen_process';
Expand Down Expand Up @@ -92,26 +90,9 @@ class WebDriverAgent {

async cleanupObsoleteProcesses () {
const obsoletePids = await getPIDsListeningOnPort(this.url.port,
(cmdLine) => (cmdLine.includes('/WebDriverAgentRunner') || cmdLine.includes('/iproxy')) &&
(cmdLine) => cmdLine.includes('/WebDriverAgentRunner') &&
!cmdLine.toLowerCase().includes(this.device.udid.toLowerCase()));

if (this.realDevice) {
// https://github.com/appium/appium/issues/12649
const allIproxyDevicePids = await getPIDsUsingPattern(
`iproxy[[:space:]]+[0-9]+[[:space:]]+${WDA_AGENT_PORT}[[:space:]]+${this.device.udid}`, {
multi: true,
});
if (allIproxyDevicePids) {
const iproxyDevicePidsOnLocalPort = await getPIDsUsingPattern(
`iproxy[[:space:]]+${this.url.port}[[:space:]]+${WDA_AGENT_PORT}[[:space:]]+${this.device.udid}`, {
multi: true,
});
if (!_.isEqual(allIproxyDevicePids, iproxyDevicePidsOnLocalPort)) {
obsoletePids.push(..._.difference(allIproxyDevicePids, iproxyDevicePidsOnLocalPort || []));
}
}
}

if (_.isEmpty(obsoletePids)) {
log.debug(`No obsolete cached processes from previous WDA sessions ` +
`listening on port ${this.url.port} have been found`);
Expand Down Expand Up @@ -240,14 +221,11 @@ class WebDriverAgent {
});
}
// We need to provide WDA local port, because it might be occupied with
// iproxy instance initiated by some preceding run with a real device
// (iproxy instances are not killed on session termination by default)
await resetXCTestProcesses(this.device.udid, !this.realDevice, {wdaLocalPort: this.url.port});

if (this.realDevice) {
if (isLocalHost(this.wdaBaseUrl)) {
this.iproxy = new iProxy(this.device.udid, this.url.port, WDA_AGENT_PORT);
await this.iproxy.start();
this.iproxy = this.createIProxy();
} else {
log.info(`Skip starting iproxy since Appium will communicate with WDA via '${this.wdaBaseUrl}'`);
}
Expand All @@ -262,6 +240,17 @@ class WebDriverAgent {
return await this.xcodebuild.start();
}

createIProxy () {
const iproxy = new iProxy(this.device.udid, this.url.port, WDA_AGENT_PORT);
try {
iproxy.start();
return iproxy;
} catch (e) {
iproxy.quit();
throw new Error(`Couldn't start port forwarding on port ${WDA_AGENT_PORT}. Please provide a different port using 'wdaLocalPort' capability`);
}
}

async isSourceFresh () {
for (const subPath of [
CARTHAGE_ROOT,
Expand Down Expand Up @@ -296,7 +285,7 @@ class WebDriverAgent {
log.info('Shutting down sub-processes');

if (this.iproxy) {
await this.iproxy.quit();
this.iproxy.quit();
}

await this.xcodebuild.quit();
Expand Down Expand Up @@ -333,12 +322,7 @@ class WebDriverAgent {
}

set fullyStarted (started = false) {
// before WDA is started we expect errors from iproxy, since it is not
// communicating with anything yet
this.started = started;
if (this.iproxy) {
this.iproxy.expectIProxyErrors = !started;
}
}

async retrieveDerivedDataPath () {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@babel/runtime": "^7.0.0",
"appium-base-driver": "^3.0.0",
"appium-idb": "^0.1.0",
"appium-ios-device": "^0.1.1",
"appium-ios-device": "^0.2.1",
"appium-ios-driver": "^4.0.0",
"appium-ios-simulator": "^3.11.0",
"appium-remote-debugger": "^4.0.0",
Expand Down

0 comments on commit 41c5eb6

Please sign in to comment.