Skip to content

Commit 6b42671

Browse files
Merge pull request #2 from telerik/milanov/fix-device-log-bug
Fix device log bug
2 parents 8336a72 + 88e1ec4 commit 6b42671

File tree

5 files changed

+145
-40
lines changed

5 files changed

+145
-40
lines changed

IOSDeviceLib/Printing.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
#include <mutex>
2+
13
#include "SocketHelper.h"
24
#include "Printing.h"
35
#include "Constants.h"
46
#include "json.hpp"
57

8+
std::mutex print_mutex;
9+
610
void print(const char* str)
711
{
12+
// We need to lock the print method because in some cases we print different parts of messages
13+
// from different threads.
14+
print_mutex.lock();
815
LengthEncodedMessage length_encoded_message = get_message_with_encoded_length(str);
916
char* buff = new char[length_encoded_message.length];
1017
std::setvbuf(stdout, buff, _IOFBF, length_encoded_message.length);
1118
fwrite(length_encoded_message.message, length_encoded_message.length, 1, stdout);
1219
fflush(stdout);
1320
delete[] buff;
21+
print_mutex.unlock();
1422
}
1523

1624
void print(const nlohmann::json& message)

constants.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"use strict";
2+
3+
exports.DataEventName = "data";
4+
exports.DeviceFoundEventName = "deviceFound";
5+
exports.DeviceLostEventName = "deviceLost";
6+
7+
exports.DeviceEventEnum = {
8+
kDeviceFound: "deviceFound",
9+
kDeviceLost: "deviceLost",
10+
kDeviceTrusted: "deviceTrusted",
11+
kDeviceUnknown: "deviceUnknown"
12+
};

index.js

+17-39
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
"use strict";
22

3-
const bufferpack = require("bufferpack");
43
const uuid = require('node-uuid');
5-
const spawn = require("child_process").spawn;
6-
const path = require("path");
7-
const fs = require("fs");
8-
const os = require("os");
94
const EventEmitter = require("events");
105

11-
const DeviceEventEnum = {
12-
kDeviceFound: "deviceFound",
13-
kDeviceLost: "deviceLost",
14-
kDeviceTrusted: "deviceTrusted",
15-
kDeviceUnknown: "deviceUnknown"
16-
};
6+
const Constants = require("./constants");
7+
8+
const IOSDeviceLibStdioHandler = require("./ios-device-lib-stdio-handler").IOSDeviceLibStdioHandler;
179

1810
const MethodNames = {
1911
install: "install",
@@ -33,27 +25,14 @@ const MethodNames = {
3325
const Events = {
3426
deviceLogData: "deviceLogData"
3527
};
36-
const DataEventName = "data";
3728

3829
class IOSDeviceLib extends EventEmitter {
3930
constructor(onDeviceFound, onDeviceLost) {
4031
super();
41-
this._chProc = spawn(path.join(__dirname, "bin", os.platform(), os.arch(), "ios-device-lib"));
42-
this._chProc.stdout.on(DataEventName, (data) => {
43-
const parsedMessage = this._read(data);
44-
switch (parsedMessage.event) {
45-
case DeviceEventEnum.kDeviceFound:
46-
onDeviceFound(parsedMessage);
47-
break;
48-
case DeviceEventEnum.kDeviceLost:
49-
onDeviceLost(parsedMessage);
50-
break;
51-
case DeviceEventEnum.kDeviceTrusted:
52-
onDeviceLost(parsedMessage);
53-
onDeviceFound(parsedMessage);
54-
break;
55-
}
56-
});
32+
this._iosDeviceLibStdioHandler = new IOSDeviceLibStdioHandler();
33+
this._iosDeviceLibStdioHandler.startReadingData();
34+
this._iosDeviceLibStdioHandler.on(Constants.DeviceFoundEventName, onDeviceFound);
35+
this._iosDeviceLibStdioHandler.on(Constants.DeviceLostEventName, onDeviceLost);
5736
}
5837

5938
install(ipaPath, deviceIdentifiers) {
@@ -105,8 +84,8 @@ class IOSDeviceLib extends EventEmitter {
10584
}
10685

10786
dispose(signal) {
108-
this._chProc.removeAllListeners();
109-
this._chProc.kill(signal);
87+
this.removeAllListeners();
88+
this._iosDeviceLibStdioHandler.dispose(signal);
11089
}
11190

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

11897
const id = uuid.v4();
119-
const eventHandler = (data) => {
120-
let response = this._read(data, id);
121-
if (response) {
122-
delete response.id;
98+
const eventHandler = (message) => {
99+
if (message) {
100+
delete message.id;
123101
if (options && options.shouldEmit) {
124-
this.emit(Events.deviceLogData, response);
102+
this.emit(Events.deviceLogData, message);
125103
} else {
126-
response.error ? reject(response.error) : resolve(response);
127-
this._chProc.stdout.removeListener(DataEventName, eventHandler);
104+
message.error ? reject(message.error) : resolve(message);
105+
this._iosDeviceLibStdioHandler.removeListener(Constants.DataEventName, eventHandler);
128106
}
129107
}
130108
};
131109

132-
this._chProc.stdout.on(DataEventName, eventHandler);
133-
this._chProc.stdin.write(this._getMessage(id, methodName, args));
110+
this._iosDeviceLibStdioHandler.on(Constants.DataEventName, eventHandler);
111+
this._iosDeviceLibStdioHandler.writeData(this._getMessage(id, methodName, args));
134112
});
135113
}
136114

ios-device-lib-stdio-handler.js

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"use strict";
2+
3+
const EventEmitter = require("events");
4+
const path = require("path");
5+
const os = require("os");
6+
const spawn = require("child_process").spawn;
7+
8+
const bufferpack = require("bufferpack");
9+
const Constants = require("./constants");
10+
11+
const HeaderSize = 4;
12+
13+
class IOSDeviceLibStdioHandler extends EventEmitter {
14+
constructor() {
15+
super();
16+
this._unfinishedMessage = Buffer.alloc(0);
17+
}
18+
19+
startReadingData() {
20+
this._chProc = spawn(path.join(__dirname, "bin", os.platform(), os.arch(), "ios-device-lib"));
21+
this._chProc.stdout.on("data", (data) => {
22+
this._unpackMessages(data);
23+
});
24+
}
25+
26+
writeData(data) {
27+
this._chProc.stdin.write(data);
28+
}
29+
30+
dispose(signal) {
31+
this.removeAllListeners();
32+
this._chProc.removeAllListeners();
33+
this._chProc.kill(signal);
34+
}
35+
36+
_distributeMessage(message) {
37+
if (message.event) {
38+
switch (message.event) {
39+
case Constants.DeviceEventEnum.kDeviceFound:
40+
this.emit(Constants.DeviceFoundEventName, message);
41+
break;
42+
case DeviceEventEnum.kDeviceLost:
43+
this.emit(Constants.DeviceLostEventName, message);
44+
break;
45+
case DeviceEventEnum.kDeviceTrusted:
46+
this.emit(Constants.DeviceLostEventName, message);
47+
this.emit(Constants.DeviceFoundEventName, message);
48+
break;
49+
}
50+
} else {
51+
this.emit(Constants.DataEventName, message);
52+
}
53+
}
54+
55+
_unpackMessages(data) {
56+
if (!this._unfinishedMessage.length && data.length >= HeaderSize) {
57+
// Get the message length header.
58+
const messageSizeBuffer = data.slice(0, HeaderSize);
59+
const messageLength = bufferpack.unpack(">i", messageSizeBuffer)[0];
60+
const dataLengthWithoutHeader = data.length - HeaderSize;
61+
62+
if (messageLength > dataLengthWithoutHeader) {
63+
// Less than one message in the buffer.
64+
// Store the unfinished message untill the next call of the function.
65+
this._unfinishedMessage = data;
66+
} else if (dataLengthWithoutHeader > messageLength) {
67+
// More than one message in the buffer.
68+
const messageBuffer = this._getMessageFromBuffer(data, messageLength);
69+
70+
// Get the rest of the message here.
71+
// Since our messages are not separated by whitespace or newlie
72+
// we do not need to add somethig when we slice the original buffer.
73+
const slicedBuffer = data.slice(messageBuffer.length + HeaderSize);
74+
this._distributeMessage(this._parseMessageFromBuffer(messageBuffer));
75+
this._unpackMessages(slicedBuffer);
76+
} else {
77+
// One message in the buffer.
78+
const messageBuffer = this._getMessageFromBuffer(data, messageLength);
79+
this._distributeMessage(this._parseMessageFromBuffer(messageBuffer));
80+
}
81+
} else if (this._unfinishedMessage.length && data.length >= HeaderSize) {
82+
// Append the new data to the unfinished message and try to unpack again.
83+
const concatenatedMessage = Buffer.concat([this._unfinishedMessage, data]);
84+
85+
// Clear the unfinished message buffer.
86+
this._unfinishedMessage = Buffer.alloc(0);
87+
this._unpackMessages(concatenatedMessage);
88+
} else {
89+
// While debugging the data here contains one character - \0, null or 0.
90+
// I think we get here when the Node inner buffer for the data of the data event
91+
// is filled with data and the last symbol is a part of the length header of the next message.
92+
// That's why we need this concat.
93+
// This code is tested with 10 000 000 messages in while loop.
94+
this._unfinishedMessage = Buffer.concat([this._unfinishedMessage, data]);
95+
}
96+
}
97+
98+
_getMessageFromBuffer(buffer, messageLength) {
99+
return buffer.slice(HeaderSize, messageLength + HeaderSize);
100+
}
101+
102+
_parseMessageFromBuffer(buffer) {
103+
return JSON.parse(buffer.toString().trim());
104+
}
105+
}
106+
107+
exports.IOSDeviceLibStdioHandler = IOSDeviceLibStdioHandler;

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ios-device-lib",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "",
55
"types": "./typings/ios-device-lib.d.ts",
66
"main": "index.js",

0 commit comments

Comments
 (0)