/* **************************************************************************** * * 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; }
/* **************************************************************************** * * restService - */ std::string restService(ConnectionInfo* ciP, RestService* serviceV) { std::vector<std::string> compV; int components; JsonRequest* jsonReqP = NULL; ParseData parseData; JsonDelayedRelease jsonRelease; if ((ciP->url.length() == 0) || ((ciP->url.length() == 1) && (ciP->url.c_str()[0] == '/'))) { OrionError error(SccBadRequest, "The Orion Context Broker is a REST service, not a 'web page'"); std::string response = error.render(ciP, ""); alarmMgr.badInput(clientIp, "The Orion Context Broker is a REST service, not a 'web page'"); restReply(ciP, response); return std::string("Empty URL"); } ciP->httpStatusCode = SccOk; components = stringSplit(ciP->url, '/', compV); for (unsigned int ix = 0; serviceV[ix].treat != NULL; ++ix) { if ((serviceV[ix].components != 0) && (serviceV[ix].components != components)) { continue; } if ((ciP->method != serviceV[ix].verb) && (serviceV[ix].verb != "*")) { continue; } strncpy(ciP->payloadWord, serviceV[ix].payloadWord.c_str(), sizeof(ciP->payloadWord)); bool match = true; for (int compNo = 0; compNo < components; ++compNo) { if (serviceV[ix].compV[compNo] == "*") { continue; } if (strcasecmp(serviceV[ix].compV[compNo].c_str(), compV[compNo].c_str()) != 0) { match = false; break; } } if (match == false) { continue; } if ((ciP->payload != NULL) && (ciP->payloadSize != 0) && (ciP->payload[0] != 0) && (serviceV[ix].verb != "*")) { std::string response; LM_T(LmtParsedPayload, ("Parsing payload for URL '%s', method '%s', service vector index: %d", ciP->url.c_str(), ciP->method.c_str(), ix)); ciP->parseDataP = &parseData; LM_T(LmtPayload, ("Parsing payload '%s'", ciP->payload)); response = payloadParse(ciP, &parseData, &serviceV[ix], &jsonReqP, &jsonRelease, compV); LM_T(LmtParsedPayload, ("payloadParse returns '%s'", response.c_str())); if (response != "OK") { alarmMgr.badInput(clientIp, response); restReply(ciP, response); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); return response; } } LM_T(LmtService, ("Treating service %s %s", serviceV[ix].verb.c_str(), ciP->url.c_str())); // Sacred - used in 'heavyTest' if (ciP->payloadSize == 0) { ciP->inFormat = NOFORMAT; } statisticsUpdate(serviceV[ix].request, ciP->inFormat); // Tenant to connectionInfo ciP->tenant = ciP->tenantFromHttpHeader; lmTransactionSetService(ciP->tenant.c_str()); // // A tenant string must not be longer than 50 characters and may only contain // underscores and alphanumeric characters. // std::string result; if ((ciP->tenant != "") && ((result = tenantCheck(ciP->tenant)) != "OK")) { OrionError error(SccBadRequest, result); std::string response = error.render(ciP, ""); alarmMgr.badInput(clientIp, result); if (ciP->apiVersion != "v1") { ciP->httpStatusCode = SccBadRequest; // FIXME P9: OK for all versions? } restReply(ciP, response); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); return response; } LM_T(LmtTenant, ("tenant: '%s'", ciP->tenant.c_str())); commonFilters(ciP, &parseData, &serviceV[ix]); scopeFilter(ciP, &parseData, &serviceV[ix]); // // If we have gotten this far the Input is OK. // Except for all the badVerb/badRequest, etc. // A common factor for all these 'services' is that the verb is '*' // // So, the 'Bad Input' alarm is cleared for this client. // if (serviceV[ix].verb != "*") { alarmMgr.badInputReset(clientIp); } std::string response = serviceV[ix].treat(ciP, components, compV, &parseData); filterRelease(&parseData, serviceV[ix].request); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); if (response == "DIE") { orionExitFunction(0, "Received a 'DIE' request on REST interface"); } restReply(ciP, response); return response; } std::string details = std::string("service '") + ciP->url + "' not recognized"; alarmMgr.badInput(clientIp, details); ciP->httpStatusCode = SccBadRequest; std::string answer = restErrorReplyGet(ciP, "", ciP->payloadWord, SccBadRequest, std::string("unrecognized request")); restReply(ciP, answer); compV.clear(); return answer; }
/* **************************************************************************** * * restService - */ std::string restService(ConnectionInfo* ciP, RestService* serviceV) { std::vector<std::string> compV; int components; XmlRequest* reqP = NULL; JsonRequest* jsonReqP = NULL; ParseData parseData; if ((ciP->url.length() == 0) || ((ciP->url.length() == 1) && (ciP->url.c_str()[0] == '/'))) { OrionError error(SccBadRequest, "The Orion Context Broker is a REST service, not a 'web page'"); std::string response = error.render(ciP->outFormat, ""); LM_W(("Bad Input (The Orion Context Broker is a REST service, not a 'web page')")); restReply(ciP, response); return std::string("Empty URL"); } ciP->httpStatusCode = SccOk; components = stringSplit(ciP->url, '/', compV); for (unsigned int ix = 0; serviceV[ix].treat != NULL; ++ix) { if ((serviceV[ix].components != 0) && (serviceV[ix].components != components)) { continue; } if ((ciP->method != serviceV[ix].verb) && (serviceV[ix].verb != "*")) { continue; } strncpy(ciP->payloadWord, serviceV[ix].payloadWord.c_str(), sizeof(ciP->payloadWord)); bool match = true; for (int compNo = 0; compNo < components; ++compNo) { if (serviceV[ix].compV[compNo] == "*") { continue; } if (strcasecmp(serviceV[ix].compV[compNo].c_str(), compV[compNo].c_str()) != 0) { match = false; break; } } if (match == false) { continue; } if ((ciP->payload != NULL) && (ciP->payloadSize != 0) && (ciP->payload[0] != 0) && (serviceV[ix].verb != "*")) { std::string response; LM_T(LmtParsedPayload, ("Parsing payload for URL '%s', method '%s', service vector index: %d", ciP->url.c_str(), ciP->method.c_str(), ix)); ciP->parseDataP = &parseData; response = payloadParse(ciP, &parseData, &serviceV[ix], &reqP, &jsonReqP); LM_T(LmtParsedPayload, ("payloadParse returns '%s'", response.c_str())); if (response != "OK") { restReply(ciP, response); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); return response; } } LM_T(LmtService, ("Treating service %s %s", serviceV[ix].verb.c_str(), ciP->url.c_str())); // Sacred - used in 'heavyTest' statisticsUpdate(serviceV[ix].request, ciP->inFormat); // Tenant to connectionInfo ciP->tenant = ciP->tenantFromHttpHeader; // // A tenant string must not be longer than 50 characters and may only contain // underscores and alphanumeric characters. // std::string result; if ((ciP->tenant != "") && ((result = tenantCheck(ciP->tenant)) != "OK")) { OrionError error(SccBadRequest, "tenant name not accepted - a tenant string must not be longer than " MAX_TENANT_NAME_LEN_STRING " characters" " and may only contain underscores and alphanumeric characters"); std::string response = error.render(ciP->outFormat, ""); LM_W(("Bad Input (%s)", error.details.c_str())); restReply(ciP, response); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); return response; } LM_T(LmtTenant, ("tenant: '%s'", ciP->tenant.c_str())); commonFilters(ciP, &parseData, &serviceV[ix]); scopeFilter(ciP, &parseData, &serviceV[ix]); std::string response = serviceV[ix].treat(ciP, components, compV, &parseData); filterRelease(&parseData, serviceV[ix].request); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); if (response == "DIE") { orionExitFunction(0, "Received a 'DIE' request on REST interface"); } restReply(ciP, response); return response; } LM_W(("Bad Input (service '%s' not recognized)", ciP->url.c_str())); ciP->httpStatusCode = SccBadRequest; std::string answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->payloadWord, SccBadRequest, std::string("unrecognized request")); restReply(ciP, answer); compV.clear(); return answer; }
/* **************************************************************************** * * 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; }
/* **************************************************************************** * * QueueWorkers::start() - */ static void *workerFunc(void* pSyncQ) { SyncQOverflow<SenderThreadParams*> *queue = (SyncQOverflow<SenderThreadParams*> *) pSyncQ; CURL *curl; // Initialize curl context curl = curl_easy_init(); if (curl == NULL) { LM_E(("Runtime Error (curl_easy_init)")); pthread_exit(NULL); } for (;;) { SenderThreadParams* params = queue->pop(); struct timespec now; struct timespec howlong; size_t estimatedQSize; QueueStatistics::incOut(); clock_gettime(CLOCK_REALTIME, &now); clock_difftime(&now, ¶ms->timeStamp, &howlong); estimatedQSize = queue->size(); QueueStatistics::addTimeInQWithSize(&howlong, estimatedQSize); strncpy(transactionId, params->transactionId, sizeof(transactionId)); LM_T(LmtNotifier, ("worker 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) { LM_T(LmtNotifier, ("simulatedNotification is 'true', skipping outgoing request")); __sync_fetch_and_add(&noOfSimulatedNotifications, 1); } else // we'll send the notification { std::string out; int r; r = httpRequestSendWithCurl(curl, 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); // // FIXME: ok and error counter should be incremented in the other notification modes (generalizing the concept, i.e. // not as member of QueueStatistics:: which seems to be tied to just the threadpool notification mode) // if (r == 0) { statisticsUpdate(NotifyContextSent, params->format); QueueStatistics::incSentOK(); } else { QueueStatistics::incSentError(); } } // Free params memory delete params; // Reset curl for next iteration curl_easy_reset(curl); } }