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