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   return Handler.onNotify(*Method, std::move(Params));
198 }
199 
200 // Tries to read a line up to and including \n.
201 // If failing, feof(), ferror(), or shutdownRequested() will be set.
202 bool readLine(std::FILE *In, llvm::SmallVectorImpl<char> &Out) {
203   // Big enough to hold any reasonable header line. May not fit content lines
204   // in delimited mode, but performance doesn't matter for that mode.
205   static constexpr int BufSize = 128;
206   size_t Size = 0;
207   Out.clear();
208   for (;;) {
209     Out.resize_for_overwrite(Size + BufSize);
210     // Handle EINTR which is sent when a debugger attaches on some platforms.
211     if (!retryAfterSignalUnlessShutdown(
212             nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
213       return false;
214     clearerr(In);
215     // If the line contained null bytes, anything after it (including \n) will
216     // be ignored. Fortunately this is not a legal header or JSON.
217     size_t Read = std::strlen(&Out[Size]);
218     if (Read > 0 && Out[Size + Read - 1] == '\n') {
219       Out.resize(Size + Read);
220       return true;
221     }
222     Size += Read;
223   }
224 }
225 
226 // Returns None when:
227 //  - ferror(), feof(), or shutdownRequested() are set.
228 //  - Content-Length is missing or empty (protocol error)
229 bool JSONTransport::readStandardMessage(std::string &JSON) {
230   // A Language Server Protocol message starts with a set of HTTP headers,
231   // delimited  by \r\n, and terminated by an empty line (\r\n).
232   unsigned long long ContentLength = 0;
233   llvm::SmallString<128> Line;
234   while (true) {
235     if (feof(In) || ferror(In) || !readLine(In, Line))
236       return false;
237     InMirror << Line;
238 
239     llvm::StringRef LineRef = Line;
240 
241     // We allow comments in headers. Technically this isn't part
242 
243     // of the LSP specification, but makes writing tests easier.
244     if (LineRef.startswith("#"))
245       continue;
246 
247     // Content-Length is a mandatory header, and the only one we handle.
248     if (LineRef.consume_front("Content-Length: ")) {
249       if (ContentLength != 0) {
250         elog("Warning: Duplicate Content-Length header received. "
251              "The previous value for this message ({0}) was ignored.",
252              ContentLength);
253       }
254       llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
255       continue;
256     }
257 
258     // An empty line indicates the end of headers.
259     // Go ahead and read the JSON.
260     if (LineRef.trim().empty())
261       break;
262 
263     // It's another header, ignore it.
264   }
265 
266   // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
267   if (ContentLength > 1 << 30) { // 1024M
268     elog("Refusing to read message with long Content-Length: {0}. "
269          "Expect protocol errors",
270          ContentLength);
271     return false;
272   }
273   if (ContentLength == 0) {
274     log("Warning: Missing Content-Length header, or zero-length message.");
275     return false;
276   }
277 
278   JSON.resize(ContentLength);
279   for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
280     // Handle EINTR which is sent when a debugger attaches on some platforms.
281     Read = retryAfterSignalUnlessShutdown(0, [&]{
282       return std::fread(&JSON[Pos], 1, ContentLength - Pos, In);
283     });
284     if (Read == 0) {
285       elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
286            ContentLength);
287       return false;
288     }
289     InMirror << llvm::StringRef(&JSON[Pos], Read);
290     clearerr(In); // If we're done, the error was transient. If we're not done,
291                   // either it was transient or we'll see it again on retry.
292     Pos += Read;
293   }
294   return true;
295 }
296 
297 // For lit tests we support a simplified syntax:
298 // - messages are delimited by '---' on a line by itself
299 // - lines starting with # are ignored.
300 // This is a testing path, so favor simplicity over performance here.
301 // When returning false: feof(), ferror(), or shutdownRequested() will be set.
302 bool JSONTransport::readDelimitedMessage(std::string &JSON) {
303   JSON.clear();
304   llvm::SmallString<128> Line;
305   while (readLine(In, Line)) {
306     InMirror << Line;
307     auto LineRef = Line.str().trim();
308     if (LineRef.startswith("#")) // comment
309       continue;
310 
311     // found a delimiter
312     if (LineRef.rtrim() == "---")
313       break;
314 
315     JSON += Line;
316   }
317 
318   if (shutdownRequested())
319     return false;
320   if (ferror(In)) {
321     elog("Input error while reading message!");
322     return false;
323   }
324   return true; // Including at EOF
325 }
326 
327 } // namespace
328 
329 std::unique_ptr<Transport> newJSONTransport(std::FILE *In,
330                                             llvm::raw_ostream &Out,
331                                             llvm::raw_ostream *InMirror,
332                                             bool Pretty,
333                                             JSONStreamStyle Style) {
334   return std::make_unique<JSONTransport>(In, Out, InMirror, Pretty, Style);
335 }
336 
337 } // namespace clangd
338 } // namespace clang
339