🐛 fix(core): return JSON-RPC protocol errors
This commit is contained in:
@@ -233,13 +233,22 @@ namespace lsp::core
|
||||
protocol::ErrorCodes code,
|
||||
std::string_view message)
|
||||
{
|
||||
protocol::ResponseMessage response;
|
||||
response.id = std::move(id);
|
||||
response.error = protocol::ResponseError{
|
||||
.jsonrpc = "2.0",
|
||||
.code = static_cast<protocol::integer>(code),
|
||||
.message = std::string(message),
|
||||
.data = std::nullopt,
|
||||
protocol::LSPAny response_id(std::nullptr_t{});
|
||||
if (id)
|
||||
{
|
||||
response_id = std::visit(
|
||||
[](const auto& value) -> protocol::LSPAny { return value; }, *id);
|
||||
}
|
||||
|
||||
protocol::LSPObject error{
|
||||
{ "code", static_cast<protocol::integer>(code) },
|
||||
{ "message", protocol::string(message) },
|
||||
};
|
||||
|
||||
protocol::LSPObject response{
|
||||
{ "jsonrpc", protocol::string("2.0") },
|
||||
{ "id", std::move(response_id) },
|
||||
{ "error", std::move(error) },
|
||||
};
|
||||
return transform::Serialize(response).value();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ export namespace lsp::core
|
||||
void SendError(const protocol::RequestMessage& request,
|
||||
protocol::ErrorCodes code,
|
||||
std::string_view message);
|
||||
void SendError(std::optional<protocol::RequestId> id,
|
||||
protocol::ErrorCodes code,
|
||||
std::string_view message);
|
||||
|
||||
std::istream& input_;
|
||||
std::ostream& output_;
|
||||
@@ -82,6 +85,32 @@ export namespace lsp::core
|
||||
|
||||
namespace lsp::core
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<protocol::RequestId> ExtractRequestId(
|
||||
const protocol::LSPObject& object)
|
||||
{
|
||||
const auto id = object.find("id");
|
||||
if (id == object.end())
|
||||
return std::nullopt;
|
||||
if (id->second.Is<protocol::integer>())
|
||||
return protocol::RequestId{ id->second.Get<protocol::integer>() };
|
||||
if (id->second.Is<protocol::uinteger>())
|
||||
{
|
||||
const auto value = id->second.Get<protocol::uinteger>();
|
||||
if (value <= static_cast<protocol::uinteger>(
|
||||
std::numeric_limits<protocol::integer>::max()))
|
||||
{
|
||||
return protocol::RequestId{ static_cast<protocol::integer>(value) };
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
if (id->second.Is<protocol::string>())
|
||||
return protocol::RequestId{ id->second.Get<protocol::string>() };
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
LspServer::LspServer(std::istream& input,
|
||||
std::ostream& output,
|
||||
ProviderRegistrar registrar,
|
||||
@@ -190,13 +219,30 @@ namespace lsp::core
|
||||
void LspServer::HandleMessage(const std::string& raw_message)
|
||||
{
|
||||
auto any = transform::Deserialize<protocol::LSPAny>(raw_message);
|
||||
if (!any || !any->Is<protocol::LSPObject>())
|
||||
if (!any)
|
||||
{
|
||||
spdlog::warn("Failed to parse message: {}", raw_message);
|
||||
SendError(std::nullopt, protocol::ErrorCodes::ParseError, "Parse error");
|
||||
return;
|
||||
}
|
||||
if (!any->Is<protocol::LSPObject>())
|
||||
{
|
||||
SendError(std::nullopt,
|
||||
protocol::ErrorCodes::InvalidRequest,
|
||||
"Invalid Request");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& object = any->Get<protocol::LSPObject>();
|
||||
const auto id = ExtractRequestId(object);
|
||||
const auto jsonrpc = object.find("jsonrpc");
|
||||
if (jsonrpc == object.end() ||
|
||||
!jsonrpc->second.Is<protocol::string>() ||
|
||||
jsonrpc->second.Get<protocol::string>() != "2.0")
|
||||
{
|
||||
SendError(id, protocol::ErrorCodes::InvalidRequest, "Invalid Request");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool has_id = object.contains("id");
|
||||
const bool has_method = object.contains("method");
|
||||
const bool has_result = object.contains("result");
|
||||
@@ -204,15 +250,33 @@ namespace lsp::core
|
||||
|
||||
if (has_method && has_id)
|
||||
{
|
||||
const auto method = object.find("method");
|
||||
if (!id || !method->second.Is<protocol::string>())
|
||||
{
|
||||
SendError(id, protocol::ErrorCodes::InvalidRequest, "Invalid Request");
|
||||
return;
|
||||
}
|
||||
if (auto request = transform::Deserialize<protocol::RequestMessage>(raw_message))
|
||||
{
|
||||
HandleRequest(*request);
|
||||
}
|
||||
else
|
||||
spdlog::warn("Failed to parse request message");
|
||||
{
|
||||
SendError(id, protocol::ErrorCodes::InvalidRequest, "Invalid Request");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_method)
|
||||
{
|
||||
const auto method = object.find("method");
|
||||
if (!method->second.Is<protocol::string>())
|
||||
{
|
||||
SendError(std::nullopt,
|
||||
protocol::ErrorCodes::InvalidRequest,
|
||||
"Invalid Request");
|
||||
return;
|
||||
}
|
||||
if (auto notification =
|
||||
transform::Deserialize<protocol::NotificationMessage>(raw_message))
|
||||
{
|
||||
@@ -220,12 +284,14 @@ namespace lsp::core
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::warn("Failed to parse notification message");
|
||||
SendError(std::nullopt,
|
||||
protocol::ErrorCodes::InvalidRequest,
|
||||
"Invalid Request");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_id && (has_result || has_error))
|
||||
if (has_id && (has_result != has_error))
|
||||
{
|
||||
if (auto response = transform::Deserialize<protocol::ResponseMessage>(raw_message))
|
||||
HandleResponse(*response);
|
||||
@@ -234,7 +300,13 @@ namespace lsp::core
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::warn("Unrecognized message: {}", raw_message);
|
||||
if (has_result || has_error)
|
||||
{
|
||||
spdlog::warn("Ignoring invalid response-like message");
|
||||
return;
|
||||
}
|
||||
|
||||
SendError(id, protocol::ErrorCodes::InvalidRequest, "Invalid Request");
|
||||
}
|
||||
|
||||
void LspServer::SendMessage(const std::string& message)
|
||||
@@ -484,6 +556,13 @@ namespace lsp::core
|
||||
protocol::ErrorCodes code,
|
||||
std::string_view message)
|
||||
{
|
||||
SendMessage(BuildErrorResponseMessage(request, code, message));
|
||||
SendError(request.id, code, message);
|
||||
}
|
||||
|
||||
void LspServer::SendError(std::optional<protocol::RequestId> id,
|
||||
protocol::ErrorCodes code,
|
||||
std::string_view message)
|
||||
{
|
||||
SendMessage(BuildErrorResponseMessage(std::move(id), code, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ export namespace lsp::protocol
|
||||
|
||||
struct ResponseError
|
||||
{
|
||||
string jsonrpc = "2.0";
|
||||
integer code;
|
||||
string message;
|
||||
std::optional<LSPAny> data;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace glz
|
||||
struct meta<lsp::protocol::ResponseError>
|
||||
{
|
||||
using T = lsp::protocol::ResponseError;
|
||||
static constexpr auto value = glz::object(&T::jsonrpc, &T::code, &T::message, &T::data);
|
||||
static constexpr auto value = glz::object(&T::code, &T::message, &T::data);
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
Reference in New Issue
Block a user