/* **************************************************************************** * * updateForward - * * An entity/attribute has been found on some context provider. * We need to forward the update request to the context provider, indicated in upcrsP->contextProvider * * 1. Parse the providing application to extract IP, port and URI-path * 2. Render the string of the request we want to forward * 3. Send the request to the providing application (and await the response) * 4. Parse the response and fill in a binary UpdateContextResponse * 5. Fill in the response from the redirection into the response of this function * 6. 'Fix' StatusCode * 7. Freeing memory * * * FIXME P5: The function 'updateForward' is implemented to pick the format (XML or JSON) based on the * count of the Format for all the participating attributes. If we have more attributes 'preferring' * XML than JSON, the forward is done in XML, etc. This is all OK. * What is not OK is that the Accept HTTP header is set to the same format as the Content-Type HTTP Header. * While this is acceptable, it is not great. As the broker understands both XML and JSON, we could send * the forward message with an Acceot header of XML/JSON and then at reading the response, instead of * throwing away the HTTP headers, we could read the "Content-Type" and do the parse according the Content-Type. */ static void updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP, Format format) { std::string ip; std::string protocol; int port; std::string prefix; std::string answer; // // 1. Parse the providing application to extract IP, port and URI-path // if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false) { LM_W(("Bad Input (invalid providing application '%s')", upcrP->contextProvider.c_str())); // Somehow, if we accepted this providing application, it is the brokers fault ... // SccBadRequest should have been returned before, when it was registered! upcrsP->errorCode.fill(SccContextElementNotFound, ""); return; } // // 2. Render the string of the request we want to forward // Format outFormat = ciP->outFormat; std::string payload; char* cleanPayload; ciP->outFormat = format; payload = upcrP->render(ciP, UpdateContext, ""); ciP->outFormat = outFormat; cleanPayload = (char*) payload.c_str(); if (format == XML) { if ((cleanPayload = xmlPayloadClean(payload.c_str(), "<updateContextRequest>")) == NULL) { LM_E(("Runtime Error (error rendering forward-request)")); upcrsP->errorCode.fill(SccContextElementNotFound, ""); return; } } // // 3. Send the request to the Context Provider (and await the reply) // FIXME P7: Should Rush be used? // std::string out; std::string verb = "POST"; std::string resource = prefix + "/updateContext"; std::string tenant = ciP->tenant; std::string servicePath = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : ""; std::string mimeType = (format == XML)? "application/xml" : "application/json"; out = httpRequestSend(ip, port, protocol, verb, tenant, servicePath, ciP->httpHeaders.xauthToken, resource, mimeType, cleanPayload, false, true, mimeType); if ((out == "error") || (out == "")) { upcrsP->errorCode.fill(SccContextElementNotFound, ""); LM_E(("Runtime Error (error forwarding 'Update' to providing application)")); return; } // // 4. Parse the response and fill in a binary UpdateContextResponse // std::string s; std::string errorMsg; if (format == XML) { cleanPayload = xmlPayloadClean(out.c_str(), "<updateContextResponse>"); } else { cleanPayload = jsonPayloadClean(out.c_str()); } if ((cleanPayload == NULL) || (cleanPayload[0] == 0)) { // // This is really an internal error in the Context Provider // It is not in the orion broker though, so 404 is returned // LM_W(("Other Error (context provider response to UpdateContext is empty)")); upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response"); return; } // // NOTE // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName, // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a // POST /v1/updateContext. // So, here we change the verb/method for POST. // ParseData parseData; ciP->verb = POST; ciP->method = "POST"; parseData.upcrs.res.errorCode.fill(SccOk); if (format == XML) { s = xmlTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL, &errorMsg); } else { s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL); } if (s != "OK") { LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str())); upcrsP->errorCode.fill(SccContextElementNotFound, ""); parseData.upcr.res.release(); parseData.upcrs.res.release(); return; } // // 5. Fill in the response from the redirection into the response of this function // upcrsP->fill(&parseData.upcrs.res); // // 6. 'Fix' StatusCode // if (upcrsP->errorCode.code == SccNone) { upcrsP->errorCode.fill(SccOk); } if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound)) { upcrsP->errorCode.fill(SccContextElementNotFound); } // // 7. Freeing memory // parseData.upcr.res.release(); parseData.upcrs.res.release(); }
/* **************************************************************************** * * updateForward - * * An entity/attribute has been found on some context provider. * We need to forward the update request to the context provider, indicated in upcrsP->contextProvider * * 1. Parse the providing application to extract IP, port and URI-path * 2. Render the string of the request we want to forward * 3. Send the request to the providing application (and await the response) * 4. Parse the response and fill in a binary UpdateContextResponse * 5. Fill in the response from the redirection into the response of this function * 6. 'Fix' StatusCode * 7. Freeing memory * */ static bool updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP) { std::string ip; std::string protocol; int port; std::string prefix; bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON); // // 1. Parse the providing application to extract IP, port and URI-path // if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false) { std::string details = std::string("invalid providing application '") + upcrP->contextProvider + "'"; alarmMgr.badInput(clientIp, details); // // Somehow, if we accepted this providing application, it is the brokers fault ... // SccBadRequest should have been returned before, when it was registered! // upcrsP->errorCode.fill(SccContextElementNotFound, ""); return false; } LM_T(LmtForward, ("*** Provider Format: %d", upcrP->providerFormat)); // // 2. Render the string of the request we want to forward // MimeType outMimeType = ciP->outMimeType; std::string payload; char* cleanPayload; ciP->outMimeType = JSON; std::string verb; std::string resource; std::string tenant = ciP->tenant; std::string servicePath = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : ""; std::string mimeType = "application/json"; std::string out; int r; if (upcrP->providerFormat == PfJson) { TIMED_RENDER(payload = upcrP->toJsonV1(asJsonObject)); verb = "POST"; resource = prefix + "/updateContext"; } else { TIMED_RENDER(payload = upcrP->toJson()); verb = "POST"; resource = prefix + "/op/update"; #if 0 // FIXME #3485: this part is not removed by the moment, in the case it may be useful in the // context of issue #3485 std::vector<std::string> nullFilter; Entity* eP = upcrP->entityVector[0]; eP->renderId = false; TIMED_RENDER(payload = eP->toJson(NGSI_V2_NORMALIZED, nullFilter, false, nullFilter)); resource = prefix + "/entities/" + eP->id + "/attrs"; verb = "PATCH"; if (eP->type != "") { // Add ?type=<TYPE> to 'resource' resource += "?type=" + eP->type; } #endif } ciP->outMimeType = outMimeType; cleanPayload = (char*) payload.c_str(); // // 3. Send the request to the Context Provider (and await the reply) // FIXME P7: Should Rush be used? // LM_T(LmtCPrForwardRequestPayload, ("forward updateContext request payload: %s", payload.c_str())); std::map<std::string, std::string> noHeaders; long long statusCode; // not used by the moment r = httpRequestSend(fromIp, // thread variable ip, port, protocol, verb, tenant, servicePath, ciP->httpHeaders.xauthToken, resource, mimeType, cleanPayload, ciP->httpHeaders.correlator, "", false, &out, &statusCode, noHeaders, mimeType); if (r != 0) { upcrsP->errorCode.fill(SccContextElementNotFound, "error forwarding update"); LM_E(("Runtime Error (error '%s' forwarding 'Update' to providing application)", out.c_str())); return false; } LM_T(LmtCPrForwardResponsePayload, ("forward updateContext response payload: %s", out.c_str())); // // If NGSIv1 (providerFormat == PfJson): // 4. Parse the response and fill in a binary UpdateContextResponse // 5. Fill in the response from the redirection into the response of this function // 6. 'Fix' StatusCode // 7. Free up memory // // If NGSIv2: // 4. Look for "204 No Content" in the response of the forwarded request // 5. If found: OK, else, error // if (upcrP->providerFormat == PfJson) { LM_T(LmtForward, ("upcrP->providerFormat == PfJson")); // // 4. Parse the response and fill in a binary UpdateContextResponse // std::string s; std::string errorMsg; cleanPayload = jsonPayloadClean(out.c_str()); if ((cleanPayload == NULL) || (cleanPayload[0] == 0)) { // // This is really an internal error in the Context Provider // It is not in the orion broker though, so 404 is returned // LM_W(("Other Error (context provider response to UpdateContext is empty)")); upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response"); return false; } // // NOTE // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName, // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a // POST /v1/updateContext. // So, here we change the verb/method for POST. // ParseData parseData; ciP->verb = POST; ciP->method = "POST"; parseData.upcrs.res.errorCode.fill(SccOk); LM_T(LmtForward, ("Parsing Response of Forwarded Request: '%s'", cleanPayload)); s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, NULL); LM_T(LmtForward, ("Parse Result: %s", s.c_str())); if (s != "OK") { LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str())); upcrsP->errorCode.fill(SccContextElementNotFound, ""); parseData.upcr.res.release(); parseData.upcrs.res.release(); return false; } // // 5. Fill in the response from the redirection into the response of this function // upcrsP->fill(&parseData.upcrs.res); // // 6. 'Fix' StatusCode // if (upcrsP->errorCode.code == SccNone) { upcrsP->errorCode.fill(SccOk); } if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound)) { upcrsP->errorCode.fill(SccContextElementNotFound); } // // 7. Free up memory // parseData.upcr.res.release(); parseData.upcrs.res.release(); return true; } else // NGSIv2 { LM_T(LmtForward, ("upcrP->providerFormat == V2. out: '%s'", out.c_str())); // NGSIv2 forward - no payload to be received, just a 204 No Content HTTP Header if (strstr(out.c_str(), "204 No Content") != NULL) { LM_T(LmtForward, ("Found '204 No Content'")); upcrsP->errorCode.fill(SccNone); return true; } LM_W(("Other Error (unexpected response from context provider: %s)", out.c_str())); upcrsP->errorCode.fill(SccReceiverInternalError); return false; } // Can't reach this point - no return-statement needed }