Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions IOSDeviceLib/Printing.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
#include <mutex>

#include "SocketHelper.h"
#include "Printing.h"
#include "Constants.h"
#include "json.hpp"

std::mutex print_mutex;

void print(const char* str)
{
// We need to lock the print method because in some cases we print different parts of messages
// from different threads.
print_mutex.lock();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to lock printing after all can we place the mutex on the device log operations only so as to not slow down all the other operations 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer using the mutex whenever we print on the terminal as this way we'll guarantee correct order of messages even if only part of them is sent to stdout's on data event

LengthEncodedMessage length_encoded_message = get_message_with_encoded_length(str);
char* buff = new char[length_encoded_message.length];
std::setvbuf(stdout, buff, _IOFBF, length_encoded_message.length);
fwrite(length_encoded_message.message, length_encoded_message.length, 1, stdout);
fflush(stdout);
delete[] buff;
print_mutex.unlock();
}

void print(const nlohmann::json& message)
Expand Down
12 changes: 12 additions & 0 deletions constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use strict";

exports.DataEventName = "data";
exports.DeviceFoundEventName = "deviceFound";
exports.DeviceLostEventName = "deviceLost";

exports.DeviceEventEnum = {
kDeviceFound: "deviceFound",
kDeviceLost: "deviceLost",
kDeviceTrusted: "deviceTrusted",
kDeviceUnknown: "deviceUnknown"
};
56 changes: 17 additions & 39 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
"use strict";

const bufferpack = require("bufferpack");
const uuid = require('node-uuid');
const spawn = require("child_process").spawn;
const path = require("path");
const fs = require("fs");
const os = require("os");
const EventEmitter = require("events");

const DeviceEventEnum = {
kDeviceFound: "deviceFound",
kDeviceLost: "deviceLost",
kDeviceTrusted: "deviceTrusted",
kDeviceUnknown: "deviceUnknown"
};
const Constants = require("./constants");

const IOSDeviceLibStdioHandler = require("./ios-device-lib-stdio-handler").IOSDeviceLibStdioHandler;

const MethodNames = {
install: "install",
Expand All @@ -33,27 +25,14 @@ const MethodNames = {
const Events = {
deviceLogData: "deviceLogData"
};
const DataEventName = "data";

class IOSDeviceLib extends EventEmitter {
constructor(onDeviceFound, onDeviceLost) {
super();
this._chProc = spawn(path.join(__dirname, "bin", os.platform(), os.arch(), "ios-device-lib"));
this._chProc.stdout.on(DataEventName, (data) => {
const parsedMessage = this._read(data);
switch (parsedMessage.event) {
case DeviceEventEnum.kDeviceFound:
onDeviceFound(parsedMessage);
break;
case DeviceEventEnum.kDeviceLost:
onDeviceLost(parsedMessage);
break;
case DeviceEventEnum.kDeviceTrusted:
onDeviceLost(parsedMessage);
onDeviceFound(parsedMessage);
break;
}
});
this._iosDeviceLibStdioHandler = new IOSDeviceLibStdioHandler();
this._iosDeviceLibStdioHandler.startReadingData();
this._iosDeviceLibStdioHandler.on(Constants.DeviceFoundEventName, onDeviceFound);
this._iosDeviceLibStdioHandler.on(Constants.DeviceLostEventName, onDeviceLost);
}

install(ipaPath, deviceIdentifiers) {
Expand Down Expand Up @@ -105,8 +84,8 @@ class IOSDeviceLib extends EventEmitter {
}

dispose(signal) {
this._chProc.removeAllListeners();
this._chProc.kill(signal);
this.removeAllListeners();
this._iosDeviceLibStdioHandler.dispose(signal);
}

_getPromise(methodName, args, options) {
Expand All @@ -116,21 +95,20 @@ class IOSDeviceLib extends EventEmitter {
}

const id = uuid.v4();
const eventHandler = (data) => {
let response = this._read(data, id);
if (response) {
delete response.id;
const eventHandler = (message) => {
if (message) {
delete message.id;
if (options && options.shouldEmit) {
this.emit(Events.deviceLogData, response);
this.emit(Events.deviceLogData, message);
} else {
response.error ? reject(response.error) : resolve(response);
this._chProc.stdout.removeListener(DataEventName, eventHandler);
message.error ? reject(message.error) : resolve(message);
this._iosDeviceLibStdioHandler.removeListener(Constants.DataEventName, eventHandler);
}
}
};

this._chProc.stdout.on(DataEventName, eventHandler);
this._chProc.stdin.write(this._getMessage(id, methodName, args));
this._iosDeviceLibStdioHandler.on(Constants.DataEventName, eventHandler);
this._iosDeviceLibStdioHandler.writeData(this._getMessage(id, methodName, args));
});
}

Expand Down
106 changes: 106 additions & 0 deletions ios-device-lib-stdio-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use strict";

const EventEmitter = require("events");
const path = require("path");
const os = require("os");
const spawn = require("child_process").spawn;

const bufferpack = require("bufferpack");
const Constants = require("./constants");

const HeaderSize = 4;

class IOSDeviceLibStdioHandler extends EventEmitter {
constructor() {
super();
this._unfinishedMessage = new Buffer(0);
}

startReadingData() {
this._chProc = spawn(path.join(__dirname, "bin", os.platform(), os.arch(), "ios-device-lib"));
this._chProc.stdout.on("data", (data) => {
this._unpackMessages(data);
});
}

writeData(data) {
this._chProc.stdin.write(data);
}

dispose(signal) {
this.removeAllListeners();
this._chProc.removeAllListeners();
this._chProc.kill(signal);
}

_distributeMessage(message) {
if (message.event) {
switch (message.event) {
case Constants.DeviceEventEnum.kDeviceFound:
this.emit(Constants.DeviceFoundEventName, message);
break;
case DeviceEventEnum.kDeviceLost:
this.emit(Constants.DeviceLostEventName, message);
break;
case DeviceEventEnum.kDeviceTrusted:
this.emit(Constants.DeviceLostEventName, message);
this.emit(Constants.DeviceFoundEventName, message);
break;
}
} else {
this.emit(Constants.DataEventName, message);
}
}

_unpackMessages(data) {
if (!this._unfinishedMessage.length && data.length >= HeaderSize) {
// Get the message length header.
const messageSizeBuffer = data.slice(0, HeaderSize);
const messageLength = bufferpack.unpack(">i", messageSizeBuffer)[0];

if (messageLength > data.length - HeaderSize) {
// Less than one message in the buffer.
// Store the unfinished message untill the next call of the function.
this._unfinishedMessage = data;
} else if (data.length - 4 > messageLength) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why - 4, shouldn't this be - HeaderSize?

// More than one message in the buffer.
const messageBuffer = this._getMessageFromBuffer(data, messageLength);

// Get the rest of the message here.
// Since our messages are not separated by whitespace or newlie
// we do not need to add somethig when we slice the original buffer.
const slicedBuffer = data.slice(messageBuffer.length + HeaderSize);
this._distributeMessage(this._parseMessageFromBuffer(messageBuffer));
this._unpackMessages(slicedBuffer);
} else {
// One message in the buffer.
const messageBuffer = this._getMessageFromBuffer(data, messageLength);
this._distributeMessage(this._parseMessageFromBuffer(messageBuffer));
}
} else if (this._unfinishedMessage.length && data.length >= HeaderSize) {
// Append the new data to the unfinished message and try to unpack again.
const concatenatedMessage = Buffer.concat([this._unfinishedMessage, data]);

// Clear the unfinished message buffer.
this._unfinishedMessage = new Buffer(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this._unpackMessages(concatenatedMessage);
} else {
// While debugging the data here contains one character - \0, null or 0.
// I think we get here when the Node inner buffer for the data of the data event
// is filled with data and the last symbol is a part of the length header of the next message.
// That's why we need this concat.
// This code is tested with 10 000 000 messages in while loop.
this._unfinishedMessage = Buffer.concat([this._unfinishedMessage, data]);
}
}

_getMessageFromBuffer(buffer, messageLength) {
return buffer.slice(HeaderSize, messageLength + HeaderSize);
}

_parseMessageFromBuffer(buffer) {
return JSON.parse(buffer.toString().trim());
}
}

exports.IOSDeviceLibStdioHandler = IOSDeviceLibStdioHandler;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ios-device-lib",
"version": "0.1.0",
"version": "0.1.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

breaking changes should increase the minor version when your version is 0.x.x

"description": "",
"types": "./typings/ios-device-lib.d.ts",
"main": "index.js",
Expand Down