From b1394e37d1e235d518091083e17965f28bb905be Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 7 Sep 2025 11:12:24 +0000 Subject: [PATCH 01/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/telemetry2_0.c | 2 + source/xconf-client/Makefile.am | 2 +- source/xconf-client/multicurlinterface.c | 161 +++++++++++++++++++++++ source/xconf-client/multicurlinterface.h | 31 +++++ source/xconf-client/xconfclient.c | 4 + 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100755 source/xconf-client/multicurlinterface.c create mode 100755 source/xconf-client/multicurlinterface.h diff --git a/source/telemetry2_0.c b/source/telemetry2_0.c index fafef5f8..f797dacf 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 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 100755 index 00000000..83b69d05 --- /dev/null +++ b/source/xconf-client/multicurlinterface.c @@ -0,0 +1,161 @@ +/* + * 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" + +//Global variables +#define MAX_POOL_SIZE 5 + +// High-level design for connection pooling +typedef struct { + CURLM *multi_handle; + CURL *easy_handles[MAX_POOL_SIZE]; + bool handle_available[MAX_POOL_SIZE]; + pthread_mutex_t pool_mutex; +} 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; + +T2ERROR init_connection_pool() +{ + T2Info("%s ++in\n", __FUNCTION__); + 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, 30); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); + + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); // Connection cache size + } + + pthread_mutex_init(&pool.pool_mutex, NULL); + T2Info("%s ++out\n", __FUNCTION__); + return T2ERROR_SUCCESS; +} + +T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData *response) +{ + T2Info("%s ++in\n", __FUNCTION__); + T2Info("%s ; url = %s\n", __FUNCTION__, url); + CURL *easy; + //CURLcode res; + int idx = -1; + + //(void *) payload; + //(void *) response; + 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]; + + // Set URL and payload + curl_easy_setopt(easy, CURLOPT_URL, url); + curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); + + // Response handling + //curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); + + // Add to multi handle + curl_multi_add_handle(pool.multi_handle, easy); + + int still_running; + do { + curl_multi_perform(pool.multi_handle, &still_running); + T2Info("%s ; Performing curl request for %s\n", __FUNCTION__, url); + // Wait for activity or timeout + curl_multi_wait(pool.multi_handle, NULL, 0, 1000, NULL); + } while(still_running); + size_t len = strlen(response->data); + T2Info("%s ; Response data size = %zu\n", __FUNCTION__, len); + if (response->data) + { + T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response->size); + } + else + { + T2Error("%s ; No response data received\n", __FUNCTION__); + } + // 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 T2ERROR_SUCCESS; +} + +T2ERROR http_pool_cleanup(void) +{ + for(int i = 0; i < MAX_POOL_SIZE; i++) { + curl_easy_cleanup(pool.easy_handles[i]); + } + curl_multi_cleanup(pool.multi_handle); + pthread_mutex_destroy(&pool.pool_mutex); + return T2ERROR_SUCCESS; +} diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h new file mode 100755 index 00000000..dc8e4399 --- /dev/null +++ b/source/xconf-client/multicurlinterface.h @@ -0,0 +1,31 @@ +/* + * 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" + +T2ERROR init_connection_pool(); +T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData* response); +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..d0281b4c 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" @@ -671,6 +672,9 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) httpResponse->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) httpResponse->size = 0; + //http_pool_request(httpsUrl, NULL, httpResponse); + T2Info("%s : http_pool_request called with url %s \n", __FUNCTION__, httpsUrl); + curl = curl_easy_init(); if(curl) From 8a064a90b94bfba1c7bbb452e18d6ae34baec904 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Tue, 9 Sep 2025 12:36:08 +0000 Subject: [PATCH 02/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/telemetry2_0.c | 1 + source/xconf-client/multicurlinterface.c | 145 +++++++++++++++++++++-- source/xconf-client/multicurlinterface.h | 2 +- source/xconf-client/xconfclient.c | 20 +++- 4 files changed, 152 insertions(+), 16 deletions(-) diff --git a/source/telemetry2_0.c b/source/telemetry2_0.c index f797dacf..62924100 100644 --- a/source/telemetry2_0.c +++ b/source/telemetry2_0.c @@ -127,6 +127,7 @@ static void terminate() #endif ReportProfiles_uninit(); } + http_pool_cleanup(); } static void _print_stack_backtrace(void) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 83b69d05..6e578d70 100755 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -8,7 +8,7 @@ * 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 + * 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, @@ -17,7 +17,6 @@ * limitations under the License. */ - #include #include #include @@ -34,9 +33,11 @@ #include "multicurlinterface.h" #include "t2log_wrapper.h" #include "xconfclient.h" +#include "t2MtlsUtils.h" //Global variables #define MAX_POOL_SIZE 5 +#define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" // High-level design for connection pooling typedef struct { @@ -55,9 +56,35 @@ typedef struct http_pool_config { 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; + +char *ptr = (char*) realloc(httpResponse->data, + httpResponse->size + realsize + 1); +if (!ptr) +{ +T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); +return 0; +} +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; +} + T2ERROR init_connection_pool() { + 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 @@ -72,7 +99,42 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); // Connection cache size + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); + + 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)); + } + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_WRITEFUNCTION, httpGetCallBack); + if(code != CURLE_OK) + { + T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); + } + if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) + { + 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)); + } + } } pthread_mutex_init(&pool.pool_mutex, NULL); @@ -80,15 +142,24 @@ T2ERROR init_connection_pool() return T2ERROR_SUCCESS; } -T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData *response) +T2ERROR http_pool_request(const char *url, const char *payload, char **data) { T2Info("%s ++in\n", __FUNCTION__); T2Info("%s ; url = %s\n", __FUNCTION__, url); CURL *easy; //CURLcode res; int idx = -1; + ssize_t readBytes = 0; + + curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); + response->data = (char*)malloc(1); + response->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) + response->size = 0; - //(void *) payload; + if(payload) + { + T2Info("%s ; payload = %s\n", __FUNCTION__, payload); + } //(void *) response; pthread_mutex_lock(&pool.pool_mutex); @@ -113,10 +184,10 @@ T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData // Set URL and payload curl_easy_setopt(easy, CURLOPT_URL, url); - curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); + //curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); // Response handling - //curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); // Add to multi handle @@ -124,21 +195,73 @@ T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData int still_running; do { + T2Info("%s ; Going to Perform curl request\n", __FUNCTION__); curl_multi_perform(pool.multi_handle, &still_running); T2Info("%s ; Performing curl request for %s\n", __FUNCTION__, url); // Wait for activity or timeout curl_multi_wait(pool.multi_handle, NULL, 0, 1000, NULL); + T2Info("%s ; After wait\n", __FUNCTION__); } while(still_running); - size_t len = strlen(response->data); - T2Info("%s ; Response data size = %zu\n", __FUNCTION__, len); + + T2Info("%s ; Performed curl request\n", __FUNCTION__); if (response->data) { - T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response->size); + T2Info("%s ; Response data size = %zu\nResponse data = %s\n", __FUNCTION__, response->size, response->data); } else { T2Error("%s ; No response data received\n", __FUNCTION__); } + + 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); + } + + *data = NULL; + if(response->size <= SIZE_MAX) + { + *data = (char*)malloc(response->size + 1); + } + if(*data == NULL) + { + T2Error("Unable to allocate memory for XCONF config data \n"); + //ret = T2ERROR_FAILURE; + } + else + { + if(response->size <= SIZE_MAX) + { + memset(*data, '\0', response->size + 1); + } + FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "r+"); + if(httpOutput) + { + // Read the whole file content + if(response->size <= SIZE_MAX) + { + readBytes = fread(*data, response->size, 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); + } + } + + free(response->data); + free(response); + // Cleanup curl_multi_remove_handle(pool.multi_handle, easy); @@ -158,4 +281,4 @@ T2ERROR http_pool_cleanup(void) curl_multi_cleanup(pool.multi_handle); pthread_mutex_destroy(&pool.pool_mutex); return T2ERROR_SUCCESS; -} +} \ No newline at end of file diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h index dc8e4399..ca3d9eb8 100755 --- a/source/xconf-client/multicurlinterface.h +++ b/source/xconf-client/multicurlinterface.h @@ -25,7 +25,7 @@ #include "telemetry2_0.h" T2ERROR init_connection_pool(); -T2ERROR http_pool_request(const char *url, const char *payload, curlResponseData* response); +T2ERROR http_pool_request(const char *url, const char *payload, char** data); 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 d0281b4c..bf5f2e4a 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -525,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) { @@ -547,6 +547,7 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, return realsize; } + #endif #ifdef LIBRDKCERTSEL_BUILD void xcCertSelectorFree() @@ -583,15 +584,18 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) T2Debug("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); +#if 0 CURL *curl; CURLcode code = CURLE_OK; long http_code = 0; CURLcode curl_code = CURLE_OK; +#endif #ifdef LIBRDKCERTSEL_BUILD rdkcertselectorStatus_t xcGetCertStatus; char *pCertURI = NULL; char *pEngine = NULL; #endif +#if 0 char *pCertFile = NULL; char *pPasswd = NULL; #ifdef LIBRDKCONFIG_BUILD @@ -600,6 +604,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) // char *pKeyType = "PEM" ; bool mtls_enable = false; pid_t childPid; +#endif int sharedPipeFdStatus[2]; int sharedPipeFdDataLen[2]; @@ -646,7 +651,11 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) } #endif #endif - mtls_enable = isMtlsEnabled(); + //mtls_enable = isMtlsEnabled(); + + http_pool_request(httpsUrl, NULL, data); + T2Info("%s : http_pool_request called with url %s \n", __FUNCTION__, httpsUrl); +#if 0 // block the userdefined signal handlers before fork pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); if((childPid = fork()) < 0) @@ -672,8 +681,6 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) httpResponse->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) httpResponse->size = 0; - //http_pool_request(httpsUrl, NULL, httpResponse); - T2Info("%s : http_pool_request called with url %s \n", __FUNCTION__, httpsUrl); curl = curl_easy_init(); @@ -835,6 +842,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) write(sharedPipeFdDataLen[1], &len, sizeof(size_t)); close(sharedPipeFdDataLen[1]); +#if 0 FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); if(httpOutput) { @@ -846,6 +854,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) { T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); } +#endif free(httpResponse->data); free(httpResponse); @@ -991,6 +1000,9 @@ status_return : return ret; } +#endif + T2Debug("%s --out\n", __FUNCTION__); + return T2ERROR_SUCCESS; } From 2d6de88b2abb7a3343a728e7be741e0f96f7c75b Mon Sep 17 00:00:00 2001 From: Yogeswaran K <166126056+yogeswaransky@users.noreply.github.com> Date: Fri, 12 Sep 2025 20:44:33 +0530 Subject: [PATCH 03/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices --- source/xconf-client/xconfclient.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index bf5f2e4a..c451d9e1 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -591,10 +591,12 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) CURLcode curl_code = CURLE_OK; #endif #ifdef LIBRDKCERTSEL_BUILD +#if 0 rdkcertselectorStatus_t xcGetCertStatus; char *pCertURI = NULL; char *pEngine = NULL; #endif +#endif #if 0 char *pCertFile = NULL; char *pPasswd = NULL; From ca243b7154178c608e522b4a2e720e438740d988 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 14 Sep 2025 14:25:32 +0000 Subject: [PATCH 04/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 6e578d70..be15bce2 100755 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -184,11 +184,17 @@ T2ERROR http_pool_request(const char *url, const char *payload, char **data) // Set URL and payload curl_easy_setopt(easy, CURLOPT_URL, url); - //curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); // Response handling - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); - curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); + if (payload) + { + curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); + } + else + { + 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); @@ -281,4 +287,4 @@ T2ERROR http_pool_cleanup(void) curl_multi_cleanup(pool.multi_handle); pthread_mutex_destroy(&pool.pool_mutex); return T2ERROR_SUCCESS; -} \ No newline at end of file +} From 3c9ca7d8d19f03e705924edd5399e647eac839e8 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 15 Sep 2025 08:04:04 +0000 Subject: [PATCH 05/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 341 +++---------------- source/xconf-client/multicurlinterface.c | 212 +++++++----- source/xconf-client/multicurlinterface.h | 19 +- source/xconf-client/xconfclient.c | 414 ++--------------------- 4 files changed, 223 insertions(+), 763 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 3d99a63d..8b380bf3 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://" @@ -68,8 +69,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 +79,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 +311,62 @@ 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]; + static bool pool_initialized = false; T2Debug("%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 + // Initialize connection pool on first use + if(!pool_initialized) { - thisCertSel = curlCertSelector; + ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + pool_initialized = true; + T2Info("Connection pool initialized successfully\n"); } -#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); + // Configure for POST request (sendReportOverHTTP) + http_pool_request_config_t config = { + .type = HTTP_REQUEST_POST, + .url = httpUrl, + .payload = payload, + .response_data = NULL, // POST doesn't need response data + .enable_mtls = true, + .enable_file_output = false // No file output for POST + }; - if(T2ERROR_SUCCESS == getParameterValue(TR181_DEVICE_CURRENT_WAN_IFNAME, ¶mVal)) + // Use the enhanced connection pool function + ret = http_pool_request_ex(&config); + + 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); - } -#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; + T2Error("Failed to send report using connection pool\n"); } - /** - * 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; } + T2Debug("%s --out\n", __FUNCTION__); + return ret; } T2ERROR sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index be15bce2..2ba980ca 100755 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -142,25 +142,36 @@ T2ERROR init_connection_pool() return T2ERROR_SUCCESS; } -T2ERROR http_pool_request(const char *url, const char *payload, char **data) +T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { T2Info("%s ++in\n", __FUNCTION__); - T2Info("%s ; url = %s\n", __FUNCTION__, url); + + 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"); + CURL *easy; - //CURLcode res; + //CURLcode code; int idx = -1; - ssize_t readBytes = 0; + //ssize_t readBytes = 0; + struct curl_slist *headers = NULL; + char *pCertFile = NULL; + char *pPasswd = NULL; + //FILE *fp = NULL; curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); response->data = (char*)malloc(1); - response->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) + response->data[0] = '\0'; response->size = 0; - if(payload) - { - T2Info("%s ; payload = %s\n", __FUNCTION__, payload); + if(config->payload) { + T2Info("%s ; payload = %s\n", __FUNCTION__, config->payload); } - //(void *) response; + pthread_mutex_lock(&pool.pool_mutex); // Find an available handle @@ -177,98 +188,130 @@ T2ERROR http_pool_request(const char *url, const char *payload, char **data) if(idx == -1) { T2Error("No available HTTP handles\n"); + free(response->data); + free(response); return T2ERROR_FAILURE; } easy = pool.easy_handles[idx]; - // Set URL and payload - curl_easy_setopt(easy, CURLOPT_URL, url); + // Reset handle to clean state + curl_easy_reset(easy); - // Response handling - if (payload) - { - curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); - } - else - { + // Set common options + curl_easy_setopt(easy, CURLOPT_URL, config->url); + curl_easy_setopt(easy, CURLOPT_TIMEOUT, 30); + curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, 10); + curl_easy_setopt(easy, CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(easy, CURLOPT_TCP_KEEPIDLE, 120L); + curl_easy_setopt(easy, CURLOPT_TCP_KEEPINTVL, 60L); + curl_easy_setopt(easy, CURLOPT_MAXCONNECTS, 5L); + curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + +#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 + + // Configure based on request type + if (config->type == HTTP_REQUEST_POST) { + // POST request configuration (for sendReportOverHTTP) + curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "POST"); + + // Set headers for POST + headers = curl_slist_append(NULL, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-type: application/json"); + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers); + + if (config->payload) { + curl_easy_setopt(easy, CURLOPT_POSTFIELDS, config->payload); + curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(config->payload)); + } + + // For POST, we might not need response data, use simple callback + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); + curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); + + } 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); } + + // Handle mTLS if enabled + if (config->enable_mtls && T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) { + curl_easy_setopt(easy, CURLOPT_SSLCERTTYPE, "P12"); + curl_easy_setopt(easy, CURLOPT_SSLCERT, pCertFile); + curl_easy_setopt(easy, CURLOPT_KEYPASSWD, pPasswd); + curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L); + } // Add to multi handle curl_multi_add_handle(pool.multi_handle, easy); int still_running; do { - T2Info("%s ; Going to Perform curl request\n", __FUNCTION__); + T2Info("%s ; Performing curl request\n", __FUNCTION__); curl_multi_perform(pool.multi_handle, &still_running); - T2Info("%s ; Performing curl request for %s\n", __FUNCTION__, url); - // Wait for activity or timeout curl_multi_wait(pool.multi_handle, NULL, 0, 1000, NULL); - T2Info("%s ; After wait\n", __FUNCTION__); } while(still_running); - - T2Info("%s ; Performed curl request\n", __FUNCTION__); - if (response->data) - { - T2Info("%s ; Response data size = %zu\nResponse data = %s\n", __FUNCTION__, response->size, response->data); - } - else - { - T2Error("%s ; No response data received\n", __FUNCTION__); - } - - 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); - } - - *data = NULL; - if(response->size <= SIZE_MAX) - { - *data = (char*)malloc(response->size + 1); - } - if(*data == NULL) - { - T2Error("Unable to allocate memory for XCONF config data \n"); - //ret = T2ERROR_FAILURE; - } - else - { - if(response->size <= SIZE_MAX) - { - memset(*data, '\0', response->size + 1); - } - FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "r+"); - if(httpOutput) - { - // Read the whole file content - if(response->size <= SIZE_MAX) - { - readBytes = fread(*data, response->size, 1, httpOutput); + + T2Info("%s ; Curl request completed\n", __FUNCTION__); + + // Get response code + 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->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); + } } - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; + + // 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'; + } + } } - T2Debug("Configuration obtained from http server : \n %s \n", *data); - fclose(httpOutput); + } else { + T2Error("%s ; No response data received\n", __FUNCTION__); } + } else { + T2Error("%s ; HTTP request failed with code: %ld\n", __FUNCTION__, http_code); } + // Cleanup free(response->data); free(response); - - // Cleanup + + if (headers) { + curl_slist_free_all(headers); + } + curl_multi_remove_handle(pool.multi_handle, easy); pthread_mutex_lock(&pool.pool_mutex); @@ -276,7 +319,22 @@ T2ERROR http_pool_request(const char *url, const char *payload, char **data) pthread_mutex_unlock(&pool.pool_mutex); T2Info("%s ++out\n", __FUNCTION__); - return T2ERROR_SUCCESS; + 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) diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h index ca3d9eb8..cf01cc82 100755 --- a/source/xconf-client/multicurlinterface.h +++ b/source/xconf-client/multicurlinterface.h @@ -24,8 +24,25 @@ #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(); -T2ERROR http_pool_request(const char *url, const char *payload, char** data); +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 c451d9e1..4211e6dc 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -580,35 +580,10 @@ static void xcCertSelectorInit() #endif T2ERROR doHttpGet(char* httpsUrl, char **data) { - T2Debug("%s ++in\n", __FUNCTION__); - T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); -#if 0 - CURL *curl; - CURLcode code = CURLE_OK; - long http_code = 0; - CURLcode curl_code = CURLE_OK; -#endif -#ifdef LIBRDKCERTSEL_BUILD -#if 0 - rdkcertselectorStatus_t xcGetCertStatus; - char *pCertURI = NULL; - char *pEngine = NULL; -#endif -#endif -#if 0 - char *pCertFile = NULL; - char *pPasswd = NULL; -#ifdef LIBRDKCONFIG_BUILD - size_t sPasswdSize = 0; -#endif - // char *pKeyType = "PEM" ; - bool mtls_enable = false; - pid_t childPid; -#endif - int sharedPipeFdStatus[2]; - int sharedPipeFdDataLen[2]; + + static bool pool_initialized = false; if(NULL == httpsUrl) { @@ -616,21 +591,20 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) return T2ERROR_FAILURE; } - if(pipe(sharedPipeFdStatus) != 0) + // Initialize connection pool on first use + if(!pool_initialized) { - T2Error("Failed to create pipe for status !!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + pool_initialized = true; + T2Info("Connection pool initialized successfully\n"); } - 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)); @@ -653,359 +627,31 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) } #endif #endif - //mtls_enable = isMtlsEnabled(); - http_pool_request(httpsUrl, NULL, data); - T2Info("%s : http_pool_request called with url %s \n", __FUNCTION__, httpsUrl); -#if 0 - // block the userdefined signal handlers before fork - pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); - if((childPid = fork()) < 0) + // Configure for GET request (doHttpGet) + http_pool_request_config_t config = { + .type = HTTP_REQUEST_GET, + .url = httpsUrl, + .payload = NULL, // No payload for GET requests + .response_data = data, // Store response data for GET + .enable_mtls = true, + .enable_file_output = true // Enable file output for GET requests + }; + + // Use the enhanced connection pool function + T2ERROR ret = http_pool_request_ex(&config); + + if(ret == T2ERROR_SUCCESS) { - 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; + T2Info("HTTP GET request completed successfully using connection pool\n"); } - - /** - * 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) + else { - - 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]); - -#if 0 - 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); - } -#endif - - 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); - + T2Error("Failed to perform HTTP GET request using connection pool\n"); } - else // Parent - { - 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; - - } -#endif T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_SUCCESS; - + return ret; } #if defined(ENABLE_REMOTE_PROFILE_DOWNLOAD) From 58b8a8439e84884c41eb6c61351dd4772609fe0d Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 15 Sep 2025 09:46:24 +0000 Subject: [PATCH 06/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 8b380bf3..3db37050 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -59,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) From b5d7e556abdb8c136d42f607342cc31952d287ba Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 15 Sep 2025 13:39:01 +0000 Subject: [PATCH 07/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 16 +++++----------- source/xconf-client/multicurlinterface.c | 13 +++++++++++++ source/xconf-client/xconfclient.c | 19 ++++++------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 3db37050..f3ac044e 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -318,7 +318,6 @@ static void curlCertSelectorInit() T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) { T2ERROR ret = T2ERROR_FAILURE; - static bool pool_initialized = false; T2Debug("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) @@ -326,17 +325,12 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) return ret; } - // Initialize connection pool on first use - if(!pool_initialized) + // Initialize connection pool - now handled internally with thread safety + ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) { - ret = init_connection_pool(); - if(ret != T2ERROR_SUCCESS) - { - T2Error("Failed to initialize connection pool\n"); - return ret; - } - pool_initialized = true; - T2Info("Connection pool initialized successfully\n"); + T2Error("Failed to initialize connection pool\n"); + return ret; } // Configure for POST request (sendReportOverHTTP) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 2ba980ca..15906f86 100755 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -81,6 +81,17 @@ return realsize; T2ERROR init_connection_pool() { + static bool pool_initialized = false; + static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; + + // Thread-safe initialization check + pthread_mutex_lock(&init_mutex); + if(pool_initialized) { + pthread_mutex_unlock(&init_mutex); + T2Info("Connection pool already initialized\n"); + return T2ERROR_SUCCESS; + } + CURLcode code = CURLE_OK; T2Info("%s ++in\n", __FUNCTION__); char *pCertFile = NULL; @@ -138,6 +149,8 @@ T2ERROR init_connection_pool() } pthread_mutex_init(&pool.pool_mutex, NULL); + pool_initialized = true; + pthread_mutex_unlock(&init_mutex); T2Info("%s ++out\n", __FUNCTION__); return T2ERROR_SUCCESS; } diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 4211e6dc..f6a7ffdd 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -582,8 +582,6 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) { T2Debug("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); - - static bool pool_initialized = false; if(NULL == httpsUrl) { @@ -591,17 +589,12 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) return T2ERROR_FAILURE; } - // Initialize connection pool on first use - if(!pool_initialized) + // Initialize connection pool - now handled internally with thread safety + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) { - T2ERROR ret = init_connection_pool(); - if(ret != T2ERROR_SUCCESS) - { - T2Error("Failed to initialize connection pool\n"); - return ret; - } - pool_initialized = true; - T2Info("Connection pool initialized successfully\n"); + T2Error("Failed to initialize connection pool\n"); + return ret; } #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) @@ -639,7 +632,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) }; // Use the enhanced connection pool function - T2ERROR ret = http_pool_request_ex(&config); + ret = http_pool_request_ex(&config); if(ret == T2ERROR_SUCCESS) { From f0456c763d4db2a1254dad8a7ec4bb06e661e172 Mon Sep 17 00:00:00 2001 From: Yogeswaran K <166126056+yogeswaransky@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:55:01 +0530 Subject: [PATCH 08/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices --- source/protocol/http/curlinterface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index f3ac044e..bd331998 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -319,7 +319,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) { T2ERROR ret = T2ERROR_FAILURE; - T2Debug("%s ++in\n", __FUNCTION__); + T2Info("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) { return ret; @@ -361,7 +361,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) *outForkedPid = 0; } - T2Debug("%s --out\n", __FUNCTION__); + T2Info("%s --out\n", __FUNCTION__); return ret; } From 7412d7db6f7865eb5b0969f4fe9d550c2b4a76a9 Mon Sep 17 00:00:00 2001 From: Yogeswaran K <166126056+yogeswaransky@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:56:53 +0530 Subject: [PATCH 09/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices --- source/xconf-client/xconfclient.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index f6a7ffdd..aaed297d 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -580,7 +580,7 @@ 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); if(NULL == httpsUrl) @@ -643,7 +643,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) T2Error("Failed to perform HTTP GET request using connection pool\n"); } - T2Debug("%s --out\n", __FUNCTION__); + T2Info("%s --out\n", __FUNCTION__); return ret; } From 0ff8c660c7178bb4d7d2c15f95fe9af166aacff0 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 04:33:46 +0000 Subject: [PATCH 10/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 9 +- source/xconf-client/multicurlinterface.c | 193 +++++++++++++---------- source/xconf-client/multicurlinterface.h | 8 +- source/xconf-client/xconfclient.c | 11 +- 4 files changed, 126 insertions(+), 95 deletions(-) mode change 100755 => 100644 source/xconf-client/multicurlinterface.c mode change 100755 => 100644 source/xconf-client/multicurlinterface.h diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index bd331998..a0a3ba66 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -319,7 +319,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) { T2ERROR ret = T2ERROR_FAILURE; - T2Info("%s ++in\n", __FUNCTION__); + T2Debug("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) { return ret; @@ -334,7 +334,8 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) } // Configure for POST request (sendReportOverHTTP) - http_pool_request_config_t config = { + http_pool_request_config_t config = + { .type = HTTP_REQUEST_POST, .url = httpUrl, .payload = payload, @@ -345,7 +346,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) // Use the enhanced connection pool function ret = http_pool_request_ex(&config); - + if(ret == T2ERROR_SUCCESS) { T2Info("Report Sent Successfully over HTTP using connection pool\n"); @@ -361,7 +362,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) *outForkedPid = 0; } - T2Info("%s --out\n", __FUNCTION__); + T2Debug("%s --out\n", __FUNCTION__); return ret; } diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c old mode 100755 new mode 100644 index 15906f86..5d577624 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -8,7 +8,7 @@ * 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 + * 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, @@ -40,14 +40,16 @@ #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" // High-level design for connection pooling -typedef struct { +typedef struct +{ CURLM *multi_handle; CURL *easy_handles[MAX_POOL_SIZE]; bool handle_available[MAX_POOL_SIZE]; pthread_mutex_t pool_mutex; } http_connection_pool_t; -typedef struct http_pool_config { +typedef struct http_pool_config +{ int max_connections; int connection_timeout; int keep_alive_timeout; @@ -57,52 +59,50 @@ typedef struct http_pool_config { http_connection_pool_t pool; static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, - void *stream) + void *stream) { T2Info("%s ++in\n", __FUNCTION__); -size_t realsize = len * nmemb; -curlResponseData* httpResponse = (curlResponseData*) stream; + size_t realsize = len * nmemb; + curlResponseData* httpResponse = (curlResponseData*) stream; -char *ptr = (char*) realloc(httpResponse->data, - httpResponse->size + realsize + 1); -if (!ptr) -{ -T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); -return 0; -} -httpResponse->data = ptr; -memcpy(&(httpResponse->data[httpResponse->size]), response, realsize); -httpResponse->size += realsize; -httpResponse->data[httpResponse->size] = 0; + char *ptr = (char*) realloc(httpResponse->data, + httpResponse->size + realsize + 1); + if (!ptr) + { + T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); + return 0; + } + 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; + T2Info("%s ++out\n", __FUNCTION__); + return realsize; } T2ERROR init_connection_pool() { static bool pool_initialized = false; - static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; - - // Thread-safe initialization check - pthread_mutex_lock(&init_mutex); - if(pool_initialized) { - pthread_mutex_unlock(&init_mutex); + + 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++) { + 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, 30); curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10); @@ -147,10 +147,9 @@ T2ERROR init_connection_pool() } } } - + pthread_mutex_init(&pool.pool_mutex, NULL); pool_initialized = true; - pthread_mutex_unlock(&init_mutex); T2Info("%s ++out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -158,15 +157,16 @@ T2ERROR init_connection_pool() T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { T2Info("%s ++in\n", __FUNCTION__); - - if (!config || !config->url) { + + if (!config || !config->url) + { T2Error("Invalid configuration parameters\n"); return T2ERROR_FAILURE; } - - T2Info("%s ; url = %s, type = %s\n", __FUNCTION__, config->url, + + T2Info("%s ; url = %s, type = %s\n", __FUNCTION__, config->url, (config->type == HTTP_REQUEST_GET) ? "GET" : "POST"); - + CURL *easy; //CURLcode code; int idx = -1; @@ -181,36 +181,40 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) response->data[0] = '\0'; response->size = 0; - if(config->payload) { + if(config->payload) + { T2Info("%s ; payload = %s\n", __FUNCTION__, config->payload); } pthread_mutex_lock(&pool.pool_mutex); - + // Find an available handle - for(int i = 0; i < MAX_POOL_SIZE; i++) { - if(pool.handle_available[i]) { + 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) { + + if(idx == -1) + { T2Error("No available HTTP handles\n"); free(response->data); free(response); return T2ERROR_FAILURE; } - + easy = pool.easy_handles[idx]; - + // Reset handle to clean state - curl_easy_reset(easy); - + //curl_easy_reset(easy); + // Set common options curl_easy_setopt(easy, CURLOPT_URL, config->url); curl_easy_setopt(easy, CURLOPT_TIMEOUT, 30); @@ -230,25 +234,29 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) #endif // Configure based on request type - if (config->type == HTTP_REQUEST_POST) { + if (config->type == HTTP_REQUEST_POST) + { // POST request configuration (for sendReportOverHTTP) curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "POST"); - + // Set headers for POST headers = curl_slist_append(NULL, "Accept: application/json"); headers = curl_slist_append(headers, "Content-type: application/json"); curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers); - - if (config->payload) { + + if (config->payload) + { curl_easy_setopt(easy, CURLOPT_POSTFIELDS, config->payload); curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(config->payload)); } - + // For POST, we might not need response data, use simple callback curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); - - } else { + + } + else + { // GET request configuration (for doHttpGet) curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); @@ -256,81 +264,98 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) } // Handle mTLS if enabled - if (config->enable_mtls && T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) { + if (config->enable_mtls && T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) + { curl_easy_setopt(easy, CURLOPT_SSLCERTTYPE, "P12"); curl_easy_setopt(easy, CURLOPT_SSLCERT, pCertFile); curl_easy_setopt(easy, CURLOPT_KEYPASSWD, pPasswd); curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L); } - + // Add to multi handle curl_multi_add_handle(pool.multi_handle, easy); - + int still_running; - do { + do + { T2Info("%s ; Performing curl request\n", __FUNCTION__); curl_multi_perform(pool.multi_handle, &still_running); curl_multi_wait(pool.multi_handle, NULL, 0, 1000, NULL); - } while(still_running); - + } + while(still_running); + T2Info("%s ; Curl request completed\n", __FUNCTION__); - + // Get response code 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) { + if (http_code == 200) + { ret = T2ERROR_SUCCESS; - - if (response->data) { + + if (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) { + if (config->type == HTTP_REQUEST_GET && config->enable_file_output) + { FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); - if(httpOutput) { + if(httpOutput) + { T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); fputs(response->data, httpOutput); fclose(httpOutput); - } else { + } + 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) { + if (config->response_data && config->type == HTTP_REQUEST_GET) + { *config->response_data = NULL; - if(response->size <= SIZE_MAX) { + if(response->size <= SIZE_MAX) + { *config->response_data = (char*)malloc(response->size + 1); - if(*config->response_data) { + if(*config->response_data) + { memcpy(*config->response_data, response->data, response->size); (*config->response_data)[response->size] = '\0'; } } } - } else { + } + else + { T2Error("%s ; No response data received\n", __FUNCTION__); } - } else { + } + else + { T2Error("%s ; HTTP request failed with code: %ld\n", __FUNCTION__, http_code); } // Cleanup free(response->data); free(response); - - if (headers) { + + if (headers) + { curl_slist_free_all(headers); } - + 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; } @@ -338,7 +363,8 @@ 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 wrapper - http_pool_request_config_t config = { + http_pool_request_config_t config = + { .type = payload ? HTTP_REQUEST_POST : HTTP_REQUEST_GET, .url = url, .payload = payload, @@ -346,13 +372,14 @@ T2ERROR http_pool_request(const char *url, const char *payload, char **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) { - for(int i = 0; i < MAX_POOL_SIZE; i++) { + for(int i = 0; i < MAX_POOL_SIZE; i++) + { curl_easy_cleanup(pool.easy_handles[i]); } curl_multi_cleanup(pool.multi_handle); diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h old mode 100755 new mode 100644 index cf01cc82..2057b4a1 --- a/source/xconf-client/multicurlinterface.h +++ b/source/xconf-client/multicurlinterface.h @@ -25,13 +25,15 @@ #include "telemetry2_0.h" // Request types for different HTTP operations -typedef enum { +typedef enum +{ HTTP_REQUEST_GET, // For doHttpGet (XCONF client) HTTP_REQUEST_POST // For sendReportOverHTTP } http_request_type_t; // Request configuration structure -typedef struct { +typedef struct +{ http_request_type_t type; const char *url; const char *payload; // NULL for GET requests @@ -43,6 +45,6 @@ typedef struct { T2ERROR init_connection_pool(); 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); +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 aaed297d..86801acc 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -547,7 +547,7 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, return realsize; } - #endif +#endif #ifdef LIBRDKCERTSEL_BUILD void xcCertSelectorFree() @@ -580,7 +580,7 @@ static void xcCertSelectorInit() #endif T2ERROR doHttpGet(char* httpsUrl, char **data) { - T2Info("%s ++in\n", __FUNCTION__); + T2Debug("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); if(NULL == httpsUrl) @@ -622,7 +622,8 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) #endif // Configure for GET request (doHttpGet) - http_pool_request_config_t config = { + http_pool_request_config_t config = + { .type = HTTP_REQUEST_GET, .url = httpsUrl, .payload = NULL, // No payload for GET requests @@ -633,7 +634,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) // Use the enhanced connection pool function ret = http_pool_request_ex(&config); - + if(ret == T2ERROR_SUCCESS) { T2Info("HTTP GET request completed successfully using connection pool\n"); @@ -643,7 +644,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) T2Error("Failed to perform HTTP GET request using connection pool\n"); } - T2Info("%s --out\n", __FUNCTION__); + T2Debug("%s --out\n", __FUNCTION__); return ret; } From 0fdb6ef453c442727df2a4e09390a93139958360 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 05:39:04 +0000 Subject: [PATCH 11/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 4 ++-- source/xconf-client/multicurlinterface.c | 17 +++++++---------- source/xconf-client/xconfclient.c | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index a0a3ba66..92774ce8 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -319,7 +319,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) { T2ERROR ret = T2ERROR_FAILURE; - T2Debug("%s ++in\n", __FUNCTION__); + T2Info("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) { return ret; @@ -362,7 +362,7 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) *outForkedPid = 0; } - T2Debug("%s --out\n", __FUNCTION__); + T2Info("%s --out\n", __FUNCTION__); return ret; } diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 5d577624..33f66e1b 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -81,6 +81,12 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, return realsize; } +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; +} + T2ERROR init_connection_pool() { static bool pool_initialized = false; @@ -181,11 +187,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) response->data[0] = '\0'; response->size = 0; - if(config->payload) - { - T2Info("%s ; payload = %s\n", __FUNCTION__, config->payload); - } - pthread_mutex_lock(&pool.pool_mutex); // Find an available handle @@ -250,10 +251,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(config->payload)); } - // For POST, we might not need response data, use simple callback - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); - curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); - + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writeToFile); } else { @@ -286,7 +284,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) T2Info("%s ; Curl request completed\n", __FUNCTION__); - // Get response code long http_code; curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); T2Info("%s ; HTTP response code: %ld\n", __FUNCTION__, http_code); diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 86801acc..4383c1e0 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -580,7 +580,7 @@ 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); if(NULL == httpsUrl) @@ -644,7 +644,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) T2Error("Failed to perform HTTP GET request using connection pool\n"); } - T2Debug("%s --out\n", __FUNCTION__); + T2Info("%s --out\n", __FUNCTION__); return ret; } From 12fe3ef166f3fee3a192a8ad47de2e561b7dec17 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 07:25:55 +0000 Subject: [PATCH 12/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 8 -------- source/xconf-client/multicurlinterface.c | 21 ++++++++++++--------- source/xconf-client/xconfclient.c | 8 -------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 92774ce8..2efabf76 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -325,14 +325,6 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) return ret; } - // Initialize connection pool - now handled internally with thread safety - ret = init_connection_pool(); - if(ret != T2ERROR_SUCCESS) - { - T2Error("Failed to initialize connection pool\n"); - return ret; - } - // Configure for POST request (sendReportOverHTTP) http_pool_request_config_t config = { diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 33f66e1b..970bd8a0 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -38,6 +38,7 @@ //Global variables #define MAX_POOL_SIZE 5 #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" +static bool pool_initialized = false; // High-level design for connection pooling typedef struct @@ -89,8 +90,6 @@ static size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) T2ERROR init_connection_pool() { - static bool pool_initialized = false; - if(pool_initialized) { T2Info("Connection pool already initialized\n"); @@ -173,6 +172,16 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) 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; //CURLcode code; int idx = -1; @@ -182,6 +191,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) char *pPasswd = NULL; //FILE *fp = NULL; + curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); response->data = (char*)malloc(1); response->data[0] = '\0'; @@ -218,13 +228,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) // Set common options curl_easy_setopt(easy, CURLOPT_URL, config->url); - curl_easy_setopt(easy, CURLOPT_TIMEOUT, 30); - curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, 10); - curl_easy_setopt(easy, CURLOPT_TCP_KEEPALIVE, 1L); - curl_easy_setopt(easy, CURLOPT_TCP_KEEPIDLE, 120L); - curl_easy_setopt(easy, CURLOPT_TCP_KEEPINTVL, 60L); - curl_easy_setopt(easy, CURLOPT_MAXCONNECTS, 5L); - curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 4383c1e0..70dc1bc1 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -589,14 +589,6 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) return T2ERROR_FAILURE; } - // Initialize connection pool - now handled internally with thread safety - T2ERROR ret = init_connection_pool(); - if(ret != T2ERROR_SUCCESS) - { - T2Error("Failed to initialize connection pool\n"); - return ret; - } - #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) char *paramVal = NULL; From c0585019926b57617e6207bdbdd4daafdaa0960f Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 07:39:26 +0000 Subject: [PATCH 13/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/xconfclient.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 70dc1bc1..6722628c 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -582,6 +582,7 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) { T2Info("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); + T2ERROR ret; if(NULL == httpsUrl) { From e9f88cc7fc63f5bb7559059bad67977d7da6e9f4 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 10:09:52 +0000 Subject: [PATCH 14/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 36 ++++++++++-------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 970bd8a0..74ee9764 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -38,6 +38,7 @@ //Global variables #define MAX_POOL_SIZE 5 #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" +#define RESPONSE_BUFFER_SIZE 8192 static bool pool_initialized = false; // High-level design for connection pooling @@ -47,6 +48,8 @@ typedef struct CURL *easy_handles[MAX_POOL_SIZE]; bool handle_available[MAX_POOL_SIZE]; pthread_mutex_t pool_mutex; + struct curl_slist *post_headers; + struct curl_slist *get_headers; } http_connection_pool_t; typedef struct http_pool_config @@ -117,6 +120,9 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if(code != CURLE_OK) { @@ -152,7 +158,11 @@ T2ERROR init_connection_pool() } } } - + pool.post_headers = curl_slist_append(NULL, "Accept: application/json"); + pool.post_headers = curl_slist_append(pool.post_headers, "Content-type: application/json"); + + pool.get_headers = curl_slist_append(NULL, "Accept: application/json"); + pthread_mutex_init(&pool.pool_mutex, NULL); pool_initialized = true; T2Info("%s ++out\n", __FUNCTION__); @@ -193,7 +203,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); - response->data = (char*)malloc(1); + response->data = (char*)malloc(RESPONSE_BUFFER_SIZE); response->data[0] = '\0'; response->size = 0; @@ -243,10 +253,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) // POST request configuration (for sendReportOverHTTP) curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "POST"); - // Set headers for POST - headers = curl_slist_append(NULL, "Accept: application/json"); - headers = curl_slist_append(headers, "Content-type: application/json"); - curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.post_headers); if (config->payload) { @@ -260,19 +267,11 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { // GET request configuration (for doHttpGet) curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.get_headers); curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); } - // Handle mTLS if enabled - if (config->enable_mtls && T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) - { - curl_easy_setopt(easy, CURLOPT_SSLCERTTYPE, "P12"); - curl_easy_setopt(easy, CURLOPT_SSLCERT, pCertFile); - curl_easy_setopt(easy, CURLOPT_KEYPASSWD, pPasswd); - curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L); - } - // Add to multi handle curl_multi_add_handle(pool.multi_handle, easy); @@ -281,7 +280,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { T2Info("%s ; Performing curl request\n", __FUNCTION__); curl_multi_perform(pool.multi_handle, &still_running); - curl_multi_wait(pool.multi_handle, NULL, 0, 1000, NULL); + curl_multi_wait(pool.multi_handle, NULL, 0, 50, NULL); } while(still_running); @@ -345,11 +344,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) free(response->data); free(response); - if (headers) - { - curl_slist_free_all(headers); - } - curl_multi_remove_handle(pool.multi_handle, easy); pthread_mutex_lock(&pool.pool_mutex); From 977ded3b930ec242246cd290227be2c6f0611695 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 10:27:42 +0000 Subject: [PATCH 15/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 74ee9764..041d7cad 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -122,7 +122,7 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); - + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if(code != CURLE_OK) { @@ -160,9 +160,9 @@ T2ERROR init_connection_pool() } pool.post_headers = curl_slist_append(NULL, "Accept: application/json"); pool.post_headers = curl_slist_append(pool.post_headers, "Content-type: application/json"); - + pool.get_headers = curl_slist_append(NULL, "Accept: application/json"); - + pthread_mutex_init(&pool.pool_mutex, NULL); pool_initialized = true; T2Info("%s ++out\n", __FUNCTION__); @@ -196,9 +196,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) //CURLcode code; int idx = -1; //ssize_t readBytes = 0; - struct curl_slist *headers = NULL; - char *pCertFile = NULL; - char *pPasswd = NULL; //FILE *fp = NULL; From 0950a5753fd8932bfae7f37e8e62fa88d17ce1d0 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 17 Sep 2025 13:14:42 +0000 Subject: [PATCH 16/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 041d7cad..f4c04fc8 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -277,7 +277,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { T2Info("%s ; Performing curl request\n", __FUNCTION__); curl_multi_perform(pool.multi_handle, &still_running); - curl_multi_wait(pool.multi_handle, NULL, 0, 50, NULL); + curl_multi_wait(pool.multi_handle, NULL, 0, 500, NULL); } while(still_running); From d6a8de5e5c36fa7c80764ca493fd489652d897fa Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Thu, 18 Sep 2025 09:31:52 +0000 Subject: [PATCH 17/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 112 ++++++++++++++++++----- 1 file changed, 87 insertions(+), 25 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index f4c04fc8..7ef037f9 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -50,6 +50,9 @@ typedef struct pthread_mutex_t pool_mutex; struct curl_slist *post_headers; struct curl_slist *get_headers; + // FIX: Add reusable response buffers to prevent frequent malloc/free + curlResponseData response_buffers[MAX_POOL_SIZE]; + bool buffer_available[MAX_POOL_SIZE]; } http_connection_pool_t; typedef struct http_pool_config @@ -69,12 +72,20 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, 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__); - return 0; + // 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); @@ -133,7 +144,18 @@ T2ERROR init_connection_pool() { T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); } - if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) + + // Initialize response buffers + pool.response_buffers[i].data = (char*)malloc(RESPONSE_BUFFER_SIZE); + pool.response_buffers[i].data[0] = '\0'; + pool.response_buffers[i].size = 0; + pool.buffer_available[i] = true; + } + + // Configure mTLS once and free certificates immediately + 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) @@ -157,7 +179,18 @@ T2ERROR init_connection_pool() T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); } } + + // FIX: Free certificate memory immediately after use + 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"); @@ -193,45 +226,39 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) pool_initialized = true; } CURL *easy; - //CURLcode code; int idx = -1; - //ssize_t readBytes = 0; - //FILE *fp = NULL; - - - curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); - response->data = (char*)malloc(RESPONSE_BUFFER_SIZE); - response->data[0] = '\0'; - response->size = 0; + int buffer_idx = -1; pthread_mutex_lock(&pool.pool_mutex); - // Find an available handle + // Find an available handle and buffer for(int i = 0; i < MAX_POOL_SIZE; i++) { - if(pool.handle_available[i]) + if(pool.handle_available[i] && pool.buffer_available[i]) { - T2Info("%s ; Available handle = %d\n", __FUNCTION__, i); + T2Info("%s ; Available handle = %d, buffer = %d\n", __FUNCTION__, i, i); idx = i; + buffer_idx = i; pool.handle_available[i] = false; + pool.buffer_available[i] = false; break; } } pthread_mutex_unlock(&pool.pool_mutex); - if(idx == -1) + if(idx == -1 || buffer_idx == -1) { - T2Error("No available HTTP handles\n"); - free(response->data); - free(response); + T2Error("No available HTTP handles or buffers\n"); return T2ERROR_FAILURE; } easy = pool.easy_handles[idx]; + curlResponseData* response = &pool.response_buffers[buffer_idx]; - // Reset handle to clean state - //curl_easy_reset(easy); + // Reset response buffer + response->data[0] = '\0'; + response->size = 0; // Set common options curl_easy_setopt(easy, CURLOPT_URL, config->url); @@ -338,13 +365,11 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) } // Cleanup - free(response->data); - free(response); - curl_multi_remove_handle(pool.multi_handle, easy); pthread_mutex_lock(&pool.pool_mutex); pool.handle_available[idx] = true; + pool.buffer_available[buffer_idx] = true; pthread_mutex_unlock(&pool.pool_mutex); T2Info("%s ++out\n", __FUNCTION__); @@ -369,11 +394,48 @@ T2ERROR http_pool_request(const char *url, const char *payload, char **data) 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; + } + if(pool.get_headers) { + curl_slist_free_all(pool.get_headers); + pool.get_headers = NULL; + } + + // Cleanup all easy handles and response buffers for(int i = 0; i < MAX_POOL_SIZE; i++) { - curl_easy_cleanup(pool.easy_handles[i]); + if(pool.easy_handles[i]) { + curl_easy_cleanup(pool.easy_handles[i]); + pool.easy_handles[i] = NULL; + } + if(pool.response_buffers[i].data) { + free(pool.response_buffers[i].data); + pool.response_buffers[i].data = NULL; + } + } + + // Cleanup multi handle + if(pool.multi_handle) { + curl_multi_cleanup(pool.multi_handle); + pool.multi_handle = NULL; } - curl_multi_cleanup(pool.multi_handle); + + // Destroy mutex pthread_mutex_destroy(&pool.pool_mutex); + + // Reset initialization flag + pool_initialized = false; + + T2Info("%s ++out\n", __FUNCTION__); return T2ERROR_SUCCESS; } From c62e1d5082096c0e26f8e48aa599b84850d16831 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Thu, 18 Sep 2025 12:19:30 +0000 Subject: [PATCH 18/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 30 +++++++----------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 7ef037f9..46da40ca 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -49,9 +49,7 @@ typedef struct bool handle_available[MAX_POOL_SIZE]; pthread_mutex_t pool_mutex; struct curl_slist *post_headers; - struct curl_slist *get_headers; - // FIX: Add reusable response buffers to prevent frequent malloc/free - curlResponseData response_buffers[MAX_POOL_SIZE]; + curlResponseData response_buffers[MAX_POOL_SIZE]; //Might need to revisit bool buffer_available[MAX_POOL_SIZE]; } http_connection_pool_t; @@ -123,12 +121,15 @@ T2ERROR init_connection_pool() pool.handle_available[i] = true; // Set common options once - curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); +#ifdef CURLOPT_TCP_KEEPCNT + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPCNT, 15L); // Allow up to 15 probes +#endif curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); @@ -139,11 +140,6 @@ T2ERROR init_connection_pool() { T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); } - code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_WRITEFUNCTION, httpGetCallBack); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } // Initialize response buffers pool.response_buffers[i].data = (char*)malloc(RESPONSE_BUFFER_SIZE); @@ -152,7 +148,7 @@ T2ERROR init_connection_pool() pool.buffer_available[i] = true; } - // Configure mTLS once and free certificates immediately + //mtls if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) { for(int i = 0; i < MAX_POOL_SIZE; i++) @@ -180,7 +176,6 @@ T2ERROR init_connection_pool() } } - // FIX: Free certificate memory immediately after use if(pCertFile) { free(pCertFile); pCertFile = NULL; @@ -194,8 +189,6 @@ T2ERROR init_connection_pool() pool.post_headers = curl_slist_append(NULL, "Accept: application/json"); pool.post_headers = curl_slist_append(pool.post_headers, "Content-type: application/json"); - pool.get_headers = curl_slist_append(NULL, "Accept: application/json"); - pthread_mutex_init(&pool.pool_mutex, NULL); pool_initialized = true; T2Info("%s ++out\n", __FUNCTION__); @@ -291,7 +284,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { // GET request configuration (for doHttpGet) curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.get_headers); curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *) response); } @@ -406,11 +398,7 @@ T2ERROR http_pool_cleanup(void) curl_slist_free_all(pool.post_headers); pool.post_headers = NULL; } - if(pool.get_headers) { - curl_slist_free_all(pool.get_headers); - pool.get_headers = NULL; - } - + // Cleanup all easy handles and response buffers for(int i = 0; i < MAX_POOL_SIZE; i++) { From 462d472ce8257503e565c7cde55c31ef54905e3a Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Fri, 19 Sep 2025 12:01:55 +0000 Subject: [PATCH 19/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/protocol/http/curlinterface.c | 15 +- source/xconf-client/multicurlinterface.c | 341 ++++++++++++++++++++--- source/xconf-client/multicurlinterface.h | 6 + source/xconf-client/xconfclient.c | 15 +- 4 files changed, 318 insertions(+), 59 deletions(-) diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 2efabf76..dd92eb8c 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -325,19 +325,8 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) return ret; } - // Configure for POST request (sendReportOverHTTP) - http_pool_request_config_t config = - { - .type = HTTP_REQUEST_POST, - .url = httpUrl, - .payload = payload, - .response_data = NULL, // POST doesn't need response data - .enable_mtls = true, - .enable_file_output = false // No file output for POST - }; - - // Use the enhanced connection pool function - ret = http_pool_request_ex(&config); + // Use new dedicated POST API + ret = http_pool_post(httpUrl, payload); if(ret == T2ERROR_SUCCESS) { diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 46da40ca..0ef3dfd6 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -36,12 +36,12 @@ #include "t2MtlsUtils.h" //Global variables -#define MAX_POOL_SIZE 5 +#define MAX_POOL_SIZE 2 #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" #define RESPONSE_BUFFER_SIZE 8192 static bool pool_initialized = false; -// High-level design for connection pooling +// High-level design for connection pooling - Memory optimized typedef struct { CURLM *multi_handle; @@ -49,8 +49,7 @@ typedef struct bool handle_available[MAX_POOL_SIZE]; pthread_mutex_t pool_mutex; struct curl_slist *post_headers; - curlResponseData response_buffers[MAX_POOL_SIZE]; //Might need to revisit - bool buffer_available[MAX_POOL_SIZE]; + // Response buffers removed - only used by GET requests locally } http_connection_pool_t; typedef struct http_pool_config @@ -125,12 +124,14 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); //TODO: get the internal call details + + curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); //TODO: libcurl dev + #ifdef CURLOPT_TCP_KEEPCNT curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPCNT, 15L); // Allow up to 15 probes #endif - curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); @@ -141,11 +142,9 @@ T2ERROR init_connection_pool() T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); } - // Initialize response buffers - pool.response_buffers[i].data = (char*)malloc(RESPONSE_BUFFER_SIZE); - pool.response_buffers[i].data[0] = '\0'; - pool.response_buffers[i].size = 0; - pool.buffer_available[i] = true; + // 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); } //mtls @@ -195,6 +194,257 @@ T2ERROR init_connection_pool() return T2ERROR_SUCCESS; } +// Shared multi_perform execution function - simplified +static T2ERROR http_pool_execute_request(CURL *easy, int idx) +{ + T2Info("%s ++in\n", __FUNCTION__); + + // 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); + + 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; +} + +// 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; +} + +// 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); + + CURL *easy; + curlResponseData *response; + int idx; + + T2ERROR ret = acquire_pool_handle(&easy, &idx); + if (ret != T2ERROR_SUCCESS) + { + return ret; + } + + // 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; + } + + // 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 + 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; + } + + // Free local response buffer + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } + + 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); + + CURL *easy; + curlResponseData *response = NULL; // Not used for POST + int idx; + + T2ERROR ret = acquire_pool_handle(&easy, &idx); + if (ret != T2ERROR_SUCCESS) + { + return ret; + } + + // 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)); + curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writeToFile); + +#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 + 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__); @@ -220,38 +470,58 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) } CURL *easy; int idx = -1; - int buffer_idx = -1; pthread_mutex_lock(&pool.pool_mutex); - // Find an available handle and buffer + // Find an available handle for(int i = 0; i < MAX_POOL_SIZE; i++) { - if(pool.handle_available[i] && pool.buffer_available[i]) + if(pool.handle_available[i]) { - T2Info("%s ; Available handle = %d, buffer = %d\n", __FUNCTION__, i, i); + T2Info("%s ; Available handle = %d\n", __FUNCTION__, i); idx = i; - buffer_idx = i; pool.handle_available[i] = false; - pool.buffer_available[i] = false; break; } } pthread_mutex_unlock(&pool.pool_mutex); - if(idx == -1 || buffer_idx == -1) + if(idx == -1) { - T2Error("No available HTTP handles or buffers\n"); + T2Error("No available HTTP handles\n"); return T2ERROR_FAILURE; } easy = pool.easy_handles[idx]; - curlResponseData* response = &pool.response_buffers[buffer_idx]; - // Reset response buffer - response->data[0] = '\0'; - response->size = 0; + 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); @@ -264,7 +534,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) #endif #endif - // Configure based on request type + //TODO: separate POST and GET into 2 apis if (config->type == HTTP_REQUEST_POST) { // POST request configuration (for sendReportOverHTTP) @@ -296,7 +566,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { 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); + curl_multi_wait(pool.multi_handle, NULL, 0, 500, NULL); // TODO: Wait has to be revisited } while(still_running); @@ -311,7 +581,7 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) { ret = T2ERROR_SUCCESS; - if (response->data) + if (response && response->data) { T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response->size); @@ -361,9 +631,18 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) pthread_mutex_lock(&pool.pool_mutex); pool.handle_available[idx] = true; - pool.buffer_available[buffer_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; } @@ -399,17 +678,13 @@ T2ERROR http_pool_cleanup(void) pool.post_headers = NULL; } - // Cleanup all easy handles and response buffers + // 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(pool.response_buffers[i].data) { - free(pool.response_buffers[i].data); - pool.response_buffers[i].data = NULL; - } } // Cleanup multi handle diff --git a/source/xconf-client/multicurlinterface.h b/source/xconf-client/multicurlinterface.h index 2057b4a1..2b0a2108 100644 --- a/source/xconf-client/multicurlinterface.h +++ b/source/xconf-client/multicurlinterface.h @@ -43,6 +43,12 @@ typedef struct } 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); diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 6722628c..6e7711db 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -614,19 +614,8 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) #endif #endif - // Configure for GET request (doHttpGet) - http_pool_request_config_t config = - { - .type = HTTP_REQUEST_GET, - .url = httpsUrl, - .payload = NULL, // No payload for GET requests - .response_data = data, // Store response data for GET - .enable_mtls = true, - .enable_file_output = true // Enable file output for GET requests - }; - - // Use the enhanced connection pool function - ret = http_pool_request_ex(&config); + // Use new dedicated GET API + ret = http_pool_get(httpsUrl, data, true); if(ret == T2ERROR_SUCCESS) { From 410cfeea9247ddc0613dc05fee31335298321aa9 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Fri, 19 Sep 2025 12:17:26 +0000 Subject: [PATCH 20/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 0ef3dfd6..76168753 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -413,7 +413,6 @@ T2ERROR http_pool_post(const char *url, const char *payload) T2Info("%s ; POST url = %s\n", __FUNCTION__, url); CURL *easy; - curlResponseData *response = NULL; // Not used for POST int idx; T2ERROR ret = acquire_pool_handle(&easy, &idx); @@ -701,4 +700,4 @@ T2ERROR http_pool_cleanup(void) T2Info("%s ++out\n", __FUNCTION__); return T2ERROR_SUCCESS; -} +} \ No newline at end of file From 153f17098edb4314d932691c7ecaddedea9d983c Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 07:43:58 +0000 Subject: [PATCH 21/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 76168753..d214eb26 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -121,21 +121,25 @@ T2ERROR init_connection_pool() // Set common options once curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 30L); + +#if 0 curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); //TODO: get the internal call details curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); //TODO: libcurl dev +#endif #ifdef CURLOPT_TCP_KEEPCNT curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPCNT, 15L); // Allow up to 15 probes #endif - curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); +#if 0 curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); - +#endif code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if(code != CURLE_OK) { @@ -307,7 +311,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou response = (curlResponseData*)malloc(sizeof(curlResponseData)); if (response) { - response->data = (char*)malloc(RESPONSE_BUFFER_SIZE); + response->data = (char*)malloc(1); if (response->data) { response->data[0] = '\0'; From e2971eff522ae72bb25b0a0189261cdbee60a224 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 09:15:51 +0000 Subject: [PATCH 22/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 40 +++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index d214eb26..a2d63837 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -99,6 +99,39 @@ static size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) return written; } +// Add this debug callback function +static int curl_debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) +{ + const char *text; + + 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) @@ -149,8 +182,13 @@ T2ERROR init_connection_pool() // 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); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_DEBUGDATA, NULL); + } - + //mtls if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) { From 353dfcd24471e4705edf608b34d1bc3201664f2c Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 10:07:47 +0000 Subject: [PATCH 23/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index a2d63837..2a2a1ba0 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -100,9 +100,10 @@ static size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) } // Add this debug callback function -static int curl_debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) +static int curl_debug_callback_func(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) { - const char *text; + (void)handle; + (void)userptr; switch (type) { case CURLINFO_TEXT: @@ -184,7 +185,7 @@ T2ERROR init_connection_pool() 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); + curl_easy_setopt(pool.easy_handles[i], CURLOPT_DEBUGFUNCTION, curl_debug_callback_func); curl_easy_setopt(pool.easy_handles[i], CURLOPT_DEBUGDATA, NULL); } From 91d5030ced52d6ff592af90ce220415dc08f9a54 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 11:51:38 +0000 Subject: [PATCH 24/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 136 ++++++++++++++--------- 1 file changed, 85 insertions(+), 51 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 2a2a1ba0..62327666 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -36,7 +36,7 @@ #include "t2MtlsUtils.h" //Global variables -#define MAX_POOL_SIZE 2 +#define MAX_POOL_SIZE 3 #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" #define RESPONSE_BUFFER_SIZE 8192 static bool pool_initialized = false; @@ -158,7 +158,7 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 30L); -#if 0 +#if 1 curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPIDLE, 120L); //TODO: get the internal call details @@ -170,7 +170,7 @@ T2ERROR init_connection_pool() #endif curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); -#if 0 +#if 1 curl_easy_setopt(pool.easy_handles[i], CURLOPT_FORBID_REUSE, 0L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_FRESH_CONNECT, 0L); #endif @@ -282,6 +282,7 @@ static T2ERROR http_pool_execute_request(CURL *easy, int idx) return ret; } +#if 0 // Helper function to acquire handle only static T2ERROR acquire_pool_handle(CURL **easy, int *idx) { @@ -322,6 +323,7 @@ static T2ERROR acquire_pool_handle(CURL **easy, int *idx) *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) @@ -336,45 +338,55 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou T2Info("%s ; GET url = %s\n", __FUNCTION__, url); - CURL *easy; - curlResponseData *response; - int idx; - - T2ERROR ret = acquire_pool_handle(&easy, &idx); - if (ret != T2ERROR_SUCCESS) + if (!pool_initialized) { - return ret; + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } } - // Allocate response buffer locally for GET requests - response = (curlResponseData*)malloc(sizeof(curlResponseData)); - if (response) + // 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]) { - response->data = (char*)malloc(1); - 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; - } + pthread_mutex_unlock(&pool.pool_mutex); + T2Error("GET handle (index 0) is not available\n"); + return T2ERROR_FAILURE; } - else + + // 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 memory for response structure\n"); + 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); + 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) @@ -385,11 +397,11 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou #endif // Execute the request - ret = http_pool_execute_request(easy, idx); + T2ERROR ret = http_pool_execute_request(easy, idx); - if (ret == T2ERROR_SUCCESS && response->data) + if (ret == T2ERROR_SUCCESS && response.data) { - T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response->size); + T2Info("%s ; Response data size = %zu\n", __FUNCTION__, response.size); // Handle file output for GET requests if (enable_file_output) @@ -398,7 +410,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou if(httpOutput) { T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); - fputs(response->data, httpOutput); + fputs(response.data, httpOutput); fclose(httpOutput); } else @@ -411,13 +423,13 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou if (response_data) { *response_data = NULL; - if(response->size <= SIZE_MAX) + if(response.size <= SIZE_MAX) { - *response_data = (char*)malloc(response->size + 1); + *response_data = (char*)malloc(response.size + 1); if(*response_data) { - memcpy(*response_data, response->data, response->size); - (*response_data)[response->size] = '\0'; + memcpy(*response_data, response.data, response.size); + (*response_data)[response.size] = '\0'; } } } @@ -428,15 +440,8 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou ret = T2ERROR_FAILURE; } - // Free local response buffer - if (response) - { - if (response->data) - { - free(response->data); - } - free(response); - } + // Clean up local response buffer + free(response.data); T2Info("%s ++out\n", __FUNCTION__); return ret; @@ -455,15 +460,44 @@ T2ERROR http_pool_post(const char *url, const char *payload) 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; + int idx = -1; - T2ERROR ret = acquire_pool_handle(&easy, &idx); - if (ret != T2ERROR_SUCCESS) + pthread_mutex_lock(&pool.pool_mutex); + + // Find an available handle starting from index 1 + for(int i = 1; i < MAX_POOL_SIZE; i++) { - return ret; + 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"); @@ -481,7 +515,7 @@ T2ERROR http_pool_post(const char *url, const char *payload) #endif // Execute the request - ret = http_pool_execute_request(easy, idx); + T2ERROR ret = http_pool_execute_request(easy, idx); T2Info("%s ++out\n", __FUNCTION__); return ret; From 731a7d0a3cadd79b46310ecb3b393ed4a1fe7c9c Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 14:43:44 +0000 Subject: [PATCH 25/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 38 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index 62327666..c3aebfe0 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -153,27 +153,42 @@ T2ERROR init_connection_pool() pool.easy_handles[i] = curl_easy_init(); pool.handle_available[i] = true; - // Set common options once + //Set common options once curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 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, 120L); //TODO: get the internal call details - - curl_easy_setopt(pool.easy_handles[i], CURLOPT_TCP_KEEPINTVL, 60L); //TODO: libcurl dev -#endif + 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 - curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 5L); -#if 1 + // 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); -#endif + + // 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 + + // Enable connection pooling with limited cache size + curl_easy_setopt(pool.easy_handles[i], CURLOPT_MAXCONNECTS, 1L); // Only 1 connection per handle + code = curl_easy_setopt(pool.easy_handles[i], CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if(code != CURLE_OK) { @@ -187,7 +202,6 @@ T2ERROR init_connection_pool() 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 From c05c4108c1dde7231c9aa1dadc9d7b48f32e7def Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 14:47:49 +0000 Subject: [PATCH 26/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index c3aebfe0..d09319a1 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -157,6 +157,7 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); +#if 0 // 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 @@ -185,9 +186,9 @@ T2ERROR init_connection_pool() // 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, 1L); // Only 1 connection per handle + 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) From 8bc58d9d0087f792f7a22507eaf6a76cc1b01fe0 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 18:00:35 +0000 Subject: [PATCH 27/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 29 ++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index d09319a1..bf601da9 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -93,12 +93,6 @@ static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, return realsize; } -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; -} - // Add this debug callback function static int curl_debug_callback_func(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) { @@ -145,7 +139,7 @@ T2ERROR init_connection_pool() T2Info("%s ++in\n", __FUNCTION__); char *pCertFile = NULL; char *pPasswd = NULL; - pool.multi_handle = curl_multi_init(); + //pool.multi_handle = curl_multi_init(); // Pre-allocate easy handles for(int i = 0; i < MAX_POOL_SIZE; i++) @@ -257,6 +251,7 @@ 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); @@ -268,7 +263,18 @@ static T2ERROR http_pool_execute_request(CURL *easy, int idx) 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; @@ -287,7 +293,7 @@ static T2ERROR http_pool_execute_request(CURL *easy, int idx) } // Cleanup - curl_multi_remove_handle(pool.multi_handle, easy); + //curl_multi_remove_handle(pool.multi_handle, easy); pthread_mutex_lock(&pool.pool_mutex); pool.handle_available[idx] = true; @@ -519,7 +525,6 @@ T2ERROR http_pool_post(const char *url, const char *payload) curl_easy_setopt(easy, CURLOPT_HTTPHEADER, pool.post_headers); curl_easy_setopt(easy, CURLOPT_POSTFIELDS, payload); curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(payload)); - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writeToFile); #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) @@ -638,8 +643,6 @@ T2ERROR http_pool_request_ex(const http_pool_request_config_t *config) curl_easy_setopt(easy, CURLOPT_POSTFIELDS, config->payload); curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, strlen(config->payload)); } - - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writeToFile); } else { @@ -778,12 +781,14 @@ T2ERROR http_pool_cleanup(void) } } +#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); From 1707e57ec780b49300cc445d8a3fbcc207a9e1ed Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 22 Sep 2025 18:05:04 +0000 Subject: [PATCH 28/28] RDK-58546: [POC] T2 Thread And Curl Connection Pool Addition For Efficient Memory Utilization On Low End Devices Signed-off-by: Yogeswaran K --- source/xconf-client/multicurlinterface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/xconf-client/multicurlinterface.c b/source/xconf-client/multicurlinterface.c index bf601da9..be06445d 100644 --- a/source/xconf-client/multicurlinterface.c +++ b/source/xconf-client/multicurlinterface.c @@ -151,7 +151,7 @@ T2ERROR init_connection_pool() curl_easy_setopt(pool.easy_handles[i], CURLOPT_TIMEOUT, 30L); curl_easy_setopt(pool.easy_handles[i], CURLOPT_CONNECTTIMEOUT, 10L); -#if 0 +#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