FastLED 3.9.15
Loading...
Searching...
No Matches

◆ handle()

json fl::Rpc::handle ( const json & request)

Process a JSON-RPC request.

Request format: {"method": "name", "params": [...], "id": ...} Response format: {"result": ..., "id": ...} or {"error": {...}, "id": ...}

Definition at line 67 of file rpc.cpp.hpp.

67 {
68 // Extract method name
69 if (!request.contains("method")) {
70 FL_ERROR("RPC: Invalid Request - missing 'method' field");
71 return detail::makeJsonRpcError(-32600, "Invalid Request: missing 'method'", request["id"]);
72 }
73
74 auto methodOpt = request["method"].as_string();
75 if (!methodOpt.has_value()) {
76 FL_ERROR("RPC: Invalid Request - 'method' must be a string");
77 return detail::makeJsonRpcError(-32600, "Invalid Request: 'method' must be a string", request["id"]);
78 }
79 fl::string methodName = methodOpt.value();
80
81#if FL_PLATFORM_HAS_LARGE_MEMORY
82 // Handle built-in rpc.discover method. Gated on Low-memory targets because
83 // it transitively anchors `TypedSchemaGenerator<Sig>::params()` for every
84 // registered method (each 800+ B per signature) plus the schema-building
85 // JSON tree. The LPC8xx / AVR-class JSON-RPC bring-up surface treats the
86 // RPC catalog as a known constant -- callers don't introspect at runtime.
87 // See FastLED #3081.
88 if (methodName == "rpc.discover") {
89 json response = json::object();
90 response.set("jsonrpc", "2.0");
91 response.set("result", schema());
92 if (request.contains("id")) {
93 response.set("id", request["id"]);
94 }
95 return response;
96 }
97#endif
98
99 // Look up the method
100 auto it = mRegistry.find(methodName);
101 if (it == mRegistry.end()) {
102 FL_WARN("RPC: Method not found: " << methodName.c_str());
103 return detail::makeJsonRpcError(-32601, "Method not found: " + methodName, request["id"]);
104 }
105
106 // Extract params (default to empty array)
107 json params = request.contains("params") ? request["params"] : json::parse("[]");
108 if (!params.is_array()) {
109 FL_ERROR("RPC: Invalid params - must be an array for method: " << methodName.c_str());
110 return detail::makeJsonRpcError(-32602, "Invalid params: must be an array", request["id"]);
111 }
112
113 // Check if this is an async function
114 const detail::RpcEntry& entry = it->second;
115 bool isAsync = (entry.mMode == RpcMode::ASYNC || entry.mMode == RpcMode::ASYNC_STREAM);
116
117 // Check if this is a response-aware function (uses ResponseSend&)
118 bool isResponseAware = entry.mIsResponseAware;
119
120 // For async functions, send ACK immediately
121 if (isAsync && mResponseSink && request.contains("id")) {
122 json ack = json::object();
123 ack.set("jsonrpc", "2.0");
124 ack.set("id", request["id"]);
125
126 json ackResult = json::object();
127 ackResult.set("acknowledged", true);
128 ack.set("result", ackResult);
129
130 mResponseSink(ack);
131 FL_DBG("RPC: Sent ACK for async method: " << methodName.c_str());
132 }
133
134 fl::tuple<TypeConversionResult, json> resultTuple;
135
136 // Handle response-aware methods (with ResponseSend& parameter)
137 if (isResponseAware) {
138 // Create ResponseSend instance
139 fl::json requestId = request.contains("id") ? request["id"] : json(nullptr);
140 ResponseSend responseSend(requestId, mResponseSink);
141
142 // Invoke user function with ResponseSend& and raw JSON params
143 entry.mResponseAwareFn(responseSend, params);
144
145 // Return success with null result (actual responses sent via ResponseSend)
146 resultTuple = fl::make_tuple(TypeConversionResult::success(), json(nullptr));
147 } else {
148 // Regular invocation
149 resultTuple = entry.mInvoker->invoke(params);
150 }
151
152 TypeConversionResult convResult = fl::get<0>(resultTuple);
153 json returnVal = fl::get<1>(resultTuple);
154
155 // Check for conversion errors
156 if (!convResult.ok()) {
157 FL_ERROR("RPC: Invalid params for method '" << methodName.c_str() << "': " << convResult.errorMessage().c_str());
158 return detail::makeJsonRpcError(-32602, "Invalid params: " + convResult.errorMessage(), request["id"]);
159 }
160
161 // Build success response
162 json response = json::object();
163 response.set("jsonrpc", "2.0");
164 response.set("result", returnVal);
165
166 // Include id if present (for request/response correlation)
167 if (request.contains("id")) {
168 response.set("id", request["id"]);
169 }
170
171 // Include warnings if any
172 if (convResult.hasWarning()) {
173 json warnings = json::array();
174 for (fl::size i = 0; i < convResult.warnings().size(); ++i) {
175 warnings.push_back(json(convResult.warnings()[i]));
176 }
177 response.set("warnings", warnings);
178 }
179
180 // For async functions, mark response to not queue it (ACK already sent)
181 if (isAsync) {
182 response.set("__async", true); // Internal marker
183 }
184
185 return response;
186}
int requestId
fl::unordered_map< fl::string, detail::RpcEntry > mRegistry
Definition rpc.h:387
fl::function< void(const fl::json &)> mResponseSink
Definition rpc.h:388
json schema() const
Returns flat schema document.
Definition rpc.cpp.hpp:264
static TypeConversionResult success()
const char * c_str() const FL_NOEXCEPT
static json parse(const fl::string &txt) FL_NOEXCEPT
Definition json.h:677
static json object() FL_NOEXCEPT
Definition json.h:692
static json array() FL_NOEXCEPT
Definition json.h:688
#define FL_WARN(X)
Definition log.h:276
#define FL_ERROR(X)
Definition log.h:219
#define FL_DBG
Definition log.h:388
json makeJsonRpcError(int code, const fl::string &message, const json &id)
tuple< typename fl::decay< Ts >::type... > make_tuple(Ts &&... args) FL_NOEXCEPT
Definition tuple.h:104
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NOEXCEPT
Definition pair.h:115

References fl::json::array(), fl::json::as_string(), fl::ASYNC, fl::ASYNC_STREAM, fl::basic_string::c_str(), fl::json::contains(), fl::TypeConversionResult::errorMessage(), FL_DBG, FL_ERROR, FL_WARN, fl::get(), fl::TypeConversionResult::hasWarning(), fl::json::is_array(), fl::make_tuple(), fl::detail::makeJsonRpcError(), fl::detail::RpcEntry::mInvoker, fl::detail::RpcEntry::mIsResponseAware, fl::detail::RpcEntry::mMode, mRegistry, fl::detail::RpcEntry::mResponseAwareFn, mResponseSink, fl::json::object(), fl::TypeConversionResult::ok(), fl::json::parse(), fl::json::push_back(), requestId, schema(), fl::json::set(), fl::TypeConversionResult::success(), and fl::TypeConversionResult::warnings().

Referenced by handle_maybe().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: