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