1 /******************************************************************************
2
3 This source file is part of the MoleQueue project.
4
5 Copyright 2012 Kitware, Inc.
6
7 This source code is released under the New BSD License, (the "License").
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14
15 ******************************************************************************/
16
17 #include "message.h"
18
19 #include "connection.h"
20 #include "messageidmanager_p.h"
21
22 #include <QtCore/QJsonArray>
23 #include <QtCore/QJsonDocument>
24 #include <QtCore/QStringList>
25 #include <QtCore/QDebug>
26
27 namespace MoleQueue {
28
29 // Dummy value returned by *Ref() functions called with invalid types.
30 QJsonValue dummyValue(QJsonValue::Null);
31
Message(Connection * conn,EndpointIdType endpoint_)32 Message::Message(Connection *conn, EndpointIdType endpoint_)
33 : m_type(Invalid),
34 m_errorCode(0),
35 m_connection(conn),
36 m_endpoint(endpoint_)
37 {
38 }
39
Message(Message::MessageType type_,Connection * conn,EndpointIdType endpoint_)40 Message::Message(Message::MessageType type_, Connection *conn,
41 EndpointIdType endpoint_)
42 : m_type(type_),
43 m_errorCode(0),
44 m_connection(conn),
45 m_endpoint(endpoint_)
46 {
47 }
48
Message(const QJsonObject & rawJson,Connection * conn,EndpointIdType endpoint_)49 Message::Message(const QJsonObject &rawJson, Connection *conn,
50 EndpointIdType endpoint_)
51 : m_type(Raw),
52 m_errorCode(0),
53 m_rawJson(rawJson),
54 m_connection(conn),
55 m_endpoint(endpoint_)
56 {
57 }
58
Message(const Message & other)59 Message::Message(const Message &other)
60 : m_type(other.m_type),
61 m_method(other.m_method),
62 m_id(other.m_id),
63 m_params(other.m_params),
64 m_result(other.m_result),
65 m_errorCode(other.m_errorCode),
66 m_errorMessage(other.m_errorMessage),
67 m_errorData(other.m_errorData),
68 m_rawJson(other.m_rawJson),
69 m_connection(other.m_connection),
70 m_endpoint(other.m_endpoint)
71 {
72 }
73
operator =(const Message & other)74 Message& Message::operator=(const Message &other)
75 {
76 m_type = other.m_type;
77 m_method = other.m_method;
78 m_id = other.m_id;
79 m_params = other.m_params;
80 m_result = other.m_result;
81 m_errorCode = other.m_errorCode;
82 m_errorMessage = other.m_errorMessage;
83 m_errorData = other.m_errorData;
84 m_rawJson = other.m_rawJson;
85 m_connection = other.m_connection;
86 m_endpoint = other.m_endpoint;
87 return *this;
88 }
89
type() const90 Message::MessageType Message::type() const
91 {
92 return m_type;
93 }
94
method() const95 QString Message::method() const
96 {
97 if (!checkType(Q_FUNC_INFO, Request | Notification | Response | Error))
98 return QString();
99
100 return m_method;
101 }
102
setMethod(const QString & m)103 void Message::setMethod(const QString &m)
104 {
105 if (!checkType(Q_FUNC_INFO, Request | Notification | Response | Error))
106 return;
107
108 m_method = m;
109 }
110
params() const111 QJsonValue Message::params() const
112 {
113 if (!checkType(Q_FUNC_INFO, Request | Notification))
114 return QJsonValue();
115
116 return m_params;
117 }
118
paramsRef()119 QJsonValue& Message::paramsRef()
120 {
121 if (!checkType(Q_FUNC_INFO, Request | Notification))
122 return dummyValue;
123
124 return m_params;
125 }
126
setParams(const QJsonArray & p)127 void Message::setParams(const QJsonArray &p)
128 {
129 if (!checkType(Q_FUNC_INFO, Request | Notification))
130 return;
131
132 m_params = p;
133 }
134
setParams(const QJsonObject & p)135 void Message::setParams(const QJsonObject &p)
136 {
137 if (!checkType(Q_FUNC_INFO, Request | Notification))
138 return;
139
140 m_params = p;
141 }
142
result() const143 QJsonValue Message::result() const
144 {
145 if (!checkType(Q_FUNC_INFO, Response))
146 return QJsonValue();
147
148 return m_result;
149 }
150
resultRef()151 QJsonValue& Message::resultRef()
152 {
153 if (!checkType(Q_FUNC_INFO, Response))
154 return dummyValue;
155
156 return m_result;
157 }
158
setResult(const QJsonValue & r)159 void Message::setResult(const QJsonValue &r)
160 {
161 if (!checkType(Q_FUNC_INFO, Response))
162 return;
163
164 m_result = r;
165 }
166
errorCode() const167 int Message::errorCode() const
168 {
169 if (!checkType(Q_FUNC_INFO, Error))
170 return 0;
171
172 return m_errorCode;
173 }
174
setErrorCode(int e)175 void Message::setErrorCode(int e)
176 {
177 if (!checkType(Q_FUNC_INFO, Error))
178 return;
179
180 m_errorCode = e;
181 }
182
errorMessage() const183 QString Message::errorMessage() const
184 {
185 if (!checkType(Q_FUNC_INFO, Error))
186 return QString();
187
188 return m_errorMessage;
189 }
190
setErrorMessage(const QString & e)191 void Message::setErrorMessage(const QString &e)
192 {
193 if (!checkType(Q_FUNC_INFO,Error))
194 return;
195
196 m_errorMessage = e;
197 }
198
errorData() const199 QJsonValue Message::errorData() const
200 {
201 if (!checkType(Q_FUNC_INFO, Error))
202 return QJsonValue();
203
204 return m_errorData;
205 }
206
errorDataRef()207 QJsonValue& Message::errorDataRef()
208 {
209 if (!checkType(Q_FUNC_INFO, Error))
210 return dummyValue;
211
212 return m_errorData;
213 }
214
setErrorData(const QJsonValue & e)215 void Message::setErrorData(const QJsonValue &e)
216 {
217 if (!checkType(Q_FUNC_INFO, Error))
218 return;
219
220 m_errorData = e;
221 }
222
id() const223 MessageIdType Message::id() const
224 {
225 if (!checkType(Q_FUNC_INFO, Request | Response | Error))
226 return MessageIdType();
227
228 return m_id;
229 }
230
setId(const MessageIdType & i)231 void Message::setId(const MessageIdType &i)
232 {
233 if (!checkType(Q_FUNC_INFO, Request | Response | Error))
234 return;
235
236 m_id = i;
237 }
238
connection() const239 Connection* Message::connection() const
240 {
241 return m_connection;
242 }
243
setConnection(Connection * c)244 void Message::setConnection(Connection* c)
245 {
246 m_connection = c;
247 }
248
endpoint() const249 EndpointIdType Message::endpoint() const
250 {
251 return m_endpoint;
252 }
253
setEndpoint(const EndpointIdType & e)254 void Message::setEndpoint(const EndpointIdType &e)
255 {
256 m_endpoint = e;
257 }
258
toJsonObject() const259 QJsonObject Message::toJsonObject() const
260 {
261 QJsonObject obj;
262
263 switch (m_type) {
264 case MoleQueue::Message::Request:
265 obj.insert("jsonrpc", QLatin1String("2.0"));
266 obj.insert("method", m_method);
267 if ((m_params.isObject() && !m_params.toObject().isEmpty()) ||
268 (m_params.isArray() && !m_params.toArray().isEmpty())) {
269 obj.insert("params", m_params);
270 }
271 obj.insert("id", m_id);
272 break;
273 case MoleQueue::Message::Notification:
274 obj.insert("jsonrpc", QLatin1String("2.0"));
275 obj.insert("method", m_method);
276 if ((m_params.isObject() && !m_params.toObject().isEmpty()) ||
277 (m_params.isArray() && !m_params.toArray().isEmpty())) {
278 obj.insert("params", m_params);
279 }
280 break;
281 case MoleQueue::Message::Response:
282 obj.insert("jsonrpc", QLatin1String("2.0"));
283 obj.insert("result", m_result);
284 obj.insert("id", m_id);
285 break;
286 case MoleQueue::Message::Error: {
287 QJsonObject errorObject;
288 errorObject.insert("code", m_errorCode);
289 errorObject.insert("message", m_errorMessage);
290 if (!m_errorData.isNull())
291 errorObject.insert("data", m_errorData);
292
293 obj.insert("jsonrpc", QLatin1String("2.0"));
294 obj.insert("error", errorObject);
295 obj.insert("id", m_id);
296 }
297 break;
298 case MoleQueue::Message::Raw:
299 obj = m_rawJson;
300 break;
301 case MoleQueue::Message::Invalid:
302 qWarning() << "Cannot convert invalid message to a JSON object.";
303 break;
304 }
305 return obj;
306 }
307
toJson() const308 PacketType Message::toJson() const
309 {
310 QJsonDocument doc(toJsonObject());
311 return PacketType(doc.toJson());
312 }
313
send()314 bool Message::send()
315 {
316 if (m_type == Invalid || !m_connection || !m_connection->isOpen())
317 return false;
318
319 if (m_type == Request)
320 m_id = MessageIdManager::registerMethod(m_method);
321
322 return m_connection->send(toJson(), m_endpoint);
323 }
324
generateResponse() const325 Message Message::generateResponse() const
326 {
327 if (!checkType(Q_FUNC_INFO, Request))
328 return Message();
329
330 Message resp(Response, m_connection, m_endpoint);
331 resp.m_method = m_method;
332 resp.m_id = m_id;
333 return resp;
334 }
335
generateErrorResponse() const336 Message Message::generateErrorResponse() const
337 {
338 if (!checkType(Q_FUNC_INFO, Request | Raw | Invalid))
339 return Message();
340
341 Message resp(Error, m_connection, m_endpoint);
342 resp.m_method = m_method;
343 resp.m_id = m_id;
344 return resp;
345 }
346
parse()347 bool Message::parse()
348 {
349 Message message;
350 return parse(message);
351 }
352
parse(Message & errorMessage_)353 bool Message::parse(Message &errorMessage_)
354 {
355 // Can only parse Raw types -- return true if this message is already parsed
356 // or invalid.
357 if (m_type != Raw)
358 return true;
359
360 // Validate the message
361 QStringList errors;
362
363 // jsonrpc must equal "2.0"
364 if (!m_rawJson.contains("jsonrpc"))
365 errors << "jsonrpc key missing.";
366 if (!m_rawJson.value("jsonrpc").isString())
367 errors << "jsonrpc key must be a string.";
368 if (m_rawJson.value("jsonrpc").toString() != "2.0") {
369 errors << QString("Unrecognized jsonrpc string: %1")
370 .arg(m_rawJson.value("jsonrpc").toString());
371 }
372
373 // Must have either id or method
374 if (!m_rawJson.contains("id") && !m_rawJson.contains("method"))
375 errors << "Missing both id and method.";
376
377 // If method is present, it must be a string.
378 QString method_;
379 if (m_rawJson.contains("method")) {
380 if (!m_rawJson.value("method").isString())
381 errors << "method must be a string.";
382 else
383 method_ = m_rawJson.value("method").toString();
384 }
385 else {
386 // Lookup method for response/error.
387 method_ = MessageIdManager::lookupMethod(m_rawJson.value("id"));
388 }
389
390 // If any errors have occurred, prep the response:
391 if (!errors.empty()) {
392 errors.prepend("Invalid request:");
393 QJsonObject errorDataObject;
394 errorDataObject.insert("description", errors.join(" "));
395 errorDataObject.insert("request", m_rawJson);
396 errorMessage_ = generateErrorResponse();
397 errorMessage_.setErrorCode(-32600);
398 errorMessage_.setErrorMessage("Invalid request");
399 errorMessage_.setErrorData(errorDataObject);
400 return false;
401 }
402
403 // Results, errors, and notifications cannot return errors. Parse them
404 // as best we can and return true.
405 if (m_rawJson.contains("result")) {
406 interpretResponse(m_rawJson, method_);
407 return true;
408 }
409 else if (m_rawJson.contains("error")) {
410 interpretError(m_rawJson, method_);
411 return true;
412 }
413 else if (!m_rawJson.contains("id")) {
414 interpretNotification(m_rawJson);
415 return true;
416 }
417
418 // Assume anything else is a request.
419 return interpretRequest(m_rawJson, errorMessage_);
420 }
421
422 inline
checkType(const char * method_,MessageTypes validTypes) const423 bool Message::checkType(const char *method_, MessageTypes validTypes) const
424 {
425 if (m_type & validTypes)
426 return true;
427
428 qWarning() << "Invalid message type in call.\n"
429 << " Method:" << method_ << "\n"
430 << " Valid types:" << validTypes << "\n"
431 << " Actual type:" << m_type;
432 return false;
433 }
434
interpretRequest(const QJsonObject & json,Message & errorMessage_)435 bool Message::interpretRequest(const QJsonObject &json, Message &errorMessage_)
436 {
437 QStringList errors;
438
439 // method must exist and be a string.
440 if (!json.value("method").isString())
441 errors << "method is not a string.";
442
443 // id must be present.
444 if (!json.contains("id"))
445 errors << "id missing.";
446
447 // params is optional, but must be structured if present.
448 if (json.contains("params") &&
449 !json.value("params").isArray() &&
450 !json.value("params").isObject()) {
451 errors << "params must be structured if present.";
452 }
453
454 if (!errors.empty()) {
455 errors.prepend("Invalid request:");
456 QJsonObject errorDataObject;
457 errorDataObject.insert("description", errors.join(" "));
458 errorDataObject.insert("request", json);
459 errorMessage_ = generateErrorResponse();
460 errorMessage_.setErrorCode(-32600);
461 errorMessage_.setErrorMessage("Invalid request");
462 errorMessage_.setErrorData(errorDataObject);
463 return false;
464 }
465
466 m_type = Request;
467 m_method = json.value("method").toString();
468 if (json.contains("params"))
469 m_params = json.value("params");
470 else
471 m_params = QJsonValue();
472 m_id = MessageIdType(json.value("id"));
473 return true;
474 }
475
interpretNotification(const QJsonObject & json)476 void Message::interpretNotification(const QJsonObject &json)
477 {
478 m_type = Notification;
479 m_method = json.value("method").toString();
480 if (json.contains("params"))
481 m_params = json.value("params");
482 else
483 m_params = QJsonValue();
484 m_id = MessageIdType();
485 return;
486 }
487
interpretResponse(const QJsonObject & json,const QString & method_)488 void Message::interpretResponse(const QJsonObject &json, const QString &method_)
489 {
490 m_type = Response;
491 m_method = method_;
492 m_result = json.value("result");
493 m_id = json.value("id");
494 return;
495 }
496
interpretError(const QJsonObject & json,const QString & method_)497 void Message::interpretError(const QJsonObject &json, const QString &method_)
498 {
499 m_type = Error;
500 m_method = method_;
501 m_id = json.value("id");
502
503 // We cannot send an error reply if we receive a malformated error message.
504 // If this happens, generate a server error (-32000) with the original error
505 // member as the error data (there must be an error member defined for this
506 // function to be called)
507 QStringList errors;
508 QJsonValue errorValue = json.value("error");
509 if (!errorValue.isObject()) {
510 errors << "error must be an object.";
511 }
512 else {
513 QJsonObject errorObject = errorValue.toObject();
514
515 // error.code validation
516 if (!errorObject.contains("code")) {
517 errors << "error.code missing.";
518 }
519 else {
520 // Check that error.code is integral. There is no QJsonValue.isInt()
521 // method, only isDouble, so the test is more complicated than it should
522 // be...
523 if (!errorObject.value("code").isDouble()) {
524 errors << "error.code is not numeric.";
525 }
526 else {
527 double code = errorObject.value("code").toDouble();
528 if (qAbs(code - static_cast<double>(static_cast<int>(code))) > 1e-5)
529 errors << "error.code is not integral.";
530 else
531 m_errorCode = static_cast<int>(code);
532 }
533 }
534
535 // error.message validation
536 if (!errorObject.contains("message")) {
537 errors << "error.message missing.";
538 }
539 else {
540 if (!errorObject.value("message").isString())
541 errors << "error.message is not a string.";
542 else
543 m_errorMessage = errorObject.value("message").toString();
544 }
545
546 if (errorObject.contains("data"))
547 m_errorData = errorObject.value("data");
548 }
549
550 // If any errors occured, reset the error members to a server error.
551 if (!errors.empty()) {
552 m_errorCode = -32000;
553 m_errorMessage = "Server error";
554
555 QJsonObject errorDataObject;
556 errors.prepend("Malformed error response:");
557 errorDataObject.insert("description", errors.join(" "));
558 errorDataObject.insert("origMessage", errorValue);
559 m_errorData = errorDataObject;
560 }
561 }
562
563 } // namespace MoleQueue
564