Process a JSON-RPC request.
Request format: {"method": "name", "params": [...], "id": ...} Response format: {"result": ..., "id": ...} or {"error": {...}, "id": ...}
126 {
127
128 if (!request.contains("method")) {
129 FL_ERROR(
"RPC: Invalid Request - missing 'method' field");
131 }
132
133 auto methodOpt = request["method"].as_string();
134 if (!methodOpt.has_value()) {
135 FL_ERROR(
"RPC: Invalid Request - 'method' must be a string");
137 }
138 fl::string methodName = methodOpt.value();
139
140#if FL_PLATFORM_HAS_LARGE_MEMORY
141
142
143
144
145
146
147 if (methodName == "rpc.discover") {
151 if (request.contains("id")) {
153 }
155 }
156#endif
157
158
161 FL_WARN(
"RPC: Method not found: " << methodName.
c_str());
163 }
164
165
166 json params = request.contains(
"params") ? request[
"params"] :
json::parse(
"[]");
167 if (!params.is_array()) {
168 FL_ERROR(
"RPC: Invalid params - must be an array for method: " << methodName.
c_str());
170 }
171
172
173 const detail::RpcEntry& entry = it->second;
174#if FL_PLATFORM_HAS_LARGE_MEMORY
175 if (entry.mIsStreaming) {
176 if (!mResponseStreamSink) {
178 }
179
181 fl::json paramsCopy = params;
182 fl::json
requestId = request.contains(
"id") ? request[
"id"] : json(
nullptr);
183 const bool includeId = request.contains("id");
184 mResponseStreamSink([streamFn, paramsCopy,
requestId, includeId](fl::JsonStreamWriter& writer) {
186 writer,
188 includeId,
189 [streamFn, paramsCopy,
requestId](fl::JsonStreamWriter& resultWriter) {
190 if (streamFn) {
191 streamFn(resultWriter, paramsCopy,
requestId);
192 } else {
194 }
195 });
196 });
197
199 skip.set("noEnqueue", true);
200 return skip;
201 }
202
204
205
206 bool isResponseAware = entry.mIsResponseAware;
207
208
211 ack.set("jsonrpc", "2.0");
212 ack.set("id", request["id"]);
213
215 ackResult.set("acknowledged", true);
216 ack.set("result", ackResult);
217
219 FL_DBG(
"RPC: Sent ACK for async method: " << methodName.
c_str());
220 }
221#endif
222
223 fl::tuple<TypeConversionResult, json> resultTuple;
224
225#if FL_PLATFORM_HAS_LARGE_MEMORY
226
227 if (isResponseAware) {
228
229 fl::json
requestId = request.contains(
"id") ? request[
"id"] : json(
nullptr);
231
232
233 entry.mResponseAwareFn(responseSend, params);
234
235
237 } else {
238
239 resultTuple = entry.mInvoker->invoke(params);
240 }
241#else
242
243
244
245 resultTuple = entry.mInvoker->invoke(params);
246#endif
247
248 TypeConversionResult convResult =
fl::get<0>(resultTuple);
250
251
252 if (!convResult.ok()) {
253 FL_ERROR(
"RPC: Invalid params for method '" << methodName.
c_str() <<
"': " << convResult.errorMessage().c_str());
255 }
256
257
261
262
263 if (request.contains("id")) {
265 }
266
267#if FL_PLATFORM_HAS_LARGE_MEMORY
268
269
270
271
272
273 if (convResult.hasWarning()) {
275 for (fl::size i = 0; i < convResult.warnings().size(); ++i) {
276 warnings.push_back(json(convResult.warnings()[i]));
277 }
279 }
280
281
282
283
284
285 if (isAsync) {
287 }
288#endif
289
291}
void valueNull() FL_NO_EXCEPT
fl::unordered_map< fl::string, detail::RpcEntry > mRegistry
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