nixd
Loading...
Searching...
No Matches
Connection.cpp
Go to the documentation of this file.
2#include "lspserver/Logger.h"
4
5#include <llvm/ADT/SmallString.h>
6#include <llvm/Support/CommandLine.h>
7
8#include <poll.h>
9#include <sys/poll.h>
10#include <sys/stat.h>
11
12#include <csignal>
13#include <cstdint>
14#include <cstdio>
15#include <memory>
16#include <optional>
17#include <system_error>
18
19namespace {
20
21llvm::cl::opt<int> ClientProcessID{
22 "clientProcessId",
23 llvm::cl::desc(
24 "Client process ID, if this PID died, the server should exit."),
25 llvm::cl::init(getppid())};
26
27} // namespace
28
29namespace lspserver {
30
31static llvm::json::Object encodeError(llvm::Error Error) {
32 std::string Message;
34 auto HandlerFn = [&](const LSPError &E) -> llvm::Error {
35 Message = E.Message;
36 Code = E.Code;
37 return llvm::Error::success();
38 };
39 if (llvm::Error Unhandled = llvm::handleErrors(std::move(Error), HandlerFn))
40 Message = llvm::toString(std::move(Unhandled));
41
42 return llvm::json::Object{
43 {"message", std::move(Message)},
44 {"code", int64_t(Code)},
45 };
46}
47
48/// Decode the given JSON object into an error.
49llvm::Error decodeError(const llvm::json::Object &O) {
50 llvm::StringRef Message =
51 O.getString("message").value_or("Unspecified error");
52 if (std::optional<int64_t> Code = O.getInteger("code"))
53 return llvm::make_error<LSPError>(Message.str(), ErrorCode(*Code));
54 return llvm::make_error<llvm::StringError>(llvm::inconvertibleErrorCode(),
55 Message.str());
56}
57
58void OutboundPort::notify(llvm::StringRef Method, llvm::json::Value Params) {
59 sendMessage(llvm::json::Object{
60 {"jsonrpc", "2.0"},
61 {"method", Method},
62 {"params", std::move(Params)},
63 });
64}
65void OutboundPort::call(llvm::StringRef Method, llvm::json::Value Params,
66 llvm::json::Value ID) {
67 sendMessage(llvm::json::Object{
68 {"jsonrpc", "2.0"},
69 {"id", std::move(ID)},
70 {"method", Method},
71 {"params", std::move(Params)},
72 });
73}
74void OutboundPort::reply(llvm::json::Value ID,
75 llvm::Expected<llvm::json::Value> Result) {
76 if (Result) {
77 sendMessage(llvm::json::Object{
78 {"jsonrpc", "2.0"},
79 {"id", std::move(ID)},
80 {"result", std::move(*Result)},
81 });
82 } else {
83 sendMessage(llvm::json::Object{
84 {"jsonrpc", "2.0"},
85 {"id", std::move(ID)},
86 {"error", encodeError(Result.takeError())},
87 });
88 }
89}
90
91void OutboundPort::sendMessage(llvm::json::Value Message) {
92 // Make sure our outputs are not interleaving between messages (json)
93 vlog(">>> {0}", Message);
94 std::lock_guard<std::mutex> Guard(Mutex);
95 OutputBuffer.clear();
96 llvm::raw_svector_ostream SVecOS(OutputBuffer);
97 SVecOS << (Pretty ? llvm::formatv("{0:2}", Message)
98 : llvm::formatv("{0}", Message));
99 Outs << "Content-Length: " << OutputBuffer.size() << "\r\n\r\n"
100 << OutputBuffer;
101 Outs.flush();
102}
103
104bool InboundPort::dispatch(llvm::json::Value Message, MessageHandler &Handler) {
105 // Message must be an object with "jsonrpc":"2.0".
106 auto *Object = Message.getAsObject();
107 if (!Object ||
108 Object->getString("jsonrpc") != std::optional<llvm::StringRef>("2.0")) {
109 elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
110 return false;
111 }
112 // ID may be any JSON value. If absent, this is a notification.
113 std::optional<llvm::json::Value> ID;
114 if (auto *I = Object->get("id"))
115 ID = std::move(*I);
116 auto Method = Object->getString("method");
117 if (!Method) { // This is a response.
118 if (!ID) {
119 elog("No method and no response ID: {0:2}", Message);
120 return false;
121 }
122 if (auto *Err = Object->getObject("error"))
123 // TODO: Logging & reply errors.
124 return Handler.onReply(std::move(*ID), decodeError(*Err));
125 // Result should be given, use null if not.
126 llvm::json::Value Result = nullptr;
127 if (auto *R = Object->get("result"))
128 Result = std::move(*R);
129 return Handler.onReply(std::move(*ID), std::move(Result));
130 }
131 // Params should be given, use null if not.
132 llvm::json::Value Params = nullptr;
133 if (auto *P = Object->get("params"))
134 Params = std::move(*P);
135
136 if (ID)
137 return Handler.onCall(*Method, std::move(Params), std::move(*ID));
138 return Handler.onNotify(*Method, std::move(Params));
139}
140
141bool readLine(int fd, const std::atomic<bool> &Close,
142 llvm::SmallString<128> &Line) {
143 Line.clear();
144
145 std::vector<pollfd> FDs;
146 FDs.emplace_back(pollfd{
147 fd,
148 POLLIN | POLLPRI,
149 0,
150 });
151 for (;;) {
152 char Ch;
153 // FIXME: inefficient
154 int Poll = poll(FDs.data(), FDs.size(), 1000);
155 if (Poll < 0)
156 return false;
157 if (Close)
158 return false;
159
160 if (FDs[0].revents & POLLIN) {
161 ssize_t BytesRead = read(fd, &Ch, 1);
162 if (BytesRead == -1) {
163 if (errno != EINTR)
164 return false;
165 } else if (BytesRead == 0)
166 return false;
167 else {
168 if (Ch == '\n')
169 return true;
170 Line += Ch;
171 }
172 }
173
174 if (kill(ClientProcessID, 0) < 0) {
175 // Parent died.
176 return false;
177 }
178 }
179}
180
182 unsigned long long ContentLength = 0;
183 llvm::SmallString<128> Line;
184 while (true) {
185 if (!readLine(In, Close, Line))
186 return false;
187
188 llvm::StringRef LineRef = Line;
189
190 // Content-Length is a mandatory header, and the only one we handle.
191 if (LineRef.consume_front("Content-Length: ")) {
192 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
193 continue;
194 }
195 // An empty line indicates the end of headers.
196 // Go ahead and read the JSON.
197 if (LineRef.trim().empty())
198 break;
199 // It's another header, ignore it.
200 }
201
203 for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
204
205 Read = read(In, JSONString.data() + Pos, ContentLength - Pos);
206
207 if (Read == 0) {
208 elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
210 return false;
211 }
212 }
213 return true;
214}
215
217 JSONString.clear();
218 llvm::SmallString<128> Line;
219 bool IsInputBlock = false;
220 while (readLine(In, Close, Line)) {
221 auto LineRef = Line.str().trim();
222 if (IsInputBlock) {
223 // We are in input blocks, read lines and append JSONString.
224 if (LineRef.startswith("#")) // comment
225 continue;
226
227 // End of the block
228 if (LineRef.startswith("```")) {
229 IsInputBlock = false;
230 break;
231 }
232
233 JSONString += Line;
234 } else {
235 if (LineRef.startswith("```json"))
236 IsInputBlock = true;
237 }
238 }
239 return true; // Including at EOF
240}
241
243 switch (StreamStyle) {
244
249 break;
250 }
251 assert(false && "Invalid stream style");
253}
254
256 std::string JSONString;
257 llvm::SmallString<128> Line;
258
259 for (;;) {
260 if (readMessage(JSONString)) {
261 vlog("<<< {0}", JSONString);
262 if (auto ExpectedParsedJSON = llvm::json::parse(JSONString)) {
264 return;
265 } else {
266 auto Err = ExpectedParsedJSON.takeError();
267 elog("The received json cannot be parsed, reason: {0}", Err);
268 return;
269 }
270 } else {
271 return;
272 }
273 }
274}
275
276} // namespace lspserver
bool dispatch(llvm::json::Value Message, MessageHandler &Handler)
JSONStreamStyle StreamStyle
Definition Connection.h:41
bool readStandardMessage(std::string &JSONString)
bool readMessage(std::string &JSONString)
bool readDelimitedMessage(std::string &JSONString)
void loop(MessageHandler &Handler)
void sendMessage(llvm::json::Value Message)
void notify(llvm::StringRef Method, llvm::json::Value Params)
void reply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result)
void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID)
Whether current platform treats paths case insensitively.
Definition Connection.h:11
@ Error
An error message.
bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path)
llvm::Error decodeError(const llvm::json::Object &O)
Decode the given JSON object into an error.
void elog(const char *Fmt, Ts &&...Vals)
Definition Logger.h:52
void vlog(const char *Fmt, Ts &&...Vals)
Definition Logger.h:63
bool readLine(int fd, const std::atomic< bool > &Close, llvm::SmallString< 128 > &Line)