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 break;
154 default:
155 break;
156 }
157
158 handler(createPredefinedError(code: QJsonRpcProtocol::ErrorCode::InvalidRequest, id: request.id));
159}
160
161void QJsonRpcProtocol::sendNotification(const QJsonRpcProtocol::Notification &notification)
162{
163 d->sendMessage(value: createNotification(notification));
164}
165
166void 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
195void QJsonRpcProtocol::setTransport(QJsonRpcTransport *transport)
196{
197 d->setTransport(transport);
198}
199
200void QJsonRpcProtocol::setProtocolErrorHandler(const QJsonRpcProtocol::ResponseHandler &handler)
201{
202 d->setProtocolErrorHandler(handler);
203}
204
205QJsonRpcProtocol::ResponseHandler QJsonRpcProtocol::protocolErrorHandler() const
206{
207 return d->protocolErrorHandler();
208}
209
210void QJsonRpcProtocol::setInvalidResponseHandler(const QJsonRpcProtocol::ResponseHandler &handler)
211{
212 d->setInvalidResponseHandler(handler);
213}
214
215QJsonRpcProtocol::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
231QJsonRpcProtocol::MessagePreprocessor QJsonRpcProtocol::messagePreprocessor() const
232{
233 return d->messagePreprocessor();
234}
235
236void QJsonRpcProtocol::installMessagePreprocessor(const QJsonRpcProtocol::MessagePreprocessor &h)
237{
238 return d->installMessagePreprocessor(preHandler: h);
239}
240
241void 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
259static 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
268static 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
276void 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
337RequestBatchHandler::~RequestBatchHandler()
338{
339 if (m_transport && !m_finished.isEmpty())
340 m_transport->sendMessage(packet: QJsonDocument(m_finished));
341}
342
343void 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
356void 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
384void 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
391void 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
459QJsonRpcProtocol::MessageHandler::MessageHandler() = default;
460QJsonRpcProtocol::MessageHandler::~MessageHandler() = default;
461
462void 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
469void QJsonRpcProtocol::MessageHandler::handleNotification(
470 const QJsonRpcProtocol::Notification &notification)
471{
472 Q_UNUSED(notification);
473}
474
475QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::error(QJsonRpcProtocol::ErrorCode code)
476{
477 return createPredefinedError(code);
478}
479
480QJsonRpcProtocol::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
490QJsonRpcProtocol::Response QJsonRpcProtocol::MessageHandler::result(const QJsonValue &result)
491{
492 QJsonRpcProtocol::Response response;
493 response.data = result;
494 return response;
495}
496
497QJsonRpcProtocol::Batch::Batch() : d(std::make_unique<BatchPrivate>()) { }
498QJsonRpcProtocol::Batch::~Batch() = default;
499QJsonRpcProtocol::Batch::Batch(QJsonRpcProtocol::Batch &&) noexcept = default;
500QJsonRpcProtocol::Batch &
501QJsonRpcProtocol::Batch::operator=(QJsonRpcProtocol::Batch &&) noexcept = default;
502
503void QJsonRpcProtocol::Batch::addNotification(const Notification &notification)
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
511void 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
520QT_END_NAMESPACE
521

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