blob: 43be9f71aaf8480f7b3af4df9c4ebd7e6f3220e2 [file] [log] [blame]
Sam McCalldc8f3cf2018-10-17 07:32:05 +00001//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Sam McCalldc8f3cf2018-10-17 07:32:05 +00006//
7//===----------------------------------------------------------------------===//
8#include "Logger.h"
9#include "Protocol.h" // For LSPError
Sam McCall19ac0eaf2019-11-25 19:51:07 +010010#include "Shutdown.h"
Sam McCalldc8f3cf2018-10-17 07:32:05 +000011#include "Transport.h"
12#include "llvm/Support/Errno.h"
Sam McCall19ac0eaf2019-11-25 19:51:07 +010013#include "llvm/Support/Error.h"
Sam McCalldc8f3cf2018-10-17 07:32:05 +000014
Sam McCalldc8f3cf2018-10-17 07:32:05 +000015namespace clang {
16namespace clangd {
17namespace {
18
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000019llvm::json::Object encodeError(llvm::Error E) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +000020 std::string Message;
21 ErrorCode Code = ErrorCode::UnknownErrorCode;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000022 if (llvm::Error Unhandled = llvm::handleErrors(
23 std::move(E), [&](const LSPError &L) -> llvm::Error {
Sam McCalldc8f3cf2018-10-17 07:32:05 +000024 Message = L.Message;
25 Code = L.Code;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000026 return llvm::Error::success();
Sam McCalldc8f3cf2018-10-17 07:32:05 +000027 }))
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000028 Message = llvm::toString(std::move(Unhandled));
Sam McCalldc8f3cf2018-10-17 07:32:05 +000029
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000030 return llvm::json::Object{
Sam McCalldc8f3cf2018-10-17 07:32:05 +000031 {"message", std::move(Message)},
32 {"code", int64_t(Code)},
33 };
34}
35
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000036llvm::Error decodeError(const llvm::json::Object &O) {
Benjamin Krameradcd0262020-01-28 20:23:46 +010037 std::string Msg =
38 std::string(O.getString("message").getValueOr("Unspecified error"));
Sam McCalldc8f3cf2018-10-17 07:32:05 +000039 if (auto Code = O.getInteger("code"))
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000040 return llvm::make_error<LSPError>(std::move(Msg), ErrorCode(*Code));
41 return llvm::make_error<llvm::StringError>(std::move(Msg),
42 llvm::inconvertibleErrorCode());
Sam McCalldc8f3cf2018-10-17 07:32:05 +000043}
44
45class JSONTransport : public Transport {
46public:
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000047 JSONTransport(std::FILE *In, llvm::raw_ostream &Out,
48 llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
49 : In(In), Out(Out), InMirror(InMirror ? *InMirror : llvm::nulls()),
Sam McCalldc8f3cf2018-10-17 07:32:05 +000050 Pretty(Pretty), Style(Style) {}
51
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000052 void notify(llvm::StringRef Method, llvm::json::Value Params) override {
53 sendMessage(llvm::json::Object{
Sam McCalldc8f3cf2018-10-17 07:32:05 +000054 {"jsonrpc", "2.0"},
55 {"method", Method},
56 {"params", std::move(Params)},
57 });
58 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000059 void call(llvm::StringRef Method, llvm::json::Value Params,
60 llvm::json::Value ID) override {
61 sendMessage(llvm::json::Object{
Sam McCalldc8f3cf2018-10-17 07:32:05 +000062 {"jsonrpc", "2.0"},
63 {"id", std::move(ID)},
64 {"method", Method},
65 {"params", std::move(Params)},
66 });
67 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000068 void reply(llvm::json::Value ID,
69 llvm::Expected<llvm::json::Value> Result) override {
Sam McCalldc8f3cf2018-10-17 07:32:05 +000070 if (Result) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000071 sendMessage(llvm::json::Object{
Sam McCalldc8f3cf2018-10-17 07:32:05 +000072 {"jsonrpc", "2.0"},
73 {"id", std::move(ID)},
74 {"result", std::move(*Result)},
75 });
76 } else {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000077 sendMessage(llvm::json::Object{
Sam McCalldc8f3cf2018-10-17 07:32:05 +000078 {"jsonrpc", "2.0"},
79 {"id", std::move(ID)},
80 {"error", encodeError(Result.takeError())},
81 });
82 }
83 }
84
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000085 llvm::Error loop(MessageHandler &Handler) override {
Sam McCalldc8f3cf2018-10-17 07:32:05 +000086 while (!feof(In)) {
Sam McCall19ac0eaf2019-11-25 19:51:07 +010087 if (shutdownRequested())
88 return llvm::createStringError(
89 std::make_error_code(std::errc::operation_canceled),
90 "Got signal, shutting down");
Sam McCalldc8f3cf2018-10-17 07:32:05 +000091 if (ferror(In))
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000092 return llvm::errorCodeToError(
93 std::error_code(errno, std::system_category()));
Sam McCalldc8f3cf2018-10-17 07:32:05 +000094 if (auto JSON = readRawMessage()) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000095 if (auto Doc = llvm::json::parse(*JSON)) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +000096 vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
97 if (!handleMessage(std::move(*Doc), Handler))
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000098 return llvm::Error::success(); // we saw the "exit" notification.
Sam McCalldc8f3cf2018-10-17 07:32:05 +000099 } else {
100 // Parse error. Log the raw message.
101 vlog("<<< {0}\n", *JSON);
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000102 elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000103 }
104 }
105 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000106 return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000107 }
108
109private:
110 // Dispatches incoming message to Handler onNotify/onCall/onReply.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000111 bool handleMessage(llvm::json::Value Message, MessageHandler &Handler);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000112 // Writes outgoing message to Out stream.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000113 void sendMessage(llvm::json::Value Message) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000114 std::string S;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000115 llvm::raw_string_ostream OS(S);
116 OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000117 OS.flush();
118 Out << "Content-Length: " << S.size() << "\r\n\r\n" << S;
119 Out.flush();
120 vlog(">>> {0}\n", S);
121 }
122
123 // Read raw string messages from input stream.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000124 llvm::Optional<std::string> readRawMessage() {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000125 return Style == JSONStreamStyle::Delimited ? readDelimitedMessage()
126 : readStandardMessage();
127 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000128 llvm::Optional<std::string> readDelimitedMessage();
129 llvm::Optional<std::string> readStandardMessage();
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000130
131 std::FILE *In;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000132 llvm::raw_ostream &Out;
133 llvm::raw_ostream &InMirror;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000134 bool Pretty;
135 JSONStreamStyle Style;
136};
137
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000138bool JSONTransport::handleMessage(llvm::json::Value Message,
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000139 MessageHandler &Handler) {
140 // Message must be an object with "jsonrpc":"2.0".
141 auto *Object = Message.getAsObject();
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000142 if (!Object ||
143 Object->getString("jsonrpc") != llvm::Optional<llvm::StringRef>("2.0")) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000144 elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
145 return false;
146 }
147 // ID may be any JSON value. If absent, this is a notification.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000148 llvm::Optional<llvm::json::Value> ID;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000149 if (auto *I = Object->get("id"))
150 ID = std::move(*I);
151 auto Method = Object->getString("method");
152 if (!Method) { // This is a response.
153 if (!ID) {
154 elog("No method and no response ID: {0:2}", Message);
155 return false;
156 }
157 if (auto *Err = Object->getObject("error"))
158 return Handler.onReply(std::move(*ID), decodeError(*Err));
159 // Result should be given, use null if not.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000160 llvm::json::Value Result = nullptr;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000161 if (auto *R = Object->get("result"))
162 Result = std::move(*R);
163 return Handler.onReply(std::move(*ID), std::move(Result));
164 }
165 // Params should be given, use null if not.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000166 llvm::json::Value Params = nullptr;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000167 if (auto *P = Object->get("params"))
168 Params = std::move(*P);
169
170 if (ID)
171 return Handler.onCall(*Method, std::move(Params), std::move(*ID));
172 else
173 return Handler.onNotify(*Method, std::move(Params));
174}
175
176// Tries to read a line up to and including \n.
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100177// If failing, feof(), ferror(), or shutdownRequested() will be set.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000178bool readLine(std::FILE *In, std::string &Out) {
179 static constexpr int BufSize = 1024;
180 size_t Size = 0;
181 Out.clear();
182 for (;;) {
183 Out.resize(Size + BufSize);
184 // Handle EINTR which is sent when a debugger attaches on some platforms.
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100185 if (!retryAfterSignalUnlessShutdown(
186 nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000187 return false;
188 clearerr(In);
189 // If the line contained null bytes, anything after it (including \n) will
190 // be ignored. Fortunately this is not a legal header or JSON.
191 size_t Read = std::strlen(&Out[Size]);
192 if (Read > 0 && Out[Size + Read - 1] == '\n') {
193 Out.resize(Size + Read);
194 return true;
195 }
196 Size += Read;
197 }
198}
199
200// Returns None when:
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100201// - ferror(), feof(), or shutdownRequested() are set.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000202// - Content-Length is missing or empty (protocol error)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000203llvm::Optional<std::string> JSONTransport::readStandardMessage() {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000204 // A Language Server Protocol message starts with a set of HTTP headers,
205 // delimited by \r\n, and terminated by an empty line (\r\n).
206 unsigned long long ContentLength = 0;
207 std::string Line;
208 while (true) {
209 if (feof(In) || ferror(In) || !readLine(In, Line))
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000210 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000211 InMirror << Line;
212
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000213 llvm::StringRef LineRef(Line);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000214
215 // We allow comments in headers. Technically this isn't part
216
217 // of the LSP specification, but makes writing tests easier.
218 if (LineRef.startswith("#"))
219 continue;
220
221 // Content-Length is a mandatory header, and the only one we handle.
222 if (LineRef.consume_front("Content-Length: ")) {
223 if (ContentLength != 0) {
224 elog("Warning: Duplicate Content-Length header received. "
225 "The previous value for this message ({0}) was ignored.",
226 ContentLength);
227 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000228 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000229 continue;
230 } else if (!LineRef.trim().empty()) {
231 // It's another header, ignore it.
232 continue;
233 } else {
234 // An empty line indicates the end of headers.
235 // Go ahead and read the JSON.
236 break;
237 }
238 }
239
240 // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
241 if (ContentLength > 1 << 30) { // 1024M
242 elog("Refusing to read message with long Content-Length: {0}. "
243 "Expect protocol errors",
244 ContentLength);
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000245 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000246 }
247 if (ContentLength == 0) {
248 log("Warning: Missing Content-Length header, or zero-length message.");
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000249 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000250 }
251
252 std::string JSON(ContentLength, '\0');
253 for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
254 // Handle EINTR which is sent when a debugger attaches on some platforms.
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100255 Read = retryAfterSignalUnlessShutdown(0, [&]{
256 return std::fread(&JSON[Pos], 1, ContentLength - Pos, In);
257 });
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000258 if (Read == 0) {
259 elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
260 ContentLength);
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000261 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000262 }
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000263 InMirror << llvm::StringRef(&JSON[Pos], Read);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000264 clearerr(In); // If we're done, the error was transient. If we're not done,
265 // either it was transient or we'll see it again on retry.
266 Pos += Read;
267 }
268 return std::move(JSON);
269}
270
271// For lit tests we support a simplified syntax:
272// - messages are delimited by '---' on a line by itself
273// - lines starting with # are ignored.
274// This is a testing path, so favor simplicity over performance here.
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100275// When returning None, feof(), ferror(), or shutdownRequested() will be set.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000276llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000277 std::string JSON;
278 std::string Line;
279 while (readLine(In, Line)) {
280 InMirror << Line;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000281 auto LineRef = llvm::StringRef(Line).trim();
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000282 if (LineRef.startswith("#")) // comment
283 continue;
284
285 // found a delimiter
286 if (LineRef.rtrim() == "---")
287 break;
288
289 JSON += Line;
290 }
291
Sam McCall19ac0eaf2019-11-25 19:51:07 +0100292 if (shutdownRequested())
293 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000294 if (ferror(In)) {
295 elog("Input error while reading message!");
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000296 return llvm::None;
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000297 }
298 return std::move(JSON); // Including at EOF
299}
300
301} // namespace
302
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000303std::unique_ptr<Transport> newJSONTransport(std::FILE *In,
304 llvm::raw_ostream &Out,
305 llvm::raw_ostream *InMirror,
306 bool Pretty,
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000307 JSONStreamStyle Style) {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000308 return std::make_unique<JSONTransport>(In, Out, InMirror, Pretty, Style);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000309}
310
311} // namespace clangd
312} // namespace clang