1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qjsonrpcprotocol_p_p.h" |
5 | |
6 | #include <QtCore/qjsonarray.h> |
7 | #include <QtCore/qstring.h> |
8 | #include <QtCore/qjsonobject.h> |
9 | |
10 | #include <functional> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | using namespace Qt::StringLiterals; |
15 | |
16 | static QJsonObject createResponse(const QJsonValue &id, const QJsonRpcProtocol::Response &response) |
17 | { |
18 | QJsonObject object; |
19 | object.insert(key: u"jsonrpc" , value: u"2.0"_s ); |
20 | object.insert(key: u"id" , value: id); |
21 | if (response.errorCode.isDouble()) { |
22 | QJsonObject error; |
23 | error.insert(key: u"code" , value: response.errorCode); |
24 | error.insert(key: u"message" , value: response.errorMessage); |
25 | if (!response.data.isUndefined()) |
26 | error.insert(key: u"data" , value: response.data); |
27 | object.insert(key: u"error" , value: error); |
28 | } else { |
29 | object.insert(key: u"result" , value: response.data); |
30 | } |
31 | return object; |
32 | } |
33 | |
34 | static QJsonRpcProtocol::Response |
35 | createPredefinedError(QJsonRpcProtocol::ErrorCode code, |
36 | const QJsonValue &id = QJsonValue::Undefined) |
37 | { |
38 | QJsonRpcProtocol::Response response; |
39 | response.errorCode = static_cast<double>(code); |
40 | switch (code) { |
41 | case QJsonRpcProtocol::ErrorCode::ParseError: |
42 | response.errorMessage = u"Parse error"_s ; |
43 | break; |
44 | case QJsonRpcProtocol::ErrorCode::InvalidRequest: |
45 | response.errorMessage = u"Invalid Request"_s ; |
46 | break; |
47 | case QJsonRpcProtocol::ErrorCode::MethodNotFound: |
48 | response.errorMessage = u"Method not found"_s ; |
49 | break; |
50 | case QJsonRpcProtocol::ErrorCode::InvalidParams: |
51 | response.errorMessage = u"Invalid Parameters"_s ; |
52 | break; |
53 | case QJsonRpcProtocol::ErrorCode::InternalError: |
54 | response.errorMessage = u"Internal Error"_s ; |
55 | break; |
56 | } |
57 | response.id = id; |
58 | return response; |
59 | } |
60 | |
61 | std::exception_ptr createLocalInvalidRequestException(const QJsonValue &id = QJsonValue::Null) |
62 | { |
63 | return std::make_exception_ptr( |
64 | ex: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest, id)); |
65 | } |
66 | |
67 | static QJsonObject createParseErrorResponse() |
68 | { |
69 | return createResponse(id: QJsonValue::Null, |
70 | response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::ParseError)); |
71 | } |
72 | |
73 | static QJsonObject createInvalidRequestResponse(const QJsonValue &id = QJsonValue::Null) |
74 | { |
75 | return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest)); |
76 | } |
77 | |
78 | static QJsonObject createMethodNotFoundResponse(const QJsonValue &id) |
79 | { |
80 | return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::MethodNotFound)); |
81 | } |
82 | |
83 | template<typename Notification> |
84 | static QJsonObject createNotification(const Notification ¬ification) |
85 | { |
86 | QJsonObject object; |
87 | object.insert(key: u"jsonrpc" , value: u"2.0"_s ); |
88 | object.insert(u"method" , notification.method); |
89 | object.insert(u"params" , notification.params); |
90 | return object; |
91 | } |
92 | |
93 | class RequestBatchHandler |
94 | { |
95 | Q_DISABLE_COPY(RequestBatchHandler) |
96 | public: |
97 | RequestBatchHandler() = default; |
98 | RequestBatchHandler(RequestBatchHandler &&) noexcept = default; |
99 | RequestBatchHandler &operator=(RequestBatchHandler &&) noexcept = default; |
100 | ~RequestBatchHandler(); |
101 | |
102 | void processMessages(QJsonRpcProtocolPrivate *protocol, const QJsonArray &messages); |
103 | |
104 | private: |
105 | QJsonArray m_finished; |
106 | QJsonRpcTransport *m_transport = nullptr; |
107 | uint m_pending = 0; |
108 | }; |
109 | |
110 | QJsonRpcProtocol::QJsonRpcProtocol() : d(std::make_unique<QJsonRpcProtocolPrivate>()) { } |
111 | |
112 | QJsonRpcProtocol &QJsonRpcProtocol::operator=(QJsonRpcProtocol &&) noexcept = default; |
113 | QJsonRpcProtocol::QJsonRpcProtocol(QJsonRpcProtocol &&) noexcept = default; |
114 | QJsonRpcProtocol::~QJsonRpcProtocol() = default; |
115 | |
116 | void QJsonRpcProtocol::setMessageHandler(const QString &method, |
117 | QJsonRpcProtocol::MessageHandler *handler) |
118 | { |
119 | d->setMessageHandler(method, handler: std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler)); |
120 | } |
121 | |
122 | void QJsonRpcProtocol::setDefaultMessageHandler(QJsonRpcProtocol::MessageHandler *handler) |
123 | { |
124 | d->setDefaultMessageHandler(std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler)); |
125 | } |
126 | |
127 | QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::messageHandler(const QString &method) const |
128 | { |
129 | return d->messageHandler(method); |
130 | } |
131 | |
132 | QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::defaultMessageHandler() const |
133 | { |
134 | return d->defaultMessageHandler(); |
135 | } |
136 | |
137 | template<typename Request> |
138 | static QJsonObject createRequest(const Request &request) |
139 | { |
140 | QJsonObject object; |
141 | object.insert(key: u"jsonrpc" , value: u"2.0"_s ); |
142 | object.insert(u"id" , request.id); |
143 | object.insert(u"method" , request.method); |
144 | object.insert(u"params" , request.params); |
145 | return object; |
146 | } |
147 | |
148 | void QJsonRpcProtocol::sendRequest(const Request &request, |
149 | const QJsonRpcProtocol::Handler<Response> &handler) |
150 | { |
151 | switch (request.id.type()) { |
152 | case QJsonValue::Null: |
153 | case QJsonValue::Double: |
154 | case QJsonValue::String: |
155 | if (d->addPendingRequest(id: request.id, handler)) { |
156 | d->sendMessage(value: createRequest(request)); |
157 | return; |
158 | } |
159 | default: |
160 | break; |
161 | } |
162 | |
163 | handler(createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest, id: request.id)); |
164 | } |
165 | |
166 | void QJsonRpcProtocol::sendNotification(const QJsonRpcProtocol::Notification ¬ification) |
167 | { |
168 | d->sendMessage(value: createNotification(notification)); |
169 | } |
170 | |
171 | void QJsonRpcProtocol::sendBatch( |
172 | QJsonRpcProtocol::Batch &&batch, |
173 | const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &handler) |
174 | { |
175 | QJsonArray array; |
176 | for (BatchPrivate::Item &item : batch.d->m_items) { |
177 | if (item.id.isUndefined()) { |
178 | array.append(value: createNotification(notification: item)); |
179 | } else { |
180 | switch (item.id.type()) { |
181 | case QJsonValue::Null: |
182 | case QJsonValue::Double: |
183 | case QJsonValue::String: |
184 | if (d->addPendingRequest(id: item.id, handler)) { |
185 | array.append(value: createRequest(request: item)); |
186 | break; |
187 | } |
188 | Q_FALLTHROUGH(); |
189 | default: |
190 | handler(createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest, |
191 | id: item.id)); |
192 | break; |
193 | } |
194 | } |
195 | } |
196 | if (!array.isEmpty()) |
197 | d->sendMessage(value: array); |
198 | } |
199 | |
200 | void QJsonRpcProtocol::setTransport(QJsonRpcTransport *transport) |
201 | { |
202 | d->setTransport(transport); |
203 | } |
204 | |
205 | void QJsonRpcProtocol::setProtocolErrorHandler(const QJsonRpcProtocol::ResponseHandler &handler) |
206 | { |
207 | d->setProtocolErrorHandler(handler); |
208 | } |
209 | |
210 | QJsonRpcProtocol::ResponseHandler QJsonRpcProtocol::protocolErrorHandler() const |
211 | { |
212 | return d->protocolErrorHandler(); |
213 | } |
214 | |
215 | void QJsonRpcProtocol::setInvalidResponseHandler(const QJsonRpcProtocol::ResponseHandler &handler) |
216 | { |
217 | d->setInvalidResponseHandler(handler); |
218 | } |
219 | |
220 | QJsonRpcProtocol::ResponseHandler QJsonRpcProtocol::invalidResponseHandler() const |
221 | { |
222 | return d->invalidResponseHandler(); |
223 | } |
224 | |
225 | /*! |
226 | * \internal |
227 | * \typealias QJsonRpcProtocol::MessagePreprocessor |
228 | * \brief A function preprocessing incoming messages |
229 | * |
230 | * A type representing a function receiving a const QJsonDocument &doc, const QJsonParseError |
231 | * &error, const QJsonRpcProtocol::Handler<Response> &responseHandler) and returning either |
232 | * Processing::Continue or Processing::Stop. Handler can be used to return a response when |
233 | * Processing::Stop is returned and doc is a request, i.e. doc.object().contains(u"id") is true. |
234 | */ |
235 | |
236 | QJsonRpcProtocol::MessagePreprocessor QJsonRpcProtocol::messagePreprocessor() const |
237 | { |
238 | return d->messagePreprocessor(); |
239 | } |
240 | |
241 | void QJsonRpcProtocol::installMessagePreprocessor(const QJsonRpcProtocol::MessagePreprocessor &h) |
242 | { |
243 | return d->installMessagePreprocessor(preHandler: h); |
244 | } |
245 | |
246 | void QJsonRpcProtocolPrivate::setTransport(QJsonRpcTransport *newTransport) |
247 | { |
248 | if (newTransport == m_transport) |
249 | return; |
250 | |
251 | if (m_transport) |
252 | m_transport->setMessageHandler(nullptr); |
253 | |
254 | m_transport = newTransport; |
255 | |
256 | if (m_transport) { |
257 | m_transport->setMessageHandler( |
258 | [this](const QJsonDocument &message, const QJsonParseError &error) { |
259 | processMessage(message, error); |
260 | }); |
261 | } |
262 | } |
263 | |
264 | static QJsonRpcProtocol::Request parseRequest(const QJsonObject &object) |
265 | { |
266 | QJsonRpcProtocol::Request request; |
267 | request.id = object.value(key: u"id" ); |
268 | request.method = object.value(key: u"method" ).toString(); |
269 | request.params = object.value(key: u"params" ); |
270 | return request; |
271 | } |
272 | |
273 | static QJsonRpcProtocol::Notification parseNotification(const QJsonObject &object) |
274 | { |
275 | QJsonRpcProtocol::Notification notification; |
276 | notification.method = object.value(key: u"method" ).toString(); |
277 | notification.params = object.value(key: u"params" ); |
278 | return notification; |
279 | } |
280 | |
281 | void RequestBatchHandler::processMessages(QJsonRpcProtocolPrivate *protocol, |
282 | const QJsonArray &messages) |
283 | { |
284 | m_transport = protocol->transport(); |
285 | |
286 | for (const QJsonValue &value : messages) { |
287 | if (!value.isObject()) { |
288 | m_finished.append(value: createInvalidRequestResponse()); |
289 | continue; |
290 | } |
291 | |
292 | QJsonObject object = value.toObject(); |
293 | |
294 | if (!object.contains(key: u"method" ) || !object.value(key: u"method" ).isString()) { |
295 | m_finished.append(value: createInvalidRequestResponse()); |
296 | continue; |
297 | } |
298 | |
299 | if (!object.contains(key: u"id" )) { |
300 | QJsonRpcProtocol::Notification notification = parseNotification(object); |
301 | if (QJsonRpcProtocol::MessageHandler *handler = |
302 | protocol->messageHandler(method: notification.method)) { |
303 | handler->handleNotification(notification); |
304 | } |
305 | continue; |
306 | } |
307 | |
308 | QJsonRpcProtocol::Request request = parseRequest(object); |
309 | QJsonRpcProtocol::MessageHandler *handler = protocol->messageHandler(method: request.method); |
310 | if (!handler) { |
311 | m_finished.append(value: createMethodNotFoundResponse(id: request.id)); |
312 | continue; |
313 | } |
314 | |
315 | ++m_pending; |
316 | const QJsonValue id = request.id; |
317 | handler->handleRequest(request, handler: [this, id](const QJsonRpcProtocol::Response &response) { |
318 | m_finished.append(value: createResponse(id, response)); |
319 | bool found = false; |
320 | for (QJsonValueRef entry : m_finished) { |
321 | if (entry.toObject()[u"id" ] == id) { |
322 | found = true; |
323 | break; |
324 | } |
325 | } |
326 | if (!found) { |
327 | m_finished.append(value: createResponse( |
328 | id, |
329 | response: { .id: id, .data: QJsonValue::Undefined, |
330 | .errorCode: QJsonValue(static_cast<int>(QJsonRpcProtocol::ErrorCode::InternalError)), |
331 | .errorMessage: u"Message handler did not produce a result."_s })); |
332 | } |
333 | |
334 | if (--m_pending == 0) |
335 | delete this; |
336 | }); |
337 | } |
338 | if (m_pending == 0) |
339 | delete this; |
340 | } |
341 | |
342 | RequestBatchHandler::~RequestBatchHandler() |
343 | { |
344 | if (m_transport && !m_finished.isEmpty()) |
345 | m_transport->sendMessage(packet: QJsonDocument(m_finished)); |
346 | } |
347 | |
348 | void QJsonRpcProtocolPrivate::processRequest(const QJsonObject &object) |
349 | { |
350 | QJsonRpcProtocol::Request request = parseRequest(object); |
351 | if (auto handler = messageHandler(method: request.method)) { |
352 | const QJsonValue id = request.id; |
353 | handler->handleRequest(request, handler: [id, this](const QJsonRpcProtocol::Response &response) { |
354 | sendMessage(value: createResponse(id, response)); |
355 | }); |
356 | } else { |
357 | sendMessage(value: createMethodNotFoundResponse(id: request.id)); |
358 | } |
359 | } |
360 | |
361 | void QJsonRpcProtocolPrivate::processResponse(const QJsonObject &object) |
362 | { |
363 | QJsonRpcProtocol::Response response; |
364 | |
365 | response.id = object.value(key: u"id" ); |
366 | if (object.contains(key: u"error" )) { |
367 | const QJsonObject error = object.value(key: u"error" ).toObject(); |
368 | response.errorCode = error.value(key: u"code" ); |
369 | response.errorMessage = error.value(key: u"message" ).toString(); |
370 | response.data = error.value(key: u"data" ); |
371 | } else if (object.contains(key: u"result" )) { |
372 | response.data = object.value(key: u"result" ); |
373 | } |
374 | |
375 | auto pending = m_pendingRequests.find(x: response.id); |
376 | if (pending != m_pendingRequests.end()) { |
377 | auto handler = pending->second; |
378 | m_pendingRequests.erase(position: pending); |
379 | handler(response); |
380 | } else if (response.id.isNull()) { |
381 | if (m_protocolErrorHandler) |
382 | m_protocolErrorHandler(response); |
383 | } else { |
384 | if (m_invalidResponseHandler) |
385 | m_invalidResponseHandler(response); |
386 | } |
387 | } |
388 | |
389 | void QJsonRpcProtocolPrivate::processNotification(const QJsonObject &object) |
390 | { |
391 | QJsonRpcProtocol::Notification notification = parseNotification(object); |
392 | if (auto handler = messageHandler(method: notification.method)) |
393 | handler->handleNotification(notification); |
394 | } |
395 | |
396 | void QJsonRpcProtocolPrivate::processMessage(const QJsonDocument &message, |
397 | const QJsonParseError &error) |
398 | { |
399 | if (m_messagePreprocessor |
400 | && m_messagePreprocessor(message, error, |
401 | [message, this](const QJsonRpcProtocol::Response &r) { |
402 | Q_ASSERT(message.object().contains(u"id" )); |
403 | this->sendMessage(value: createResponse(id: message.object()[u"id" ], response: r)); |
404 | }) |
405 | != QJsonRpcProtocol::Processing::Continue) { |
406 | return; |
407 | } |
408 | if (error.error != QJsonParseError::NoError) { |
409 | sendMessage(value: createParseErrorResponse()); |
410 | } else if (message.isObject()) { |
411 | const QJsonObject object = message.object(); |
412 | if (object.contains(key: u"method" )) { |
413 | if (!object.value(key: u"method" ).isString()) { |
414 | sendMessage(value: createInvalidRequestResponse()); |
415 | } else if (object.contains(key: u"id" )) { |
416 | switch (object.value(key: u"id" ).type()) { |
417 | case QJsonValue::Null: |
418 | case QJsonValue::Double: |
419 | case QJsonValue::String: |
420 | processRequest(object); |
421 | break; |
422 | default: |
423 | sendMessage(value: createInvalidRequestResponse()); |
424 | break; |
425 | } |
426 | } else { |
427 | processNotification(object); |
428 | } |
429 | } else if (object.contains(key: u"id" )) { |
430 | processResponse(object); |
431 | } else { |
432 | sendMessage(value: createInvalidRequestResponse()); |
433 | } |
434 | } else if (message.isArray()) { |
435 | const QJsonArray array = message.array(); |
436 | if (array.isEmpty()) { |
437 | sendMessage(value: createInvalidRequestResponse()); |
438 | return; |
439 | } |
440 | |
441 | for (const QJsonValue &item : array) { |
442 | if (!item.isObject()) { |
443 | (new RequestBatchHandler)->processMessages(protocol: this, messages: array); // Will delete itself |
444 | return; |
445 | } |
446 | |
447 | const QJsonObject object = item.toObject(); |
448 | // If it's not clearly a response, consider the whole batch as request-y |
449 | if (object.contains(key: u"method" ) || !object.contains(key: u"id" )) { |
450 | (new RequestBatchHandler)->processMessages(protocol: this, messages: array); // Will delete itself |
451 | return; |
452 | } |
453 | } |
454 | |
455 | // As we haven't returned above, we can consider them all responseses now. |
456 | for (const QJsonValue &item : array) |
457 | processResponse(object: item.toObject()); |
458 | |
459 | } else { |
460 | sendMessage(value: createInvalidRequestResponse()); |
461 | } |
462 | } |
463 | |
464 | QJsonRpcProtocol::MessageHandler::MessageHandler() = default; |
465 | QJsonRpcProtocol::MessageHandler::~MessageHandler() = default; |
466 | |
467 | void QJsonRpcProtocol::MessageHandler::handleRequest(const QJsonRpcProtocol::Request &request, |
468 | const ResponseHandler &handler) |
469 | { |
470 | Q_UNUSED(request); |
471 | handler(error(code: QJsonRpcProtocol::ErrorCode::MethodNotFound)); |
472 | } |
473 | |
474 | void QJsonRpcProtocol::MessageHandler::handleNotification( |
475 | const QJsonRpcProtocol::Notification ¬ification) |
476 | { |
477 | Q_UNUSED(notification); |
478 | } |
479 | |
480 | QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::error(QJsonRpcProtocol::ErrorCode code) |
481 | { |
482 | return createPredefinedError(code); |
483 | } |
484 | |
485 | QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::error(int code, const QString &message, |
486 | const QJsonValue &data) |
487 | { |
488 | QJsonRpcProtocol::Response response; |
489 | response.errorCode = code; |
490 | response.errorMessage = message; |
491 | response.data = data; |
492 | return response; |
493 | } |
494 | |
495 | QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::result(const QJsonValue &result) |
496 | { |
497 | QJsonRpcProtocol::Response response; |
498 | response.data = result; |
499 | return response; |
500 | } |
501 | |
502 | QJsonRpcProtocol::Batch::Batch() : d(std::make_unique<BatchPrivate>()) { } |
503 | QJsonRpcProtocol::Batch::~Batch() = default; |
504 | QJsonRpcProtocol::Batch::Batch(QJsonRpcProtocol::Batch &&) noexcept = default; |
505 | QJsonRpcProtocol::Batch & |
506 | QJsonRpcProtocol::Batch::operator=(QJsonRpcProtocol::Batch &&) noexcept = default; |
507 | |
508 | void QJsonRpcProtocol::Batch::addNotification(const Notification ¬ification) |
509 | { |
510 | BatchPrivate::Item item; |
511 | item.method = notification.method; |
512 | item.params = notification.params; |
513 | d->m_items.push_back(x: std::move(item)); |
514 | } |
515 | |
516 | void QJsonRpcProtocol::Batch::addRequest(const Request &request) |
517 | { |
518 | BatchPrivate::Item item; |
519 | item.id = request.id; |
520 | item.method = request.method; |
521 | item.params = request.params; |
522 | d->m_items.push_back(x: std::move(item)); |
523 | } |
524 | |
525 | QT_END_NAMESPACE |
526 | |