Process a JSON-RPC request.
Request format: {"method": "name", "params": [...], "id": ...} Response format: {"result": ..., "id": ...} or {"error": {...}, "id": ...}
134 {
135
136 if (!request.contains("method")) {
137 FL_ERROR(
"RPC: Invalid Request - missing 'method' field");
139 }
140
141 auto methodOpt = request["method"].as_string();
142 if (!methodOpt.has_value()) {
143 FL_ERROR(
"RPC: Invalid Request - 'method' must be a string");
145 }
146 fl::string methodName = methodOpt.value();
147
148#if FL_PLATFORM_HAS_LARGE_MEMORY
149
150
151
152
153
154
155 if (methodName == "rpc.discover") {
156
157
158
159
160 const bool scheduled = request.contains("timestamp") &&
161 request["timestamp"].as_int().value_or(0) != 0;
162 if (!scheduled && mResponseStreamSink &&
163 (!mResponseStreamAvailable || mResponseStreamAvailable())) {
164 fl::json
requestId = request.contains(
"id") ? request[
"id"] : json(
nullptr);
165 const bool includeId = request.contains("id");
166 const Rpc* self =
this;
167 mResponseStreamSink([self,
requestId, includeId](fl::JsonStreamWriter& writer) {
170 [self](fl::JsonStreamWriter& resultWriter) {
171 self->writeSchema(resultWriter);
172 });
173 });
175 skip.set("noEnqueue", true);
176 return skip;
177 }
181 if (request.contains("id")) {
183 }
185 }
186#endif
187
188
191 FL_WARN(
"RPC: Method not found: " << methodName.
c_str());
193 }
194
195
196 json params = request.contains(
"params") ? request[
"params"] :
json::parse(
"[]");
197 if (!params.is_array()) {
198 FL_ERROR(
"RPC: Invalid params - must be an array for method: " << methodName.
c_str());
200 }
201
202
203 const detail::RpcEntry& entry = it->second;
204#if FL_PLATFORM_HAS_LARGE_MEMORY
205 if (entry.mIsStreaming) {
206 if (!mResponseStreamSink) {
208 }
209
211 fl::json paramsCopy = params;
212 fl::json
requestId = request.contains(
"id") ? request[
"id"] : json(
nullptr);
213 const bool includeId = request.contains("id");
214 mResponseStreamSink([streamFn, paramsCopy,
requestId, includeId](fl::JsonStreamWriter& writer) {
216 writer,
218 includeId,
219 [streamFn, paramsCopy,
requestId](fl::JsonStreamWriter& resultWriter) {
220 if (streamFn) {
221 streamFn(resultWriter, paramsCopy,
requestId);
222 } else {
224 }
225 });
226 });
227
229 skip.set("noEnqueue", true);
230 return skip;
231 }
232
234
235
236 bool isResponseAware = entry.mIsResponseAware;
237
238
241 ack.set("jsonrpc", "2.0");
242 ack.set("id", request["id"]);
243
245 ackResult.set("acknowledged", true);
246 ack.set("result", ackResult);
247
249 FL_DBG(
"RPC: Sent ACK for async method: " << methodName.
c_str());
250 }
251#endif
252
253 fl::tuple<TypeConversionResult, json> resultTuple;
254
255#if FL_PLATFORM_HAS_LARGE_MEMORY
256
257 if (isResponseAware) {
258
259 fl::json
requestId = request.contains(
"id") ? request[
"id"] : json(
nullptr);
261
262
263 entry.mResponseAwareFn(responseSend, params);
264
265
267 } else {
268
269 resultTuple = entry.mInvoker->invoke(params);
270 }
271#else
272
273
274
275 resultTuple = entry.mInvoker->invoke(params);
276#endif
277
278 TypeConversionResult convResult =
fl::get<0>(resultTuple);
280
281
282 if (!convResult.ok()) {
283 FL_ERROR(
"RPC: Invalid params for method '" << methodName.
c_str() <<
"': " << convResult.errorMessage().c_str());
285 }
286
287
291
292
293 if (request.contains("id")) {
295 }
296
297#if FL_PLATFORM_HAS_LARGE_MEMORY
298
299
300
301
302
303 if (convResult.hasWarning()) {
305 for (fl::size i = 0; i < convResult.warnings().size(); ++i) {
306 warnings.push_back(json(convResult.warnings()[i]));
307 }
309 }
310
311
312
313
314
315 if (isAsync) {
317 }
318#endif
319
321}
void valueNull() FL_NO_EXCEPT
fl::unordered_map< fl::string, detail::RpcEntry > mRegistry
Rpc() FL_NO_EXCEPT=default
fl::function< void(const fl::json &)> mResponseSink
json schema() const FL_NO_EXCEPT
Returns flat schema document.
static TypeConversionResult success() FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
static json parse(const fl::string &txt) FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
tuple< typename fl::decay< Ts >::type... > make_tuple(Ts &&... args) FL_NO_EXCEPT
json makeJsonRpcError(int code, const fl::string &message, const json &id) FL_NO_EXCEPT
fl::function< void(fl::JsonStreamWriter &, const fl::json &, const fl::json &)> StreamingRpcHandler
void streamJsonRpcResultEnvelope(fl::JsonStreamWriter &writer, const fl::json &requestId, bool includeId, const fl::JsonStreamCallback &writeResult) FL_NO_EXCEPT
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NO_EXCEPT
#define FL_RPC_ERR_NO_METHOD
#define FL_RPC_ERR_INVALID_PARAMS_PREFIX
#define FL_RPC_ERR_METHOD_NOT_STR
#define FL_RPC_ERR_PARAMS_NOT_ARRAY
#define FL_RPC_ERR_METHOD_NOT_FOUND_PREFIX