1 //===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===//
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 #include "Protocol.h" // For LSPError
9 #include "Transport.h"
10 #include "support/Cancellation.h"
11 #include "support/Logger.h"
12 #include "support/Shutdown.h"
13 #include "support/ThreadCrashReporter.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/Support/Errno.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/Threading.h"
18 #include <system_error>
19 
20 namespace clang {
21 namespace clangd {
22 namespace {
23 
24 llvm::json::Object encodeError(llvm::Error E) {
25   std::string Message;
26   ErrorCode Code = ErrorCode::UnknownErrorCode;
27   // FIXME: encode cancellation errors using RequestCancelled or ContentModified
28   // as appropriate.
29   if (llvm::Error Unhandled = llvm::handleErrors(
30           std::move(E),
31           [&](const CancelledError &C) -> llvm::Error {
32             switch (C.Reason) {
33             case static_cast<int>(ErrorCode::ContentModified):
34               Code = ErrorCode::ContentModified;
35               Message = "Request cancelled because the document was modified";
36               break;
37             default:
38               Code = ErrorCode::RequestCancelled;
39               Message = "Request cancelled";
40               break;
41             }
42             return llvm::Error::success();
43           },
44           [&](const LSPError &L) -> llvm::Error {
45             Message = L.Message;
46             Code = L.Code;
47             return llvm::Error::success();
48           }))
49     Message = llvm::toString(std::move(Unhandled));
50 
51   return llvm::json::Object{
52       {"message", std::move(Message)},
53       {"code", int64_t(Code)},
54   };
55 }
56 
57 llvm::Error decodeError(const llvm::json::Object &O) {
58   llvm::StringRef Msg = O.getString("message").getValueOr("Unspecified error");
59   if (auto Code = O.getInteger("code"))
60     return llvm::make_error<LSPError>(Msg.str(), ErrorCode(*Code));
61   return error(Msg.str());
62 }
63 
64 class JSONTransport : public Transport {
65 public:
66   JSONTransport(std::FILE *In, llvm::raw_ostream &Out,
67                 llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
68       : In(In), Out(Out), InMirror(InMirror ? *InMirror : llvm::nulls()),
69         Pretty(Pretty), Style(Style) {}
70 
71   void notify(llvm::StringRef Method, llvm::json::Value Params) override {
72     sendMessage(llvm::json::Object{
73         {"jsonrpc", "2.0"},
74         {"method", Method},
75         {"params", std::move(Params)},
76     });
77   }
78   void call(llvm::StringRef Method, llvm::json::Value Params,
79             llvm::json::Value ID) override {
80     sendMessage(llvm::json::Object{
81         {"jsonrpc", "2.0"},
82         {"id", std::move(ID)},
83         {"method", Method},
84         {"params", std::move(Params)},
85     });
86   }
87   void reply(llvm::json::Value ID,
88              llvm::Expected<llvm::json::Value> Result) override {
89     if (Result) {
90       sendMessage(llvm::json::Object{
91           {"jsonrpc", "2.0"},
92           {"id", std::move(ID)},
93           {"result", std::move(*Result)},
94       });
95     } else {
96       sendMessage(llvm::json::Object{
97           {"jsonrpc", "2.0"},
98           {"id", std::move(ID)},
99           {"error", encodeError(Result.takeError())},
100       });
101     }
102   }
103 
104   llvm::Error loop(MessageHandler &Handler) override {
105     std::string JSON; // Messages may be large, reuse same big buffer.
106     while (!feof(In)) {
107       if (shutdownRequested())
108         return error(std::make_error_code(std::errc::operation_canceled),
109                      "Got signal, shutting down");
110       if (ferror(In))
111         return llvm::errorCodeToError(
112             std::error_code(errno, std::system_category()));
113       if (readRawMessage(JSON)) {
114         ThreadCrashReporter ScopedReporter([&JSON]() {
115           auto &OS = llvm::errs();
116           OS << "Signalled while processing message:\n";
117           OS << JSON << "\n";
118         });
119         if (auto Doc = llvm::json::parse(JSON)) {
120           vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
121           if (!handleMessage(std::move(*Doc), Handler))
122             return llvm::Error::success(); // we saw the "exit" notification.
123         } else {
124           // Parse error. Log the raw message.
125           vlog("<<< {0}\n", JSON);
126           elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
127         }
128       }
129     }
130     return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
131   }
132 
133 private:
134   // Dispatches incoming message to Handler onNotify/onCall/onReply.
135   bool handleMessage(llvm::json::Value Message, MessageHandler &Handler);
136   // Writes outgoing message to Out stream.
137   void sendMessage(llvm::json::Value Message) {
138     OutputBuffer.clear();
139     llvm::raw_svector_ostream OS(OutputBuffer);
140     OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message);
141     Out << "Content-Length: " << OutputBuffer.size() << "\r\n\r\n"
142         << OutputBuffer;
143     Out.flush();
144     vlog(">>> {0}\n", OutputBuffer);
145   }
146 
147   // Read raw string messages from input stream.
148   bool readRawMessage(std::string &JSON) {
149     return Style == JSONStreamStyle::Delimited ? readDelimitedMessage(JSON)
150                                                : readStandardMessage(JSON);
151   }
152   bool readDelimitedMessage(std::string &JSON);
153   bool readStandardMessage(std::string &JSON);
154 
155   llvm::SmallVector<char, 0> OutputBuffer;
156   std::FILE *In;
157   llvm::raw_ostream &Out;
158   llvm::raw_ostream &InMirror;
159   bool Pretty;
160   JSONStreamStyle Style;
161 };
162 
163 bool JSONTransport::handleMessage(llvm::json::Value Message,
164                                   MessageHandler &Handler) {
165   // Message must be an object with "jsonrpc":"2.0".
166   auto *Object = Message.getAsObject();
167   if (!Object ||
168       Object->getString("jsonrpc") != llvm::Optional<llvm::StringRef>("2.0")) {
169     elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
170     return false;
171   }
172   // ID may be any JSON value. If absent, this is a notification.
173   llvm::Optional<llvm::json::Value> ID;
174   if (auto *I = Object->get("id"))
175     ID = std::move(*I);
176   auto Method = Object->getString("method");
177   if (!Method) { // This is a response.
178     if (!ID) {
179       elog("No method and no response ID: {0:2}", Message);
180       return false;
181     }
182     if (auto *Err = Object->getObject("error"))
183       return Handler.onReply(std::move(*ID), decodeError(*Err));
184     // Result should be given, use null if not.
185     llvm::json::Value Result = nullptr;
186     if (auto *R = Object->get("result"))
187       Result = std::move(*R);
188     return Handler.onReply(std::move(*ID), std::move(Result));
189   }
190   // Params should be given, use null if not.
191   llvm::json::Value Params = nullptr;
192   if (auto *P = Object->get("params"))
193     Params = std::move(*P);
194 
195   if (ID)
196     return Handler.onCall(*Method, std::move(Params), std::move(*ID));
197   else
198     return Handler.onNotify(*Method, std::move(Params));
199 }
200 
201 // Tries to read a line up to and including \n.
202 // If failing, feof(), ferror(), or shutdownRequested() will be set.
203 bool readLine(std::FILE *In, llvm::SmallVectorImpl<char> &Out) {
204   // Big enough to hold any reasonable header line. May not fit content lines
205   // in delimited mode, but performance doesn't matter for that mode.
206   static constexpr int BufSize = 128;
207   size_t Size = 0;
208   Out.clear();
209   for (;;) {
210     Out.resize_for_overwrite(Size + BufSize);
211     // Handle EINTR which is sent when a debugger attaches on some platforms.
212     if (!retryAfterSignalUnlessShutdown(
213             nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
214       return false;
215     clearerr(In);
216     // If the line contained null bytes, anything after it (including \n) will
217     // be ignored. Fortunately this is not a legal header or JSON.
218     size_t Read = std::strlen(&Out[Size]);
219     if (Read > 0 && Out[Size + Read - 1] == '\n') {
220       Out.resize(Size + Read);
221       return true;
222     }
223     Size += Read;
224   }
225 }
226 
227 // Returns None when:
228 //  - ferror(), feof(), or shutdownRequested() are set.
229 //  - Content-Length is missing or empty (protocol error)
230 bool JSONTransport::readStandardMessage(std::string &JSON) {
231   // A Language Server Protocol message starts with a set of HTTP headers,
232   // delimited  by \r\n, and terminated by an empty line (\r\n).
233   unsigned long long ContentLength = 0;
234   llvm::SmallString<128> Line;
235   while (true) {
236     if (feof(In) || ferror(In) || !readLine(In, Line))
237       return false;
238     InMirror << Line;
239 
240     llvm::StringRef LineRef = Line;
241 
242     // We allow comments in headers. Technically this isn't part
243 
244     // of the LSP specification, but makes writing tests easier.
245     if (LineRef.startswith("#"))
246       continue;
247 
248     // Content-Length is a mandatory header, and the only one we handle.
249     if (LineRef.consume_front("Content-Length: ")) {
250       if (ContentLength != 0) {
251         elog("Warning: Duplicate Content-Length header received. "
252              "The previous value for this message ({0}) was ignored.",
253              ContentLength);
254       }
255       llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
256       continue;
257     } else if (!LineRef.trim().empty()) {
258       // It's another header, ignore it.
259       continue;
260     } else {
261       // An empty line indicates the end of headers.
262       // Go ahead and read the JSON.
263       break;
264     }
265   }
266 
267   // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
268   if (ContentLength > 1 << 30) { // 1024M
269     elog("Refusing to read message with long Content-Length: {0}. "
270          "Expect protocol errors",
271          ContentLength);
272     return false;
273   }
274   if (ContentLength == 0) {
275     log("Warning: Missing Content-Length header, or zero-length message.");
276     return false;
277   }
278 
279   JSON.resize(ContentLength);
280   for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
281     // Handle EINTR which is sent when a debugger attaches on some platforms.
282     Read = retryAfterSignalUnlessShutdown(0, [&]{
283       return std::fread(&JSON[Pos], 1, ContentLength - Pos, In);
284     });
285     if (Read == 0) {
286       elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
287            ContentLength);
288       return false;
289     }
290     InMirror << llvm::StringRef(&JSON[Pos], Read);
291     clearerr(In); // If we're done, the error was transient. If we're not done,
292                   // either it was transient or we'll see it again on retry.
293     Pos += Read;
294   }
295   return true;
296 }
297 
298 // For lit tests we support a simplified syntax:
299 // - messages are delimited by '---' on a line by itself
300 // - lines starting with # are ignored.
301 // This is a testing path, so favor simplicity over performance here.
302 // When returning false: feof(), ferror(), or shutdownRequested() will be set.
303 bool JSONTransport::readDelimitedMessage(std::string &JSON) {
304   JSON.clear();
305   llvm::SmallString<128> Line;
306   while (readLine(In, Line)) {
307     InMirror << Line;
308     auto LineRef = Line.str().trim();
309     if (LineRef.startswith("#")) // comment
310       continue;
311 
312     // found a delimiter
313     if (LineRef.rtrim() == "---")
314       break;
315 
316     JSON += Line;
317   }
318 
319   if (shutdownRequested())
320     return false;
321   if (ferror(In)) {
322     elog("Input error while reading message!");
323     return false;
324   }
325   return true; // Including at EOF
326 }
327 
328 } // namespace
329 
330 std::unique_ptr<Transport> newJSONTransport(std::FILE *In,
331                                             llvm::raw_ostream &Out,
332                                             llvm::raw_ostream *InMirror,
333                                             bool Pretty,
334                                             JSONStreamStyle Style) {
335   return std::make_unique<JSONTransport>(In, Out, InMirror, Pretty, Style);
336 }
337 
338 } // namespace clangd
339 } // namespace clang
340