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
12QT_BEGIN_NAMESPACE
13
14using namespace Qt::StringLiterals;
15
16static 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
34static QJsonRpcProtocol::Response
35createPredefinedError(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
61static QJsonObject createParseErrorResponse()
62{
63 return createResponse(id: QJsonValue::Null,
64 response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::ParseError));
65}
66
67static QJsonObject createInvalidRequestResponse(const QJsonValue &id = QJsonValue::Null)
68{
69 return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest));
70}
71
72static QJsonObject createMethodNotFoundResponse(const QJsonValue &id)
73{
74 return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::MethodNotFound));
75}
76
77template<typename Notification>
78static QJsonObject createNotification(const Notification &notification)
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
87class RequestBatchHandler
88{
89 Q_DISABLE_COPY(RequestBatchHandler)
90public:
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
98private:
99 QJsonArray m_finished;
100 QJsonRpcTransport *m_transport = nullptr;
101 uint m_pending = 0;
102};
103
104QJsonRpcProtocol::QJsonRpcProtocol() : d(std::make_unique<QJsonRpcProtocolPrivate>()) { }
105
106QJsonRpcProtocol &QJsonRpcProtocol::operator=(QJsonRpcProtocol &&) noexcept = default;
107QJsonRpcProtocol::QJsonRpcProtocol(QJsonRpcProtocol &&) noexcept = default;
108QJsonRpcProtocol::~QJsonRpcProtocol() = default;
109
110void QJsonRpcProtocol::setMessageHandler(const QString &method,
111 QJsonRpcProtocol::MessageHandler *handler)
112{
113 d->setMessageHandler(method, handler: std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler));
114}
115
116void QJsonRpcProtocol::setDefaultMessageHandler(QJsonRpcProtocol::MessageHandler *handler)
117{
118 d->setDefaultMessageHandler(std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler));
119}
120
121QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::messageHandler(const QString &method) const
122{
123 return d->messageHandler(method);
124}
125
126QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::defaultMessageHandler() const
127{
128 return d->defaultMessageHandler();
129}
130
131template<typename Request>
132static 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
142void 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
160void QJsonRpcProtocol::sendNotification(const QJsonRpcProtocol::Notification &notification)
161{
162 d->sendMessage(value: createNotification(notification));
163}
164
165void 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
194void QJsonRpcProtocol::setTransport(QJsonRpcTransport *transport)
195{
196 d->setTransport(transport);
197}
198
199void QJsonRpcProtocol::setProtocolErrorHandler(const QJsonRpcProtocol::ResponseHandler &handler)
200{
201 d->setProtocolErrorHandler(handler);
202}
203
204QJsonRpcProtocol::ResponseHandler QJsonRpcProtocol::protocolErrorHandler() const
205{
206 return d->protocolErrorHandler();
207}
208
209void QJsonRpcProtocol::setInvalidResponseHandler(const QJsonRpcProtocol::ResponseHandler &handler)
210{
211 d->setInvalidResponseHandler(handler);
212}
213
214QJsonRpcProtocol::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
230QJsonRpcProtocol::MessagePreprocessor QJsonRpcProtocol::messagePreprocessor() const
231{
232 return d->messagePreprocessor();
233}
234
235void QJsonRpcProtocol::installMessagePreprocessor(const QJsonRpcProtocol::MessagePreprocessor &h)
236{
237 return d->installMessagePreprocessor(preHandler: h);
238}
239
240void 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
258static 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
267static 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
275void 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
336RequestBatchHandler::~RequestBatchHandler()
337{
338 if (m_transport && !m_finished.isEmpty())
339 m_transport->sendMessage(packet: QJsonDocument(m_finished));
340}
341
342void 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
355void 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
383void 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
390void 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
458QJsonRpcProtocol::MessageHandler::MessageHandler() = default;
459QJsonRpcProtocol::MessageHandler::~MessageHandler() = default;
460
461void 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
468void QJsonRpcProtocol::MessageHandler::handleNotification(
469 const QJsonRpcProtocol::Notification &notification)
470{
471 Q_UNUSED(notification);
472}
473
474QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::error(QJsonRpcProtocol::ErrorCode code)
475{
476 return createPredefinedError(code);
477}
478
479QJsonRpcProtocol::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
489QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::result(const QJsonValue &result)
490{
491 QJsonRpcProtocol::Response response;
492 response.data = result;
493 return response;
494}
495
496QJsonRpcProtocol::Batch::Batch() : d(std::make_unique<BatchPrivate>()) { }
497QJsonRpcProtocol::Batch::~Batch() = default;
498QJsonRpcProtocol::Batch::Batch(QJsonRpcProtocol::Batch &&) noexcept = default;
499QJsonRpcProtocol::Batch &
500QJsonRpcProtocol::Batch::operator=(QJsonRpcProtocol::Batch &&) noexcept = default;
501
502void QJsonRpcProtocol::Batch::addNotification(const Notification &notification)
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
510void 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
519QT_END_NAMESPACE
520

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtlanguageserver/src/jsonrpc/qjsonrpcprotocol.cpp