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 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | using namespace Qt::StringLiterals; |
13 | |
14 | static const QByteArray s_contentLengthFieldName = "Content-Length" ; |
15 | static const QByteArray s_contentTypeFieldName = "Content-Type" ; |
16 | static const QByteArray s_fieldSeparator = ": " ; |
17 | static const QByteArray = "\r\n" ; |
18 | static const QByteArray = "\r\n\r\n" ; |
19 | static const QByteArray s_utf8 = "utf-8" ; |
20 | static const QByteArray s_brokenUtf8 = "utf8" ; |
21 | |
22 | QLanguageServerJsonRpcTransport::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 | |
37 | void 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 | |
54 | void QLanguageServerJsonRpcTransport::receiveData(const QByteArray &data) |
55 | { |
56 | m_messageStreamParser.receiveData(data); |
57 | } |
58 | |
59 | void QLanguageServerJsonRpcTransport::(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 | |
79 | void 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 | |
96 | QT_END_NAMESPACE |
97 | |