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
61std::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
67static QJsonObject createParseErrorResponse()
68{
69 return createResponse(id: QJsonValue::Null,
70 response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::ParseError));
71}
72
73static QJsonObject createInvalidRequestResponse(const QJsonValue &id = QJsonValue::Null)
74{
75 return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest));
76}
77
78static QJsonObject createMethodNotFoundResponse(const QJsonValue &id)
79{
80 return createResponse(id, response: createPredefinedError(code: QJsonRpcProtocol::ErrorCode::MethodNotFound));
81}
82
83template<typename Notification>
84static QJsonObject createNotification(const Notification &notification)
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
93class RequestBatchHandler
94{
95 Q_DISABLE_COPY(RequestBatchHandler)
96public:
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
104private:
105 QJsonArray m_finished;
106 QJsonRpcTransport *m_transport = nullptr;
107 uint m_pending = 0;
108};
109
110QJsonRpcProtocol::QJsonRpcProtocol() : d(std::make_unique<QJsonRpcProtocolPrivate>()) { }
111
112QJsonRpcProtocol &QJsonRpcProtocol::operator=(QJsonRpcProtocol &&) noexcept = default;
113QJsonRpcProtocol::QJsonRpcProtocol(QJsonRpcProtocol &&) noexcept = default;
114QJsonRpcProtocol::~QJsonRpcProtocol() = default;
115
116void QJsonRpcProtocol::setMessageHandler(const QString &method,
117 QJsonRpcProtocol::MessageHandler *handler)
118{
119 d->setMessageHandler(method, handler: std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler));
120}
121
122void QJsonRpcProtocol::setDefaultMessageHandler(QJsonRpcProtocol::MessageHandler *handler)
123{
124 d->setDefaultMessageHandler(std::unique_ptr<QJsonRpcProtocol::MessageHandler>(handler));
125}
126
127QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::messageHandler(const QString &method) const
128{
129 return d->messageHandler(method);
130}
131
132QJsonRpcProtocol::MessageHandler *QJsonRpcProtocol::defaultMessageHandler() const
133{
134 return d->defaultMessageHandler();
135}
136
137template<typename Request>
138static 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
148void 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
166void QJsonRpcProtocol::sendNotification(const QJsonRpcProtocol::Notification &notification)
167{
168 d->sendMessage(value: createNotification(notification));
169}
170
171void 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
200void QJsonRpcProtocol::setTransport(QJsonRpcTransport *transport)
201{
202 d->setTransport(transport);
203}
204
205void QJsonRpcProtocol::setProtocolErrorHandler(const QJsonRpcProtocol::ResponseHandler &handler)
206{
207 d->setProtocolErrorHandler(handler);
208}
209
210QJsonRpcProtocol::ResponseHandler QJsonRpcProtocol::protocolErrorHandler() const
211{
212 return d->protocolErrorHandler();
213}
214
215void QJsonRpcProtocol::setInvalidResponseHandler(const QJsonRpcProtocol::ResponseHandler &handler)
216{
217 d->setInvalidResponseHandler(handler);
218}
219
220QJsonRpcProtocol::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
236QJsonRpcProtocol::MessagePreprocessor QJsonRpcProtocol::messagePreprocessor() const
237{
238 return d->messagePreprocessor();
239}
240
241void QJsonRpcProtocol::installMessagePreprocessor(const QJsonRpcProtocol::MessagePreprocessor &h)
242{
243 return d->installMessagePreprocessor(preHandler: h);
244}
245
246void 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
264static 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
273static 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
281void 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
342RequestBatchHandler::~RequestBatchHandler()
343{
344 if (m_transport && !m_finished.isEmpty())
345 m_transport->sendMessage(packet: QJsonDocument(m_finished));
346}
347
348void 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
361void 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
389void 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
396void 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
464QJsonRpcProtocol::MessageHandler::MessageHandler() = default;
465QJsonRpcProtocol::MessageHandler::~MessageHandler() = default;
466
467void 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
474void QJsonRpcProtocol::MessageHandler::handleNotification(
475 const QJsonRpcProtocol::Notification &notification)
476{
477 Q_UNUSED(notification);
478}
479
480QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::error(QJsonRpcProtocol::ErrorCode code)
481{
482 return createPredefinedError(code);
483}
484
485QJsonRpcProtocol::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
495QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::result(const QJsonValue &result)
496{
497 QJsonRpcProtocol::Response response;
498 response.data = result;
499 return response;
500}
501
502QJsonRpcProtocol::Batch::Batch() : d(std::make_unique<BatchPrivate>()) { }
503QJsonRpcProtocol::Batch::~Batch() = default;
504QJsonRpcProtocol::Batch::Batch(QJsonRpcProtocol::Batch &&) noexcept = default;
505QJsonRpcProtocol::Batch &
506QJsonRpcProtocol::Batch::operator=(QJsonRpcProtocol::Batch &&) noexcept = default;
507
508void QJsonRpcProtocol::Batch::addNotification(const Notification &notification)
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
516void 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
525QT_END_NAMESPACE
526

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