| // Copyright 2021 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "common/lsp/json-rpc-dispatcher.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| namespace lsp { | |
| void JsonRpcDispatcher::DispatchMessage(absl::string_view data) { | |
| nlohmann::json request; | |
| try { | |
| request = nlohmann::json::parse(data); | |
| } catch (const std::exception &e) { | |
| ++statistic_counters_[e.what()]; | |
| ++exception_count_; | |
| SendReply(CreateError(request, kParseError, e.what())); | |
| return; | |
| } | |
| if (request.find("method") == request.end()) { | |
| SendReply( | |
| CreateError(request, kMethodNotFound, "Method required in request")); | |
| ++statistic_counters_["Request without method"]; | |
| return; | |
| } | |
| const std::string &method = request["method"]; | |
| // Direct dispatch, later maybe send to an executor that returns futures ? | |
| const bool is_notification = (request.find("id") == request.end()); | |
| VLOG(1) << "Got " << (is_notification ? "notification" : "method call") | |
| << " '" << method << "'; req-size: " << data.size(); | |
| bool handled = false; | |
| if (is_notification) { | |
| handled = CallNotification(request, method); | |
| } else { | |
| handled = CallRequestHandler(request, method); | |
| } | |
| ++statistic_counters_[method + (handled ? "" : " (unhandled)") + | |
| (is_notification ? " ev" : " RPC")]; | |
| } | |
| bool JsonRpcDispatcher::CallNotification(const nlohmann::json &req, | |
| const std::string &method) { | |
| const auto &found = notifications_.find(method); | |
| if (found == notifications_.end()) { | |
| LOG(ERROR) << "Unhandled notification '" << method << "'"; | |
| return false; | |
| } | |
| try { | |
| found->second(req["params"]); | |
| return true; | |
| } catch (const std::exception &e) { | |
| ++exception_count_; | |
| ++statistic_counters_[method + " : " + e.what()]; | |
| LOG(ERROR) << "Notification error for '" << method << "' :" << e.what(); | |
| } | |
| return false; | |
| } | |
| bool JsonRpcDispatcher::CallRequestHandler(const nlohmann::json &req, | |
| const std::string &method) { | |
| const auto &found = handlers_.find(method); | |
| if (found == handlers_.end()) { | |
| SendReply(CreateError(req, kMethodNotFound, | |
| "method '" + method + "' not found.")); | |
| LOG(ERROR) << "Unhandled method '" << method << "'"; | |
| return false; | |
| } | |
| try { | |
| SendReply(MakeResponse(req, found->second(req["params"]))); | |
| return true; | |
| } catch (const std::exception &e) { | |
| ++exception_count_; | |
| ++statistic_counters_[method + " : " + e.what()]; | |
| SendReply(CreateError(req, kInternalError, e.what())); | |
| LOG(ERROR) << "Method error for '" << method << "' :" << e.what(); | |
| } | |
| return false; | |
| } | |
| void JsonRpcDispatcher::SendNotification(const std::string &method, | |
| const nlohmann::json ¬ification) { | |
| nlohmann::json result = {{"jsonrpc", "2.0"}}; | |
| result["method"] = method; | |
| result["params"] = notification; | |
| SendReply(result); | |
| } | |
| /*static*/ nlohmann::json JsonRpcDispatcher::CreateError( | |
| const nlohmann::json &request, int code, absl::string_view message) { | |
| nlohmann::json result = { | |
| {"jsonrpc", "2.0"}, | |
| }; | |
| result["error"] = {{"code", code}}; | |
| if (!message.empty()) { | |
| result["error"]["message"] = message; | |
| } | |
| if (request.find("id") != request.end()) { | |
| result["id"] = request["id"]; | |
| } | |
| return result; | |
| } | |
| /*static*/ nlohmann::json JsonRpcDispatcher::MakeResponse( | |
| const nlohmann::json &request, const nlohmann::json &call_result) { | |
| nlohmann::json result = { | |
| {"jsonrpc", "2.0"}, | |
| }; | |
| result["id"] = request["id"]; | |
| result["result"] = call_result; | |
| return result; | |
| } | |
| void JsonRpcDispatcher::SendReply(const nlohmann::json &response) { | |
| std::stringstream out_bytes; | |
| out_bytes << response << "\n"; | |
| write_fun_(out_bytes.str()); | |
| } | |
| } // namespace lsp | |
| } // namespace verible |