blob: 4abe7b7d69b3400a4402375a1577dc93e328ee91 [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"
15#include "llvm/Support/SourceMgr.h"
16#include "llvm/Support/YAMLParser.h"
Ilya Biryukov687b92a2017-05-16 15:23:55 +000017#include <istream>
18
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000019using namespace clang;
20using namespace clangd;
21
Sam McCalldd0566b2017-11-06 15:40:30 +000022void JSONOutput::writeMessage(const json::Expr &Message) {
23 std::string S;
24 llvm::raw_string_ostream OS(S);
25 if (Pretty)
26 OS << llvm::formatv("{0:2}", Message);
27 else
28 OS << Message;
29 OS.flush();
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000030
Benjamin Kramerd0b2ccd2017-02-10 14:08:40 +000031 std::lock_guard<std::mutex> Guard(StreamMutex);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000032 // Log without headers.
Sam McCalldd0566b2017-11-06 15:40:30 +000033 Logs << "--> " << S << '\n';
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000034 Logs.flush();
35
36 // Emit message with header.
Sam McCalldd0566b2017-11-06 15:40:30 +000037 Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000038 Outs.flush();
39}
40
Benjamin Kramere14bd422017-02-15 16:44:11 +000041void JSONOutput::log(const Twine &Message) {
Sam McCall8567cb32017-11-02 09:21:51 +000042 trace::log(Message);
Benjamin Kramere14bd422017-02-15 16:44:11 +000043 std::lock_guard<std::mutex> Guard(StreamMutex);
44 Logs << Message;
45 Logs.flush();
46}
47
Ilya Biryukove6dbb582017-10-10 09:08:47 +000048void JSONOutput::mirrorInput(const Twine &Message) {
49 if (!InputMirror)
50 return;
51
52 *InputMirror << Message;
53 InputMirror->flush();
54}
55
Sam McCalldd0566b2017-11-06 15:40:30 +000056void RequestContext::reply(json::Expr &&Result) {
57 if (!ID) {
Sam McCall8a5dded2017-10-12 13:29:58 +000058 Out.log("Attempted to reply to a notification!\n");
59 return;
60 }
Sam McCalldd0566b2017-11-06 15:40:30 +000061 Out.writeMessage(json::obj{
62 {"jsonrpc", "2.0"},
63 {"id", *ID},
64 {"result", std::move(Result)},
65 });
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000066}
67
Sam McCall8a5dded2017-10-12 13:29:58 +000068void RequestContext::replyError(int code, const llvm::StringRef &Message) {
69 Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
Sam McCalldd0566b2017-11-06 15:40:30 +000070 if (ID) {
71 Out.writeMessage(json::obj{
72 {"jsonrpc", "2.0"},
73 {"id", *ID},
74 {"error", json::obj{{"code", code}, {"message", Message}}},
75 });
Sam McCall8a5dded2017-10-12 13:29:58 +000076 }
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000077}
78
Sam McCalldd0566b2017-11-06 15:40:30 +000079void RequestContext::call(StringRef Method, json::Expr &&Params) {
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000080 // FIXME: Generate/Increment IDs for every request so that we can get proper
81 // replies once we need to.
Sam McCalldd0566b2017-11-06 15:40:30 +000082 Out.writeMessage(json::obj{
83 {"jsonrpc", "2.0"},
84 {"id", 1},
85 {"method", Method},
86 {"params", std::move(Params)},
87 });
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000088}
89
Sam McCall8a5dded2017-10-12 13:29:58 +000090void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000091 assert(!Handlers.count(Method) && "Handler already registered!");
92 Handlers[Method] = std::move(H);
93}
94
95static void
Sam McCall8a5dded2017-10-12 13:29:58 +000096callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
Sam McCalldd0566b2017-11-06 15:40:30 +000097 llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr> ID,
Sam McCall8a5dded2017-10-12 13:29:58 +000098 llvm::yaml::MappingNode *Params,
99 const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) {
100 llvm::SmallString<64> MethodStorage;
Sam McCall8567cb32017-11-02 09:21:51 +0000101 llvm::StringRef MethodStr = Method->getValue(MethodStorage);
102 auto I = Handlers.find(MethodStr);
Sam McCall8a5dded2017-10-12 13:29:58 +0000103 auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
Sam McCall8567cb32017-11-02 09:21:51 +0000104 trace::Span Tracer(MethodStr);
Sam McCalldd0566b2017-11-06 15:40:30 +0000105 Handler(RequestContext(Out, std::move(ID)), Params);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000106}
107
Sam McCall8a5dded2017-10-12 13:29:58 +0000108bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000109 llvm::SourceMgr SM;
110 llvm::yaml::Stream YAMLStream(Content, SM);
111
112 auto Doc = YAMLStream.begin();
113 if (Doc == YAMLStream.end())
114 return false;
115
Benjamin Kramerdecd8a72017-10-27 16:33:15 +0000116 auto *Object = dyn_cast_or_null<llvm::yaml::MappingNode>(Doc->getRoot());
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000117 if (!Object)
118 return false;
119
120 llvm::yaml::ScalarNode *Version = nullptr;
121 llvm::yaml::ScalarNode *Method = nullptr;
122 llvm::yaml::MappingNode *Params = nullptr;
Sam McCalldd0566b2017-11-06 15:40:30 +0000123 llvm::Optional<json::Expr> ID;
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000124 for (auto &NextKeyValue : *Object) {
Benjamin Kramerdecd8a72017-10-27 16:33:15 +0000125 auto *KeyString =
126 dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000127 if (!KeyString)
128 return false;
129
130 llvm::SmallString<10> KeyStorage;
131 StringRef KeyValue = KeyString->getValue(KeyStorage);
132 llvm::yaml::Node *Value = NextKeyValue.getValue();
133 if (!Value)
134 return false;
135
136 if (KeyValue == "jsonrpc") {
137 // This should be "2.0". Always.
138 Version = dyn_cast<llvm::yaml::ScalarNode>(Value);
139 if (!Version || Version->getRawValue() != "\"2.0\"")
140 return false;
141 } else if (KeyValue == "method") {
142 Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
143 } else if (KeyValue == "id") {
Sam McCalldd0566b2017-11-06 15:40:30 +0000144 // ID may be either a string or a number.
145 if (auto *IdNode = dyn_cast<llvm::yaml::ScalarNode>(Value)) {
146 llvm::SmallString<32> S;
147 llvm::StringRef V = IdNode->getValue(S);
148 if (IdNode->getRawValue().startswith("\"")) {
149 ID.emplace(V.str());
150 } else {
151 double D;
152 if (!V.getAsDouble(D))
153 ID.emplace(D);
154 }
155 }
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000156 } else if (KeyValue == "params") {
157 if (!Method)
158 return false;
159 // We have to interleave the call of the function here, otherwise the
160 // YAMLParser will die because it can't go backwards. This is unfortunate
161 // because it will break clients that put the id after params. A possible
162 // fix would be to split the parsing and execution phases.
163 Params = dyn_cast<llvm::yaml::MappingNode>(Value);
Sam McCalldd0566b2017-11-06 15:40:30 +0000164 callHandler(Handlers, Method, std::move(ID), Params, UnknownHandler, Out);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000165 return true;
166 } else {
167 return false;
168 }
169 }
170
171 // In case there was a request with no params, call the handler on the
172 // leftovers.
173 if (!Method)
174 return false;
Sam McCalldd0566b2017-11-06 15:40:30 +0000175 callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000176
177 return true;
178}
Ilya Biryukovafb55542017-05-16 14:40:30 +0000179
180void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
181 JSONRPCDispatcher &Dispatcher,
182 bool &IsDone) {
183 while (In.good()) {
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000184 // A Language Server Protocol message starts with a set of HTTP headers,
185 // delimited by \r\n, and terminated by an empty line (\r\n).
186 unsigned long long ContentLength = 0;
187 while (In.good()) {
188 std::string Line;
189 std::getline(In, Line);
190 if (!In.good() && errno == EINTR) {
191 In.clear();
192 continue;
193 }
194
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000195 Out.mirrorInput(Line);
196 // Mirror '\n' that gets consumed by std::getline, but is not included in
197 // the resulting Line.
198 // Note that '\r' is part of Line, so we don't need to mirror it
199 // separately.
200 if (!In.eof())
201 Out.mirrorInput("\n");
202
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000203 llvm::StringRef LineRef(Line);
204
205 // We allow YAML-style comments in headers. Technically this isn't part
206 // of the LSP specification, but makes writing tests easier.
207 if (LineRef.startswith("#"))
208 continue;
209
210 // Content-Type is a specified header, but does nothing.
211 // Content-Length is a mandatory header. It specifies the length of the
212 // following JSON.
213 // It is unspecified what sequence headers must be supplied in, so we
214 // allow any sequence.
215 // The end of headers is signified by an empty line.
216 if (LineRef.consume_front("Content-Length: ")) {
217 if (ContentLength != 0) {
218 Out.log("Warning: Duplicate Content-Length header received. "
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000219 "The previous value for this message (" +
220 std::to_string(ContentLength) + ") was ignored.\n");
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000221 }
222
223 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
224 continue;
225 } else if (!LineRef.trim().empty()) {
226 // It's another header, ignore it.
227 continue;
228 } else {
229 // An empty line indicates the end of headers.
230 // Go ahead and read the JSON.
231 break;
232 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000233 }
234
Benjamin Kramer1d053792017-10-27 17:06:41 +0000235 // Guard against large messages. This is usually a bug in the client code
236 // and we don't want to crash downstream because of it.
237 if (ContentLength > 1 << 30) { // 1024M
238 In.ignore(ContentLength);
239 Out.log("Skipped overly large message of " + Twine(ContentLength) +
240 " bytes.\n");
241 continue;
242 }
243
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000244 if (ContentLength > 0) {
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000245 std::vector<char> JSON(ContentLength + 1, '\0');
Sam McCall8567cb32017-11-02 09:21:51 +0000246 llvm::StringRef JSONRef;
247 {
248 trace::Span Tracer("Reading request");
249 // Now read the JSON. Insert a trailing null byte as required by the
250 // YAML parser.
251 In.read(JSON.data(), ContentLength);
252 Out.mirrorInput(StringRef(JSON.data(), In.gcount()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000253
Sam McCall8567cb32017-11-02 09:21:51 +0000254 // If the stream is aborted before we read ContentLength bytes, In
255 // will have eofbit and failbit set.
256 if (!In) {
257 Out.log("Input was aborted. Read only " +
258 std::to_string(In.gcount()) + " bytes of expected " +
259 std::to_string(ContentLength) + ".\n");
260 break;
261 }
262
263 JSONRef = StringRef(JSON.data(), ContentLength);
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000264 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000265
Ilya Biryukovafb55542017-05-16 14:40:30 +0000266 // Log the message.
267 Out.log("<-- " + JSONRef + "\n");
268
269 // Finally, execute the action for this JSON message.
Sam McCall8a5dded2017-10-12 13:29:58 +0000270 if (!Dispatcher.call(JSONRef, Out))
Ilya Biryukovafb55542017-05-16 14:40:30 +0000271 Out.log("JSON dispatch failed!\n");
272
273 // If we're done, exit the loop.
274 if (IsDone)
275 break;
Ilya Biryukov1fab4f82017-09-04 12:28:15 +0000276 } else {
Ilya Biryukove6dbb582017-10-10 09:08:47 +0000277 Out.log("Warning: Missing Content-Length header, or message has zero "
278 "length.\n");
Ilya Biryukovafb55542017-05-16 14:40:30 +0000279 }
280 }
281}