-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
jsoncomm.cpp
244 lines (221 loc) · 7.3 KB
/
jsoncomm.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2013-2023 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <luz@plan44.ch>
//
// This file is part of p44utils.
//
// p44utils is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44utils is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44utils. If not, see <http://www.gnu.org/licenses/>.
//
#include "jsoncomm.hpp"
using namespace p44;
JsonComm::JsonComm(MainLoop &aMainLoop) :
inherited(aMainLoop),
mEOM('\n'), // default to linefeed
tokener(NULL),
ignoreUntilNextEOM(false),
closeWhenSent(false)
{
setReceiveHandler(boost::bind(&JsonComm::gotData, this, _1));
}
JsonComm::~JsonComm()
{
if (tokener) {
json_tokener_free(tokener);
tokener = NULL;
}
}
void JsonComm::setMessageHandler(JSonMessageCB aJsonMessageHandler)
{
rawMessageHandler = NoOP;
jsonMessageHandler = aJsonMessageHandler;
}
void JsonComm::setRawMessageHandler(TextLineCB aRawMessageHandler)
{
jsonMessageHandler = NoOP;
rawMessageHandler = aRawMessageHandler;
}
void JsonComm::gotData(ErrorPtr aError)
{
JsonCommPtr keepMeAlive(this); // make sure this object lives until routine terminates
if (Error::isOK(aError)) {
// no error, read data we've got so far
size_t dataSz = numBytesReady();
if (dataSz>0) {
// temporary buffer
uint8_t *buf = new uint8_t[dataSz];
size_t receivedBytes = receiveBytes(dataSz, buf, aError);
if (Error::isOK(aError)) {
// check for end-of-message (mEOM or NULL char), make spaces from any other ctrl char
size_t bom = 0;
while (bom<receivedBytes) {
// data to process, scan for EOM
size_t eom = bom;
bool messageComplete = false;
while (eom<receivedBytes) {
if (buf[eom]<0x20) {
if (buf[eom]==mEOM || buf[eom]==0) {
// end of message
buf[eom] = 0; // terminate message here
messageComplete = true;
break;
}
else {
// other control char, convert to space
buf[eom] = ' ';
}
}
eom++;
}
if (rawMessageHandler) {
// just append to line buffer
if (eom>0 && !ignoreUntilNextEOM) {
// append data to text line buffer
textLine.append((const char *)buf+bom, eom-bom);
if (messageComplete)
rawMessageHandler(ErrorPtr(),textLine);
// begin next line
textLine.clear();
}
}
else {
// create JSON tokener to parse message, if none found already
if (!tokener) {
tokener = json_tokener_new();
}
if (eom>0 && !ignoreUntilNextEOM) {
// feed data to tokener
struct json_object *o = json_tokener_parse_ex(tokener, (const char *)buf+bom, (int)(eom-bom));
if (o==NULL) {
// error (or incomplete JSON, which is fine)
JsonError::ErrorCodes err = json_tokener_get_error(tokener);
if (err!=json_tokener_continue) {
// real error
if (jsonMessageHandler) {
jsonMessageHandler(ErrorPtr(new JsonError(err)), JsonObjectPtr());
}
// reset the parser
ignoreUntilNextEOM = true;
json_tokener_reset(tokener);
}
}
else {
// got JSON object
JsonObjectPtr message = JsonObject::newObj(o);
if (jsonMessageHandler) {
// pass json_object into handler, will consume it
jsonMessageHandler(ErrorPtr(), message);
}
ignoreUntilNextEOM = true;
json_tokener_reset(tokener);
}
}
}
// now check for having reached the end of the message in this data chunk
if (messageComplete) {
// new message starts, don't ignore any more
ignoreUntilNextEOM = false;
// skip any control chars
while (eom<receivedBytes && buf[eom]<0x20) eom++;
}
// now eom becomes the new bom
bom = eom;
} // while data to process
} // no read error
delete[] buf; buf = NULL;
} // some data seems to be ready
} // no connection error
if (Error::notOK(aError)) {
// error occurred, report
if (jsonMessageHandler) {
jsonMessageHandler(aError, JsonObjectPtr());
}
else if (rawMessageHandler) {
rawMessageHandler(aError, "");
}
ignoreUntilNextEOM = false;
if (tokener) json_tokener_reset(tokener);
}
}
ErrorPtr JsonComm::sendMessage(JsonObjectPtr aJsonObject)
{
string json_string = aJsonObject->json_c_str();
json_string.append(1, mEOM);
return sendRaw(json_string);
}
ErrorPtr JsonComm::sendRaw(string &aRawBytes)
{
ErrorPtr err;
if (transmitBuffer.size()>0) {
// other messages are already waiting, append entire message
transmitBuffer.append(aRawBytes);
}
else {
size_t rawSize = aRawBytes.size();
// nothing in buffer yet, start new send
size_t sentBytes = transmitBytes(rawSize, (uint8_t *)aRawBytes.c_str(), err);
if (Error::isOK(err)) {
// check if all could be sent
if (sentBytes<rawSize) {
// Not everything (or maybe nothing, transmitBytes() can return 0) was sent
// - enable callback for ready-for-send
setTransmitHandler(boost::bind(&JsonComm::canSendData, this, _1));
// buffer the rest, canSendData handler will take care of writing it out
transmitBuffer.assign(aRawBytes.c_str()+sentBytes, rawSize-sentBytes);
}
else {
// all sent
// - disable transmit handler
setTransmitHandler(NoOP);
}
}
}
return err;
}
void JsonComm::closeAfterSend()
{
if (transmitBuffer.size()==0) {
// nothing buffered for later, close now
closeConnection();
}
else {
closeWhenSent = true;
}
}
void JsonComm::canSendData(ErrorPtr aError)
{
size_t bytesToSend = transmitBuffer.size();
if (bytesToSend>0 && Error::isOK(aError)) {
// send data from transmit buffer
size_t sentBytes = transmitBytes(bytesToSend, (const uint8_t *)transmitBuffer.c_str(), aError);
if (Error::isOK(aError)) {
if (sentBytes==bytesToSend) {
// all sent
transmitBuffer.erase();
// - disable transmit handler
setTransmitHandler(NoOP);
}
else {
// partially sent, remove sent bytes
transmitBuffer.erase(0, sentBytes);
}
// check for closing connection when no data pending to be sent any more
if (closeWhenSent && transmitBuffer.size()==0) {
closeWhenSent = false; // done
closeConnection();
}
}
}
}