UNPKG

@u4/adbkit

Version:

A Typescript client for the Android Debug Bridge.

169 lines 6.36 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __importDefault(require("events")); const child_process_1 = require("child_process"); const parser_1 = __importDefault(require("./parser")); const dump_1 = __importDefault(require("./dump")); const net_1 = require("net"); const util_1 = require("util"); const utils_1 = __importDefault(require("./utils")); const debug = utils_1.default.debug('adb:connection'); class Connection extends events_1.default { constructor(_parent) { super(); this._parent = _parent; this.on = (event, listener) => super.on(event, listener); this.off = (event, listener) => super.off(event, listener); this.once = (event, listener) => super.once(event, listener); this.emit = (event, ...args) => super.emit(event, ...args); this.options = _parent.options || { port: 0 }; this.triedStarting = false; } get parent() { return this._parent; } async connect() { this.socket = (0, net_1.connect)(this.options); this.socket.setNoDelay(true); this.parser = new parser_1.default(this.socket); this.socket.on('connect', () => this.emit('connect')); this.socket.on('end', () => this.emit('end')); this.socket.on('drain', () => this.emit('drain')); this.socket.on('timeout', () => this.emit('timeout')); this.socket.on('close', (hadError) => this.emit('close', hadError)); try { await new Promise((resolve, reject) => { const onConnect = () => { this.socket.off('error', onError); resolve(); }; const onError = (e) => { this.socket.off('connect', onConnect); reject(e); }; this.socket.once('connect', onConnect); this.socket.once('error', onError); }); } catch (err) { if (err.code === 'ECONNREFUSED' && !this.triedStarting) { debug("Connection was refused, let's try starting the server once"); this.triedStarting = true; await this.startServer(); return this.connect(); } else { this.end(); throw err; } } // Emit unhandled error events, so that they can be handled on the client. // Without this, they would just crash node unavoidably. if (this.socket) { this.socket.on('error', (err) => { if (this.socket && this.socket.listenerCount('error') === 1) { this.emit('error', err); } }); } return this; } /** * added for Mock testing */ getSocket() { return this.socket; } end() { if (this.socket) { this.socket.end(); } return this; } /** * New Writen Call imported from Piotr Roszatycki implementation * https://github.com/dex4er/js-promise-writable * this method take care of any drain needed. * * @param data data to write * @returns number of byte writen */ write(data) { const socket = this.socket; let rejected = false; return new Promise((resolve, reject) => { // permit exta log to adbkit.dump if ADBKIT_DUMP env variable is set const enc = (0, dump_1.default)(data); if (this._errored) { const err = this._errored; this._errored = undefined; return reject(err); } if (!socket.writable || socket.destroyed) { return reject(new Error("write in a closed socket")); } const writeErrorHandler = (err) => { this._errored = undefined; rejected = true; reject(err); }; socket.once("error", writeErrorHandler); const canWrite = socket.write(enc); socket.removeListener("error", writeErrorHandler); if (canWrite) { if (!rejected) { resolve(data.length); } } else { const errorHandler = (err) => { this._errored = undefined; removeListeners(); reject(err); }; const drainHandler = () => { removeListeners(); resolve(data.length); }; const closeHandler = () => { removeListeners(); resolve(data.length); }; const finishHandler = () => { removeListeners(); resolve(data.length); }; const removeListeners = () => { socket.removeListener("close", closeHandler); socket.removeListener("drain", drainHandler); socket.removeListener("error", errorHandler); socket.removeListener("finish", finishHandler); }; socket.on("close", closeHandler); socket.on("drain", drainHandler); socket.on("error", errorHandler); socket.on("finish", finishHandler); } }); } startServer() { let port = 0; if ('port' in this.options) { port = this.options.port; } const args = port ? ['-P', String(port), 'start-server'] : ['start-server']; debug(`Starting ADB server via '${this.options.bin} ${args.join(' ')}'`); return this._exec(args, {}); } _exec(args, options) { if (!this.options.bin) throw new Error('No bin specified'); debug(`CLI: ${this.options.bin} ${args.join(' ')}`); return (0, util_1.promisify)(child_process_1.execFile)(this.options.bin, args, options); } } exports.default = Connection; //# sourceMappingURL=connection.js.map