1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "serverapplication.h"
6
7#include "common/lsp/protocol/newprotocol.h"
8
9#include <QTimer>
10#include <QProcess>
11#include <QJsonObject>
12#include <QJsonDocument>
13#include <QDebug>
14#include <QDateTime>
15
16#include <iostream>
17
18namespace newlsp {
19
20static ServerApplication *_globalServer;
21class ServerApplicationPrivate
22{
23 friend class ServerApplication;
24 StdinJsonRpcParser *stdinParser{nullptr};
25 QTimer *globalPPidWatcher;
26};
27
28ServerApplication::~ServerApplication()
29{
30 _globalServer = nullptr;
31
32 if (d) {
33 if (d->stdinParser) {
34 d->stdinParser->quit();
35 delete d->stdinParser;
36 }
37 if (d->globalPPidWatcher) {
38 d->globalPPidWatcher->stop();
39 }
40 delete d;
41 }
42}
43
44void ServerApplication::start()
45{
46 _globalServer = this;
47 QObject::connect(qApp, &QCoreApplication::destroyed, qApp, [=](){
48 delete this;
49 });
50 if (ServerCmdParse::parentPid()) {
51 unsigned int ppid = ServerCmdParse::parentPid().value();
52 d->globalPPidWatcher = new QTimer;
53 d->globalPPidWatcher->setInterval(300);
54 QObject::connect(d->globalPPidWatcher, &QTimer::timeout, [=](){
55 QProcess process;
56#ifdef __linux__
57 process.setProgram("ps");
58 process.setArguments({"--pid", QString::number(ppid)});
59 process.start();
60 process.waitForFinished();
61 QByteArray queryPidLines = process.readAllStandardOutput();
62 if (queryPidLines.isEmpty() || queryPidLines.count('\n') < 2) {
63 lspServErr << "not found parent pid:"
64 << std::to_string(ppid)
65 << "qApp quit";
66 exit(1001);
67 }
68#endif
69 });
70 d->globalPPidWatcher->start();
71 }
72
73 auto doStdioLauch = [=](){
74 if (!d->stdinParser) {
75 d->stdinParser = new StdinJsonRpcParser;
76 QObject::connect(d->stdinParser, &StdinJsonRpcParser::readedJsonObject,
77 this, &ServerApplication::identifyJsonObject,
78 Qt::ConnectionType::DirectConnection);
79 d->stdinParser->start();
80 }
81 lspServOut << "Initialization succeeded, mode's stdio";
82 };
83
84 if (ServerCmdParse::mode()) {
85 std::string mode = ServerCmdParse::mode().value();
86 if (mode == newlsp::tcp.toStdString()) {
87 lspServErr << "This function is not implemented yet. "
88 "It is started in stdio mode by default";
89 }
90 if (ServerCmdParse::mode() == newlsp::stdio.toStdString()) {
91 doStdioLauch();
92 }
93 }
94 doStdioLauch();
95}
96
97ServerApplication::ServerApplication(const QCoreApplication &app)
98 : ServerCmdParse (app)
99 , d (new ServerApplicationPrivate())
100{
101
102}
103
104ServerApplication *ServerApplication::ins()
105{
106 return _globalServer;
107}
108
109ServerApplication::Stderr ServerApplication::err(const std::vector<std::string> &perfixs)
110{
111 return Stderr(perfixs);
112}
113
114ServerApplication::Stdout ServerApplication::out(const std::vector<std::string> &perfixs)
115{
116 return Stdout(perfixs);
117}
118
119QString ServerApplication::toProtocolString(int id, const QString method, const QJsonObject &params)
120{
121 QJsonObject obj;
122 obj[K_ID] = id;
123 obj[K_METHOD] = method;
124 obj[K_PARAMS] = params;
125 return toProtocolString(obj);
126}
127
128QString ServerApplication::toProtocolString(const QString &method, const QJsonObject &params)
129{
130 QJsonObject obj;
131 obj[K_METHOD] = method;
132 obj[K_PARAMS] = params;
133 return toProtocolString(obj);
134}
135
136QString ServerApplication::toProtocolString(const QJsonObject &object)
137{
138 QJsonObject temp = object;
139 temp[K_JSON_RPC] = "2.0";
140 QString body = QJsonDocument(temp).toJson(QJsonDocument::Compact);
141 QString head = QString("Content-Length: ") + QString::number(body.size()) + "\r\n\r\n";
142 return head + body;
143}
144
145QString ServerApplication::localDateTime()
146{
147 return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
148}
149
150void ServerApplication::jsonrpcJsonOutput(const QJsonObject &obj)
151{
152 if (!ServerCmdParse::mode() || ServerCmdParse::mode() == newlsp::stdio.toStdString()) {
153 std::cout << toProtocolString(obj).toStdString() << std::endl;
154 } else {
155 lspServErr << "Output of json is not allowed in this mode"
156 << ServerCmdParse::mode().value();
157 }
158}
159
160void ServerApplication::identifyJsonObject(const QJsonObject &obj)
161{
162 auto objKeys = obj.keys();
163 if (objKeys.contains(K_ID) && objKeys.contains(K_JSON_RPC)
164 && objKeys.contains(K_METHOD) && objKeys.contains(K_PARAMS)) {
165 int idStr = obj.value(K_ID).toInt();
166 QString methodStr = obj.value(K_METHOD).toString();
167 QJsonObject params = obj.value(K_PARAMS).toObject();
168 jsonrpcMethod(idStr, methodStr, params);
169 } else if (objKeys.contains(K_JSON_RPC) && objKeys.contains(K_METHOD)
170 && objKeys.contains(K_PARAMS)) {
171 QString methodStr = obj.value(K_METHOD).toString();
172 QJsonObject params = obj.value(K_PARAMS).toObject();
173 jsonrpcNotification(methodStr, params);
174 }
175}
176
177} //newlsp
178