/* **************************************************************************** * * startSenderThread - */ void* startSenderThread(void* p) { SenderThreadParams* params = (SenderThreadParams*) p; char portV[STRING_SIZE_FOR_INT]; std::string url; snprintf(portV, sizeof(portV), "%d", params->port); url = params->ip + ":" + portV + params->resource; strncpy(transactionId, params->transactionId, sizeof(transactionId)); LM_T(LmtNotifier, ("sending to: host='%s', port=%d, verb=%s, tenant='%s', service-path: '%s', xauthToken: '%s', path='%s', content-type: %s", params->ip.c_str(), params->port, params->verb.c_str(), params->tenant.c_str(), params->servicePath.c_str(), params->xauthToken.c_str(), params->resource.c_str(), params->content_type.c_str())); if (!simulatedNotification) { std::string out; int r; r = httpRequestSend(params->ip, params->port, params->protocol, params->verb, params->tenant, params->servicePath, params->xauthToken, params->resource, params->content_type, params->content, true, NOTIFICATION_WAIT_MODE, &out); if (r == 0) { statisticsUpdate(NotifyContextSent, params->format); alarmMgr.notificationErrorReset(url); } } else { LM_T(LmtNotifier, ("simulatedNotification is 'true', skipping outgoing request")); __sync_fetch_and_add(&noOfSimulatedNotifications, 1); alarmMgr.notificationError(url, "notification failure for sender-thread"); } /* Delete the parameters after using them */ delete params; pthread_exit(NULL); return NULL; }
/* **************************************************************************** * * 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 }
/* **************************************************************************** * * templateNotify - * * This function performs the necessary substitutions according to the template of * subscription to form the desired notification and send it to the endpoint specified * in the subscription. * * */ static bool templateNotify ( const SubscriptionId& subscriptionId, const ContextElement& ce, const ngsiv2::HttpInfo& httpInfo, const std::string& tenant, const std::string& xauthToken, const std::string& fiwareCorrelator, RenderFormat renderFormat, const std::vector<std::string>& attrsOrder ) { Verb verb = httpInfo.verb; std::string method; std::string url; std::string payload; std::string mimeType; std::map<std::string, std::string> qs; std::map<std::string, std::string> headers; // // 1. Verb/Method // if (verb == NOVERB) { // Default verb/method is POST verb = POST; } method = verbName(verb); // // 2. URL // macroSubstitute(&url, httpInfo.url, ce); // // 3. Payload // if (httpInfo.payload == "") { NotifyContextRequest ncr; ContextElementResponse cer; cer.contextElement = ce; ncr.subscriptionId = subscriptionId; ncr.contextElementResponseVector.push_back(&cer); payload = ncr.toJson(renderFormat, attrsOrder); mimeType = "application/json"; } else { macroSubstitute(&payload, httpInfo.payload, ce); char* pload = curl_unescape(payload.c_str(), payload.length()); payload = pload; renderFormat = NGSI_V2_CUSTOM; mimeType = "text/plain"; // May be overridden by 'Content-Type' in 'headers' free(pload); } // // 4. URI Params (Query Strings) // for (std::map<std::string, std::string>::const_iterator it = httpInfo.qs.begin(); it != httpInfo.qs.end(); ++it) { std::string key = it->first; std::string value = it->second; macroSubstitute(&key, it->first, ce); macroSubstitute(&value, it->second, ce); if ((value == "") || (key == "")) { // To avoid e.g '?a=&b=&c=' continue; } qs[key] = value; } // // 5. HTTP Headers // for (std::map<std::string, std::string>::const_iterator it = httpInfo.headers.begin(); it != httpInfo.headers.end(); ++it) { std::string key = it->first; std::string value = it->second; macroSubstitute(&key, it->first, ce); macroSubstitute(&value, it->second, ce); if (key == "") { // To avoid empty header name continue; } headers[key] = value; } // // 6. Split URI in protocol, host, port and path // std::string protocol; std::string host; int port; std::string uriPath; if (!parseUrl(url, host, port, uriPath, protocol)) { LM_E(("Runtime Error (not sending NotifyContextRequest: malformed URL: '%s')", httpInfo.url.c_str())); return false; } // // 7. Add URI params from template to uriPath // std::string uri = uriPath; if (qs.size() != 0) { uri += "?"; int ix = 0; for (std::map<std::string, std::string>::iterator it = qs.begin(); it != qs.end(); ++it) { if (ix != 0) { uri += "&"; } uri += it->first + "=" + it->second; ++ix; } } // // 8. Send the request // // NOTE: the HTTP headers are sent to httpRequestSend via parameter 'extraHeaders' // std::string out; int r; r = httpRequestSend(host, port, protocol, method, tenant, ce.entityId.servicePath, xauthToken, uri, mimeType, payload, fiwareCorrelator, renderFormatToString(renderFormat), true, // Use Rush if CLI '--rush' allows it true, // wait for response &out, headers, "application/json", // Accept Format -1); // Timeout in milliseconds, depends on CLI '-httpTimeout' if (r == 0) { statisticsUpdate(NotifyContextSent, JSON); alarmMgr.notificationErrorReset(url); return true; } return false; }