From fd2a415b1f956bd2ab2df766f267b09bab23c39e Mon Sep 17 00:00:00 2001 From: chinglee-iot <61685396+chinglee-iot@users.noreply.github.com> Date: Tue, 7 May 2024 10:32:11 +0800 Subject: [PATCH 1/4] Update release.yml file (#179) --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1802545..f2c37d23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.commit_id }} - name: Configure git identity @@ -53,7 +53,7 @@ jobs: - name: Install ZIP tools run: sudo apt-get install zip unzip - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.commit_id }} path: coreHTTP @@ -90,7 +90,7 @@ jobs: ctest -E system --output-on-failure cd .. - name: Create artifact of ZIP - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: coreHTTP-${{ github.event.inputs.version_number }}.zip path: zip-check/coreHTTP-${{ github.event.inputs.version_number }}.zip @@ -123,7 +123,7 @@ jobs: draft: false prerelease: false - name: Download ZIP artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: coreHTTP-${{ github.event.inputs.version_number }}.zip - name: Upload Release Asset From 57e3570cb82c4085ecf6a13eb4775480afcb8c50 Mon Sep 17 00:00:00 2001 From: chinglee-iot <61685396+chinglee-iot@users.noreply.github.com> Date: Tue, 7 May 2024 10:49:57 +0800 Subject: [PATCH 2/4] Update doxygen page (#178) * Update doxygen function list * Update doxygen version in Readme.md --- README.md | 2 +- docs/doxygen/pages.dox | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dce2004..7749970f 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ repositories. ### Generating Documentation -The Doxygen references were created using Doxygen version 1.9.2. To generate the +The Doxygen references were created using Doxygen version 1.9.6. To generate the Doxygen pages, please run the following command from the root of this repository: diff --git a/docs/doxygen/pages.dox b/docs/doxygen/pages.dox index 48ac9c45..70e0f5b3 100644 --- a/docs/doxygen/pages.dox +++ b/docs/doxygen/pages.dox @@ -270,7 +270,10 @@ defined. @subpage httpclient_initializerequestheaders_function
@subpage httpclient_addheader_function
@subpage httpclient_addrangeheader_function
+@subpage httpclient_sendhttpheaders_function
+@subpage httpclient_sendhttpdata_function
@subpage httpclient_send_function
+@subpage httpclient_receiveandparsehttpresponse_function
@subpage httpclient_readheader_function
@subpage httpclient_strerror_function
@@ -286,10 +289,22 @@ defined. @snippet core_http_client.h declare_httpclient_addrangeheader @copydoc HTTPClient_AddRangeHeader +@page httpclient_sendhttpheaders_function HTTPClient_SendHttpHeaders +@snippet core_http_client.h declare_httpclient_sendhttpheaders +@copydoc HTTPClient_SendHttpHeaders + +@page httpclient_sendhttpdata_function HTTPClient_SendHttpData +@snippet core_http_client.h declare_httpclient_sendhttpdata +@copydoc HTTPClient_SendHttpData + @page httpclient_send_function HTTPClient_Send @snippet core_http_client.h declare_httpclient_send @copydoc HTTPClient_Send +@page httpclient_receiveandparsehttpresponse_function HTTPClient_ReceiveAndParseHttpResponse +@snippet core_http_client.h declare_httpclient_receiveandparsehttpresponse +@copydoc HTTPClient_ReceiveAndParseHttpResponse + @page httpclient_readheader_function HTTPClient_ReadHeader @snippet core_http_client.h declare_httpclient_readheader @copydoc HTTPClient_ReadHeader From dd62128662bb6b0651985c166934667a5117cf31 Mon Sep 17 00:00:00 2001 From: chinglee-iot <61685396+chinglee-iot@users.noreply.github.com> Date: Tue, 7 May 2024 11:01:16 +0800 Subject: [PATCH 3/4] Add compiler warning check in the CI workflow (#177) * Add compiler warning check in the CI workflow and fix compiler warning in the implementation --------- Co-authored-by: ActoryOu --- .github/.cSpellWords.txt | 5 +++++ .github/workflows/ci.yml | 5 +++++ source/core_http_client.c | 14 ++++++++------ test/CMakeLists.txt | 15 ++++++++++++--- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/.cSpellWords.txt b/.github/.cSpellWords.txt index 37112fdc..93840b88 100644 --- a/.github/.cSpellWords.txt +++ b/.github/.cSpellWords.txt @@ -49,4 +49,9 @@ utest vect Vect VECT +Wconversion +Werror +Weverything +Wextra +Wpedantic Wunused diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1444471d..7efe576a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,15 @@ jobs: - name: Build run: | sudo apt-get install -y lcov + + # Build the coverity analysis project as well to check compiler warning. + # Coverity analysis project builds coreHTTP source file only. llhttp source + # files are not built in this target. cmake -S test -B build/ \ -G "Unix Makefiles" \ -DCMAKE_BUILD_TYPE=Debug \ -DUNITTEST=1 \ + -DCOV_ANALYSIS=1 \ -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -DNDEBUG' make -C build/ all diff --git a/source/core_http_client.c b/source/core_http_client.c index 7a1ddc49..42578475 100644 --- a/source/core_http_client.c +++ b/source/core_http_client.c @@ -545,12 +545,12 @@ static int8_t caseInsensitiveStringCmp( const char * str1, /* Subtract offset to go from lowercase to uppercase ASCII character */ if( ( firstChar >= 'a' ) && ( firstChar <= 'z' ) ) { - firstChar = firstChar - offset; + firstChar = ( char ) ( firstChar - offset ); } if( ( secondChar >= 'a' ) && ( secondChar <= 'z' ) ) { - secondChar = secondChar - offset; + secondChar = ( char ) ( secondChar - offset ); } if( ( firstChar ) != ( secondChar ) ) @@ -1249,6 +1249,7 @@ static uint8_t convertInt32ToAscii( int32_t value, uint8_t numOfDigits = 0U; uint8_t index = 0U; uint8_t isNegative = 0U; + int32_t bufferIndex; char temp = '\0'; assert( pBuffer != NULL ); @@ -1263,7 +1264,7 @@ static uint8_t convertInt32ToAscii( int32_t value, *pBuffer = '-'; /* Convert the value to its absolute representation. */ - absoluteValue = value * -1; + absoluteValue = value * ( -1 ); } /* Write the absolute integer value in reverse ASCII representation. */ @@ -1279,11 +1280,12 @@ static uint8_t convertInt32ToAscii( int32_t value, for( index = 0U; index < ( numOfDigits / 2U ); index++ ) { temp = pBuffer[ isNegative + index ]; - pBuffer[ isNegative + index ] = pBuffer[ isNegative + numOfDigits - index - 1U ]; - pBuffer[ isNegative + numOfDigits - index - 1U ] = temp; + bufferIndex = ( int32_t ) isNegative + ( int32_t ) numOfDigits - ( int32_t ) index - 1; + pBuffer[ isNegative + index ] = pBuffer[ bufferIndex ]; + pBuffer[ bufferIndex ] = temp; } - return( isNegative + numOfDigits ); + return ( uint8_t ) ( isNegative + numOfDigits ); } /*-----------------------------------------------------------*/ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 66c4873a..d8e02404 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -44,7 +44,7 @@ if( COV_ANALYSIS ) # Target for Coverity analysis that builds the library. add_library( coverity_analysis - ${HTTP_SOURCES} ) + ${CMAKE_CURRENT_LIST_DIR}/../source/core_http_client.c ) # Build HTTP library target without custom config dependency. target_compile_definitions( coverity_analysis PUBLIC HTTP_DO_NOT_USE_CUSTOM_CONFIG=1 ) @@ -52,8 +52,17 @@ if( COV_ANALYSIS ) # HTTP public include path. target_include_directories( coverity_analysis PUBLIC ${HTTP_INCLUDE_PUBLIC_DIRS} ) - # Build HTTP library target without logging - target_compile_options(coverity_analysis PUBLIC -DNDEBUG ) + target_compile_options( coverity_analysis PUBLIC + # Build HTTP library target without logging + -DNDEBUG + + # GCC compiler option + -Wall + -Wextra + -Wpedantic + -Wconversion + -Werror + ) endif() # ===================== Clone needed third-party libraries ====================== From 51d580bf30e28b7671773478687bbfdb8e69a8cd Mon Sep 17 00:00:00 2001 From: chinglee-iot <61685396+chinglee-iot@users.noreply.github.com> Date: Tue, 7 May 2024 11:07:16 +0800 Subject: [PATCH 4/4] Add more unit tests for coverage (#175) * Add unit test to cover httpParserOnStatusCompleteCallback, HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG and HTTP_REQUEST_NO_USER_AGENT_FLAG * Enforce 100 percent coverage in CI --- .github/workflows/ci.yml | 2 + test/unit-test/core_http_send_utest.c | 153 +++++++++++++++++++++++--- test/unit-test/core_http_utest.c | 50 +++++++++ 3 files changed, 191 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7efe576a..e486e498 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,8 @@ jobs: uses: FreeRTOS/CI-CD-Github-Actions/coverage-cop@main with: coverage-file: ./build/coverage.info + branch-coverage-min: 100 + line-coverage-min: 100 complexity: runs-on: ubuntu-latest diff --git a/test/unit-test/core_http_send_utest.c b/test/unit-test/core_http_send_utest.c index 28197e9c..db83af52 100644 --- a/test/unit-test/core_http_send_utest.c +++ b/test/unit-test/core_http_send_utest.c @@ -75,6 +75,7 @@ /* HTTP OK Status-Line. */ #define HTTP_STATUS_LINE_OK "HTTP/1.1 200 OK\r\n" +#define HTTP_STATUS_LINE_NO_REASON_PHRASE "HTTP/1.1 200\r\n" #define HTTP_STATUS_CODE_OK 200 /* Various header lines for test response templates. */ @@ -103,11 +104,26 @@ HTTP_TEST_VARY_HEADER_LINE \ HTTP_TEST_P3P_HEADER_LINE \ HTTP_TEST_XSERVER_HEADER_LINE HTTP_HEADER_LINE_SEPARATOR -#define HTTP_TEST_RESPONSE_HEAD_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD ) - 1U ) -#define HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT 7 -#define HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH 43 -#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U ) -#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U ) +#define HTTP_TEST_RESPONSE_HEAD_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD ) - 1U ) +#define HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT 7 +#define HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH 43 +#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U ) +#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U ) + +#define HTTP_TEST_RESPONSE_HEAD_2 \ + HTTP_STATUS_LINE_NO_REASON_PHRASE \ + HTTP_TEST_CONTENT_LENGTH_HEADER_LINE \ + HTTP_TEST_CONNECTION_CLOSE_HEADER_LINE \ + HTTP_TEST_DATE_HEADER_LINE \ + HTTP_TEST_ETAG_HEADER_LINE \ + HTTP_TEST_VARY_HEADER_LINE \ + HTTP_TEST_P3P_HEADER_LINE \ + HTTP_TEST_XSERVER_HEADER_LINE HTTP_HEADER_LINE_SEPARATOR +#define HTTP_TEST_RESPONSE_HEAD_2_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD_2 ) - 1U ) +#define HTTP_TEST_RESPONSE_HEAD_2_HEADER_COUNT 7 +#define HTTP_TEST_RESPONSE_HEAD_2_CONTENT_LENGTH 43 +#define HTTP_TEST_RESPONSE_HEAD_2_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U ) +#define HTTP_TEST_RESPONSE_HEAD_2_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U ) /* Template HTTP PUT response. This has no body. */ #define HTTP_TEST_RESPONSE_PUT \ @@ -235,6 +251,9 @@ static HTTPRequestHeaders_t requestHeaders = { 0 }; /* Header parsing callback shared among the tests. */ static HTTPClient_ResponseHeaderParsingCallback_t headerParsingCallback = { 0 }; +/* Flag to indicate this callback is called. */ +static int statusCompleteCallbackFlag = 0; + /* A mocked timer query function that increments on every call. */ static uint32_t getTestTime( void ) { @@ -432,6 +451,7 @@ static void helper_parse_status_line( const char ** pNext, const llhttp_settings_t * pSettings ) { const char * pReasonPhraseStart = NULL; + const char * pNextLineStart = NULL; size_t reasonPhraseStartLen = 0; /* For purposes of unit testing the response is well formed in the non-error @@ -440,17 +460,36 @@ static void helper_parse_status_line( const char ** pNext, * always string literals. strchr() should not be used in application code. */ *pNext = strchr( *pNext, SPACE_CHARACTER ); /* Get the space before the status-code. */ *pNext += SPACE_CHARACTER_LEN; - *pNext = strchr( *pNext, SPACE_CHARACTER ); /* Get the space before the reason-phrase. */ - *pNext += SPACE_CHARACTER_LEN; - pReasonPhraseStart = *pNext; - *pNext = strstr( *pNext, HTTP_HEADER_LINE_SEPARATOR ); - reasonPhraseStartLen = ( size_t ) ( *pNext - pReasonPhraseStart ); + /* pNext points to the status code now. */ + + pReasonPhraseStart = strchr( *pNext, SPACE_CHARACTER ); + pReasonPhraseStart = &( pReasonPhraseStart[ SPACE_CHARACTER_LEN ] ); + + pNextLineStart = strstr( *pNext, HTTP_HEADER_LINE_SEPARATOR ); + pNextLineStart = &( pNextLineStart[ HTTP_HEADER_LINE_SEPARATOR_LEN ] ); + pParser->status_code = 200; - pSettings->on_status( pParser, - pReasonPhraseStart, - reasonPhraseStartLen ); - *pNext += HTTP_HEADER_LINE_SEPARATOR_LEN; + /* Check if the reason phrase exist in the header and call the corresponding callback. + * Reason phrase "OK" exists in the response "HTTP/1.1 200 OK\r\n". The callback + * on_status is called. + * Reason phrase doesn't exist in the response "HTTP/1.1 200\r\n". The callback + * on_status_complete is called. */ + if( pNextLineStart > pReasonPhraseStart ) + { + reasonPhraseStartLen = ( size_t ) ( pNextLineStart - pReasonPhraseStart ); + reasonPhraseStartLen = reasonPhraseStartLen - HTTP_HEADER_LINE_SEPARATOR_LEN; + pSettings->on_status( pParser, + pReasonPhraseStart, + reasonPhraseStartLen ); + *pNext = pNextLineStart; + } + else + { + statusCompleteCallbackFlag = 1; + pSettings->on_status_complete( pParser ); + *pNext = pNextLineStart; + } } /* Mock helper that parses all of the headers starting from pNext. */ @@ -805,6 +844,7 @@ void setUp( void ) response.pBuffer = httpBuffer; response.bufferLen = sizeof( httpBuffer ); response.pHeaderParsingCallback = &headerParsingCallback; + statusCompleteCallbackFlag = 0; /* Ignore third-party init functions that return void. */ llhttp_init_Ignore(); @@ -846,6 +886,72 @@ void test_HTTPClient_Send_HEAD_request_parse_whole_response( void ) /*-----------------------------------------------------------*/ +/* Test successfully parsing a response to a HEAD request. The full response + * message is present in the response buffer on the first network read. */ +void test_HTTPClient_Send_HEAD_request_no_parse_body( void ) +{ + HTTPStatus_t returnStatus = HTTPSuccess; + + llhttp_execute_Stub( llhttp_execute_whole_response ); + + response.respOptionFlags |= HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG; + + returnStatus = HTTPClient_Send( &transportInterface, + &requestHeaders, + NULL, + 0, + &response, + 0 ); + TEST_ASSERT_EQUAL( HTTPSuccess, returnStatus ); + TEST_ASSERT_EQUAL( NULL, response.pBody ); + TEST_ASSERT_EQUAL( 0U, response.bodyLen ); + TEST_ASSERT_EQUAL( response.pBuffer + ( sizeof( HTTP_STATUS_LINE_OK ) - 1U ), response.pHeaders ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_LENGTH - ( sizeof( HTTP_STATUS_LINE_OK ) - 1U ) - HTTP_HEADER_END_INDICATOR_LEN, + response.headersLen ); + TEST_ASSERT_EQUAL( HTTP_STATUS_CODE_OK, response.statusCode ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH, response.contentLength ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT, response.headerCount ); + TEST_ASSERT_BITS_HIGH( HTTP_RESPONSE_CONNECTION_CLOSE_FLAG, response.respFlags ); + TEST_ASSERT_BITS_LOW( HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG, response.respFlags ); +} + +/*-----------------------------------------------------------*/ + +/* Test successfully parsing a response to a HEAD request. The full response + * message is present in the response buffer on the first network read. The response + * contains a status code but without a reason string. The on_status_complete is called + * in this case. */ +void test_HTTPClient_Send_HEAD_request_parse_whole_response_no_reason_string( void ) +{ + HTTPStatus_t returnStatus = HTTPSuccess; + + pNetworkData = ( uint8_t * ) HTTP_TEST_RESPONSE_HEAD_2; + networkDataLen = HTTP_TEST_RESPONSE_HEAD_2_LENGTH; + + llhttp_execute_Stub( llhttp_execute_whole_response ); + + returnStatus = HTTPClient_Send( &transportInterface, + &requestHeaders, + NULL, + 0, + &response, + 0 ); + TEST_ASSERT_EQUAL( HTTPSuccess, returnStatus ); + TEST_ASSERT_EQUAL( NULL, response.pBody ); + TEST_ASSERT_EQUAL( 0U, response.bodyLen ); + TEST_ASSERT_EQUAL( response.pBuffer + ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) - 1U ), response.pHeaders ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_LENGTH - ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) - 1U ) - HTTP_HEADER_END_INDICATOR_LEN, + response.headersLen ); + TEST_ASSERT_EQUAL( HTTP_STATUS_CODE_OK, response.statusCode ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_CONTENT_LENGTH, response.contentLength ); + TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_HEADER_COUNT, response.headerCount ); + TEST_ASSERT_BITS_HIGH( HTTP_RESPONSE_CONNECTION_CLOSE_FLAG, response.respFlags ); + TEST_ASSERT_BITS_LOW( HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG, response.respFlags ); + TEST_ASSERT_EQUAL( 1, statusCompleteCallbackFlag ); +} + +/*-----------------------------------------------------------*/ + /* Test successfully parsing a response to a PUT request. The full response * message is present in the response buffer on the first network read. */ void test_HTTPClient_Send_PUT_request_parse_whole_response( void ) @@ -1757,6 +1863,25 @@ void test_HTTPClient_Send_parsing_errors( void ) 0 ); TEST_ASSERT_EQUAL( HTTPSecurityAlertInvalidContentLength, returnStatus ); + httpParsingErrno = HPE_PAUSED; + returnStatus = HTTPClient_Send( &transportInterface, + &requestHeaders, + NULL, + 0, + &response, + 0 ); + TEST_ASSERT_EQUAL( HTTPParserPaused, returnStatus ); + + httpParsingErrno = HPE_PAUSED; + response.respOptionFlags |= HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG; + returnStatus = HTTPClient_Send( &transportInterface, + &requestHeaders, + NULL, + 0, + &response, + 0 ); + TEST_ASSERT_EQUAL( HTTPNoResponse, returnStatus ); + /* Use -1 to indicate an unknown error. */ httpParsingErrno = -1; returnStatus = HTTPClient_Send( &transportInterface, diff --git a/test/unit-test/core_http_utest.c b/test/unit-test/core_http_utest.c index ec22b1c2..e6b4fd14 100644 --- a/test/unit-test/core_http_utest.c +++ b/test/unit-test/core_http_utest.c @@ -72,6 +72,10 @@ typedef struct _headers "%s: %s\r\n" \ "%s: %s\r\n\r\n" +#define HTTP_TEST_HEADER_NO_USER_AGENT_FORMAT \ + "%s %s %s\r\n" \ + "%s: %s\r\n\r\n" + #define HTTP_TEST_EXTRA_HEADER_FORMAT \ "%s %s %s\r\n" \ "%s: %s\r\n" \ @@ -94,6 +98,19 @@ typedef struct _headers HTTP_TEST_HOST_VALUE_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \ HTTP_HEADER_LINE_SEPARATOR_LEN ) +/* Length of the following template HTTP header. + * \r\n + * : \r\n + * \r\n + * This is used to initialize the expectedHeader string. */ +#define HTTP_TEST_PREFIX_HEADER_NO_USER_AGENT_LEN \ + ( HTTP_METHOD_GET_LEN + SPACE_CHARACTER_LEN + \ + HTTP_TEST_REQUEST_PATH_LEN + SPACE_CHARACTER_LEN + \ + HTTP_PROTOCOL_VERSION_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \ + HTTP_HOST_FIELD_LEN + HTTP_HEADER_FIELD_SEPARATOR_LEN + \ + HTTP_TEST_HOST_VALUE_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \ + HTTP_HEADER_LINE_SEPARATOR_LEN ) + /* Add 1 because snprintf(...) writes a null byte at the end. */ #define HTTP_TEST_INITIALIZED_HEADER_BUFFER_LEN \ ( HTTP_TEST_PREFIX_HEADER_LEN + 1 ) @@ -472,6 +489,39 @@ void test_Http_InitializeRequestHeaders_Happy_Path() expectedHeaders.dataLen ); } +/** + * @brief Test happy path with HTTP_REQUEST_NO_USER_AGENT_FLAG. + */ +void test_Http_InitializeRequestHeaders_no_user_agent_flag() +{ + HTTPStatus_t httpStatus = HTTPSuccess; + HTTPRequestHeaders_t requestHeaders = { 0 }; + HTTPRequestInfo_t requestInfo = { 0 }; + int numBytes = 0; + + setupRequestInfo( &requestInfo ); + requestInfo.reqFlags |= HTTP_REQUEST_NO_USER_AGENT_FLAG; + + expectedHeaders.dataLen = HTTP_TEST_PREFIX_HEADER_NO_USER_AGENT_LEN; + setupBuffer( &requestHeaders ); + + /* Happy Path testing. */ + numBytes = snprintf( ( char * ) expectedHeaders.buffer, sizeof( expectedHeaders.buffer ), + HTTP_TEST_HEADER_NO_USER_AGENT_FORMAT, + HTTP_METHOD_GET, HTTP_TEST_REQUEST_PATH, + HTTP_PROTOCOL_VERSION, + HTTP_HOST_FIELD, HTTP_TEST_HOST_VALUE ); + /* Make sure that the entire pre-existing data was printed to the buffer. */ + TEST_ASSERT_GREATER_THAN( 0, numBytes ); + TEST_ASSERT_LESS_THAN( sizeof( expectedHeaders.buffer ), ( size_t ) numBytes ); + + httpStatus = HTTPClient_InitializeRequestHeaders( &requestHeaders, &requestInfo ); + TEST_ASSERT_EQUAL( HTTPSuccess, httpStatus ); + TEST_ASSERT_EQUAL( expectedHeaders.dataLen, requestHeaders.headersLen ); + TEST_ASSERT_EQUAL_MEMORY( expectedHeaders.buffer, requestHeaders.pBuffer, + expectedHeaders.dataLen ); +} + /** * @brief Test NULL parameters, following order of else-if blocks in the HTTP library. */