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