diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 3d99a63d..dd92eb8c 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -37,6 +37,7 @@ #include "t2MtlsUtils.h" #include "t2log_wrapper.h" #include "busInterface.h" +#include "../xconf-client/multicurlinterface.h" #ifdef LIBRDKCERTSEL_BUILD #include "rdkcertselector.h" #define FILESCHEME "file://" @@ -58,9 +59,11 @@ typedef struct } childResponse ; #ifdef LIBRDKCERTSEL_BUILD +#if 0 static rdkcertselector_h curlCertSelector = NULL; static rdkcertselector_h curlRcvryCertSelector = NULL; #endif +#endif #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) @@ -68,8 +71,8 @@ static rdkcertselector_h curlRcvryCertSelector = NULL; static char waninterface[256]; #endif #endif -static pthread_once_t curlFileMutexOnce = PTHREAD_ONCE_INIT; -static pthread_mutex_t curlFileMutex; +//static pthread_once_t curlFileMutexOnce = PTHREAD_ONCE_INIT; +//static pthread_mutex_t curlFileMutex; typedef enum _ADDRESS_TYPE { @@ -78,18 +81,22 @@ typedef enum _ADDRESS_TYPE ADDR_IPV6 } ADDRESS_TYPE; +#if 0 static void sendOverHTTPInit() { pthread_mutex_init(&curlFileMutex, NULL); } + static size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *) stream); return written; } +#endif +#if 0 static T2ERROR setHeader(CURL *curl, const char* destURL, struct curl_slist **headerList, childResponse *childCurlResponse) { @@ -306,328 +313,38 @@ static void curlCertSelectorInit() } #endif +#endif + T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) { - CURL *curl = NULL; - FILE *fp = NULL; - CURLcode code = CURLE_OK; T2ERROR ret = T2ERROR_FAILURE; - childResponse childCurlResponse = {0}; - struct curl_slist *headerList = NULL; - CURLcode curl_code = CURLE_OK; -#ifdef LIBRDKCERTSEL_BUILD - rdkcertselector_h thisCertSel = NULL; - rdkcertselectorStatus_t curlGetCertStatus; - char *pCertURI = NULL; - char *pEngine = NULL; - bool state_red_enable = false; -#endif - char *pCertFile = NULL; - char *pCertPC = NULL; -#ifdef LIBRDKCONFIG_BUILD - size_t sKey = 0; -#endif - long http_code; - bool mtls_enable = false; - pid_t childPid; - int sharedPipeFds[2]; - T2Debug("%s ++in\n", __FUNCTION__); + T2Info("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) { return ret; } - if(pipe(sharedPipeFds) != 0) - { - T2Error("Failed to create pipe !!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return ret; - } -#ifdef LIBRDKCERTSEL_BUILD - curlCertSelectorInit(); - state_red_enable = isStateRedEnabled(); - T2Info("%s: state_red_enable: %d\n", __func__, state_red_enable ); - if (state_red_enable) - { - thisCertSel = curlRcvryCertSelector; - } - else - { - thisCertSel = curlCertSelector; - } -#endif -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - char *paramVal = NULL; - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", INTERFACE); + // Use new dedicated POST API + ret = http_pool_post(httpUrl, payload); - if(T2ERROR_SUCCESS == getParameterValue(TR181_DEVICE_CURRENT_WAN_IFNAME, ¶mVal)) + if(ret == T2ERROR_SUCCESS) { - if(strlen(paramVal) > 0) - { - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", paramVal); - } - - free(paramVal); - paramVal = NULL; + T2Info("Report Sent Successfully over HTTP using connection pool\n"); } else { - T2Error("Failed to get Value for %s\n", TR181_DEVICE_CURRENT_WAN_IFNAME); + T2Error("Failed to send report using connection pool\n"); } -#endif -#endif - mtls_enable = isMtlsEnabled(); -#ifndef LIBRDKCERTSEL_BUILD - if(mtls_enable == true && T2ERROR_SUCCESS != getMtlsCerts(&pCertFile, &pCertPC)) - { - T2Error("mTLS_cert get failed\n"); - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } - return ret; - } -#endif - // Block the userdefined signal handlers before fork - pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); - if((childPid = fork()) < 0) - { - T2Error("Failed to fork !!! exiting...\n"); - // Unblock the userdefined signal handler -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } - - /** - * Openssl has growing RSS which gets cleaned up only with OPENSSL_cleanup . - * This cleanup is not thread safe and classified as run once per application life cycle. - * Forking the libcurl calls so that it executes and terminates to release memory per execution. - */ - if(childPid == 0) - { - curl = curl_easy_init(); - if(curl) - { - childCurlResponse.curlStatus = true; - if(setHeader(curl, httpUrl, &headerList, &childCurlResponse) != T2ERROR_SUCCESS) - { - curl_easy_cleanup(curl); - goto child_cleanReturn; - } - if (setPayload(curl, payload, &childCurlResponse) != T2ERROR_SUCCESS) - { - curl_easy_cleanup(curl); // CID 189985: Resource leak - goto child_cleanReturn; - } -#ifdef LIBRDKCERTSEL_BUILD - pEngine = rdkcertselector_getEngine(thisCertSel); - if(pEngine != NULL) - { - code = curl_easy_setopt(curl, CURLOPT_SSLENGINE, pEngine); - } - else - { - code = curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L); - } - if(code != CURLE_OK) - { - curl_easy_cleanup(curl); - goto child_cleanReturn; - } - do - { - pCertFile = NULL; - pCertPC = NULL; - pCertURI = NULL; - curlGetCertStatus = rdkcertselector_getCert(thisCertSel, &pCertURI, &pCertPC); - if(curlGetCertStatus != certselectorOk) - { - T2Error("%s, T2:Failed to retrieve the certificate.\n", __func__); - curlCertSelectorFree(); - curl_easy_cleanup(curl); - goto child_cleanReturn; - } - else - { - // skip past file scheme in URI - pCertFile = pCertURI; - if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) - { - pCertFile += (sizeof(FILESCHEME) - 1); - } -#endif - if((mtls_enable == true) && (setMtlsHeaders(curl, pCertFile, pCertPC, &childCurlResponse) != T2ERROR_SUCCESS)) - { - curl_easy_cleanup(curl); // CID 189985: Resource leak - goto child_cleanReturn; - } - pthread_once(&curlFileMutexOnce, sendOverHTTPInit); - pthread_mutex_lock(&curlFileMutex); - - fp = fopen(CURL_OUTPUT_FILE, "wb"); - if(fp) - { - /* CID 143029 Unchecked return value from library */ - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)fp); - if(code != CURLE_OK) - { - // This might not be working we need to review this - childCurlResponse.curlSetopCode = code; - } - curl_code = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if(curl_code != CURLE_OK || http_code != 200) - { -#ifdef LIBRDKCERTSEL_BUILD - T2Error("%s: Failed to establish connection using xPKI certificate: %s, curl failed: %d\n", __func__, pCertFile, curl_code); -#endif - fprintf(stderr, "curl failed: %s\n", curl_easy_strerror(curl_code)); - childCurlResponse.lineNumber = __LINE__; - } - else - { - T2Info("%s: Using xpki Certs connection certname: %s\n", __func__, pCertFile); - childCurlResponse.lineNumber = __LINE__; - } - childCurlResponse.curlResponse = curl_code; - childCurlResponse.http_code = http_code; - - fclose(fp); - } - pthread_mutex_unlock(&curlFileMutex); -#ifdef LIBRDKCERTSEL_BUILD - } - } - while(rdkcertselector_setCurlStatus(thisCertSel, curl_code, (const char*)httpUrl) == TRY_ANOTHER); -#endif - curl_slist_free_all(headerList); - curl_easy_cleanup(curl); - } - else - { - childCurlResponse.curlStatus = false; - } -child_cleanReturn : -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - close(sharedPipeFds[0]); - if( -1 == write(sharedPipeFds[1], &childCurlResponse, sizeof(childResponse))) - { - fprintf(stderr, "unable to write to shared pipe from pid : %d \n", getpid()); - T2Error("unable to write \n"); - } - close(sharedPipeFds[1]); - exit(0); - - } - else + // Set outForkedPid to 0 since we're not forking anymore + if(outForkedPid) { - T2ERROR ret = T2ERROR_FAILURE; - if(outForkedPid) - { - *outForkedPid = childPid ; - } - // Use waitpid insted of wait we can have multiple child process - waitpid(childPid, NULL, 0); - // Unblock the userdefined signal handlers before fork - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - // Get the return status via IPC from child process - if ( -1 == close(sharedPipeFds[1])) - { - T2Error("Failed in close \n"); - } - if( -1 == read(sharedPipeFds[0], &childCurlResponse, sizeof(childResponse))) - { - T2Error("unable to read from the pipe \n"); - } - if ( -1 == close(sharedPipeFds[0])) - { - T2Error("Failed in close the pipe\n"); - } -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - T2Info("The return status from the child with pid %d is CurlStatus : %d\n", childPid, childCurlResponse.curlStatus); - //if(childCurlResponse.curlStatus == CURLE_OK) commenting this as we are observing childCurlResponse.curlStatus as 1, from line with CID 143029 Unchecked return value from library - T2Info("The return status from the child with pid %d SetopCode: %s; ResponseCode : %s; HTTP_CODE : %ld; Line Number : %d \n", childPid, curl_easy_strerror(childCurlResponse.curlSetopCode), curl_easy_strerror(childCurlResponse.curlResponse), childCurlResponse.http_code, childCurlResponse.lineNumber); - if (childCurlResponse.http_code == 200 || childCurlResponse.curlResponse == CURLE_OK) - { - ret = T2ERROR_SUCCESS; - T2Info("Report Sent Successfully over HTTP : %ld\n", childCurlResponse.http_code); - } - T2Debug("%s --out\n", __FUNCTION__); - return ret; + *outForkedPid = 0; } + T2Info("%s --out\n", __FUNCTION__); + return ret; } T2ERROR sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) diff --git a/source/telemetry2_0.c b/source/telemetry2_0.c index fafef5f8..62924100 100644 --- a/source/telemetry2_0.c +++ b/source/telemetry2_0.c @@ -45,6 +45,7 @@ #include "scheduler.h" #include "t2eventreceiver.h" #include "t2common.h" +#include "multicurlinterface.h" #ifdef INCLUDE_BREAKPAD #ifndef ENABLE_RDKC_SUPPORT @@ -72,6 +73,7 @@ T2ERROR initTelemetry() T2Debug("%s ++in\n", __FUNCTION__); initWhoamiSupport(); + init_connection_pool(); if(T2ERROR_SUCCESS == initReportProfiles()) { #ifndef DEVICE_EXTENDER @@ -125,6 +127,7 @@ static void terminate() #endif ReportProfiles_uninit(); } + http_pool_cleanup(); } static void _print_stack_backtrace(void) diff --git a/source/xconf-client/Makefile.am b/source/xconf-client/Makefile.am index 64c7f1e6..7d1df71e 100644 --- a/source/xconf-client/Makefile.am +++ b/source/xconf-client/Makefile.am @@ -20,7 +20,7 @@ AM_CFLAGS = lib_LTLIBRARIES = libxconfclient.la -libxconfclient_la_SOURCES = xconfclient.c +libxconfclient_la_SOURCES = xconfclient.c multicurlinterface.c libxconfclient_la_LDFLAGS = -shared -fPIC -lcjson -lcurl if IS_LIBRDKCERTSEL_ENABLED libxconfclient_la_CFLAGS = $(LIBRDKCERTSEL_FLAG) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c new file mode 100644 index 00000000..be06445d --- /dev/null +++ b/source/xconf-client/multicurlinterface.c @@ -0,0 +1,800 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multicurlinterface.h" +#include "t2log_wrapper.h" +#include "xconfclient.h" +#include "t2MtlsUtils.h" + +//Global variables +#define MAX_POOL_SIZE 3 +#define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" +#define RESPONSE_BUFFER_SIZE 8192 +static bool pool_initialized = false; + +// High-level design for connection pooling - Memory optimized +typedef struct +{ + CURLM *multi_handle; + CURL *easy_handles[MAX_POOL_SIZE]; + bool handle_available[MAX_POOL_SIZE]; + pthread_mutex_t pool_mutex; + struct curl_slist *post_headers; + // Response buffers removed - only used by GET requests locally +} http_connection_pool_t; + +typedef struct http_pool_config +{ + int max_connections; + int connection_timeout; + int keep_alive_timeout; + bool enable_mtls; +} http_pool_config_t; + +http_connection_pool_t pool; + +static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, + void *stream) +{ + T2Info("%s ++in\n", __FUNCTION__); + size_t realsize = len * nmemb; + curlResponseData* httpResponse = (curlResponseData*) stream; + + // FIX: Check for NULL stream to prevent crashes + if (!httpResponse) { + T2Error("httpGetCallBack: NULL stream parameter\n"); + return 0; + } + + char *ptr = (char*) realloc(httpResponse->data, + httpResponse->size + realsize + 1); + if (!ptr) + { + T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); + // FIX: Don't return 0 on realloc failure - this can cause curl to retry indefinitely + // Keep the original data intact + return realsize; // Tell curl we processed the data even though we couldn't store it + } + httpResponse->data = ptr; + memcpy(&(httpResponse->data[httpResponse->size]), response, realsize); + httpResponse->size += realsize; + httpResponse->data[httpResponse->size] = 0; + + T2Info("%s ++out\n", __FUNCTION__); + return realsize; +} + +// Add this debug callback function +static int curl_debug_callback_func(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) +{ + (void)handle; + (void)userptr; + + switch (type) { + case CURLINFO_TEXT: + T2Info("curl: %.*s", (int)size, data); + break; + case CURLINFO_HEADER_OUT: + T2Info("curl: => Send header: %.*s", (int)size, data); + break; + case CURLINFO_DATA_OUT: + T2Info("curl: => Send data: %zu bytes", size); + break; + case CURLINFO_SSL_DATA_OUT: + T2Info("curl: => Send SSL data: %zu bytes", size); + break; + case CURLINFO_HEADER_IN: + T2Info("curl: <= Recv header: %.*s", (int)size, data); + break; + case CURLINFO_DATA_IN: + T2Info("curl: <= Recv data: %zu bytes", size); + break; + case CURLINFO_SSL_DATA_IN: + T2Info("curl: <= Recv SSL data: %zu bytes", size); + break; + default: + break; + } + return 0; +} + +T2ERROR init_connection_pool() +{ + if(pool_initialized) + { + T2Info("Connection pool already initialized\n"); + return T2ERROR_SUCCESS; + } + + CURLcode code = CURLE_OK; + T2Info("%s ++in\n", __FUNCTION__); + char *pCertFile = NULL; + char *pPasswd = NULL; + //pool.multi_handle = curl_multi_init(); + + // Pre-allocate easy handles + for(int i = 0; i < MAX_POOL_SIZE; i++) + { + pool.easy_handles[i] = curl_easy_init(); + pool.handle_available[i] = true; + + //Set common options once + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); + +#if 1 + // More aggressive keepalive settings for your environment + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 50L); // 1 minute instead of 2 + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 30L); // 30 seconds instead of 60 + +#ifdef CURLOPT_TCP_KEEPCNT + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPCNT, 15L); // Allow up to 15 probes +#endif + + // Add connection reuse validation + curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); + + // Set connection timeout to handle dead connections faster + curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); + + // Add low-level socket options for better connection health detection + curl_easy_setopt(pool.easy_handles[i], CURLOPT_NOSIGNAL, 1L); + + // Add HTTP-level keep-alive headers for better server compatibility + curl_easy_setopt(pool.easy_handles[i], CURLOPT_HTTPHEADER, NULL); // Reset any existing headers first + + // Add low-level socket options for better connection health detection + curl_easy_setopt(pool.easy_handles[i], CURLOPT_NOSIGNAL, 1L); + + // Connection management options that work with older libcurl + curl_easy_setopt(pool.easy_handles[i], CURLOPT_PIPEWAIT, 0L); // Don't wait for pipelining + curl_easy_setopt(pool.easy_handles[i], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); // Use HTTP/1.1 +#endif + // Enable connection pooling with limited cache size + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 3L); // Only 1 connection per handle + + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + + // Certificate selector and SSL/TLS specific options from original code + curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLENGINE_DEFAULT, 1L); + + curl_easy_setopt(pool.easy_handles[i], CURLOPT_VERBOSE, 1L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_DEBUGFUNCTION, curl_debug_callback_func); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_DEBUGDATA, NULL); + } + + //mtls + if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) + { + for(int i = 0; i < MAX_POOL_SIZE; i++) + { + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLCERTTYPE, "P12"); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLCERT, pCertFile); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_KEYPASSWD, pPasswd); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + /* disconnect if authentication fails */ + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSL_VERIFYPEER, 1L); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + } + + if(pCertFile) { + free(pCertFile); + pCertFile = NULL; + } + if(pPasswd) { + free(pPasswd); + pPasswd = NULL; + } + } + + pool.post_headers = curl_slist_append(NULL, "Accept: application/json"); + pool.post_headers = curl_slist_append(pool.post_headers, "Content-type: application/json"); + + pthread_mutex_init(&pool.pool_mutex, NULL); + pool_initialized = true; + T2Info("%s ++out\n", __FUNCTION__); + return T2ERROR_SUCCESS; +} + +// Shared multi_perform execution function - simplified +static T2ERROR http_pool_execute_request(CURL *easy, int idx) +{ + T2Info("%s ++in\n", __FUNCTION__); + +#if 0 + // Add to multi handle + curl_multi_add_handle(pool.multi_handle, easy); + + int still_running; + do + { + T2Info("%s ; Performing curl request\n", __FUNCTION__); + curl_multi_perform(pool.multi_handle, &still_running); + curl_multi_wait(pool.multi_handle, NULL, 0, 100, NULL); // Reduced wait time for better performance + } + while(still_running); +#endif + + CURLcode res = curl_easy_perform(easy); + if (res != CURLE_OK) { + T2Error("curl_easy_perform failed: %s\n", curl_easy_strerror(res)); + + pthread_mutex_lock(&pool.pool_mutex); + pool.handle_available[idx] = true; + pthread_mutex_unlock(&pool.pool_mutex); + + return T2ERROR_FAILURE; + } + T2Info("%s ; Curl request completed\n", __FUNCTION__); + + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + T2Info("%s ; HTTP response code: %ld\n", __FUNCTION__, http_code); + + T2ERROR ret = T2ERROR_FAILURE; + if (http_code == 200) + { + ret = T2ERROR_SUCCESS; + T2Info("%s ; Request successful\n", __FUNCTION__); + } + else + { + T2Error("%s ; HTTP request failed with code: %ld\n", __FUNCTION__, http_code); + } + + // Cleanup + //curl_multi_remove_handle(pool.multi_handle, easy); + + pthread_mutex_lock(&pool.pool_mutex); + pool.handle_available[idx] = true; + pthread_mutex_unlock(&pool.pool_mutex); + + T2Info("%s ++out\n", __FUNCTION__); + return ret; +} + +#if 0 +// Helper function to acquire handle only +static T2ERROR acquire_pool_handle(CURL **easy, int *idx) +{ + if (!pool_initialized) + { + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + } + + *idx = -1; + + pthread_mutex_lock(&pool.pool_mutex); + + // Find an available handle + for(int i = 0; i < MAX_POOL_SIZE; i++) + { + if(pool.handle_available[i]) + { + T2Info("acquire_pool_handle ; Available handle = %d\n", i); + *idx = i; + pool.handle_available[i] = false; + break; + } + } + + pthread_mutex_unlock(&pool.pool_mutex); + + if(*idx == -1) + { + T2Error("No available HTTP handles\n"); + return T2ERROR_FAILURE; + } + + *easy = pool.easy_handles[*idx]; + return T2ERROR_SUCCESS; +} +#endif + +// Dedicated GET API +T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_output) +{ + T2Info("%s ++in\n", __FUNCTION__); + + if (!url) + { + T2Error("Invalid URL parameter\n"); + return T2ERROR_FAILURE; + } + + T2Info("%s ; GET url = %s\n", __FUNCTION__, url); + + if (!pool_initialized) + { + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + } + + // Always use handle 0 for GET requests + int idx = 0; + CURL *easy; + + pthread_mutex_lock(&pool.pool_mutex); + + // Check if handle 0 is available + if (!pool.handle_available[idx]) + { + pthread_mutex_unlock(&pool.pool_mutex); + T2Error("GET handle (index 0) is not available\n"); + return T2ERROR_FAILURE; + } + + // Reserve handle 0 for GET + pool.handle_available[idx] = false; + easy = pool.easy_handles[idx]; + + pthread_mutex_unlock(&pool.pool_mutex); + + // Allocate response buffer locally for GET requests only + curlResponseData response; + response.data = (char*)malloc(RESPONSE_BUFFER_SIZE); + if (!response.data) + { + T2Error("Failed to allocate response buffer\n"); + pthread_mutex_lock(&pool.pool_mutex); + pool.handle_available[idx] = true; + pthread_mutex_unlock(&pool.pool_mutex); + return T2ERROR_FAILURE; + } + response.data[0] = '\0'; + response.size = 0; + + // Configure for GET request + curl_easy_setopt(easy, CURLOPT_URL, url); + curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); + curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) &response); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) + curl_easy_setopt(easy, CURLOPT_INTERFACE, waninterface); +#else + curl_easy_setopt(easy, CURLOPT_INTERFACE, "erouter0"); +#endif +#endif + + // Execute the request + T2ERROR ret = http_pool_execute_request(easy, idx); + + if (ret == T2ERROR_SUCCESS && response.data) + { + T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response.size); + + // Handle file output for GET requests + if (enable_file_output) + { + FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); + if(httpOutput) + { + T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); + fputs(response.data, httpOutput); + fclose(httpOutput); + } + else + { + T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); + } + } + + // Copy response data if requested + if (response_data) + { + *response_data = NULL; + if(response.size <= SIZE_MAX) + { + *response_data = (char*)malloc(response.size + 1); + if(*response_data) + { + memcpy(*response_data, response.data, response.size); + (*response_data)[response.size] = '\0'; + } + } + } + } + else if (ret == T2ERROR_SUCCESS) + { + T2Error("%s ; No response data received\n", __FUNCTION__); + ret = T2ERROR_FAILURE; + } + + // Clean up local response buffer + free(response.data); + + T2Info("%s ++out\n", __FUNCTION__); + return ret; +} + +// Dedicated POST API +T2ERROR http_pool_post(const char *url, const char *payload) +{ + T2Info("%s ++in\n", __FUNCTION__); + + if (!url || !payload) + { + T2Error("Invalid URL or payload parameter\n"); + return T2ERROR_FAILURE; + } + + T2Info("%s ; POST url = %s\n", __FUNCTION__, url); + + if (!pool_initialized) + { + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + } + + // Use handles 1+ for POST requests (handle 0 is reserved for GET) + CURL *easy; + int idx = -1; + + pthread_mutex_lock(&pool.pool_mutex); + + // Find an available handle starting from index 1 + for(int i = 1; i < MAX_POOL_SIZE; i++) + { + if(pool.handle_available[i]) + { + T2Info("%s ; Available POST handle = %d\n", __FUNCTION__, i); + idx = i; + pool.handle_available[i] = false; + break; + } + } + + pthread_mutex_unlock(&pool.pool_mutex); + + if(idx == -1) + { + T2Error("No available POST handles (handles 1-%d)\n", MAX_POOL_SIZE - 1); + return T2ERROR_FAILURE; + } + + easy = pool.easy_handles[idx]; + + // Configure for POST request + curl_easy_setopt(easy, CURLOPT_URL, url); + curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.post_headers); + curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); + curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(payload)); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) + curl_easy_setopt(easy, CURLOPT_INTERFACE, waninterface); +#else + curl_easy_setopt(easy, CURLOPT_INTERFACE, "erouter0"); +#endif +#endif + + // Execute the request + T2ERROR ret = http_pool_execute_request(easy, idx); + + T2Info("%s ++out\n", __FUNCTION__); + return ret; +} + +T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) +{ + T2Info("%s ++in\n", __FUNCTION__); + + if (!config || !config->url) + { + T2Error("Invalid configuration parameters\n"); + return T2ERROR_FAILURE; + } + + T2Info("%s ; url = %s, type = %s\n", __FUNCTION__, config->url, + (config->type == HTTP_REQUEST_GET) ? "GET" : "POST"); + + if (!pool_initialized) + { + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + pool_initialized = true; + } + CURL *easy; + int idx = -1; + + pthread_mutex_lock(&pool.pool_mutex); + + // Find an available handle + for(int i = 0; i < MAX_POOL_SIZE; i++) + { + if(pool.handle_available[i]) + { + T2Info("%s ; Available handle = %d\n", __FUNCTION__, i); + idx = i; + pool.handle_available[i] = false; + break; + } + } + + pthread_mutex_unlock(&pool.pool_mutex); + + if(idx == -1) + { + T2Error("No available HTTP handles\n"); + return T2ERROR_FAILURE; + } + + easy = pool.easy_handles[idx]; + + curlResponseData* response = NULL; + if (config->type == HTTP_REQUEST_GET) + { + // Allocate response buffer locally for GET requests + response = (curlResponseData*)malloc(sizeof(curlResponseData)); + if (response) + { + response->data = (char*)malloc(RESPONSE_BUFFER_SIZE); + if (response->data) + { + response->data[0] = '\0'; + response->size = 0; + } + else + { + free(response); + response = NULL; + T2Error("Failed to allocate memory for response buffer\n"); + return T2ERROR_FAILURE; + } + } + else + { + T2Error("Failed to allocate memory for response structure\n"); + return T2ERROR_FAILURE; + } + } + + // Set common options + curl_easy_setopt(easy, CURLOPT_URL, config->url); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) + curl_easy_setopt(easy, CURLOPT_INTERFACE, waninterface); +#else + curl_easy_setopt(easy, CURLOPT_INTERFACE, "erouter0"); +#endif +#endif + + //TODO: separate POST and GET into 2 apis + if (config->type == HTTP_REQUEST_POST) + { + // POST request configuration (for sendReportOverHTTP) + curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "POST"); + + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.post_headers); + + if (config->payload) + { + curl_easy_setopt(easy, CURLOPT_POSTFIELDS, config->payload); + curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(config->payload)); + } + } + else + { + // GET request configuration (for doHttpGet) + curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); + curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); + } + + // Add to multi handle + curl_multi_add_handle(pool.multi_handle, easy); + + int still_running; + do + { + T2Info("%s ; Performing curl request\n", __FUNCTION__); + curl_multi_perform(pool.multi_handle, &still_running); + curl_multi_wait(pool.multi_handle, NULL, 0, 500, NULL); // TODO: Wait has to be revisited + } + while(still_running); + + T2Info("%s ; Curl request completed\n", __FUNCTION__); + + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + T2Info("%s ; HTTP response code: %ld\n", __FUNCTION__, http_code); + + T2ERROR ret = T2ERROR_FAILURE; + if (http_code == 200) + { + ret = T2ERROR_SUCCESS; + + if (response && response->data) + { + T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response->size); + + // Handle file output for GET requests + if (config->type == HTTP_REQUEST_GET && config->enable_file_output) + { + FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); + if(httpOutput) + { + T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); + fputs(response->data, httpOutput); + fclose(httpOutput); + } + else + { + T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); + } + } + + // Copy response data if requested + if (config->response_data && config->type == HTTP_REQUEST_GET) + { + *config->response_data = NULL; + if(response->size <= SIZE_MAX) + { + *config->response_data = (char*)malloc(response->size + 1); + if(*config->response_data) + { + memcpy(*config->response_data, response->data, response->size); + (*config->response_data)[response->size] = '\0'; + } + } + } + } + else + { + T2Error("%s ; No response data received\n", __FUNCTION__); + } + } + else + { + T2Error("%s ; HTTP request failed with code: %ld\n", __FUNCTION__, http_code); + } + + // Cleanup + curl_multi_remove_handle(pool.multi_handle, easy); + + pthread_mutex_lock(&pool.pool_mutex); + pool.handle_available[idx] = true; + pthread_mutex_unlock(&pool.pool_mutex); + + // Free local response buffer + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } + + T2Info("%s ++out\n", __FUNCTION__); + return ret; +} + +T2ERROR http_pool_request(const char *url, const char *payload, char **data) +{ + // Backward compatibility wrapper + http_pool_request_config_t config = + { + .type = payload ? HTTP_REQUEST_POST : HTTP_REQUEST_GET, + .url = url, + .payload = payload, + .response_data = data, + .enable_mtls = true, + .enable_file_output = (payload == NULL) // Enable file output for GET requests + }; + + return http_pool_request_ex(&config); +} + +T2ERROR http_pool_cleanup(void) +{ + if (!pool_initialized) { + T2Info("Pool not initialized, nothing to cleanup\n"); + return T2ERROR_SUCCESS; + } + + T2Info("%s ++in\n", __FUNCTION__); + + // FIX: Free all header lists before cleanup + if(pool.post_headers) { + curl_slist_free_all(pool.post_headers); + pool.post_headers = NULL; + } + + // Cleanup all easy handles + for(int i = 0; i < MAX_POOL_SIZE; i++) + { + if(pool.easy_handles[i]) { + curl_easy_cleanup(pool.easy_handles[i]); + pool.easy_handles[i] = NULL; + } + } + +#if 0 + // Cleanup multi handle + if(pool.multi_handle) { + curl_multi_cleanup(pool.multi_handle); + pool.multi_handle = NULL; + } +#endif + + // Destroy mutex + pthread_mutex_destroy(&pool.pool_mutex); + + // Reset initialization flag + pool_initialized = false; + + T2Info("%s ++out\n", __FUNCTION__); + return T2ERROR_SUCCESS; +} \ No newline at end of file diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h new file mode 100644 index 00000000..2b0a2108 --- /dev/null +++ b/source/xconf-client/multicurlinterface.h @@ -0,0 +1,56 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef _MULTI_CURL_INTERFACE_H_ +#define _MULTI_CURL_INTERFACE_H_ + +#include +#include "xconfclient.h" +#include "telemetry2_0.h" + +// Request types for different HTTP operations +typedef enum +{ + HTTP_REQUEST_GET, // For doHttpGet (XCONF client) + HTTP_REQUEST_POST // For sendReportOverHTTP +} http_request_type_t; + +// Request configuration structure +typedef struct +{ + http_request_type_t type; + const char *url; + const char *payload; // NULL for GET requests + char **response_data; // For storing response (GET requests) + bool enable_mtls; // Enable mTLS authentication + bool enable_file_output; // Write to file (for GET requests) +} http_pool_request_config_t; + +T2ERROR init_connection_pool(); + +// New dedicated APIs for better separation of concerns +T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_output); +T2ERROR http_pool_post(const char *url, const char *payload); + +// Legacy APIs (maintained for backward compatibility) +T2ERROR http_pool_request_ex(const http_pool_request_config_t *config); +T2ERROR http_pool_request(const char *url, const char *payload, char** data); // Backward compatibility +T2ERROR http_pool_cleanup(void); + +#endif /* _MULTI_CURL_INTERFACE_H_ */ diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index e3ef6c67..6e7711db 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -31,6 +31,7 @@ #include +#include "multicurlinterface.h" #include "t2log_wrapper.h" #include "reportprofiles.h" #include "profilexconf.h" @@ -524,7 +525,7 @@ T2ERROR appendRequestParams(CURLU *uri) T2Debug("%s --out\n", __FUNCTION__); return ret; } - +#if 0 static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, void *stream) { @@ -546,6 +547,7 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, return realsize; } +#endif #ifdef LIBRDKCERTSEL_BUILD void xcCertSelectorFree() @@ -578,29 +580,9 @@ static void xcCertSelectorInit() #endif T2ERROR doHttpGet(char* httpsUrl, char **data) { - - T2Debug("%s ++in\n", __FUNCTION__); - + T2Info("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); - CURL *curl; - CURLcode code = CURLE_OK; - long http_code = 0; - CURLcode curl_code = CURLE_OK; -#ifdef LIBRDKCERTSEL_BUILD - rdkcertselectorStatus_t xcGetCertStatus; - char *pCertURI = NULL; - char *pEngine = NULL; -#endif - char *pCertFile = NULL; - char *pPasswd = NULL; -#ifdef LIBRDKCONFIG_BUILD - size_t sPasswdSize = 0; -#endif - // char *pKeyType = "PEM" ; - bool mtls_enable = false; - pid_t childPid; - int sharedPipeFdStatus[2]; - int sharedPipeFdDataLen[2]; + T2ERROR ret; if(NULL == httpsUrl) { @@ -608,21 +590,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) return T2ERROR_FAILURE; } - if(pipe(sharedPipeFdStatus) != 0) - { - T2Error("Failed to create pipe for status !!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } - - if(pipe(sharedPipeFdDataLen) != 0) - { - T2Error("Failed to create pipe for data length!!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) char *paramVal = NULL; memset(waninterface, 0, sizeof(waninterface)); @@ -645,349 +613,21 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) } #endif #endif - mtls_enable = isMtlsEnabled(); - // block the userdefined signal handlers before fork - pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); - if((childPid = fork()) < 0) - { - T2Error("Failed to fork !!! exiting...\n"); - // Unblock the userdefined signal handlers - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } - - /** - * Openssl has growing RSS which gets cleaned up only with OPENSSL_cleanup . - * This cleanup is not thread safe and classified as run once per application life cycle. - * Forking the libcurl calls so that it executes and terminates to release memory per execution. - */ - if(childPid == 0) - { - - T2ERROR ret = T2ERROR_FAILURE; - curlResponseData* httpResponse = (curlResponseData *) malloc(sizeof(curlResponseData)); - httpResponse->data = (char*)malloc(1); - httpResponse->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) - httpResponse->size = 0; - - curl = curl_easy_init(); - - if(curl) - { - - code = curl_easy_setopt(curl, CURLOPT_URL, httpsUrl); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, httpGetCallBack); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) httpResponse); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, waninterface); - T2Info("TR181_DEVICE_CURRENT_WAN_IFNAME ---- %s\n", waninterface); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#else - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, IFINTERFACE); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#endif - -#endif - if(mtls_enable == true) - { -#ifdef LIBRDKCERTSEL_BUILD - pEngine = rdkcertselector_getEngine(xcCertSelector); - if(pEngine != NULL) - { - code = curl_easy_setopt(curl, CURLOPT_SSLENGINE, pEngine); - } - else - { - code = curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L); - } - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - do - { - pCertFile = NULL; - pPasswd = NULL; - pCertURI = NULL; - xcGetCertStatus = rdkcertselector_getCert(xcCertSelector, &pCertURI, &pPasswd); - if(xcGetCertStatus != certselectorOk) - { - T2Error("%s, T2:Failed to retrieve the certificate.\n", __func__); - xcCertSelectorFree(); - free(httpResponse->data); - free(httpResponse); - curl_easy_cleanup(curl); - ret = T2ERROR_FAILURE; - goto status_return; - } - else - { - // skip past file scheme in URI - pCertFile = pCertURI; - if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) - { - pCertFile += (sizeof(FILESCHEME) - 1); - } -#else - if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) - { -#endif - code = curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12"); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPasswd); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - /* disconnect if authentication fails */ - code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - curl_code = curl_easy_perform(curl); -#ifdef LIBRDKCERTSEL_BUILD - if(curl_code != CURLE_OK) - { - T2Error("%s: Failed to establish connection using xPKI certificate: %s, Curl failed : %d\n", __func__, pCertFile, curl_code); - } - else - { - T2Info("%s: Using xpki Certs connection certname : %s \n", __FUNCTION__, pCertFile); - } - } - } - while(rdkcertselector_setCurlStatus(xcCertSelector, curl_code, (const char*)httpsUrl) == TRY_ANOTHER); -#else - } - else - { - free(httpResponse->data); - free(httpResponse); - curl_easy_cleanup(curl); //CID 189986:Resource leak - T2Error("mTLS_get failure\n"); - ret = T2ERROR_FAILURE; - goto status_return; - } -#endif - } - else - { - curl_code = curl_easy_perform(curl); - } - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - if(http_code == 200 && curl_code == CURLE_OK) - { - T2Info("%s:%d, T2:Telemetry XCONF communication success\n", __func__, __LINE__); - size_t len = strlen(httpResponse->data); - - // Share data with parent - close(sharedPipeFdDataLen[0]); - write(sharedPipeFdDataLen[1], &len, sizeof(size_t)); - close(sharedPipeFdDataLen[1]); - - FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); - if(httpOutput) - { - T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); - fputs(httpResponse->data, httpOutput); - fclose(httpOutput); - } - else - { - T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); - } - - free(httpResponse->data); - free(httpResponse); -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - - if(NULL != pPasswd) - { -#ifdef LIBRDKCONFIG_BUILD - sPasswdSize = strlen(pPasswd); - if (rdkconfig_free((unsigned char**)&pPasswd, sPasswdSize) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pPasswd); -#endif - } -#endif - curl_easy_cleanup(curl); - } - else - { - T2Error("%s:%d, T2:Telemetry XCONF communication Failed with http code : %ld Curl code : %d \n", __func__, __LINE__, http_code, - curl_code); - T2Error("%s : curl_easy_perform failed with error message %s from curl \n", __FUNCTION__, curl_easy_strerror(curl_code)); - free(httpResponse->data); - free(httpResponse); -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pPasswd) - { -#ifdef LIBRDKCONFIG_BUILD - sPasswdSize = strlen(pPasswd); - if (rdkconfig_free((unsigned char**)&pPasswd, sPasswdSize) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pPasswd); -#endif - } -#endif - curl_easy_cleanup(curl); - if(http_code == 404) - { - ret = T2ERROR_PROFILE_NOT_SET; - } - else - { - ret = T2ERROR_FAILURE; - } - goto status_return ; - } - } - else - { - free(httpResponse->data); - free(httpResponse); - ret = T2ERROR_FAILURE; - goto status_return ; - } - - ret = T2ERROR_SUCCESS ; -status_return : - - close(sharedPipeFdStatus[0]); - write(sharedPipeFdStatus[1], &ret, sizeof(T2ERROR)); - close(sharedPipeFdStatus[1]); - exit(0); + // Use new dedicated GET API + ret = http_pool_get(httpsUrl, data, true); + if(ret == T2ERROR_SUCCESS) + { + T2Info("HTTP GET request completed successfully using connection pool\n"); } - else // Parent + else { - T2ERROR ret = T2ERROR_FAILURE; - // Use waitpid insted of wait - waitpid(childPid, NULL, 0); - // Unblock the userdefined signal handlers after wait - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - // Get the return status via IPC from child process - close(sharedPipeFdStatus[1]); - ssize_t readBytes = read(sharedPipeFdStatus[0], &ret, sizeof(T2ERROR)); - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - close(sharedPipeFdStatus[0]); - - // Get the datas via IPC from child process - if(ret == T2ERROR_SUCCESS) - { - size_t len = 0; - close(sharedPipeFdDataLen[1]); - readBytes = read(sharedPipeFdDataLen[0], &len, sizeof(size_t)); - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - close(sharedPipeFdDataLen[0]); - *data = NULL; - if(len <= SIZE_MAX) - { - *data = (char*)malloc(len + 1); - } - if(*data == NULL) - { - T2Error("Unable to allocate memory for XCONF config data \n"); - ret = T2ERROR_FAILURE; - } - else - { - if(len <= SIZE_MAX) - { - memset(*data, '\0', len + 1); - } - FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "r+"); - if(httpOutput) - { - // Read the whole file content - if(len <= SIZE_MAX) - { - readBytes = fread(*data, len, 1, httpOutput); - } - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - T2Debug("Configuration obtained from http server : \n %s \n", *data); - fclose(httpOutput); - } - } - } - T2Debug("%s --out\n", __FUNCTION__); - return ret; - + T2Error("Failed to perform HTTP GET request using connection pool\n"); } + T2Info("%s --out\n", __FUNCTION__); + return ret; } #if defined(ENABLE_REMOTE_PROFILE_DOWNLOAD)