blob: c1a7ef8aacd86dda6e4e60c0d355fec6be2ca755 [file] [log] [blame]
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +00001//===--- 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"
Sam McCalldd0566b2017-11-06 15:40:30 +000011#include "JSONExpr.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000012#include "ProtocolHandlers.h"
Sam McCall8567cb32017-11-02 09:21:51 +000013#include "Trace.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000014#include "llvm/ADT/SmallString.h"
Sam McCall94362c62017-11-07 14:45:31 +000015#include "llvm/ADT/StringExtras.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000016#include "llvm/Support/SourceMgr.h"
17#include "llvm/Support/YAMLParser.h"
Ilya Biryukov687b92a2017-05-16 15:23:55 +000018#include <istream>
19
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000020using namespace clang;
21using namespace clangd;
22
Sam McCalldd0566b2017-11-06 15:40:30 +000023void JSONOutput::writeMessage(const json::Expr &Message) {
24 std::string S;
25 llvm::raw_string_ostream OS(S);
26 if (Pretty)
27 OS << llvm::formatv("{0:2}", Message);
28 else
29 OS << Message;
30 OS.flush();
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000031
Benjamin Kramerd0b2ccd2017-02-10 14:08:40 +000032 std::lock_guard<std::mutex> Guard(StreamMutex);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000033 // Log without headers.
Sam McCalldd0566b2017-11-06 15:40:30 +000034 Logs << "--> " << S << '\n';
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000035 Logs.flush();
36
37 // Emit message with header.
Sam McCalldd0566b2017-11-06 15:40:30 +000038 Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000039 Outs.flush();
40}
41
Benjamin Kramere14bd422017-02-15 16:44:11 +000042void JSONOutput::log(const Twine &Message) {
Sam McCall8567cb32017-11-02 09:21:51 +000043 trace::log(Message);
Benjamin Kramere14bd422017-02-15 16:44:11 +000044 std::lock_guard<std::mutex> Guard(StreamMutex);
45 Logs << Message;
46 Logs.flush();
47}
48
Ilya Biryukove6dbb582017-10-10 09:08:47 +000049void JSONOutput::mirrorInput(const Twine &Message) {
50 if (!InputMirror)
51 return;
52
53 *InputMirror << Message;
54 InputMirror->flush();
55}
56
Sam McCalldd0566b2017-11-06 15:40:30 +000057void RequestContext::reply(json::Expr &&Result) {
58 if (!ID) {
Sam McCall8a5dded2017-10-12 13:29:58 +000059 Out.log("Attempted to reply to a notification!\n");
60 return;
61 }
Sam McCall9cfd9c92017-11-23 17:12:04 +000062 SPAN_ATTACH(tracer(), "Reply", Result);
Sam McCalldd0566b2017-11-06 15:40:30 +000063 Out.writeMessage(json::obj{
64 {"jsonrpc", "2.0"},
65 {"id", *ID},
66 {"result", std::move(Result)},
67 });
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000068}
69
Ilya Biryukov9e11c4c2017-11-15 18:04:56 +000070void RequestContext::replyError(ErrorCode code,
71 const llvm::StringRef &Message) {
Haojian Wu2375c922017-11-07 10:21:02 +000072 Out.log("Error " + Twine(static_cast<int>(code)) + ": " + Message + "\n");
Sam McCall9cfd9c92017-11-23 17:12:04 +000073 SPAN_ATTACH(tracer(), "Error",
74 (json::obj{{"code", static_cast<int>(code)},
75 {"message", Message.str()}}));
Sam McCalldd0566b2017-11-06 15:40:30 +000076 if (ID) {
77 Out.writeMessage(json::obj{
78 {"jsonrpc", "2.0"},
79 {"id", *ID},
Ilya Biryukov9e11c4c2017-11-15 18:04:56 +000080 {"error",
81 json::obj{{"code", static_cast<int>(code)}, {"message", Message}}},
Sam McCalldd0566b2017-11-06 15:40:30 +000082 });
Sam McCall8a5dded2017-10-12 13:29:58 +000083 }
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000084}
85
Sam McCalldd0566b2017-11-06 15:40:30 +000086void RequestContext::call(StringRef Method, json::Expr &&Params) {
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000087 // FIXME: Generate/Increment IDs for every request so that we can get proper
88 // replies once we need to.
Sam McCall9cfd9c92017-11-23 17:12:04 +000089 SPAN_ATTACH(tracer(), "Call",
90 (json::obj{{"method", Method.str()}, {"params", Params}}));
Sam McCalldd0566b2017-11-06 15:40:30 +000091 Out.writeMessage(json::obj{
92 {"jsonrpc", "2.0"},
93 {"id", 1},
94 {"method", Method},
95 {"params", std::move(Params)},
96 });
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000097}
98
Sam McCall8a5dded2017-10-12 13:29:58 +000099void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000100 assert(!Handlers.count(Method) && "Handler already registered!");
101 Handlers[Method] = std::move(H);
102}
103
104static void
Sam McCall8a5dded2017-10-12 13:29:58 +0000105callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
Sam McCalldd0566b2017-11-06 15:40:30 +0000106 llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr> ID,
Sam McCall8a5dded2017-10-12 13:29:58 +0000107 llvm::yaml::MappingNode *Params,
108 const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) {
109 llvm::SmallString<64> MethodStorage;
Sam McCall8567cb32017-11-02 09:21:51 +0000110 llvm::StringRef MethodStr = Method->getValue(MethodStorage);
111 auto I = Handlers.find(MethodStr);
Sam McCall8a5dded2017-10-12 13:29:58 +0000112 auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
Sam McCall9cfd9c92017-11-23 17:12:04 +0000113 Handler(RequestContext(Out, MethodStr, std::move(ID)), Params);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000114}
115
Sam McCall8a5dded2017-10-12 13:29:58 +0000116bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000117 llvm::SourceMgr SM;
118 llvm::yaml::Stream YAMLStream(Content, SM);
119
120 auto Doc = YAMLStream.begin();
121 if (Doc == YAMLStream.end())
122 return false;
123
Benjamin Kramerdecd8a72017-10-27 16:33:15 +0000124 auto *Object = dyn_cast_or_null<llvm::yaml::MappingNode>(Doc->getRoot());
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000125 if (!Object)
126 return false;
127
128 llvm::yaml::ScalarNode *Version = nullptr;
129 llvm::yaml::ScalarNode *Method = nullptr;
130 llvm::yaml::MappingNode *Params = nullptr;
Sam McCalldd0566b2017-11-06 15:40:30 +0000131 llvm::Optional<json::Expr> ID;
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000132 for (auto &NextKeyValue : *Object) {
Benjamin Kramerdecd8a72017-10-27 16:33:15 +0000133 auto *KeyString =
134 dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000135 if (!KeyString)
136 return false;
137
138 llvm::SmallString<10> KeyStorage;
139 StringRef KeyValue = KeyString->getValue(KeyStorage);
140 llvm::yaml::Node *Value = NextKeyValue.getValue();
141 if (!Value)
142 return false;
143
144 if (KeyValue == "jsonrpc") {
145 // This should be "2.0". Always.
146 Version = dyn_cast<llvm::yaml::ScalarNode>(Value);
147 if (!Version || Version->getRawValue() != "\"2.0\"")
148 return false;
149 } else if (KeyValue == "method") {
150 Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
151 } else if (KeyValue == "id") {
Sam McCalldd0566b2017-11-06 15:40:30 +0000152 // ID may be either a string or a number.
153 if (auto *IdNode = dyn_cast<llvm::yaml::ScalarNode>(Value)) {
154 llvm::SmallString<32> S;
155 llvm::StringRef V = IdNode->getValue(S);
156 if (IdNode->getRawValue().startswith("\"")) {
157 ID.emplace(V.str());
158 } else {
159 double D;
Sam McCall94362c62017-11-07 14:45:31 +0000160 // FIXME: this is locale-sensitive.
161 if (llvm::to_float(V, D))
Sam McCalldd0566b2017-11-06 15:40:30 +0000162 ID.emplace(D);
163 }
164 }
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000165 } else if (KeyValue == "params") {
166 if (!Method)
167 return false;
168 // We have to interleave the call of the function here, otherwise the
169 // YAMLParser will die because it can't go backwards. This is unfortunate
170 // because it will break clients that put the id after params. A possible
171 // fix would be to split the parsing and execution phases.
172 Params = dyn_cast<llvm::yaml::MappingNode>(Value);
Sam McCalldd0566b2017-11-06 15:40:30 +0000173 callHandler(Handlers, Method, std::move(ID), Params, UnknownHandler, Out);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000174 return true;
175 } else {
176 return false;
177 }
178 }
179
180 // In case there was a request with no params, call the handler on the
181 // leftovers.
182 if (!Method)
183 return false;
Sam McCalldd0566b2017-11-06 15:40:30 +0000184 callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000185
186 return true;
187}
Ilya Biryukovafb55542017-05-16 14:40:30 +0000188
189void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
190 JSONRPCDispatcher &Dispatcher,
191 bool &IsDone) {
192 while (In.good()) {
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000193 // A Language Server Protocol message starts with a set of HTTP headers,
194 // delimited by \r\n, and terminated by an empty line (\r\n).
195 unsigned long long ContentLength = 0;
196 while (In.good()) {
197 std::string Line;
198 std::getline(In, Line);
199 if (!In.good() && errno == EINTR) {
200 In.clear();
201 continue;
202 }
203
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000204 Out.mirrorInput(Line);
205 // Mirror '\n' that gets consumed by std::getline, but is not included in
206 // the resulting Line.
207 // Note that '\r' is part of Line, so we don't need to mirror it
208 // separately.
209 if (!In.eof())
210 Out.mirrorInput("\n");
211
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000212 llvm::StringRef LineRef(Line);
213
214 // We allow YAML-style comments in headers. Technically this isn't part
215 // of the LSP specification, but makes writing tests easier.
216 if (LineRef.startswith("#"))
217 continue;
218
219 // Content-Type is a specified header, but does nothing.
220 // Content-Length is a mandatory header. It specifies the length of the
221 // following JSON.
222 // It is unspecified what sequence headers must be supplied in, so we
223 // allow any sequence.
224 // The end of headers is signified by an empty line.
225 if (LineRef.consume_front("Content-Length: ")) {
226 if (ContentLength != 0) {
227 Out.log("Warning: Duplicate Content-Length header received. "
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000228 "The previous value for this message (" +
229 std::to_string(ContentLength) + ") was ignored.\n");
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000230 }
231
232 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
233 continue;
234 } else if (!LineRef.trim().empty()) {
235 // It's another header, ignore it.
236 continue;
237 } else {
238 // An empty line indicates the end of headers.
239 // Go ahead and read the JSON.
240 break;
241 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000242 }
243
Benjamin Kramer1d053792017-10-27 17:06:41 +0000244 // Guard against large messages. This is usually a bug in the client code
245 // and we don't want to crash downstream because of it.
246 if (ContentLength > 1 << 30) { // 1024M
247 In.ignore(ContentLength);
248 Out.log("Skipped overly large message of " + Twine(ContentLength) +
249 " bytes.\n");
250 continue;
251 }
252
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000253 if (ContentLength > 0) {
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000254 std::vector<char> JSON(ContentLength + 1, '\0');
Sam McCall8567cb32017-11-02 09:21:51 +0000255 llvm::StringRef JSONRef;
256 {
Sam McCall8567cb32017-11-02 09:21:51 +0000257 // Now read the JSON. Insert a trailing null byte as required by the
258 // YAML parser.
259 In.read(JSON.data(), ContentLength);
260 Out.mirrorInput(StringRef(JSON.data(), In.gcount()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000261
Sam McCall8567cb32017-11-02 09:21:51 +0000262 // If the stream is aborted before we read ContentLength bytes, In
263 // will have eofbit and failbit set.
264 if (!In) {
265 Out.log("Input was aborted. Read only " +
266 std::to_string(In.gcount()) + " bytes of expected " +
267 std::to_string(ContentLength) + ".\n");
268 break;
269 }
270
271 JSONRef = StringRef(JSON.data(), ContentLength);
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000272 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000273
Ilya Biryukovafb55542017-05-16 14:40:30 +0000274 // Log the message.
275 Out.log("<-- " + JSONRef + "\n");
276
277 // Finally, execute the action for this JSON message.
Sam McCall8a5dded2017-10-12 13:29:58 +0000278 if (!Dispatcher.call(JSONRef, Out))
Ilya Biryukovafb55542017-05-16 14:40:30 +0000279 Out.log("JSON dispatch failed!\n");
280
281 // If we're done, exit the loop.
282 if (IsDone)
283 break;
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000284 } else {
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000285 Out.log("Warning: Missing Content-Length header, or message has zero "
286 "length.\n");
Ilya Biryukovafb55542017-05-16 14:40:30 +0000287 }
288 }
289}