appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
144 lines • 5.91 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrafficCapture = void 0;
exports.mobileStartPcap = mobileStartPcap;
exports.mobileStopPcap = mobileStopPcap;
const py_ios_device_client_1 = require("../device/clients/py-ios-device-client");
const support_1 = require("appium/support");
const utils_1 = require("../utils");
const driver_1 = require("appium/driver");
const MAX_CAPTURE_TIME_SEC = 60 * 60 * 12;
const DEFAULT_EXT = '.pcap';
class TrafficCapture {
mainProcess = null;
udid;
log;
resultPath;
constructor(udid, log, resultPath) {
this.udid = udid;
this.log = log;
this.resultPath = resultPath;
}
async start(timeoutSeconds) {
this.mainProcess = await new py_ios_device_client_1.Pyidevice({
udid: this.udid,
log: this.log,
}).collectPcap(this.resultPath);
this.mainProcess.on('line-stderr', (line) => this.log.info(`[Pcap] ${line}`));
this.log.info(`Starting network traffic capture session on the device '${this.udid}'. ` +
`Will timeout in ${timeoutSeconds}s`);
setTimeout(async () => await this.interrupt(), timeoutSeconds * 1000);
this.mainProcess.once('exit', (code, signal) => {
this.log.debug(`The traffic capture session on the device '${this.udid}' has exited ` +
`with code ${code}, signal ${signal}`);
});
}
isCapturing() {
return !!this.mainProcess?.isRunning;
}
async interrupt(force = false) {
if (this.isCapturing()) {
const interruptPromise = this.mainProcess?.stop(force ? 'SIGTERM' : 'SIGINT');
this.mainProcess = null;
try {
await interruptPromise;
}
catch (e) {
this.log.warn(`Cannot ${force ? 'terminate' : 'interrupt'} the traffic capture session. ` +
`Original error: ${e.message}`);
return false;
}
}
return true;
}
async finish() {
await this.interrupt();
return this.resultPath;
}
async cleanup() {
if (await support_1.fs.exists(this.resultPath)) {
await support_1.fs.rimraf(this.resultPath);
}
}
}
exports.TrafficCapture = TrafficCapture;
/**
* Records the given network traffic capture into a .pcap file.
*
* @param timeLimitSec - The maximum recording time, in seconds. The maximum value is `43200` (12 hours).
* @param forceRestart - Whether to restart traffic capture process forcefully when startPcap is called (`true`) or ignore the call until the current traffic capture is completed (`false`, the default value).
* @throws {Error} If network traffic capture has failed to start.
*/
async function mobileStartPcap(timeLimitSec = 180, forceRestart = false) {
if (this.isSimulator()) {
throw this.log.errorWithException('Network traffic capture only works on real devices');
}
if (this._trafficCapture?.isCapturing()) {
this.log.info(`There is an active traffic capture process`);
if (forceRestart) {
this.log.info(`Stopping it because 'forceRestart' option is set to true`);
await this._trafficCapture.interrupt(true);
}
else {
this.log.info(`Doing nothing. ` +
`Set 'forceRestart' option to true if you'd like to start a new traffic capture session`);
return;
}
}
if (this._trafficCapture) {
await this._trafficCapture.cleanup();
this._trafficCapture = null;
}
const resultPath = await support_1.tempDir.path({
prefix: `appium_${support_1.util.uuidV4().substring(0, 8)}`,
suffix: DEFAULT_EXT,
});
const trafficCollector = new TrafficCapture(this.device.udid, this.log, resultPath);
const timeoutSeconds = parseInt(String(timeLimitSec), 10);
if (isNaN(timeoutSeconds) || timeoutSeconds > MAX_CAPTURE_TIME_SEC || timeoutSeconds <= 0) {
throw new driver_1.errors.InvalidArgumentError(`The timeLimitSec value must be in range [1, ${MAX_CAPTURE_TIME_SEC}] seconds. ` +
`The value of '${timeLimitSec}' has been passed instead.`);
}
try {
await trafficCollector.start(timeoutSeconds);
}
catch (e) {
await trafficCollector.interrupt(true);
await trafficCollector.cleanup();
throw e;
}
this._trafficCapture = trafficCollector;
}
/**
* Stops network traffic capture.
*
* If no traffic capture process is running, then the endpoint will try to get the recently recorded file.
*
* If no previously recorded file is found and no active traffic capture processes are running, then the method returns an empty string.
*
* @remarks Network capture files can be viewed in [Wireshark](https://www.wireshark.org/) and other similar applications.
* @returns Base64-encoded content of the recorded pcap file or an empty string if no traffic capture has been started before.
* @throws {Error} If there was an error while getting the capture file.
*/
async function mobileStopPcap() {
if (!this._trafficCapture) {
this.log.info('Network traffic collector has not been started. There is nothing to stop');
return '';
}
let resultPath;
try {
resultPath = await this._trafficCapture.finish();
if (!(await support_1.fs.exists(resultPath))) {
throw this.log.errorWithException(`The network traffic capture utility has failed ` +
`to store the actual traffic capture at '${resultPath}'`);
}
}
catch (e) {
await this._trafficCapture.interrupt(true);
await this._trafficCapture.cleanup();
this._trafficCapture = null;
throw e;
}
return await (0, utils_1.encodeBase64OrUpload)(resultPath);
}
//# sourceMappingURL=pcap.js.map