const char *JsonObject::getCString(const char *aKey) { JsonObjectPtr p = get(aKey); if (p) return p->c_strValue(); return NULL; }
ErrorPtr JsonRpcComm::sendError(const char *aJsonRpcId, uint32_t aErrorCode, const char *aErrorMessage, JsonObjectPtr aErrorData) { JsonObjectPtr response = jsonRPCObj(); // create the error object JsonObjectPtr errorObj = JsonObject::newObj(); errorObj->add("code", JsonObject::newInt32(aErrorCode)); string errMsg; if (aErrorMessage) { errMsg = aErrorMessage; } else { errMsg = string_format("Error code %d (0x%X)", aErrorCode, aErrorCode); } errorObj->add("message", JsonObject::newString(errMsg)); // add the data object if any if (aErrorData) { errorObj->add("data", aErrorData); } // add the error object response->add("error", errorObj); // add the ID so the caller can associate with a previous request response->add("id", aJsonRpcId ? JsonObject::newString(aJsonRpcId) : JsonObjectPtr()); // now send FOCUSLOG("Sending JSON-RPC 2.0 error message:\n %s\n", response->c_strValue()); return sendMessage(response); }
void P44VdcHost::configApiRequestHandler(JsonCommPtr aJsonComm, ErrorPtr aError, JsonObjectPtr aJsonObject) { ErrorPtr err; // when coming from mg44, requests have the following form // - for GET requests like http://localhost:8080/api/json/myuri?foo=bar&this=that // {"method":"GET","uri":"myuri","uri_params":{"foo":"bar","this":"that"}} // - for POST requests like // curl "http://localhost:8080/api/json/myuri?foo=bar&this=that" --data-ascii "{ \"content\":\"data\", \"important\":false }" // {"method":"POST","uri":"myuri","uri_params":{"foo":"bar","this":"that"},"data":{"content":"data","important":false}} // curl "http://localhost:8080/api/json/myuri" --data-ascii "{ \"content\":\"data\", \"important\":false }" // {"method":"POST","uri":"myuri","data":{"content":"data","important":false}} // processing: // - a JSON request must be either specified in the URL or in the POST data, not both // - if POST data ("data" member in the incoming request) is present, "uri_params" is ignored // - "uri" selects one of possibly multiple APIs if (Error::isOK(aError)) { // not JSON level error, try to process LOG(LOG_DEBUG, "cfg -> vdcd (JSON) request received: %s", aJsonObject->c_strValue()); // find out which one is our actual JSON request // - try POST data first JsonObjectPtr request = aJsonObject->get("data"); if (!request) { // no POST data, try uri_params request = aJsonObject->get("uri_params"); } if (!request) { // empty query, that's an error aError = Error::err<P44VdcError>(415, "empty request"); } else { // have the request processed string apiselector; JsonObjectPtr uri = aJsonObject->get("uri"); if (uri) apiselector = uri->stringValue(); // dispatch according to API if (apiselector=="vdc") { // process request that basically is a vdc API request, but as simple webbish JSON, not as JSON-RPC 2.0 // and without the need to start a vdc session // Notes: // - if dSUID is specified invalid or empty, the vdc host itself is addressed. // - use x-p44-vdcs and x-p44-devices properties to find dsuids aError = processVdcRequest(aJsonComm, request); } else if (apiselector=="p44") { // process p44 specific requests aError = processP44Request(aJsonComm, request); } else { // unknown API selector aError = Error::err<P44VdcError>(400, "invalid URI, unknown API"); } } } // if error or explicit OK, send response now. Otherwise, request processing will create and send the response if (aError) { sendCfgApiResponse(aJsonComm, JsonObjectPtr(), aError); } }
ErrorPtr JsonRpcComm::sendResult(const char *aJsonRpcId, JsonObjectPtr aResult) { JsonObjectPtr response = jsonRPCObj(); // add the result, can be NULL response->add("result", aResult); // add the ID so the caller can associate with a previous request response->add("id", JsonObject::newString(aJsonRpcId)); // now send FOCUSLOG("Sending JSON-RPC 2.0 result message:\n %s\n", response->c_strValue()); return sendMessage(response); }
void HomeConnectDeviceOven::handleEventTypeNotify(const string& aKey, JsonObjectPtr aValue) { ALOG(LOG_INFO, "Oven Event 'NOTIFY' - item: %s, %s", aKey.c_str(), aValue ? aValue->c_strValue() : "<none>"); if (aKey == "Cooking.Oven.Option.SetpointTemperature") { int32_t value = (aValue != NULL) ? aValue->int32Value() : 0; targetTemperatureProp->setInt32Value(value); return; } inherited::handleEventTypeNotify(aKey, aValue); }
void P44VdcHost::sendCfgApiResponse(JsonCommPtr aJsonComm, JsonObjectPtr aResult, ErrorPtr aError) { // create response JsonObjectPtr response = JsonObject::newObj(); if (!Error::isOK(aError)) { // error, return error response response->add("error", JsonObject::newInt32((int32_t)aError->getErrorCode())); response->add("errormessage", JsonObject::newString(aError->getErrorMessage())); response->add("errordomain", JsonObject::newString(aError->getErrorDomain())); VdcApiErrorPtr ve = boost::dynamic_pointer_cast<VdcApiError>(aError); if (ve) { response->add("errortype", JsonObject::newInt32(ve->getErrorType())); response->add("userfacingmessage", JsonObject::newString(ve->getUserFacingMessage())); } } else { // no error, return result (if any) response->add("result", aResult); } LOG(LOG_DEBUG, "Config API response: %s", response->c_strValue()); aJsonComm->sendMessage(response); }
ErrorPtr JsonRpcComm::sendRequest(const char *aMethod, JsonObjectPtr aParams, JsonRpcResponseCB aResponseHandler) { JsonObjectPtr request = jsonRPCObj(); // the method or notification name request->add("method", JsonObject::newString(aMethod)); // the optional parameters if (aParams) { request->add("params", aParams); } // in any case, count this call (even if it is a notification) requestIdCounter++; // in case this is a method call (i.e. a answer handler is specified), add the call ID if (aResponseHandler) { // add the ID so the callee can include it in the response request->add("id", JsonObject::newInt32(requestIdCounter)); // remember it in our map pendingAnswers[requestIdCounter] = aResponseHandler; } // now send FOCUSLOG("Sending JSON-RPC 2.0 request message:\n %s\n", request->c_strValue()); return sendMessage(request); }
void JsonRpcComm::gotJson(ErrorPtr aError, JsonObjectPtr aJsonObject) { JsonRpcCommPtr keepMeAlive(this); // make sure this object lives until routine terminates ErrorPtr respErr; bool safeError = false; // set when reporting error is safe (i.e. not error possibly generated by malformed error, to prevent error loops) JsonObjectPtr idObj; const char *idString = NULL; if (Error::isOK(aError)) { // received proper JSON, now check JSON-RPC specifics FOCUSLOG("Received JSON message:\n %s\n", aJsonObject->c_strValue()); if (aJsonObject->isType(json_type_array)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - batch mode not supported by this implementation")); } else if (!aJsonObject->isType(json_type_object)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - request must be JSON object")); } else { // check request object fields const char *method = NULL; JsonObjectPtr o = aJsonObject->get("jsonrpc"); if (!o) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - missing 'jsonrpc'")); else if (o->stringValue()!="2.0") respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - wrong version in 'jsonrpc'")); else { // get ID param (must be present for all messages except notification) idObj = aJsonObject->get("id"); if (idObj) idString = idObj->c_strValue(); JsonObjectPtr paramsObj = aJsonObject->get("params"); // JSON-RPC version is correct, check other params method = aJsonObject->getCString("method"); if (method) { // this is a request (responses don't have the method member) safeError = idObj!=NULL; // reporting error is safe if this is a method call. Other errors are reported only when reportAllErrors is set if (*method==0) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - empty 'method'")); else { // looks like a valid method or notification call if (!jsonRequestHandler) { // no handler -> method cannot be executed respErr = ErrorPtr(new JsonRpcError(JSONRPC_METHOD_NOT_FOUND, "Method not found")); } else { if (paramsObj && !paramsObj->isType(json_type_array) && !paramsObj->isType(json_type_object)) { // invalid param object respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - 'params' must be object or array")); } else { // call handler to execute method or notification jsonRequestHandler(method, idString, paramsObj); } } } } else { // this is a response (requests always have a method member) // - check if result or error JsonObjectPtr respObj; if (!aJsonObject->get("result", respObj)) { // must be error, need further decoding respObj = aJsonObject->get("error"); if (!respObj) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INTERNAL_ERROR, "Internal JSON-RPC error - response with neither 'result' nor 'error'")); else { // dissect error object ErrorCode errCode = JSONRPC_INTERNAL_ERROR; // Internal RPC error const char *errMsg = "malformed Error response"; // - try to get error code JsonObjectPtr o = respObj->get("code"); if (o) errCode = o->int32Value(); // - try to get error message o = respObj->get("message"); if (o) errMsg = o->c_strValue(); // compose error object from this respErr = ErrorPtr(new JsonRpcError(errCode, errMsg)); // also get optional data element respObj = respObj->get("data"); } } // Now we have either result or error.data in respObj, and respErr is Ok or contains the error code + message if (!idObj) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 warning: Received response with no or NULL 'id' that cannot be dispatched:\n %s\n", aJsonObject->c_strValue()); } else { // dispatch by ID uint32_t requestId = idObj->int32Value(); PendingAnswerMap::iterator pos = pendingAnswers.find(requestId); if (pos==pendingAnswers.end()) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 error: Received response with unknown 'id'=%d : %s\n", requestId, aJsonObject->c_strValue()); } else { // found callback JsonRpcResponseCB cb = pos->second; pendingAnswers.erase(pos); // erase cb(requestId, respErr, respObj); // call } respErr.reset(); // handled } } } } } else { // no proper JSON received, create error response if (aError->isDomain(JsonError::domain())) { // some kind of parsing error respErr = ErrorPtr(new JsonRpcError(JSONRPC_PARSE_ERROR, aError->description())); } else { // some other type of server error respErr = ErrorPtr(new JsonRpcError(JSONRPC_SERVER_ERROR, aError->description())); } } // auto-generate error response for internally created errors if (!Error::isOK(respErr)) { if (safeError || reportAllErrors) sendError(idString, respErr); else LOG(LOG_WARNING,"Received data that generated error which can't be sent back: Code=%d, Message='%s'\n", respErr->getErrorCode(), respErr->description().c_str()); } }