/* **************************************************************************** * * ok - */ TEST(versionTreat, ok) { ConnectionInfo ci("/version", "GET", "1.1"); std::string out; out = restService(&ci, rs); // FIXME P2: Some day we'll do this ... // // char* expected = // "<orion>\n" // " <version>" ORION_VERSION "</version>\n" // " <uptime>*</uptime>" // " <git_hash>*</git_hash>\n" // " <compile_time>*</compile_time>\n" // " <compiled_by>*</compiled_by>\n" // " <compiled_in>*</compiled_in>\n" // "</orion>\n"; // bool match; // match = std::regex_match(expected, out.c_str()); EXPECT_TRUE(strstr(out.c_str(), "<orion>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "<version>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</version>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "<uptime>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</uptime>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "<git_hash>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</git_hash>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "<compile_time>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</compile_time>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "<compiled_in>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</compiled_in>") != NULL); EXPECT_TRUE(strstr(out.c_str(), "</orion>") != NULL); extern const char* orionUnitTestVersion; std::string expected = std::string("<version>") + orionUnitTestVersion + "</version>"; EXPECT_TRUE(strstr(out.c_str(), expected.c_str()) != NULL); versionSet("1.2.3"); out = restService(&ci, rs); EXPECT_TRUE(strstr(out.c_str(), "<version>1.2.3</version>") != NULL); versionSet("1.2.3"); std::string version = versionGet(); EXPECT_EQ("1.2.3", version); }
/* **************************************************************************** * * httpRequestSendWithCurl - * * The waitForResponse arguments specifies if the method has to wait for response * before return. If this argument is false, the return string is "" * * NOTE * We are using a hybrid approach, consisting in a static thread-local buffer of a * small size that copes with most notifications to avoid expensive * calloc/free syscalls if the notification payload is not very large. * * RETURN VALUES * httpRequestSendWithCurl returns 0 on success and a negative number on failure: * -1: Invalid port * -2: Invalid IP * -3: Invalid verb * -4: Invalid resource * -5: No Content-Type BUT content present * -6: Content-Type present but there is no content * -7: Total outgoing message size is too big * -9: Error making HTTP request */ int httpRequestSendWithCurl ( CURL *curl, const std::string& _ip, unsigned short port, const std::string& protocol, const std::string& verb, const std::string& tenant, const std::string& servicePath, const std::string& xauthToken, const std::string& resource, const std::string& orig_content_type, const std::string& content, const std::string& fiwareCorrelation, const std::string& ngisv2AttrFormat, bool useRush, bool waitForResponse, std::string* outP, const std::string& acceptFormat, long timeoutInMilliseconds ) { char portAsString[STRING_SIZE_FOR_INT]; static unsigned long long callNo = 0; std::string result; std::string ip = _ip; struct curl_slist* headers = NULL; MemoryStruct* httpResponse = NULL; CURLcode res; int outgoingMsgSize = 0; std::string content_type(orig_content_type); ++callNo; // For content-type application/json we add charset=utf-8 if (orig_content_type == "application/json") { content_type += "; charset=utf-8"; } if (timeoutInMilliseconds == -1) { timeoutInMilliseconds = defaultTimeout; } lmTransactionStart("to", ip.c_str(), port, resource.c_str()); // Preconditions check if (port == 0) { LM_E(("Runtime Error (port is ZERO)")); lmTransactionEnd(); *outP = "error"; return -1; } if (ip.empty()) { LM_E(("Runtime Error (ip is empty)")); lmTransactionEnd(); *outP = "error"; return -2; } if (verb.empty()) { LM_E(("Runtime Error (verb is empty)")); lmTransactionEnd(); *outP = "error"; return -3; } if (resource.empty()) { LM_E(("Runtime Error (resource is empty)")); lmTransactionEnd(); *outP = "error"; return -4; } if ((content_type.empty()) && (!content.empty())) { LM_E(("Runtime Error (Content-Type is empty but there is actual content)")); lmTransactionEnd(); *outP = "error"; return -5; } if ((!content_type.empty()) && (content.empty())) { LM_E(("Runtime Error (Content-Type non-empty but there is no content)")); lmTransactionEnd(); *outP = "error"; return -6; } // Allocate to hold HTTP response httpResponse = new MemoryStruct; httpResponse->memory = (char*) malloc(1); // will grow as needed httpResponse->size = 0; // no data at this point // // Rush // Every call to httpRequestSend specifies whether RUSH should be used or not. // But, this depends also on how the broker was started, so here the 'useRush' // parameter is cancelled in case the broker was started without rush. // // If rush is to be used, the IP/port is stored in rushHeaderIP/rushHeaderPort and // after that, the host and port of rush is set as ip/port for the message, that is // now sent to rush instead of to its final destination. // Also, a few HTTP headers for rush must be setup. // if ((rushPort == 0) || (rushHost == "")) { useRush = false; } if (useRush) { char rushHeaderPortAsString[STRING_SIZE_FOR_INT]; uint16_t rushHeaderPort = port; std::string rushHeaderIP = ip; std::string headerRushHttp; ip = rushHost; port = rushPort; snprintf(rushHeaderPortAsString, sizeof(rushHeaderPortAsString), "%d", rushHeaderPort); headerRushHttp = "X-relayer-host: " + rushHeaderIP + ":" + rushHeaderPortAsString; LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str())); headers = curl_slist_append(headers, headerRushHttp.c_str()); outgoingMsgSize += headerRushHttp.size(); if (protocol == "https:") { headerRushHttp = "X-relayer-protocol: https"; LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str())); headers = curl_slist_append(headers, headerRushHttp.c_str()); outgoingMsgSize += headerRushHttp.size(); } } snprintf(portAsString, sizeof(portAsString), "%u", port); // ----- User Agent char cvBuf[CURL_VERSION_MAX_LENGTH]; char headerUserAgent[HTTP_HEADER_USER_AGENT_MAX_LENGTH]; snprintf(headerUserAgent, sizeof(headerUserAgent), "User-Agent: orion/%s libcurl/%s", versionGet(), curlVersionGet(cvBuf, sizeof(cvBuf))); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerUserAgent)); headers = curl_slist_append(headers, headerUserAgent); outgoingMsgSize += strlen(headerUserAgent) + 1; // ----- Host char headerHost[HTTP_HEADER_HOST_MAX_LENGTH]; snprintf(headerHost, sizeof(headerHost), "Host: %s:%d", ip.c_str(), (int) port); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerHost)); headers = curl_slist_append(headers, headerHost); outgoingMsgSize += strlen(headerHost) + 1; // ----- Tenant if (tenant != "") { headers = curl_slist_append(headers, ("fiware-service: " + tenant).c_str()); outgoingMsgSize += tenant.size() + 16; // "fiware-service: " } // ----- Service-Path if (servicePath != "") { headers = curl_slist_append(headers, ("Fiware-ServicePath: " + servicePath).c_str()); outgoingMsgSize += servicePath.size() + strlen("Fiware-ServicePath: "); } // ----- X-Auth-Token if (xauthToken != "") { headers = curl_slist_append(headers, ("X-Auth-Token: " + xauthToken).c_str()); outgoingMsgSize += xauthToken.size() + strlen("X-Auth-Token: "); } // ----- Accept std::string acceptedFormats = "application/json"; if (acceptFormat != "") { acceptedFormats = acceptFormat; } std::string acceptString = "Accept: " + acceptedFormats; headers = curl_slist_append(headers, acceptString.c_str()); outgoingMsgSize += acceptString.size(); // ----- Expect headers = curl_slist_append(headers, "Expect: "); outgoingMsgSize += 8; // from "Expect: " // ----- Content-length std::stringstream contentLengthStringStream; contentLengthStringStream << content.size(); std::string headerContentLength = "Content-length: " + contentLengthStringStream.str(); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerContentLength.c_str())); headers = curl_slist_append(headers, headerContentLength.c_str()); outgoingMsgSize += contentLengthStringStream.str().size() + 16; // from "Content-length: " outgoingMsgSize += content.size(); // ----- Content-type headers = curl_slist_append(headers, ("Content-type: " + content_type).c_str()); outgoingMsgSize += content_type.size() + 14; // from "Content-type: " // Fiware-Correlator std::string correlation = "Fiware-Correlator: " + fiwareCorrelation; headers = curl_slist_append(headers, correlation.c_str()); outgoingMsgSize += correlation.size(); // Notify Format if ((ngisv2AttrFormat != "") && (ngisv2AttrFormat != "JSON") && (ngisv2AttrFormat != "legacy")) { std::string nFormat = "X-Ngsiv2-AttrsFormat: " + ngisv2AttrFormat; headers = curl_slist_append(headers, nFormat.c_str()); outgoingMsgSize += nFormat.size(); } // Check if total outgoing message size is too big if (outgoingMsgSize > MAX_DYN_MSG_SIZE) { LM_E(("Runtime Error (HTTP request to send is too large: %d bytes)", outgoingMsgSize)); curl_slist_free_all(headers); free(httpResponse->memory); delete httpResponse; lmTransactionEnd(); *outP = "error"; return -7; } // Contents const char* payload = content.c_str(); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (u_int8_t*) payload); // Set up URL std::string url; if (isIPv6(ip)) url = "[" + ip + "]"; else url = ip; url = url + ":" + portAsString + (resource.at(0) == '/'? "" : "/") + resource; // Prepare CURL handle with obtained options curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb.c_str()); // Set HTTP verb curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Allow redirection (?) curl_easy_setopt(curl, CURLOPT_HEADER, 1); // Activate include the header in the body output curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Put headers in place curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeMemoryCallback); // Send data here curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) httpResponse); // Custom data for response handling // // There is a known problem in libcurl (see http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame) // which is solved using CURLOPT_NOSIGNAL. If we update some day from libcurl 7.19 (the one that comes with CentOS 6.x) to a newer version // (there are some reports about the bug is no longer in libcurl 7.32), using CURLOPT_NOSIGNAL could be not necessary and this be removed). // See issue #1016 for more details. // curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); // // Timeout // // The parameter timeoutInMilliseconds holds the timeout time in milliseconds. // If the timeout time requested is 0, then no timeuot is used. // if (timeoutInMilliseconds != 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeoutInMilliseconds); } // Synchronous HTTP request // This was previously a LM_T trace, but we have "promoted" it to INFO due to it is needed to check logs in a .test case (case 000 notification_different_sizes.test) LM_I(("Sending message %lu to HTTP server: sending message of %d bytes to HTTP server", callNo, outgoingMsgSize)); res = curl_easy_perform(curl); if (res != CURLE_OK) { // // NOTE: This log line is used by the functional tests in cases/880_timeout_for_forward_and_notifications/ // So, this line should not be removed/altered, at least not without also modifying the functests. // alarmMgr.notificationError(url, "(curl_easy_perform failed: " + std::string(curl_easy_strerror(res)) + ")"); *outP = "notification failure"; } else { // The Response is here LM_I(("Notification Successfully Sent to %s", url.c_str())); outP->assign(httpResponse->memory, httpResponse->size); } // Cleanup curl environment curl_slist_free_all(headers); free(httpResponse->memory); delete httpResponse; lmTransactionEnd(); return res == CURLE_OK ? 0 : -9; }
void moduleAuth::a_ResponseVersion( void *buffer, size_t bufferSize) { const char *bytePtr = (const char*) buffer; string version; string versionLatest; // Read the current client version from the config ConfigFile config("client.conf"); config.readInto<string>(versionLatest, "IssoriaVersion", ""); // skip size bytePtr += sizeof(size_t); bufferSize -= sizeof(size_t); version.append( (const char*) bytePtr ); bytePtr += version.size() + 1; bufferSize -= version.size() + 1; // Get Client OS _OS = (* (OSTypes*) bytePtr); bytePtr += sizeof(OSTypes); bufferSize -= sizeof(OSTypes); // Read SVN Version, only if theres a buffer left... (old version support, this can be removed in the future) if(bufferSize) { _SVNREV.clear(); _SVNREV.append( (const char*) bytePtr ); bytePtr += _SVNREV.size()+1; bufferSize -= _SVNREV.size()+1; } // Spidz! if( version.find_first_of( "SPIDZ:" ) != string::npos ) { stringstream title; title << version.substr(6); _SHAok = true; _version = title.str(); _versionOk = true; return; } // (check for size for old ver support) // Get SHA of remote code section if(bufferSize>2) { string prevSHA; _lastSHA = time(0); if(_SHA.size()) prevSHA = _SHA; _SHA.clear(); _SHA.append( (const char*) bytePtr, bufferSize-1); _SHAok = SHACheck(); if(!_SHAok) { if(prevSHA != _SHA) { Debug->note(0, _client->remoteIPGet()); Debug->note(0,": "); Debug->note(0, version ); Debug->note(0,":"); if(_SHA.size()) { Debug->dumpBufferSc( (const byte*) _SHA.c_str(), _SHA.size() ); // if already authenticated, send a request for a copy of the code segment if(_authenticated) { if(memberLevel() != levelNINJA) _client->moduleGet< moduleKillarmy >(modKILLARMY)->requestCode(); } } } } //!_SHAok } _version = version; // Check if client has latest version if( version.compare( versionLatest ) ) { size_t latMajor, latMinor, latRev, cMajor, cMinor, cRev; versionGet( versionLatest, latMajor, latMinor, latRev ); versionGet( version, cMajor, cMinor, cRev ); // Client Major >= Major if( cMajor >= latMajor ) { // Client Newer than Known ver // Client Minor >= Minor && Client Rev >= Rev if( cMinor > latMinor || (cMinor == latMinor && cRev > latRev)) { // Client is newer than our version packetSend( (byte) modAUTH_VersionNew ); _versionOk = true; _versionNew = true; return; } // Client Version Match Known // Client Minor == Minor && Rev == Minor if( cMinor == latMinor && cRev == latRev ) { _versionOk = true; return; } } packetSend( (byte) modAUTH_VersionOld, versionLatest ); } else _versionOk = true; }
/* **************************************************************************** * * httpRequestSend - * * The waitForResponse arguments specifies if the method has to wait for response * before return. If this argument is false, the return string is "" * * FIXME: I don't like too much "reusing" natural output to return "error" in the * case of error. I think it would be smarter to use "std::string* error" in the * arguments or (even better) an exception. To be solved in the future in a hardening * period. * * Note, we are using a hybrid approach, consisting in an static thread-local buffer of * small size that copes with most notifications to avoid expensive * calloc/free syscalls if the notification payload is not very large. * */ std::string httpRequestSend ( const std::string& _ip, unsigned short port, const std::string& protocol, const std::string& verb, const std::string& tenant, const std::string& resource, const std::string& content_type, const std::string& content, bool useRush, bool waitForResponse, const std::string& acceptFormat, int timeoutInMilliseconds ) { char buffer[TAM_BUF]; char response[TAM_BUF]; char preContent[TAM_BUF]; char msgStatic[MAX_STA_MSG_SIZE]; char* what = (char*) "static"; char* msgDynamic = NULL; char* msg = msgStatic; // by default, use the static buffer std::string rushHeaderIP = ""; unsigned short rushHeaderPort = 0; std::string rushHttpHeaders = ""; static unsigned long long callNo = 0; std::string result; std::string ip = _ip; ++callNo; // Preconditions check if (port == 0) { LM_E(("Runtime Error (port is ZERO)")); return "error"; } if (ip.empty()) { LM_E(("Runtime Error (ip is empty)")); return "error"; } if (verb.empty()) { LM_E(("Runtime Error (verb is empty)")); return "error"; } if (resource.empty()) { LM_E(("Runtime Error (resource is empty)")); return "error"; } if ((content_type.empty()) && (!content.empty())) { LM_E(("Runtime Error (Content-Type is empty but there is actual content)")); return "error"; } if ((!content_type.empty()) && (content.empty())) { LM_E(("Runtime Error (Content-Type non-empty but there is no content)")); return "error"; } if (timeoutInMilliseconds == -1) { timeoutInMilliseconds = defaultTimeout; } // // Rush // Every call to httpRequestSend specifies whether RUSH should be used or not. // But, this depends also on how the broker was started, so here the 'useRush' // parameter is cancelled in case the broker was started without rush. // // If rush is to be used, the IP/port is stored in rushHeaderIP/rushHeaderPort and // after that, the host and port of rush is set as ip/port for the message, that is // now sent to rush instead of to its final destination. // Also, a few HTTP headers for rush must be setup. // if (useRush) { if ((rushPort == 0) || (rushHost == "")) useRush = false; else { char portAsString[16]; rushHeaderIP = ip; rushHeaderPort = port; ip = rushHost; port = rushPort; sprintf(portAsString, "%d", (int) rushHeaderPort); rushHttpHeaders = "X-relayer-host: " + rushHeaderIP + ":" + portAsString + "\n"; if (protocol == "https:") rushHttpHeaders += "X-relayer-protocol: https\n"; } } // Buffers clear memset(buffer, 0, TAM_BUF); memset(response, 0, TAM_BUF); memset(msg, 0, MAX_STA_MSG_SIZE); char cvBuf[128]; // // Accepted mime-types // if (acceptFormat == "") acceptFormat = "application/xml, application/json"; snprintf(preContent, sizeof(preContent), "%s %s HTTP/1.1\n" "User-Agent: orion/%s libcurl/%s\n" "Host: %s:%d\n" "Accept: %s\n%s", verb.c_str(), resource.c_str(), versionGet(), curlVersionGet(cvBuf, sizeof(cvBuf)), ip.c_str(), (int) port, acceptFormat.c_str(), rushHttpHeaders.c_str()); LM_T(LmtRush, ("'PRE' HTTP headers:\n--------------\n%s\n-------------", preContent)); if (tenant != "") { char tenantHeader[128]; snprintf(tenantHeader, sizeof(tenantHeader), "fiware-service: %s\n", tenant.c_str()); strncat(preContent, tenantHeader, sizeof(preContent) - strlen(preContent)); } if (!content.empty()) { char headers[512]; sprintf(headers, "Content-Type: %s\n" "Content-Length: %zu\n", content_type.c_str(), content.length() + 1); strncat(preContent, headers, sizeof(preContent) - strlen(preContent)); /* Choose the right buffer (static or dynamic) to use. Note we are using +3 due to: * + 1, for the \n between preContent and content * + 1, for the \n at the end of the message * + 1, for the \0 by the trailing character in C strings */ int neededSize = content.length() + strlen(preContent) + 3; if (neededSize > MAX_DYN_MSG_SIZE) { LM_E(("Runtime Error (HTTP request to send is too large: %d bytes)", content.length() + strlen(preContent))); return "error"; } else if (neededSize > MAX_STA_MSG_SIZE) { msgDynamic = (char*) calloc(sizeof(char), neededSize); if (msgDynamic == NULL) { LM_E(("Runtime Error (dynamic memory allocation failure)")); return "error"; } msg = msgDynamic; what = (char*) "dynamic"; } /* The above checking should ensure that the three parts fit, so we are using * sprint() instead of snprintf() */ sprintf(msg, "%s\n%s", preContent, content.c_str()); } else { /* In the case of no-content we assume that MAX_STA_MSG_SIZE is enough to send the message */ LM_T(LmtClientOutputPayload, ("Using static buffer to send HTTP request (empty content)")); sprintf(msg, "%s\n", preContent); } /* We add a final newline (I guess that HTTP protocol needs it) */ strcat(msg, "\n"); int fd = httpRequestConnect(ip, port); // Connecting to HTTP server if (fd == -1) { return "error"; } int nb; int sz = strlen(msg); LM_T(LmtClientOutputPayload, ("Sending message %lu to HTTP server: sending %s message of %d bytes to HTTP server", callNo, what, sz)); LM_T(LmtClientOutputPayloadDump, ("Sending to HTTP server payload:\n%s", msg)); nb = send(fd, msg, sz, 0); if (msgDynamic != NULL) { free(msgDynamic); } if (nb == -1) { LM_W(("Notification failure for %s:%d (send: %s)", _ip.c_str(), port, strerror(errno))); return "error"; } else if (nb != sz) { LM_W(("Notification failure for %s:%d (not entire message sent)", _ip.c_str(), port)); return "error"; } if (waitForResponse) { nb = recv(fd, &buffer, TAM_BUF - 1, 0); if (nb == -1) { LM_W(("Notification failure for %s:%d (error receiving ACK from HTTP server: %s)", _ip.c_str(), port, strerror(errno))); return "error"; } else if ( nb >= TAM_BUF) { LM_W(("Notification failure for %s:%d (message size of HTTP server reply is too big: %d (max allowed %d)) ", _ip.c_str(), port, nb, TAM_BUF)); return "error"; } else { memcpy(response, buffer, nb); LM_I(("Notification Successfully Sent to %s:%d%s", ip.c_str(), port, resource.c_str())); LM_T(LmtClientInputPayload, ("Received from HTTP server:\n%s", response)); } if (strlen(response) > 0) result = response; } else { LM_T(LmtClientInputPayload, ("not waiting for response")); LM_I(("Notification Successfully Sent to %s:%d%s", ip.c_str(), port, resource.c_str())); result = ""; } close(fd); return result; }
/* **************************************************************************** * * httpRequestSend - */ std::string httpRequestSend ( const std::string& _ip, unsigned short port, const std::string& protocol, const std::string& verb, const std::string& tenant, const std::string& servicePath, const std::string& xauthToken, const std::string& resource, const std::string& content_type, const std::string& content, bool useRush, bool waitForResponse, const std::string& acceptFormat, long timeoutInMilliseconds ) { char portAsString[16]; static unsigned long long callNo = 0; std::string result; std::string ip = _ip; struct curl_slist* headers = NULL; MemoryStruct* httpResponse = NULL; CURLcode res; int outgoingMsgSize = 0; CURL* curl; ++callNo; if (timeoutInMilliseconds == -1) { timeoutInMilliseconds = defaultTimeout; } LM_TRANSACTION_START("to", ip.c_str(), port, resource.c_str()); // Preconditions check if (port == 0) { LM_E(("Runtime Error (port is ZERO)")); LM_TRANSACTION_END(); return "error"; } if (ip.empty()) { LM_E(("Runtime Error (ip is empty)")); LM_TRANSACTION_END(); return "error"; } if (verb.empty()) { LM_E(("Runtime Error (verb is empty)")); LM_TRANSACTION_END(); return "error"; } if (resource.empty()) { LM_E(("Runtime Error (resource is empty)")); LM_TRANSACTION_END(); return "error"; } if ((content_type.empty()) && (!content.empty())) { LM_E(("Runtime Error (Content-Type is empty but there is actual content)")); LM_TRANSACTION_END(); return "error"; } if ((!content_type.empty()) && (content.empty())) { LM_E(("Runtime Error (Content-Type non-empty but there is no content)")); LM_TRANSACTION_END(); return "error"; } if ((curl = curl_easy_init()) == NULL) { LM_E(("Runtime Error (could not init libcurl)")); LM_TRANSACTION_END(); return "error"; } // Allocate to hold HTTP response httpResponse = new MemoryStruct; httpResponse->memory = (char*) malloc(1); // will grow as needed httpResponse->size = 0; // no data at this point // // Rush // Every call to httpRequestSend specifies whether RUSH should be used or not. // But, this depends also on how the broker was started, so here the 'useRush' // parameter is cancelled in case the broker was started without rush. // // If rush is to be used, the IP/port is stored in rushHeaderIP/rushHeaderPort and // after that, the host and port of rush is set as ip/port for the message, that is // now sent to rush instead of to its final destination. // Also, a few HTTP headers for rush must be setup. // if ((rushPort == 0) || (rushHost == "")) useRush = false; if (useRush) { char rushHeaderPortAsString[16]; uint16_t rushHeaderPort = 0; std::string rushHeaderIP = ""; std::string headerRushHttp = ""; rushHeaderIP = ip; rushHeaderPort = port; ip = rushHost; port = rushPort; snprintf(rushHeaderPortAsString, sizeof(rushHeaderPortAsString), "%d", rushHeaderPort); headerRushHttp = "X-relayer-host: " + rushHeaderIP + ":" + rushHeaderPortAsString; LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str())); headers = curl_slist_append(headers, headerRushHttp.c_str()); outgoingMsgSize += headerRushHttp.size(); if (protocol == "https:") { headerRushHttp = "X-relayer-protocol: https"; LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str())); headers = curl_slist_append(headers, headerRushHttp.c_str()); outgoingMsgSize += headerRushHttp.size(); } } snprintf(portAsString, sizeof(portAsString), "%d", port); // ----- User Agent char cvBuf[CURL_VERSION_MAX_LENGTH]; char headerUserAgent[HTTP_HEADER_USER_AGENT_MAX_LENGTH]; snprintf(headerUserAgent, sizeof(headerUserAgent), "User-Agent: orion/%s libcurl/%s", versionGet(), curlVersionGet(cvBuf, sizeof(cvBuf))); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerUserAgent)); headers = curl_slist_append(headers, headerUserAgent); outgoingMsgSize += strlen(headerUserAgent) + 1; // ----- Host char headerHost[HTTP_HEADER_HOST_MAX_LENGTH]; snprintf(headerHost, sizeof(headerHost), "Host: %s:%d", ip.c_str(), (int) port); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerHost)); headers = curl_slist_append(headers, headerHost); outgoingMsgSize += strlen(headerHost) + 1; // ----- Tenant if (tenant != "") { headers = curl_slist_append(headers, ("fiware-service: " + tenant).c_str()); outgoingMsgSize += tenant.size() + 16; // "fiware-service: " } // ----- Service-Path if (servicePath != "") { headers = curl_slist_append(headers, ("Fiware-ServicePath: " + servicePath).c_str()); outgoingMsgSize += servicePath.size() + strlen("Fiware-ServicePath: "); } // ----- X-Auth-Token if (xauthToken != "") { headers = curl_slist_append(headers, ("X-Auth-Token: " + xauthToken).c_str()); outgoingMsgSize += xauthToken.size() + strlen("X-Auth-Token: "); } // ----- Accept std::string acceptedFormats = "application/xml, application/json"; if (acceptFormat != "") { acceptedFormats = acceptFormat; } std::string acceptString = "Accept: " + acceptedFormats; headers = curl_slist_append(headers, acceptString.c_str()); outgoingMsgSize += acceptString.size(); // ----- Expect headers = curl_slist_append(headers, "Expect: "); outgoingMsgSize += 8; // from "Expect: " // ----- Content-length std::stringstream contentLengthStringStream; contentLengthStringStream << content.size(); std::string headerContentLength = "Content-length: " + contentLengthStringStream.str(); LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerContentLength.c_str())); headers = curl_slist_append(headers, headerContentLength.c_str()); outgoingMsgSize += contentLengthStringStream.str().size() + 16; // from "Content-length: " outgoingMsgSize += content.size(); // ----- Content-type headers = curl_slist_append(headers, ("Content-type: " + content_type).c_str()); outgoingMsgSize += content_type.size() + 14; // from "Content-type: " // Check if total outgoing message size is too big if (outgoingMsgSize > MAX_DYN_MSG_SIZE) { LM_E(("Runtime Error (HTTP request to send is too large: %d bytes)", outgoingMsgSize)); // Cleanup curl environment curl_slist_free_all(headers); curl_easy_cleanup(curl); free(httpResponse->memory); delete httpResponse; LM_TRANSACTION_END(); return "error"; } // Contents const char* payload = content.c_str(); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (u_int8_t*) payload); // Set up URL std::string url; if (isIPv6(ip)) url = "[" + ip + "]"; else url = ip; url = url + ":" + portAsString + (resource.at(0) == '/'? "" : "/") + resource; // Prepare CURL handle with obtained options curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb.c_str()); // Set HTTP verb curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Allow redirection (?) curl_easy_setopt(curl, CURLOPT_HEADER, 1); // Activate include the header in the body output curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Put headers in place curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeMemoryCallback); // Send data here curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) httpResponse); // Custom data for response handling // // Timeout // // The parameter timeoutInMilliseconds holds the timeout time in milliseconds. // If the timeout time requested is 0, then no timeuot is used. // if (timeoutInMilliseconds != 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeoutInMilliseconds); } // Synchronous HTTP request LM_T(LmtClientOutputPayload, ("Sending message %lu to HTTP server: sending message of %d bytes to HTTP server", callNo, outgoingMsgSize)); res = curl_easy_perform(curl); if (res != CURLE_OK) { // // NOTE: This log line is used by the functional tests in cases/880_timeout_for_forward_and_notifications/ // So, this line should not be removed/altered, at least not without also modifying the functests. // LM_W(("Notification failure for %s:%s (curl_easy_perform failed: %s)", ip.c_str(), portAsString, curl_easy_strerror(res))); result = ""; } else { // The Response is here LM_I(("Notification Successfully Sent to %s", url.c_str())); result.assign(httpResponse->memory, httpResponse->size); } // Cleanup curl environment curl_slist_free_all(headers); curl_easy_cleanup(curl); free(httpResponse->memory); delete httpResponse; LM_TRANSACTION_END(); return result; }