hdckit
Version:
A pure Node.js client for the OpenHarmony Device Connector
159 lines (158 loc) • 5.24 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_net_1 = __importDefault(require("node:net"));
const Emitter_1 = __importDefault(require("licia/Emitter"));
const ChannelHandShake_1 = __importDefault(require("./ChannelHandShake"));
const startWith_1 = __importDefault(require("licia/startWith"));
const node_child_process_1 = require("node:child_process");
const node_util_1 = __importDefault(require("node:util"));
const toStr_1 = __importDefault(require("licia/toStr"));
const HANDSHAKE_MESSAGE = 'OHOS HDC';
class Connection extends Emitter_1.default {
constructor(options) {
super();
this.triedStarting = false;
this.ended = false;
this.options = options;
}
connect(connectKey) {
const socket = node_net_1.default.connect(this.options);
socket.setNoDelay(true);
this.socket = socket;
return new Promise((resolve, reject) => {
socket.once('connect', async () => {
try {
await this.handshake(connectKey);
resolve(null);
}
catch (e) {
reject(e);
}
});
socket.once('error', reject);
socket.once('end', () => {
this.ended = true;
this.socket = null;
});
})
.catch((e) => {
if (e.code === 'ECONNREFUSED' && !this.triedStarting) {
this.triedStarting = true;
return this.startServer().then(() => {
return this.connect(connectKey);
});
}
else {
this.end();
throw e;
}
})
.then(() => this);
}
end() {
if (this.socket) {
this.socket.end();
}
}
write(data) {
return new Promise((resolve, reject) => {
this.socket.write(data, (err) => {
if (err) {
reject(err);
}
else {
resolve(null);
}
});
});
}
readBytes(howMany) {
const { socket } = this;
let tryRead;
let errorListener;
let endListener;
return new Promise((resolve, reject) => {
tryRead = () => {
if (howMany) {
const chunk = socket.read(howMany);
if (chunk) {
howMany -= chunk.length;
if (howMany === 0) {
return resolve(chunk);
}
}
if (this.ended) {
return reject(new Error('ended'));
}
}
else {
return resolve(Buffer.alloc(0));
}
};
endListener = () => {
this.ended = true;
return reject(new Error('ended'));
};
errorListener = (err) => reject(err);
socket.on('readable', tryRead);
socket.on('error', errorListener);
socket.on('end', endListener);
tryRead();
}).finally(() => {
socket.removeListener('readable', tryRead);
socket.removeListener('error', errorListener);
socket.removeListener('end', endListener);
});
}
readValue() {
return this.readBytes(4).then((value) => {
const length = value.readUInt32BE(0);
return this.readBytes(length);
});
}
async readAll() {
let all = Buffer.alloc(0);
while (true) {
try {
const chunk = await this.readValue();
all = Buffer.concat([all, chunk]);
}
catch (e) {
if (this.ended) {
return all;
}
throw e;
}
}
}
async send(data) {
const len = Buffer.alloc(4);
len.writeUInt32BE(data.length, 0);
await this.write(Buffer.concat([len, data]));
}
async handshake(connectKey) {
const data = await this.readValue();
const channelHandShake = new ChannelHandShake_1.default();
channelHandShake.deserialize(data);
if (!(0, startWith_1.default)(channelHandShake.banner.toString('utf8'), HANDSHAKE_MESSAGE)) {
throw new Error('Channel Hello failed');
}
if (connectKey) {
channelHandShake.connectKey = connectKey;
}
await this.send(channelHandShake.serialize());
}
async startServer() {
const { port, bin } = this.options;
return node_util_1.default.promisify(node_child_process_1.execFile)(bin, ['start'], {
env: {
...process.env,
OHOS_HDC_SERVER_PORT: (0, toStr_1.default)(port),
},
});
}
}
exports.default = Connection;