diff --git a/include/ut_kvp.h b/include/ut_kvp.h index f2fa326..3077450 100644 --- a/include/ut_kvp.h +++ b/include/ut_kvp.h @@ -71,16 +71,16 @@ void ut_kvp_destroyInstance(ut_kvp_instance_t *pInstance); * This function opens the specified KVP file, reads its contents, and parses the key-value pairs into the given KVP instance. * * @param[in] pInstance - Handle to the KVP instance where the parsed data will be stored. - * @param[in] fileName - Null-terminated string containing the path to the KVP file. + * @param[in] fileNameOrUrl - Null-terminated string containing the path to the KVP file/URL. * * @returns Status of the operation (`ut_kvp_status_t`): * @retval UT_KVP_STATUS_SUCCESS - The file was opened and parsed successfully. * @retval UT_KVP_STATUS_FILE_OPEN_ERROR - The file could not be opened. - * @retval UT_KVP_STATUS_INVALID_PARAM - One or more parameters are invalid (e.g., null pointer). + * @retval UT_KVP_STATUS_NULL_PARAM - One or more parameters are NULL (e.g., null pointer). * @retval UT_KVP_STATUS_PARSING_ERROR - An error occurred while parsing the file contents. * @retval UT_KVP_STATUS_INVALID_INSTANCE - The provided `pInstance` is not a valid KVP instance. */ -ut_kvp_status_t ut_kvp_open(ut_kvp_instance_t *pInstance, char *fileName); +ut_kvp_status_t ut_kvp_open(ut_kvp_instance_t *pInstance, const char *fileNameOrUrl); /** * @brief Parses a KVP payload from a caller-owned memory block into an instance. @@ -269,4 +269,4 @@ unsigned char* ut_kvp_getDataBytes(ut_kvp_instance_t *pInstance, const char *psz } #endif -#endif /* __UT_KVP_H__ */ \ No newline at end of file +#endif /* __UT_KVP_H__ */ diff --git a/src/ut_kvp.c b/src/ut_kvp.c index fa50fc7..a818143 100644 --- a/src/ut_kvp.c +++ b/src/ut_kvp.c @@ -37,6 +37,15 @@ ut_kvp_instance_t *gKVP_Instance = NULL; #define UT_KVP_MAGIC (0xdeadbeef) #define UT_KVP_MAX_INCLUDE_DEPTH 5 +#define UT_KVP_HTTPS_PREFIX "https://" +#define UT_KVP_HTTP_PREFIX "http://" +#define UT_KVP_FILE_PREFIX "file://" + +// Automatically calculate lengths by subtracting the '\0' null terminator +#define UT_KVP_HTTPS_PREFIX_LEN (sizeof(UT_KVP_HTTPS_PREFIX) - 1) +#define UT_KVP_HTTP_PREFIX_LEN (sizeof(UT_KVP_HTTP_PREFIX) - 1) +#define UT_KVP_FILE_PREFIX_LEN (sizeof(UT_KVP_FILE_PREFIX) - 1) + typedef struct { uint32_t magic; @@ -95,28 +104,86 @@ void ut_kvp_destroyInstance(ut_kvp_instance_t *pInstance) pInternal = NULL; } -ut_kvp_status_t ut_kvp_open(ut_kvp_instance_t *pInstance, char *fileName) +static bool is_url(const char *input) { - if (pInstance == NULL) + if (input == NULL) + { + return false; + } + + // Check for http:// + if (strncmp(input, UT_KVP_HTTP_PREFIX, UT_KVP_HTTP_PREFIX_LEN) == 0) + { + return true; + } + + // Check for https:// + if (strncmp(input, UT_KVP_HTTPS_PREFIX, UT_KVP_HTTPS_PREFIX_LEN) == 0) + { + return true; + } + + // No matches found + return false; +} + +ut_kvp_status_t ut_kvp_open(ut_kvp_instance_t *pInstance, const char *fileNameOrUrl) +{ + ut_kvp_instance_internal_t *pInternal = validateInstance(pInstance); + + // Validate KVP instance handle + if (pInternal == NULL) { return UT_KVP_STATUS_INVALID_INSTANCE; } - ut_kvp_instance_internal_t *pInternal = validateInstance(pInstance); - if (fileName == NULL) + // Validate fileNameOrUrl parameter + if (fileNameOrUrl == NULL) { - UT_LOG_ERROR("Invalid Param [fileName]"); - return UT_KVP_STATUS_INVALID_PARAM; + UT_LOG_ERROR("NULL PARAM [fileNameOrUrl]"); + return UT_KVP_STATUS_NULL_PARAM; + } + + // Determine if input is a URL(e.g., http:// or https://) + bool bFilenameIsAUrl = is_url(fileNameOrUrl); + + // Handle URL-based input + if(bFilenameIsAUrl == true) + { + char *pYaml = NULL; + + pYaml = malloc(UT_KVP_MAX_ELEMENT_SIZE); + + if ( pYaml == NULL ) + { + UT_LOG_ERROR("Malloc was not able to provide memory\n"); + return UT_KVP_STATUS_PARSING_ERROR; + } + + snprintf(pYaml, UT_KVP_MAX_ELEMENT_SIZE, "include: %s\n", fileNameOrUrl); + + // Pass the dynamically allocated YAML string to openMemory() for parsing + ut_kvp_status_t status = ut_kvp_openMemory(pInstance, pYaml, strlen(pYaml)); + free(pYaml); + return status; + } + + // Handle file-based input + if (strncmp(fileNameOrUrl, UT_KVP_FILE_PREFIX, UT_KVP_FILE_PREFIX_LEN) == 0) + { + // Skip "file://" + fileNameOrUrl = fileNameOrUrl + UT_KVP_FILE_PREFIX_LEN; } - if (access(fileName, F_OK) != 0) + // Verify that the file is accessible + if (access(fileNameOrUrl, F_OK) != 0) { - UT_LOG_ERROR("[%s] cannot be accesed", fileName); + UT_LOG_ERROR("[%s] cannot be accessed", fileNameOrUrl); return UT_KVP_STATUS_FILE_OPEN_ERROR; } // Load the new document - struct fy_document *newDoc = fy_document_build_from_file(NULL, fileName); + struct fy_document *newDoc = fy_document_build_from_file(NULL, fileNameOrUrl); if (newDoc == NULL || fy_document_resolve(newDoc) != 0) { if (newDoc) @@ -1109,7 +1176,7 @@ static struct fy_node* process_include(const char *filename, int depth, struct f return NULL; } - if (strncmp(filename, "http:", 5) == 0 || strncmp(filename, "https:", 6) == 0) + if (strncmp(filename, UT_KVP_HTTP_PREFIX, UT_KVP_HTTP_PREFIX_LEN) == 0 || strncmp(filename, UT_KVP_HTTPS_PREFIX, UT_KVP_HTTPS_PREFIX_LEN) == 0) { // URL include mChunk.memory = malloc(1); diff --git a/tests/src/ut_control_test.sh b/tests/src/ut_control_test.sh index 827f491..74c8069 100755 --- a/tests/src/ut_control_test.sh +++ b/tests/src/ut_control_test.sh @@ -27,4 +27,26 @@ cd "$(dirname "$0")" export LD_LIBRARY_PATH=/usr/lib:/lib:/home/root:${MY_DIR} -LSAN_OPTIONS="suppressions=../../lsan.supp print_suppressions=1" ./ut_control_test $@ +HTTP_PORT=8000 +HTTP_PID="" + +# Check if HTTP server is already running +if ! lsof -iTCP:${HTTP_PORT} -sTCP:LISTEN >/dev/null 2>&1; then + python3 -m http.server ${HTTP_PORT} & + HTTP_PID=$! +fi + +# Cleanup only if this script started the server +cleanup() { + if [[ -n "$HTTP_PID" ]]; then + kill "$HTTP_PID" 2>/dev/null || true + wait "$HTTP_PID" 2>/dev/null || true + fi +} +trap cleanup EXIT + +# Run test +LSAN_OPTIONS="suppressions=../../lsan.supp print_suppressions=1" ./ut_control_test "$@" +UT_STATUS=$? + +exit $UT_STATUS diff --git a/tests/src/ut_test_kvp.c b/tests/src/ut_test_kvp.c index c0535f6..1cc07be 100644 --- a/tests/src/ut_test_kvp.c +++ b/tests/src/ut_test_kvp.c @@ -42,6 +42,12 @@ #define KVP_VALID_TEST_SEQUENCE_INCLUDE_YAML "assets/include/sequence-include.yaml" #define KVP_VALID_TEST_RESOLVE_YAML_TAGS_YAML "assets/yaml_tags.yaml" #define KVP_VALID_TEST_RESOLVE_YAML_TAGS_IN_SEQUENCE_YAML "assets/yaml_tags_in_sequence.yaml" +#define KVP_VALID_TEST_URL_HTTPS "https://raw.githubusercontent.com/rdkcentral/ut-control/main/tests/src/assets/include/2s.yaml" +#define KVP_VALID_TEST_URI_HTTP "http://localhost:8000/assets/yaml_tags.yaml" +#define KVP_VALID_TEST_URI_FILE "file://assets/yaml_tags.yaml" +#define KVP_VALID_TEST_NOT_VALID_URL_HTTPS "HTTPS://raw.githubusercontent.com/rdkcentral/ut-control/main/tests/src/assets/include/2s.yaml" +#define KVP_VALID_TEST_NOT_VALID_URI_HTTP "HTTP://localhost:8000/assets/yaml_tags.yaml" +#define KVP_VALID_TEST_NOT_VALID_URI_FILE "FILE://assets/yaml_tags.yaml" static ut_kvp_instance_t *gpMainTestInstance = NULL; static UT_test_suite_t *gpKVPSuite = NULL; @@ -108,7 +114,7 @@ void test_ut_kvp_open( void ) /* Negative Read Test, NULL PARAM */ UT_LOG_STEP("ut_kvp_open( pInstance, NULL ) - Negative"); status = ut_kvp_open( pInstance, NULL); - UT_ASSERT( status == UT_KVP_STATUS_INVALID_PARAM ); + UT_ASSERT( status == UT_KVP_STATUS_NULL_PARAM ); /* Filename doesn't exist */ UT_LOG_STEP("ut_kvp_open( pInstance, %s - filename doesn't exist ) - Negative", KVP_VALID_TEST_NO_FILE); @@ -120,11 +126,41 @@ void test_ut_kvp_open( void ) status = ut_kvp_open( pInstance, KVP_VALID_TEST_ZERO_LENGTH_YAML_FILE); UT_ASSERT( status == UT_KVP_STATUS_PARSING_ERROR ); + /* Negative Read Test, KVP_VALID_TEST_NOT_VALID_URL_HTTPS PARAM */ + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URL_HTTPS ) - Negative"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URL_HTTPS); + printf("Status = %d\n", status); + UT_ASSERT( status == UT_KVP_STATUS_FILE_OPEN_ERROR ); + + /* Negative Read Test, KVP_VALID_TEST_NOT_VALID_URI_HTTP PARAM */ + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URI_HTTP ) - Negative"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URI_HTTP); + printf("Status = %d\n", status); + UT_ASSERT( status == UT_KVP_STATUS_FILE_OPEN_ERROR ); + + /* Negative Read Test, KVP_VALID_TEST_NOT_VALID_URI_FILE PARAM */ + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URI_FILE ) - Negative"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_URI_FILE); + printf("Status = %d\n", status); + UT_ASSERT( status == UT_KVP_STATUS_FILE_OPEN_ERROR ); + /* Positive Tests */ UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_YAML_FILE ) - Positive"); status = ut_kvp_open( pInstance, KVP_VALID_TEST_YAML_FILE); UT_ASSERT( status == UT_KVP_STATUS_SUCCESS ); + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_URL_HTTPS ) - Positive"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_URL_HTTPS); + UT_ASSERT( status == UT_KVP_STATUS_SUCCESS ); + + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_URI_HTTP ) - Positive"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_URI_HTTP); + UT_ASSERT( status == UT_KVP_STATUS_SUCCESS ); + + UT_LOG_STEP("ut_kvp_open( pInstance, KVP_VALID_TEST_URI_FILE ) - Positive"); + status = ut_kvp_open( pInstance, KVP_VALID_TEST_URI_FILE); + UT_ASSERT( status == UT_KVP_STATUS_SUCCESS ); + UT_LOG_STEP("ut_kvp_open( pInstance, %s ) - Postive", KVP_VALID_TEST_NOT_VALID_YAML_FORMATTED_FILE); status = ut_kvp_open( pInstance, KVP_VALID_TEST_NOT_VALID_YAML_FORMATTED_FILE); UT_ASSERT( status == UT_KVP_STATUS_SUCCESS );