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 "qlanguageserverjsonrpctransport_p.h"
5
6#include <QtCore/QtGlobal>
7
8#include <iostream>
9
10QT_BEGIN_NAMESPACE
11
12using namespace Qt::StringLiterals;
13
14static const QByteArray s_contentLengthFieldName = "Content-Length";
15static const QByteArray s_contentTypeFieldName = "Content-Type";
16static const QByteArray s_fieldSeparator = ": ";
17static const QByteArray s_headerSeparator = "\r\n";
18static const QByteArray s_headerEnd = "\r\n\r\n";
19static const QByteArray s_utf8 = "utf-8";
20static const QByteArray s_brokenUtf8 = "utf8";
21
22QLanguageServerJsonRpcTransport::QLanguageServerJsonRpcTransport() noexcept
23 : m_messageStreamParser(
24 [this](const QByteArray &field, const QByteArray &value) { hasHeader(field, value); },
25 [this](const QByteArray &body) { hasBody(body); },
26 [this](QtMsgType error, QString msg) {
27 if (auto handler = diagnosticHandler()) {
28 if (error == QtWarningMsg || error == QtInfoMsg || error == QtDebugMsg)
29 handler(Warning, msg);
30 else
31 handler(Error, msg);
32 }
33 })
34{
35}
36
37void QLanguageServerJsonRpcTransport::sendMessage(const QJsonDocument &packet)
38{
39 const QByteArray content = packet.toJson(format: QJsonDocument::Compact);
40 if (auto handler = dataHandler()) {
41 // send all data in one go, this way if handler is threadsafe the whole sendMessage is
42 // threadsafe
43 QByteArray msg;
44 msg.append(a: s_contentLengthFieldName);
45 msg.append(a: s_fieldSeparator);
46 msg.append(a: QByteArray::number(content.size()));
47 msg.append(a: s_headerSeparator);
48 msg.append(a: s_headerSeparator);
49 msg.append(a: content);
50 handler(msg);
51 }
52}
53
54void QLanguageServerJsonRpcTransport::receiveData(const QByteArray &data)
55{
56 m_messageStreamParser.receiveData(data);
57}
58
59void QLanguageServerJsonRpcTransport::hasHeader(const QByteArray &fieldName,
60 const QByteArray &fieldValue)
61{
62 if (s_contentLengthFieldName.compare(a: fieldName, cs: Qt::CaseInsensitive) == 0) {
63 // already handled by parser
64 } else if (s_contentTypeFieldName.compare(a: fieldName, cs: Qt::CaseInsensitive) == 0) {
65 if (fieldValue != s_utf8 && fieldValue != s_brokenUtf8) {
66 if (auto handler = diagnosticHandler()) {
67 handler(Warning,
68 QString::fromLatin1(ba: "Invalid %1: %2")
69 .arg(a: QString::fromUtf8(ba: fieldName))
70 .arg(a: QString::fromUtf8(ba: fieldValue)));
71 }
72 }
73 } else if (auto handler = diagnosticHandler()) {
74 handler(Warning,
75 QString::fromLatin1(ba: "Unknown header field: %1").arg(a: QString::fromUtf8(ba: fieldName)));
76 }
77}
78
79void QLanguageServerJsonRpcTransport::hasBody(const QByteArray &body)
80{
81 QJsonParseError error = { .offset: 0, .error: QJsonParseError::NoError };
82 const QJsonDocument doc = QJsonDocument::fromJson(json: body, error: &error);
83
84 if (error.error != QJsonParseError::NoError) {
85 if (auto handler = diagnosticHandler()) {
86 handler(Error,
87 u"Error %1 decoding json: %2"_s.arg(a: int(error.error))
88 .arg(a: error.errorString()));
89 }
90 }
91 if (auto handler = messageHandler()) {
92 handler(doc, error);
93 }
94}
95
96QT_END_NAMESPACE
97

source code of qtlanguageserver/src/languageserver/qlanguageserverjsonrpctransport.cpp