17bc52733SRiver Riddle //===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===// 27bc52733SRiver Riddle // 37bc52733SRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47bc52733SRiver Riddle // See https://llvm.org/LICENSE.txt for license information. 57bc52733SRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67bc52733SRiver Riddle // 77bc52733SRiver Riddle //===----------------------------------------------------------------------===// 87bc52733SRiver Riddle 9305d7185SRiver Riddle #include "mlir/Tools/lsp-server-support/Transport.h" 10516ccce7SIngo Müller #include "mlir/Support/ToolUtilities.h" 11305d7185SRiver Riddle #include "mlir/Tools/lsp-server-support/Logging.h" 12305d7185SRiver Riddle #include "mlir/Tools/lsp-server-support/Protocol.h" 137bc52733SRiver Riddle #include "llvm/ADT/SmallString.h" 147bc52733SRiver Riddle #include "llvm/Support/Errno.h" 157bc52733SRiver Riddle #include "llvm/Support/Error.h" 16305d7185SRiver Riddle #include <optional> 177bc52733SRiver Riddle #include <system_error> 187bc52733SRiver Riddle #include <utility> 197bc52733SRiver Riddle 207bc52733SRiver Riddle using namespace mlir; 217bc52733SRiver Riddle using namespace mlir::lsp; 227bc52733SRiver Riddle 237bc52733SRiver Riddle //===----------------------------------------------------------------------===// 247bc52733SRiver Riddle // Reply 257bc52733SRiver Riddle //===----------------------------------------------------------------------===// 267bc52733SRiver Riddle 277bc52733SRiver Riddle namespace { 287bc52733SRiver Riddle /// Function object to reply to an LSP call. 297bc52733SRiver Riddle /// Each instance must be called exactly once, otherwise: 307bc52733SRiver Riddle /// - if there was no reply, an error reply is sent 317bc52733SRiver Riddle /// - if there were multiple replies, only the first is sent 327bc52733SRiver Riddle class Reply { 337bc52733SRiver Riddle public: 34cbdf2ef8SRiver Riddle Reply(const llvm::json::Value &id, StringRef method, JSONTransport &transport, 35cbdf2ef8SRiver Riddle std::mutex &transportOutputMutex); 367bc52733SRiver Riddle Reply(Reply &&other); 377bc52733SRiver Riddle Reply &operator=(Reply &&) = delete; 387bc52733SRiver Riddle Reply(const Reply &) = delete; 397bc52733SRiver Riddle Reply &operator=(const Reply &) = delete; 407bc52733SRiver Riddle 417bc52733SRiver Riddle void operator()(llvm::Expected<llvm::json::Value> reply); 427bc52733SRiver Riddle 437bc52733SRiver Riddle private: 44d90159adSWalter Erquinigo std::string method; 457bc52733SRiver Riddle std::atomic<bool> replied = {false}; 467bc52733SRiver Riddle llvm::json::Value id; 477bc52733SRiver Riddle JSONTransport *transport; 48cbdf2ef8SRiver Riddle std::mutex &transportOutputMutex; 497bc52733SRiver Riddle }; 507bc52733SRiver Riddle } // namespace 517bc52733SRiver Riddle 527bc52733SRiver Riddle Reply::Reply(const llvm::json::Value &id, llvm::StringRef method, 53cbdf2ef8SRiver Riddle JSONTransport &transport, std::mutex &transportOutputMutex) 54b811ad6fSBrian Gesiak : method(method), id(id), transport(&transport), 55cbdf2ef8SRiver Riddle transportOutputMutex(transportOutputMutex) {} 567bc52733SRiver Riddle 577bc52733SRiver Riddle Reply::Reply(Reply &&other) 58b811ad6fSBrian Gesiak : method(other.method), replied(other.replied.load()), 59b811ad6fSBrian Gesiak id(std::move(other.id)), transport(other.transport), 60cbdf2ef8SRiver Riddle transportOutputMutex(other.transportOutputMutex) { 617bc52733SRiver Riddle other.transport = nullptr; 627bc52733SRiver Riddle } 637bc52733SRiver Riddle 647bc52733SRiver Riddle void Reply::operator()(llvm::Expected<llvm::json::Value> reply) { 657bc52733SRiver Riddle if (replied.exchange(true)) { 667bc52733SRiver Riddle Logger::error("Replied twice to message {0}({1})", method, id); 677bc52733SRiver Riddle assert(false && "must reply to each call only once!"); 687bc52733SRiver Riddle return; 697bc52733SRiver Riddle } 707bc52733SRiver Riddle assert(transport && "expected valid transport to reply to"); 717bc52733SRiver Riddle 72cbdf2ef8SRiver Riddle std::lock_guard<std::mutex> transportLock(transportOutputMutex); 737bc52733SRiver Riddle if (reply) { 747bc52733SRiver Riddle Logger::info("--> reply:{0}({1})", method, id); 757bc52733SRiver Riddle transport->reply(std::move(id), std::move(reply)); 767bc52733SRiver Riddle } else { 777bc52733SRiver Riddle llvm::Error error = reply.takeError(); 78*e40c5b42SLily Brown Logger::info("--> reply:{0}({1}): {2}", method, id, error); 797bc52733SRiver Riddle transport->reply(std::move(id), std::move(error)); 807bc52733SRiver Riddle } 817bc52733SRiver Riddle } 827bc52733SRiver Riddle 837bc52733SRiver Riddle //===----------------------------------------------------------------------===// 847bc52733SRiver Riddle // MessageHandler 857bc52733SRiver Riddle //===----------------------------------------------------------------------===// 867bc52733SRiver Riddle 877bc52733SRiver Riddle bool MessageHandler::onNotify(llvm::StringRef method, llvm::json::Value value) { 887bc52733SRiver Riddle Logger::info("--> {0}", method); 897bc52733SRiver Riddle 907bc52733SRiver Riddle if (method == "exit") 917bc52733SRiver Riddle return false; 927bc52733SRiver Riddle if (method == "$cancel") { 937bc52733SRiver Riddle // TODO: Add support for cancelling requests. 947bc52733SRiver Riddle } else { 957bc52733SRiver Riddle auto it = notificationHandlers.find(method); 967bc52733SRiver Riddle if (it != notificationHandlers.end()) 977bc52733SRiver Riddle it->second(std::move(value)); 987bc52733SRiver Riddle } 997bc52733SRiver Riddle return true; 1007bc52733SRiver Riddle } 1017bc52733SRiver Riddle 1027bc52733SRiver Riddle bool MessageHandler::onCall(llvm::StringRef method, llvm::json::Value params, 1037bc52733SRiver Riddle llvm::json::Value id) { 1047bc52733SRiver Riddle Logger::info("--> {0}({1})", method, id); 1057bc52733SRiver Riddle 106cbdf2ef8SRiver Riddle Reply reply(id, method, transport, transportOutputMutex); 1077bc52733SRiver Riddle 1087bc52733SRiver Riddle auto it = methodHandlers.find(method); 1097bc52733SRiver Riddle if (it != methodHandlers.end()) { 1107bc52733SRiver Riddle it->second(std::move(params), std::move(reply)); 1117bc52733SRiver Riddle } else { 1127bc52733SRiver Riddle reply(llvm::make_error<LSPError>("method not found: " + method.str(), 1137bc52733SRiver Riddle ErrorCode::MethodNotFound)); 1147bc52733SRiver Riddle } 1157bc52733SRiver Riddle return true; 1167bc52733SRiver Riddle } 1177bc52733SRiver Riddle 1187bc52733SRiver Riddle bool MessageHandler::onReply(llvm::json::Value id, 1197bc52733SRiver Riddle llvm::Expected<llvm::json::Value> result) { 120e24a7bbfSBrian Gesiak // Find the response handler in the mapping. If it exists, move it out of the 121e24a7bbfSBrian Gesiak // mapping and erase it. 122e24a7bbfSBrian Gesiak ResponseHandlerTy responseHandler; 123e24a7bbfSBrian Gesiak { 124e24a7bbfSBrian Gesiak std::lock_guard<std::mutex> responseHandlersLock(responseHandlersMutex); 125e24a7bbfSBrian Gesiak auto it = responseHandlers.find(debugString(id)); 126e24a7bbfSBrian Gesiak if (it != responseHandlers.end()) { 127e24a7bbfSBrian Gesiak responseHandler = std::move(it->second); 128e24a7bbfSBrian Gesiak responseHandlers.erase(it); 129e24a7bbfSBrian Gesiak } 130e24a7bbfSBrian Gesiak } 131e24a7bbfSBrian Gesiak 132e24a7bbfSBrian Gesiak // If we found a response handler, invoke it. Otherwise, log an error. 133e24a7bbfSBrian Gesiak if (responseHandler.second) { 134e24a7bbfSBrian Gesiak Logger::info("--> reply:{0}({1})", responseHandler.first, id); 135e24a7bbfSBrian Gesiak responseHandler.second(std::move(id), std::move(result)); 136e24a7bbfSBrian Gesiak } else { 1377bc52733SRiver Riddle Logger::error( 138e24a7bbfSBrian Gesiak "received a reply with ID {0}, but there was no such outgoing request", 139e24a7bbfSBrian Gesiak id); 1407bc52733SRiver Riddle if (!result) 1417bc52733SRiver Riddle llvm::consumeError(result.takeError()); 142e24a7bbfSBrian Gesiak } 1437bc52733SRiver Riddle return true; 1447bc52733SRiver Riddle } 1457bc52733SRiver Riddle 1467bc52733SRiver Riddle //===----------------------------------------------------------------------===// 1477bc52733SRiver Riddle // JSONTransport 1487bc52733SRiver Riddle //===----------------------------------------------------------------------===// 1497bc52733SRiver Riddle 1507bc52733SRiver Riddle /// Encode the given error as a JSON object. 1517bc52733SRiver Riddle static llvm::json::Object encodeError(llvm::Error error) { 1527bc52733SRiver Riddle std::string message; 1537bc52733SRiver Riddle ErrorCode code = ErrorCode::UnknownErrorCode; 1547bc52733SRiver Riddle auto handlerFn = [&](const LSPError &lspError) -> llvm::Error { 1557bc52733SRiver Riddle message = lspError.message; 1567bc52733SRiver Riddle code = lspError.code; 1577bc52733SRiver Riddle return llvm::Error::success(); 1587bc52733SRiver Riddle }; 1597bc52733SRiver Riddle if (llvm::Error unhandled = llvm::handleErrors(std::move(error), handlerFn)) 1607bc52733SRiver Riddle message = llvm::toString(std::move(unhandled)); 1617bc52733SRiver Riddle 1627bc52733SRiver Riddle return llvm::json::Object{ 1637bc52733SRiver Riddle {"message", std::move(message)}, 1647bc52733SRiver Riddle {"code", int64_t(code)}, 1657bc52733SRiver Riddle }; 1667bc52733SRiver Riddle } 1677bc52733SRiver Riddle 1687bc52733SRiver Riddle /// Decode the given JSON object into an error. 1697bc52733SRiver Riddle llvm::Error decodeError(const llvm::json::Object &o) { 17030c67587SKazu Hirata StringRef msg = o.getString("message").value_or("Unspecified error"); 1711da3a795SFangrui Song if (std::optional<int64_t> code = o.getInteger("code")) 1727bc52733SRiver Riddle return llvm::make_error<LSPError>(msg.str(), ErrorCode(*code)); 1737bc52733SRiver Riddle return llvm::make_error<llvm::StringError>(llvm::inconvertibleErrorCode(), 1747bc52733SRiver Riddle msg.str()); 1757bc52733SRiver Riddle } 1767bc52733SRiver Riddle 1777bc52733SRiver Riddle void JSONTransport::notify(StringRef method, llvm::json::Value params) { 1787bc52733SRiver Riddle sendMessage(llvm::json::Object{ 1797bc52733SRiver Riddle {"jsonrpc", "2.0"}, 1807bc52733SRiver Riddle {"method", method}, 1817bc52733SRiver Riddle {"params", std::move(params)}, 1827bc52733SRiver Riddle }); 1837bc52733SRiver Riddle } 1847bc52733SRiver Riddle void JSONTransport::call(StringRef method, llvm::json::Value params, 1857bc52733SRiver Riddle llvm::json::Value id) { 1867bc52733SRiver Riddle sendMessage(llvm::json::Object{ 1877bc52733SRiver Riddle {"jsonrpc", "2.0"}, 1887bc52733SRiver Riddle {"id", std::move(id)}, 1897bc52733SRiver Riddle {"method", method}, 1907bc52733SRiver Riddle {"params", std::move(params)}, 1917bc52733SRiver Riddle }); 1927bc52733SRiver Riddle } 1937bc52733SRiver Riddle void JSONTransport::reply(llvm::json::Value id, 1947bc52733SRiver Riddle llvm::Expected<llvm::json::Value> result) { 1957bc52733SRiver Riddle if (result) { 1967bc52733SRiver Riddle return sendMessage(llvm::json::Object{ 1977bc52733SRiver Riddle {"jsonrpc", "2.0"}, 1987bc52733SRiver Riddle {"id", std::move(id)}, 1997bc52733SRiver Riddle {"result", std::move(*result)}, 2007bc52733SRiver Riddle }); 2017bc52733SRiver Riddle } 2027bc52733SRiver Riddle 2037bc52733SRiver Riddle sendMessage(llvm::json::Object{ 2047bc52733SRiver Riddle {"jsonrpc", "2.0"}, 2057bc52733SRiver Riddle {"id", std::move(id)}, 2067bc52733SRiver Riddle {"error", encodeError(result.takeError())}, 2077bc52733SRiver Riddle }); 2087bc52733SRiver Riddle } 2097bc52733SRiver Riddle 2107bc52733SRiver Riddle llvm::Error JSONTransport::run(MessageHandler &handler) { 2117bc52733SRiver Riddle std::string json; 2127bc52733SRiver Riddle while (!feof(in)) { 2137bc52733SRiver Riddle if (ferror(in)) { 2147bc52733SRiver Riddle return llvm::errorCodeToError( 2157bc52733SRiver Riddle std::error_code(errno, std::system_category())); 2167bc52733SRiver Riddle } 2177bc52733SRiver Riddle 2187bc52733SRiver Riddle if (succeeded(readMessage(json))) { 2197bc52733SRiver Riddle if (llvm::Expected<llvm::json::Value> doc = llvm::json::parse(json)) { 2207bc52733SRiver Riddle if (!handleMessage(std::move(*doc), handler)) 2217bc52733SRiver Riddle return llvm::Error::success(); 2227bc52733SRiver Riddle } else { 2237bc52733SRiver Riddle Logger::error("JSON parse error: {0}", llvm::toString(doc.takeError())); 2247bc52733SRiver Riddle } 2257bc52733SRiver Riddle } 2267bc52733SRiver Riddle } 2277bc52733SRiver Riddle return llvm::errorCodeToError(std::make_error_code(std::errc::io_error)); 2287bc52733SRiver Riddle } 2297bc52733SRiver Riddle 2307bc52733SRiver Riddle void JSONTransport::sendMessage(llvm::json::Value msg) { 2317bc52733SRiver Riddle outputBuffer.clear(); 2327bc52733SRiver Riddle llvm::raw_svector_ostream os(outputBuffer); 2337bc52733SRiver Riddle os << llvm::formatv(prettyOutput ? "{0:2}\n" : "{0}", msg); 2347bc52733SRiver Riddle out << "Content-Length: " << outputBuffer.size() << "\r\n\r\n" 2357bc52733SRiver Riddle << outputBuffer; 2367bc52733SRiver Riddle out.flush(); 2377bc52733SRiver Riddle Logger::debug(">>> {0}\n", outputBuffer); 2387bc52733SRiver Riddle } 2397bc52733SRiver Riddle 2407bc52733SRiver Riddle bool JSONTransport::handleMessage(llvm::json::Value msg, 2417bc52733SRiver Riddle MessageHandler &handler) { 2427bc52733SRiver Riddle // Message must be an object with "jsonrpc":"2.0". 2437bc52733SRiver Riddle llvm::json::Object *object = msg.getAsObject(); 2447bc52733SRiver Riddle if (!object || 2450a81ace0SKazu Hirata object->getString("jsonrpc") != std::optional<StringRef>("2.0")) 2467bc52733SRiver Riddle return false; 2477bc52733SRiver Riddle 2487bc52733SRiver Riddle // `id` may be any JSON value. If absent, this is a notification. 2490a81ace0SKazu Hirata std::optional<llvm::json::Value> id; 2507bc52733SRiver Riddle if (llvm::json::Value *i = object->get("id")) 2517bc52733SRiver Riddle id = std::move(*i); 2521da3a795SFangrui Song std::optional<StringRef> method = object->getString("method"); 2537bc52733SRiver Riddle 2547bc52733SRiver Riddle // This is a response. 2557bc52733SRiver Riddle if (!method) { 2567bc52733SRiver Riddle if (!id) 2577bc52733SRiver Riddle return false; 2587bc52733SRiver Riddle if (auto *err = object->getObject("error")) 2597bc52733SRiver Riddle return handler.onReply(std::move(*id), decodeError(*err)); 2607bc52733SRiver Riddle // result should be given, use null if not. 2617bc52733SRiver Riddle llvm::json::Value result = nullptr; 2627bc52733SRiver Riddle if (llvm::json::Value *r = object->get("result")) 2637bc52733SRiver Riddle result = std::move(*r); 2647bc52733SRiver Riddle return handler.onReply(std::move(*id), std::move(result)); 2657bc52733SRiver Riddle } 2667bc52733SRiver Riddle 2677bc52733SRiver Riddle // Params should be given, use null if not. 2687bc52733SRiver Riddle llvm::json::Value params = nullptr; 2697bc52733SRiver Riddle if (llvm::json::Value *p = object->get("params")) 2707bc52733SRiver Riddle params = std::move(*p); 2717bc52733SRiver Riddle 2727bc52733SRiver Riddle if (id) 2737bc52733SRiver Riddle return handler.onCall(*method, std::move(params), std::move(*id)); 2747bc52733SRiver Riddle return handler.onNotify(*method, std::move(params)); 2757bc52733SRiver Riddle } 2767bc52733SRiver Riddle 2777bc52733SRiver Riddle /// Tries to read a line up to and including \n. 2787bc52733SRiver Riddle /// If failing, feof(), ferror(), or shutdownRequested() will be set. 2797bc52733SRiver Riddle LogicalResult readLine(std::FILE *in, SmallVectorImpl<char> &out) { 2807bc52733SRiver Riddle // Big enough to hold any reasonable header line. May not fit content lines 2817bc52733SRiver Riddle // in delimited mode, but performance doesn't matter for that mode. 2827bc52733SRiver Riddle static constexpr int bufSize = 128; 2837bc52733SRiver Riddle size_t size = 0; 2847bc52733SRiver Riddle out.clear(); 2857bc52733SRiver Riddle for (;;) { 2867bc52733SRiver Riddle out.resize_for_overwrite(size + bufSize); 2877bc52733SRiver Riddle if (!std::fgets(&out[size], bufSize, in)) 2887bc52733SRiver Riddle return failure(); 2897bc52733SRiver Riddle 2907bc52733SRiver Riddle clearerr(in); 2917bc52733SRiver Riddle 2927bc52733SRiver Riddle // If the line contained null bytes, anything after it (including \n) will 2937bc52733SRiver Riddle // be ignored. Fortunately this is not a legal header or JSON. 2947bc52733SRiver Riddle size_t read = std::strlen(&out[size]); 2957bc52733SRiver Riddle if (read > 0 && out[size + read - 1] == '\n') { 2967bc52733SRiver Riddle out.resize(size + read); 2977bc52733SRiver Riddle return success(); 2987bc52733SRiver Riddle } 2997bc52733SRiver Riddle size += read; 3007bc52733SRiver Riddle } 3017bc52733SRiver Riddle } 3027bc52733SRiver Riddle 30370c73d1bSKazu Hirata // Returns std::nullopt when: 3047bc52733SRiver Riddle // - ferror(), feof(), or shutdownRequested() are set. 3057bc52733SRiver Riddle // - Content-Length is missing or empty (protocol error) 3067bc52733SRiver Riddle LogicalResult JSONTransport::readStandardMessage(std::string &json) { 3077bc52733SRiver Riddle // A Language Server Protocol message starts with a set of HTTP headers, 3087bc52733SRiver Riddle // delimited by \r\n, and terminated by an empty line (\r\n). 3097bc52733SRiver Riddle unsigned long long contentLength = 0; 3107bc52733SRiver Riddle llvm::SmallString<128> line; 3117bc52733SRiver Riddle while (true) { 3127bc52733SRiver Riddle if (feof(in) || ferror(in) || failed(readLine(in, line))) 3137bc52733SRiver Riddle return failure(); 3147bc52733SRiver Riddle 3157bc52733SRiver Riddle // Content-Length is a mandatory header, and the only one we handle. 3167bc52733SRiver Riddle StringRef lineRef = line; 3177bc52733SRiver Riddle if (lineRef.consume_front("Content-Length: ")) { 3187bc52733SRiver Riddle llvm::getAsUnsignedInteger(lineRef.trim(), 0, contentLength); 3197bc52733SRiver Riddle } else if (!lineRef.trim().empty()) { 3207bc52733SRiver Riddle // It's another header, ignore it. 3217bc52733SRiver Riddle continue; 3227bc52733SRiver Riddle } else { 3237bc52733SRiver Riddle // An empty line indicates the end of headers. Go ahead and read the JSON. 3247bc52733SRiver Riddle break; 3257bc52733SRiver Riddle } 3267bc52733SRiver Riddle } 3277bc52733SRiver Riddle 3287bc52733SRiver Riddle // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999" 3297bc52733SRiver Riddle if (contentLength == 0 || contentLength > 1 << 30) 3307bc52733SRiver Riddle return failure(); 3317bc52733SRiver Riddle 3327bc52733SRiver Riddle json.resize(contentLength); 3337bc52733SRiver Riddle for (size_t pos = 0, read; pos < contentLength; pos += read) { 3347bc52733SRiver Riddle read = std::fread(&json[pos], 1, contentLength - pos, in); 3357bc52733SRiver Riddle if (read == 0) 3367bc52733SRiver Riddle return failure(); 3377bc52733SRiver Riddle 3387bc52733SRiver Riddle // If we're done, the error was transient. If we're not done, either it was 3397bc52733SRiver Riddle // transient or we'll see it again on retry. 3407bc52733SRiver Riddle clearerr(in); 3417bc52733SRiver Riddle pos += read; 3427bc52733SRiver Riddle } 3437bc52733SRiver Riddle return success(); 3447bc52733SRiver Riddle } 3457bc52733SRiver Riddle 3467bc52733SRiver Riddle /// For lit tests we support a simplified syntax: 3477bc52733SRiver Riddle /// - messages are delimited by '// -----' on a line by itself 3487bc52733SRiver Riddle /// - lines starting with // are ignored. 3497bc52733SRiver Riddle /// This is a testing path, so favor simplicity over performance here. 3507bc52733SRiver Riddle /// When returning failure: feof(), ferror(), or shutdownRequested() will be 3517bc52733SRiver Riddle /// set. 3527bc52733SRiver Riddle LogicalResult JSONTransport::readDelimitedMessage(std::string &json) { 3537bc52733SRiver Riddle json.clear(); 3547bc52733SRiver Riddle llvm::SmallString<128> line; 3557bc52733SRiver Riddle while (succeeded(readLine(in, line))) { 3567bc52733SRiver Riddle StringRef lineRef = line.str().trim(); 35788d319a2SKazu Hirata if (lineRef.starts_with("//")) { 3587bc52733SRiver Riddle // Found a delimiter for the message. 359516ccce7SIngo Müller if (lineRef == kDefaultSplitMarker) 3607bc52733SRiver Riddle break; 3617bc52733SRiver Riddle continue; 3627bc52733SRiver Riddle } 3637bc52733SRiver Riddle 3647bc52733SRiver Riddle json += line; 3657bc52733SRiver Riddle } 3667bc52733SRiver Riddle 3677bc52733SRiver Riddle return failure(ferror(in)); 3687bc52733SRiver Riddle } 369