JSON-RPC 2.0
JSON-RPC 2.0 Modern C++ Library
Loading...
Searching...
No Matches
dispatcher.cpp
Go to the documentation of this file.
2
3#include <spdlog/spdlog.h>
4
5namespace jsonrpc::server {
6
7Dispatcher::Dispatcher(bool enable_multithreading, size_t num_threads)
8 : enable_multithreading_(enable_multithreading),
9 thread_pool_(enable_multithreading ? num_threads : 0) {
10 // Optionally log or perform additional setup if needed
11 if (enable_multithreading_) {
12 spdlog::info(
13 "Dispatcher initialized with multithreading support using {} threads",
14 num_threads);
15 } else {
16 spdlog::info("Dispatcher initialized without multithreading support");
17 }
18}
19
20auto Dispatcher::DispatchRequest(const std::string &request_str)
21 -> std::optional<std::string> {
22 auto request_json = ParseAndValidateJson(request_str);
23 if (!request_json.has_value()) {
25 }
26
27 if (request_json->is_array()) {
28 return DispatchBatchRequest(*request_json);
29 }
30
31 return DispatchSingleRequest(*request_json);
32}
33
34auto Dispatcher::ParseAndValidateJson(const std::string &request_str)
35 -> std::optional<nlohmann::json> {
36 try {
37 return nlohmann::json::parse(request_str);
38 } catch (const nlohmann::json::parse_error &) {
39 spdlog::error("JSON parsing error: {}", request_str);
40 return std::nullopt;
41 }
42}
43
44auto Dispatcher::DispatchSingleRequest(const nlohmann::json &request_json)
45 -> std::optional<std::string> {
46 auto response_json = DispatchSingleRequestInner(request_json);
47 if (response_json.has_value()) {
48 return response_json->dump();
49 }
50 return std::nullopt;
51}
52
53auto Dispatcher::DispatchSingleRequestInner(const nlohmann::json &request_json)
54 -> std::optional<nlohmann::json> {
55 auto validation_error = ValidateRequest(request_json);
56 if (validation_error.has_value()) {
57 return validation_error->ToJson();
58 }
59
60 Request request = Request::FromJson(request_json);
61 spdlog::info("Dispatching request: method={}", request.GetMethod());
62
63 auto optional_handler = FindHandler(handlers_, request.GetMethod());
64 if (!optional_handler.has_value()) {
65 if (request.GetId().has_value()) {
67 LibErrorKind::kMethodNotFound, request.GetId())
68 .ToJson();
69 }
70 spdlog::warn("Method {} not found for notification", request.GetMethod());
71 return std::nullopt;
72 }
73
74 return HandleRequest(request, optional_handler.value());
75}
76
77auto Dispatcher::DispatchBatchRequest(const nlohmann::json &request_json)
78 -> std::optional<std::string> {
79 if (request_json.empty()) {
80 spdlog::warn("Empty batch request");
82 }
83
84 auto response_jsons = DispatchBatchRequestInner(request_json);
85 if (response_jsons.empty()) {
86 return std::nullopt;
87 }
88
89 return nlohmann::json(response_jsons).dump();
90}
91
92auto Dispatcher::DispatchBatchRequestInner(const nlohmann::json &request_json)
93 -> std::vector<nlohmann::json> {
94 std::vector<std::future<std::optional<nlohmann::json>>> futures;
95
96 for (const auto &element : request_json) {
97 if (enable_multithreading_) {
98 futures.emplace_back(thread_pool_.submit_task(
99 [this, element]() -> std::optional<nlohmann::json> {
100 return DispatchSingleRequestInner(element);
101 }));
102 } else {
103 futures.emplace_back(std::async(
104 std::launch::deferred,
105 [this, element]() -> std::optional<nlohmann::json> {
106 return DispatchSingleRequestInner(element);
107 }));
108 }
109 }
110
111 std::vector<nlohmann::json> responses;
112 for (auto &future : futures) {
113 std::optional<nlohmann::json> response = future.get();
114 if (response.has_value()) {
115 responses.push_back(response.value());
116 }
117 }
118
119 return responses;
120}
121
122auto Dispatcher::ValidateRequest(const nlohmann::json &request_json)
123 -> std::optional<Response> {
124 if (!request_json.is_object()) {
126 }
127
128 if (!request_json.contains("jsonrpc") || request_json["jsonrpc"] != "2.0") {
130 }
131
132 if (!request_json.contains("method")) {
133 if (request_json.contains("id")) {
134 // Method call without method field, will return an error
136 }
137 // Notification without method field, will be ignored
138 spdlog::warn("Request missing method field and id");
139 return std::nullopt;
140 }
141
142 if (!request_json["method"].is_string()) {
144 }
145
146 return std::nullopt; // No errors found
147}
148
149auto Dispatcher::FindHandler(
150 const std::unordered_map<std::string, Handler> &handlers,
151 const std::string &method) -> std::optional<Handler> {
152 auto it = handlers.find(method);
153 if (it != handlers.end()) {
154 return it->second;
155 }
156 return std::nullopt;
157}
158
159auto Dispatcher::HandleRequest(const Request &request, const Handler &handler)
160 -> std::optional<nlohmann::json> {
161 if (request.GetId().has_value()) {
162 // If the request has an ID, it is a method call
163 if (std::holds_alternative<MethodCallHandler>(handler)) {
164 const auto &method_call_handler = std::get<MethodCallHandler>(handler);
165 Response response = HandleMethodCall(request, method_call_handler);
166 return response.ToJson();
167 }
169 LibErrorKind::kInvalidRequest, request.GetId())
170 .ToJson();
171 }
172 // Otherwise, it is a notification
173 if (std::holds_alternative<NotificationHandler>(handler)) {
174 const auto &notification_handler = std::get<NotificationHandler>(handler);
175 HandleNotification(request, notification_handler);
176 return std::nullopt;
177 }
178 // Invalid request type for a notification
179 return std::nullopt;
180}
181
182auto Dispatcher::HandleMethodCall(
183 const Request &request, const MethodCallHandler &handler) -> Response {
184 try {
185 nlohmann::json response_json = handler(request.GetParams());
186 spdlog::debug(
187 "Method call {} returned: {}", request.GetMethod(),
188 response_json.dump());
189
190 return Response::FromUserResponse(response_json, request.GetId());
191 } catch (const std::exception &e) {
192 spdlog::error("Exception during method call handling: {}", e.what());
194 LibErrorKind::kInternalError, request.GetId());
195 }
196}
197
198void Dispatcher::HandleNotification(
199 const Request &request, const NotificationHandler &handler) {
200 try {
201 handler(request.GetParams());
202 spdlog::info("Notification {} handled successfully", request.GetMethod());
203 } catch (const std::exception &e) {
204 spdlog::error("Exception during notification handling: {}", e.what());
205 }
206}
207
209 const std::string &method, const MethodCallHandler &handler) {
210 handlers_[method] = handler;
211 spdlog::info("Dispatcher registered method call: {}", method);
212}
213
215 const std::string &method, const NotificationHandler &handler) {
216 handlers_[method] = handler;
217 spdlog::info("Dispatcher registered notification: {}", method);
218}
219
220} // namespace jsonrpc::server
Dispatcher(bool enable_multithreading=true, size_t num_threads=std::thread::hardware_concurrency())
Constructs a Dispatcher.
Definition dispatcher.cpp:7
void RegisterMethodCall(const std::string &method, const MethodCallHandler &handler)
Registers a method call handler.
auto DispatchRequest(const std::string &request) -> std::optional< std::string >
Processes a JSON-RPC request.
void RegisterNotification(const std::string &method, const NotificationHandler &handler)
Registers a notification handler.
static auto FromJson(const nlohmann::json &json_obj) -> Request
Creates a Request object from a JSON object.
Definition request.cpp:11
static auto CreateLibError(LibErrorKind error_kind, const std::optional< nlohmann::json > &id=std::nullopt) -> Response
Creates a Response object for a library error.
Definition response.cpp:67
static auto FromUserResponse(const nlohmann::json &response_json, std::optional< nlohmann::json > id) -> Response
Creates a Response object from a JSON object that represents a user response.
Definition response.cpp:26
std::function< void(const std::optional< nlohmann::json > &)> NotificationHandler
Type alias for notification handler functions.
Definition types.hpp:26
std::variant< MethodCallHandler, NotificationHandler > Handler
Type alias for a handler which can be either a method call handler or a notification handler.
Definition types.hpp:33
std::function< nlohmann::json(const std::optional< nlohmann::json > &)> MethodCallHandler
Type alias for method call handler functions.
Definition types.hpp:17