@u4/adbkit
Version:
A Typescript client for the Android Debug Bridge.
169 lines • 6.36 kB
JavaScript
"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