clang-tools  7.0.0
JSONRPCDispatcher.cpp
Go to the documentation of this file.
1 //===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "JSONRPCDispatcher.h"
11 #include "ProtocolHandlers.h"
12 #include "Trace.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Support/Chrono.h"
16 #include "llvm/Support/Errno.h"
17 #include "llvm/Support/FormatVariadic.h"
18 #include "llvm/Support/JSON.h"
19 #include "llvm/Support/SourceMgr.h"
20 #include <istream>
21 
22 using namespace llvm;
23 using namespace clang;
24 using namespace clangd;
25 
26 namespace {
27 static Key<json::Value> RequestID;
28 static Key<JSONOutput *> RequestOut;
29 
30 // When tracing, we trace a request and attach the repsonse in reply().
31 // Because the Span isn't available, we find the current request using Context.
32 class RequestSpan {
33  RequestSpan(llvm::json::Object *Args) : Args(Args) {}
34  std::mutex Mu;
35  llvm::json::Object *Args;
37 
38 public:
39  // Return a context that's aware of the enclosing request, identified by Span.
40  static Context stash(const trace::Span &Span) {
41  return Context::current().derive(
42  RSKey, std::unique_ptr<RequestSpan>(new RequestSpan(Span.Args)));
43  }
44 
45  // If there's an enclosing request and the tracer is interested, calls \p F
46  // with a json::Object where request info can be added.
47  template <typename Func> static void attach(Func &&F) {
48  auto *RequestArgs = Context::current().get(RSKey);
49  if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
50  return;
51  std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
52  F(*(*RequestArgs)->Args);
53  }
54 };
55 Key<std::unique_ptr<RequestSpan>> RequestSpan::RSKey;
56 } // namespace
57 
58 void JSONOutput::writeMessage(const json::Value &Message) {
59  std::string S;
60  llvm::raw_string_ostream OS(S);
61  if (Pretty)
62  OS << llvm::formatv("{0:2}", Message);
63  else
64  OS << Message;
65  OS.flush();
66 
67  {
68  std::lock_guard<std::mutex> Guard(StreamMutex);
69  Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
70  Outs.flush();
71  }
72  vlog(">>> {0}\n", S);
73 }
74 
76  const llvm::formatv_object_base &Message) {
77  if (Level < MinLevel)
78  return;
79  llvm::sys::TimePoint<> Timestamp = std::chrono::system_clock::now();
80  trace::log(Message);
81  std::lock_guard<std::mutex> Guard(StreamMutex);
82  Logs << llvm::formatv("{0}[{1:%H:%M:%S.%L}] {2}\n", indicator(Level),
83  Timestamp, Message);
84  Logs.flush();
85 }
86 
87 void JSONOutput::mirrorInput(const Twine &Message) {
88  if (!InputMirror)
89  return;
90 
91  *InputMirror << Message;
92  InputMirror->flush();
93 }
94 
95 void clangd::reply(json::Value &&Result) {
96  auto ID = Context::current().get(RequestID);
97  if (!ID) {
98  elog("Attempted to reply to a notification!");
99  return;
100  }
101  RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; });
102  log("--> reply({0})", *ID);
103  Context::current()
104  .getExisting(RequestOut)
105  ->writeMessage(json::Object{
106  {"jsonrpc", "2.0"},
107  {"id", *ID},
108  {"result", std::move(Result)},
109  });
110 }
111 
112 void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) {
113  elog("Error {0}: {1}", static_cast<int>(Code), Message);
114  RequestSpan::attach([&](json::Object &Args) {
115  Args["Error"] = json::Object{{"code", static_cast<int>(Code)},
116  {"message", Message.str()}};
117  });
118 
119  if (auto ID = Context::current().get(RequestID)) {
120  log("--> reply({0}) error: {1}", *ID, Message);
121  Context::current()
122  .getExisting(RequestOut)
123  ->writeMessage(json::Object{
124  {"jsonrpc", "2.0"},
125  {"id", *ID},
126  {"error", json::Object{{"code", static_cast<int>(Code)},
127  {"message", Message}}},
128  });
129  }
130 }
131 
132 void clangd::call(StringRef Method, json::Value &&Params) {
133  RequestSpan::attach([&](json::Object &Args) {
134  Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}};
135  });
136  // FIXME: Generate/Increment IDs for every request so that we can get proper
137  // replies once we need to.
138  auto ID = 1;
139  log("--> {0}({1})", Method, ID);
140  Context::current()
141  .getExisting(RequestOut)
142  ->writeMessage(json::Object{
143  {"jsonrpc", "2.0"},
144  {"id", ID},
145  {"method", Method},
146  {"params", std::move(Params)},
147  });
148 }
149 
150 void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
151  assert(!Handlers.count(Method) && "Handler already registered!");
152  Handlers[Method] = std::move(H);
153 }
154 
155 static void logIncomingMessage(const llvm::Optional<json::Value> &ID,
156  llvm::Optional<StringRef> Method,
157  const json::Object *Error) {
158  if (Method) { // incoming request
159  if (ID) // call
160  log("<-- {0}({1})", *Method, *ID);
161  else // notification
162  log("<-- {0}", *Method);
163  } else if (ID) { // response, ID must be provided
164  if (Error)
165  log("<-- reply({0}) error: {1}", *ID,
166  Error->getString("message").getValueOr("<no message>"));
167  else
168  log("<-- reply({0})", *ID);
169  }
170 }
171 
172 bool JSONRPCDispatcher::call(const json::Value &Message,
173  JSONOutput &Out) const {
174  // Message must be an object with "jsonrpc":"2.0".
175  auto *Object = Message.getAsObject();
176  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0"))
177  return false;
178  // ID may be any JSON value. If absent, this is a notification.
179  llvm::Optional<json::Value> ID;
180  if (auto *I = Object->get("id"))
181  ID = std::move(*I);
182  auto Method = Object->getString("method");
183  logIncomingMessage(ID, Method, Object->getObject("error"));
184  if (!Method) // We only handle incoming requests, and ignore responses.
185  return false;
186  // Params should be given, use null if not.
187  json::Value Params = nullptr;
188  if (auto *P = Object->get("params"))
189  Params = std::move(*P);
190 
191  auto I = Handlers.find(*Method);
192  auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
193 
194  // Create a Context that contains request information.
195  WithContextValue WithRequestOut(RequestOut, &Out);
196  llvm::Optional<WithContextValue> WithID;
197  if (ID)
198  WithID.emplace(RequestID, *ID);
199 
200  // Create a tracing Span covering the whole request lifetime.
201  trace::Span Tracer(*Method);
202  if (ID)
203  SPAN_ATTACH(Tracer, "ID", *ID);
204  SPAN_ATTACH(Tracer, "Params", Params);
205 
206  // Stash a reference to the span args, so later calls can add metadata.
207  WithContext WithRequestSpan(RequestSpan::stash(Tracer));
208  Handler(std::move(Params));
209  return true;
210 }
211 
212 // Tries to read a line up to and including \n.
213 // If failing, feof() or ferror() will be set.
214 static bool readLine(std::FILE *In, std::string &Out) {
215  static constexpr int BufSize = 1024;
216  size_t Size = 0;
217  Out.clear();
218  for (;;) {
219  Out.resize(Size + BufSize);
220  // Handle EINTR which is sent when a debugger attaches on some platforms.
221  if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In))
222  return false;
223  clearerr(In);
224  // If the line contained null bytes, anything after it (including \n) will
225  // be ignored. Fortunately this is not a legal header or JSON.
226  size_t Read = std::strlen(&Out[Size]);
227  if (Read > 0 && Out[Size + Read - 1] == '\n') {
228  Out.resize(Size + Read);
229  return true;
230  }
231  Size += Read;
232  }
233 }
234 
235 // Returns None when:
236 // - ferror() or feof() are set.
237 // - Content-Length is missing or empty (protocol error)
238 static llvm::Optional<std::string> readStandardMessage(std::FILE *In,
239  JSONOutput &Out) {
240  // A Language Server Protocol message starts with a set of HTTP headers,
241  // delimited by \r\n, and terminated by an empty line (\r\n).
242  unsigned long long ContentLength = 0;
243  std::string Line;
244  while (true) {
245  if (feof(In) || ferror(In) || !readLine(In, Line))
246  return llvm::None;
247 
248  Out.mirrorInput(Line);
249  llvm::StringRef LineRef(Line);
250 
251  // We allow comments in headers. Technically this isn't part
252  // of the LSP specification, but makes writing tests easier.
253  if (LineRef.startswith("#"))
254  continue;
255 
256  // Content-Length is a mandatory header, and the only one we handle.
257  if (LineRef.consume_front("Content-Length: ")) {
258  if (ContentLength != 0) {
259  elog("Warning: Duplicate Content-Length header received. "
260  "The previous value for this message ({0}) was ignored.",
261  ContentLength);
262  }
263  llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
264  continue;
265  } else if (!LineRef.trim().empty()) {
266  // It's another header, ignore it.
267  continue;
268  } else {
269  // An empty line indicates the end of headers.
270  // Go ahead and read the JSON.
271  break;
272  }
273  }
274 
275  // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
276  if (ContentLength > 1 << 30) { // 1024M
277  elog("Refusing to read message with long Content-Length: {0}. "
278  "Expect protocol errors",
279  ContentLength);
280  return llvm::None;
281  }
282  if (ContentLength == 0) {
283  log("Warning: Missing Content-Length header, or zero-length message.");
284  return llvm::None;
285  }
286 
287  std::string JSON(ContentLength, '\0');
288  for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
289  // Handle EINTR which is sent when a debugger attaches on some platforms.
290  Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1,
291  ContentLength - Pos, In);
292  Out.mirrorInput(StringRef(&JSON[Pos], Read));
293  if (Read == 0) {
294  elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
295  ContentLength);
296  return llvm::None;
297  }
298  clearerr(In); // If we're done, the error was transient. If we're not done,
299  // either it was transient or we'll see it again on retry.
300  Pos += Read;
301  }
302  return std::move(JSON);
303 }
304 
305 // For lit tests we support a simplified syntax:
306 // - messages are delimited by '---' on a line by itself
307 // - lines starting with # are ignored.
308 // This is a testing path, so favor simplicity over performance here.
309 // When returning None, feof() or ferror() will be set.
310 static llvm::Optional<std::string> readDelimitedMessage(std::FILE *In,
311  JSONOutput &Out) {
312  std::string JSON;
313  std::string Line;
314  while (readLine(In, Line)) {
315  auto LineRef = llvm::StringRef(Line).trim();
316  if (LineRef.startswith("#")) // comment
317  continue;
318 
319  // found a delimiter
320  if (LineRef.rtrim() == "---")
321  break;
322 
323  JSON += Line;
324  }
325 
326  if (ferror(In)) {
327  elog("Input error while reading message!");
328  return llvm::None;
329  } else { // Including EOF
330  Out.mirrorInput(
331  llvm::formatv("Content-Length: {0}\r\n\r\n{1}", JSON.size(), JSON));
332  return std::move(JSON);
333  }
334 }
335 
336 // The use of C-style std::FILE* IO deserves some explanation.
337 // Previously, std::istream was used. When a debugger attached on MacOS, the
338 // process received EINTR, the stream went bad, and clangd exited.
339 // A retry-on-EINTR loop around reads solved this problem, but caused clangd to
340 // sometimes hang rather than exit on other OSes. The interaction between
341 // istreams and signals isn't well-specified, so it's hard to get this right.
342 // The C APIs seem to be clearer in this respect.
343 void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
345  JSONRPCDispatcher &Dispatcher,
346  bool &IsDone) {
347  auto &ReadMessage =
349  while (!IsDone && !feof(In)) {
350  if (ferror(In)) {
351  elog("IO error: {0}", llvm::sys::StrError());
352  return;
353  }
354  if (auto JSON = ReadMessage(In, Out)) {
355  if (auto Doc = json::parse(*JSON)) {
356  // Log the formatted message.
357  vlog(Out.Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
358  // Finally, execute the action for this JSON message.
359  if (!Dispatcher.call(*Doc, Out))
360  elog("JSON dispatch failed!");
361  } else {
362  // Parse error. Log the raw message.
363  vlog("<<< {0}\n", *JSON);
364  elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
365  }
366  }
367  }
368 }
static llvm::Optional< std::string > readStandardMessage(std::FILE *In, JSONOutput &Out)
JSONStreamStyle
Controls the way JSON-RPC messages are encoded (both input and output).
Encapsulates output and logs streams and provides thread-safe access to them.
Some operations such as code completion produce a set of candidates.
static bool readLine(std::FILE *In, std::string &Out)
static void logIncomingMessage(const llvm::Optional< json::Value > &ID, llvm::Optional< StringRef > Method, const json::Object *Error)
void log(Logger::Level, const llvm::formatv_object_base &)
Definition: Logger.cpp:28
static llvm::Optional< std::string > readDelimitedMessage(std::FILE *In, JSONOutput &Out)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Values in a Context are indexed by typed keys.
Definition: Context.h:41
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:67
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:56
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
Definition: Context.h:101
void replyError(ErrorCode Code, const llvm::StringRef &Message)
Sends an error response to the client, and logs it.
void mirrorInput(const Twine &Message)
Mirror Message into InputMirror stream.
Messages are delimited by a &#39;—&#39; line. Comment lines start with #.
Main JSONRPC entry point.
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:70
Position Pos
WithContext replaces Context::current() with a provided scope.
Definition: Context.h:190
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::function< void(const llvm::json::Value &)> Handler
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive()...
Definition: Context.h:122
bool call(const llvm::json::Value &Message, JSONOutput &Out) const
Parses a JSONRPC message and calls the Handler for it.
void call(llvm::StringRef Method, llvm::json::Value &&Params)
Sends a request to the client.
WithContextValue extends Context::current() with a single value.
Definition: Context.h:205
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
Definition: Trace.h:90
Records an event whose duration is the lifetime of the Span object.
Definition: Trace.h:83
void reply(llvm::json::Value &&Result)
Sends a successful reply.
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Definition: Trace.h:98
void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone)
Parses input queries from LSP client (coming from In) and runs call method of Dispatcher for each que...
static llvm::cl::opt< JSONStreamStyle > InputStyle("input-style", llvm::cl::desc("Input JSON stream encoding"), llvm::cl::values(clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"), clEnumValN(JSONStreamStyle::Delimited, "delimited", "messages delimited by --- lines, with # comment support")), llvm::cl::init(JSONStreamStyle::Standard))