UNPKG

appium-remote-debugger

Version:
183 lines 7.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RpcClientSimulator = void 0; const logger_1 = require("../logger"); const lodash_1 = __importDefault(require("lodash")); const bluebird_1 = __importDefault(require("bluebird")); const node_net_1 = __importDefault(require("node:net")); const rpc_client_1 = require("./rpc-client"); const appium_ios_device_1 = require("appium-ios-device"); /** * RPC client implementation for iOS simulators. * Extends RpcClient to provide simulator-specific connection handling * via TCP sockets or Unix domain sockets. */ class RpcClientSimulator extends rpc_client_1.RpcClient { host; port; messageProxy; socket; socketPath; service; /** * @param opts - Options for configuring the RPC client, including * simulator-specific options like socketPath, host, and port. */ constructor(opts = {}) { super(opts); const { socketPath, host = '::1', port, messageProxy } = opts; // host/port config for TCP communication, socketPath for unix domain sockets this.host = host; this.port = port; this.messageProxy = messageProxy; this.socket = null; this.socketPath = socketPath; } /** * Connects to the Web Inspector service on an iOS simulator. * Supports both Unix domain sockets and TCP connections, with optional proxy support. */ async connect() { // create socket and handle its messages if (this.socketPath) { if (this.messageProxy) { // unix domain socket via proxy logger_1.log.debug(`Connecting to remote debugger via proxy through unix domain socket: '${this.messageProxy}'`); this.socket = node_net_1.default.connect(this.messageProxy); // Forward the actual socketPath to the proxy this.socket.once('connect', () => { logger_1.log.debug(`Forwarding the actual web inspector socket to the proxy: '${this.socketPath}'`); if (this.socket) { this.socket.write(JSON.stringify({ socketPath: this.socketPath, })); } }); } else { // unix domain socket logger_1.log.debug(`Connecting to remote debugger through unix domain socket: '${this.socketPath}'`); this.socket = node_net_1.default.connect(this.socketPath); } } else { if (this.messageProxy) { // connect to the proxy instead of the remote debugger directly this.port = this.messageProxy; } // tcp socket logger_1.log.debug(`Connecting to remote debugger ${this.messageProxy ? 'via proxy ' : ''}through TCP: ${this.host}:${this.port}`); this.socket = new node_net_1.default.Socket(); if (this.port && this.host) { this.socket.connect(this.port, this.host); } else { throw new Error('Both port and host must be defined for TCP connection'); } } this.socket.setNoDelay(true); this.socket.setKeepAlive(true); this.socket.on('close', () => { if (this.isConnected) { logger_1.log.debug('Debugger socket disconnected'); } this.isConnected = false; this.socket = null; }); this.socket.on('end', () => { this.isConnected = false; }); this.service = await appium_ios_device_1.services.startWebInspectorService(this.udid, { socket: this.socket, isSimulator: true, osVersion: this.platformVersion, verbose: this.logAllCommunication, verboseHexDump: this.logAllCommunicationHexDump, maxFrameLength: this.webInspectorMaxFrameLength, }); this.service.listenMessage(this.receive.bind(this)); // connect the socket return await new bluebird_1.default((resolve, reject) => { // only resolve this function when we are actually connected if (!this.socket) { return reject(new Error('RPC socket is not connected. Please contact developers')); } this.socket.on('connect', () => { logger_1.log.debug(`Debugger socket connected`); this.isConnected = true; resolve(); }); this.socket.on('error', (err) => { if (this.isConnected) { logger_1.log.error(`Socket error: ${err.message}`); this.isConnected = false; } // the connection was refused, so reject the connect promise reject(err); }); }); } /** * Disconnects from the Web Inspector service on the simulator. * Closes the socket and service connection, and cleans up resources. */ async disconnect() { if (!this.isConnected) { return; } logger_1.log.debug('Disconnecting from remote debugger'); await super.disconnect(); this.service?.close(); this.isConnected = false; } /** * Sends a command message to the Web Inspector service via the socket. * Handles socket errors and ensures the socket is available before sending. * * @param cmd - The command to send to the simulator. */ async sendMessage(cmd) { let onSocketError; return await new bluebird_1.default((resolve, reject) => { // handle socket problems onSocketError = (err) => { logger_1.log.error(`Socket error: ${err.message}`); // the connection was refused, so reject the connect promise reject(err); }; if (!this.socket || !this.service) { return reject(new Error('The RPC client is not connected. Have you called `connect()` before sending a message?')); } this.socket.on('error', onSocketError); this.service.sendMessage(cmd); resolve(); }).finally(() => { // remove this listener, so we don't exhaust the system if (this.socket && onSocketError) { this.socket.removeListener('error', onSocketError); } }); } /** * Receives data from the Web Inspector service and handles it. * Converts Buffer data to strings for certain message keys. * * @param data - The data received from the service. */ async receive(data) { if (!this.isConnected || !data) { return; } for (const key of ['WIRMessageDataKey', 'WIRDestinationKey', 'WIRSocketDataKey']) { if (!lodash_1.default.isUndefined(data[key])) { data[key] = data[key].toString('utf8'); } } await this.messageHandler.handleMessage(data); } } exports.RpcClientSimulator = RpcClientSimulator; //# sourceMappingURL=rpc-client-simulator.js.map