diff --git a/.github/memory_statistics_config.json b/.github/memory_statistics_config.json index b6ff1be2..8374f109 100644 --- a/.github/memory_statistics_config.json +++ b/.github/memory_statistics_config.json @@ -3,14 +3,22 @@ "src": [ "source/core_http_client.c", { - "file": "source/dependency/3rdparty/http_parser/http_parser.c", - "tag": "http-parser" + "file": "source/dependency/3rdparty/llhttp/src/api.c", + "tag": "llhttp" + }, + { + "file": "source/dependency/3rdparty/llhttp/src/http.c", + "tag": "llhttp" + }, + { + "file": "source/dependency/3rdparty/llhttp/src/llhttp.c", + "tag": "llhttp" } ], "include": [ "source/include", "source/interface", - "source/dependency/3rdparty/http_parser" + "source/dependency/3rdparty/llhttp/include" ], "compiler_flags": [ "HTTP_DO_NOT_USE_CUSTOM_CONFIG" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bf97d5a..2c38010a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches: ["**"] pull_request: - branches: [main] + branches: ["**"] workflow_dispatch: jobs: diff --git a/.gitmodules b/.gitmodules index 0eae6012..b49c2727 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,6 @@ path = test/cbmc/litani url = https://github.com/awslabs/aws-build-accumulator update = none -[submodule "source/dependency/3rdparty/http_parser"] - path = source/dependency/3rdparty/http_parser - url = https://github.com/nodejs/http-parser.git +[submodule "source/dependency/3rdparty/llhttp"] + path = source/dependency/3rdparty/llhttp + url = https://github.com/nodejs/llhttp.git diff --git a/README.md b/README.md index efd78bae..c47d5f12 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository contains a C language HTTP client library designed for embedded platforms. It has no dependencies on any additional libraries other than the -standard C library, [http-parser](https://github.com/nodejs/http-parser), and +standard C library, [llhttp](https://github.com/nodejs/llhttp), and a customer-implemented transport interface. This library is distributed under the [MIT Open Source License](LICENSE). diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md index 41bc6338..fed45453 100644 --- a/docs/doxygen/include/size_table.md +++ b/docs/doxygen/include/size_table.md @@ -10,16 +10,26 @@ core_http_client.c
3.2K
+
2.5K
+ + + api.c (llhttp)
2.6K
+
2.0K
+ + + http.c (llhttp) +
0.3K
+
0.3K
- http_parser.c (http-parser) -
15.7K
-
13.0K
+ llhttp.c (llhttp) +
17.9K
+
15.9K
Total estimates -
18.9K
-
15.6K
+
24.0K
+
20.7K
diff --git a/docs/doxygen/pages.dox b/docs/doxygen/pages.dox index 7c65ef21..4c225127 100644 --- a/docs/doxygen/pages.dox +++ b/docs/doxygen/pages.dox @@ -7,7 +7,7 @@ This HTTP Client library implements a subset of the HTTP/1.1 protocol. Features of this library include: - Fully synchronous API, to allow applications to completely manage their concurrency and multi-threading. - Operations on user supplied buffers, so that applications have complete control of their memory allocation strategy. -- Integration with [http-parser](https://github.com/nodejs/http-parser) to handle chunked encoding. +- Integration with [llhttp](https://github.com/nodejs/llhttp) to handle chunked encoding. Feature of HTTP/1.1 not supported in this library: - Streaming uploads and downloads. Range requests for partial content responses are highly encouraged with this API. @@ -110,7 +110,7 @@ If the request has a body it is passed as a parameter to @ref HTTPClient_Send. As soon as the response is received from the network it is parsed. The final parsed response is represented by the @ref HTTPResponse_t returned from @ref HTTPClient_Send. Parsing the HTTP response is done using -[http-parser](https://github.com/nodejs/http-parser). http-parser invokes +[llhttp](https://github.com/nodejs/llhttp). llhttp invokes callbacks for each section in the HTTP response it finds. Using these callbacks the HTTP client library sets the members of @ref HTTPResponse_t to return from @ref HTTPClient_Send. The overall flow of @ref HTTPClient_Send is @@ -125,14 +125,14 @@ can be read from the headers found in @ref HTTPResponse_t.pHeaders. The function @ref HTTPClient_ReadHeader reads the headers from an @ref HTTPResponse_t. @ref HTTPClient_ReadHeader will re-parse the response in @ref HTTPResponse_t.pBuffer, looking for the header field of interest. -Re-parsing involves using http-parser to look at each character starting from +Re-parsing involves using llhttp to look at each character starting from the beginning of @ref HTTPResponse_t.pBuffer until the header field of interest is found. If the user application wants to avoid re-parsing @ref HTTPResponse_t.pBuffer, then the user application may register a callback in @ref HTTPResponse_t.pHeaderParsingCallback. When the HTTP response message is -first received from the network, in @ref HTTPClient_Send, http-parser is invoked +first received from the network, in @ref HTTPClient_Send, llhttp is invoked to parse the response. This first parsing in @ref HTTPClient_Send will invoke @ref HTTPResponse_t.pHeaderParsingCallback for each header that is found in response. Please see the sequence diagram below for an illustration of when diff --git a/httpFilePaths.cmake b/httpFilePaths.cmake index 0ef3479a..38418f96 100644 --- a/httpFilePaths.cmake +++ b/httpFilePaths.cmake @@ -8,10 +8,12 @@ # HTTP library source files. set( HTTP_SOURCES ${CMAKE_CURRENT_LIST_DIR}/source/core_http_client.c - ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/http_parser/http_parser.c ) + ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/llhttp/src/api.c + ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/llhttp/src/llhttp.c + ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/llhttp/src/http.c ) # HTTP library public include directories. set( HTTP_INCLUDE_PUBLIC_DIRS ${CMAKE_CURRENT_LIST_DIR}/source/include ${CMAKE_CURRENT_LIST_DIR}/source/interface - ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/http_parser ) + ${CMAKE_CURRENT_LIST_DIR}/source/dependency/3rdparty/llhttp/include ) diff --git a/lexicon.txt b/lexicon.txt index e2add5f5..89b9bbfe 100644 --- a/lexicon.txt +++ b/lexicon.txt @@ -25,6 +25,7 @@ chk chunked colspan com +cond config configpagestyle const @@ -42,6 +43,7 @@ defgroup doesn doxygen endcode +endcond endif enums eof @@ -122,6 +124,9 @@ lastheadervaluelen latin len linux +llhttp +llhttpparser +llhttpsettings logdebug logerror loginfo @@ -201,7 +206,7 @@ prequestbodybuf prequestheaders prequestinfo presponse -processhttpparsererror +processllhttperror ptransport ptransportinterface pvalue diff --git a/manifest.yml b/manifest.yml index 6926c99f..b4720c7a 100644 --- a/manifest.yml +++ b/manifest.yml @@ -3,9 +3,9 @@ version: "v2.1.0" description: | "Client implementation of the HTTP/1.1 specification for embedded devices.\n" dependencies: - - name : "http-parser" - version: "v2.9.4" + - name : "llhttp" + version: "release/v6.0.5" repository: type: "git" - url: "https://github.com/nodejs/http-parser" + url: "https://github.com/nodejs/llhttp" license: "MIT" diff --git a/source/core_http_client.c b/source/core_http_client.c index 2dd3a228..492bd519 100644 --- a/source/core_http_client.c +++ b/source/core_http_client.c @@ -289,10 +289,10 @@ static HTTPStatus_t findHeaderInResponse( const uint8_t * pBuffer, * buffer. * @param[in] fieldLen The length of the header field. * - * @return Returns #HTTP_PARSER_CONTINUE_PARSING to indicate continuation with + * @return Returns #LLHTTP_CONTINUE_PARSING to indicate continuation with * parsing. */ -static int findHeaderFieldParserCallback( http_parser * pHttpParser, +static int findHeaderFieldParserCallback( llhttp_t * pHttpParser, const char * pFieldLoc, size_t fieldLen ); @@ -307,10 +307,10 @@ static int findHeaderFieldParserCallback( http_parser * pHttpParser, * buffer. * @param[in] valueLen The length of the header value. * - * @return Returns #HTTP_PARSER_STOP_PARSING, if the header field/value pair are - * found, otherwise #HTTP_PARSER_CONTINUE_PARSING is returned. + * @return Returns #LLHTTP_STOP_PARSING, if the header field/value pair are + * found, otherwise #LLHTTP_CONTINUE_PARSING is returned. */ -static int findHeaderValueParserCallback( http_parser * pHttpParser, +static int findHeaderValueParserCallback( llhttp_t * pHttpParser, const char * pValueLoc, size_t valueLen ); @@ -324,10 +324,10 @@ static int findHeaderValueParserCallback( http_parser * pHttpParser, * * @param[in] pHttpParser Parsing object containing state and callback context. * - * @return Returns #HTTP_PARSER_STOP_PARSING for the parser to halt further + * @return Returns #LLHTTP_STOP_PARSING_NO_HEADER for the parser to halt further * execution, as all headers have been parsed in the response. */ -static int findHeaderOnHeaderCompleteCallback( http_parser * pHttpParser ); +static int findHeaderOnHeaderCompleteCallback( llhttp_t * pHttpParser ); /** @@ -354,14 +354,14 @@ static void initializeParsingContextForFirstResponse( HTTPParsingContext_t * pPa * @return One of the following: * - #HTTPSuccess * - #HTTPInvalidParameter - * - Please see #processHttpParserError for parsing errors returned. + * - Please see #processLlhttpError for parsing errors returned. */ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext, HTTPResponse_t * pResponse, size_t parseLen ); /** - * @brief Callback invoked during http_parser_execute() to indicate the start of + * @brief Callback invoked during llhttp_execute() to indicate the start of * the HTTP response message. * * This callback is invoked when an "H" in the "HTTP/1.1" that starts a response @@ -369,12 +369,12 @@ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext, * * @param[in] pHttpParser Parsing object containing state and callback context. * - * @return #HTTP_PARSER_CONTINUE_PARSING to continue parsing. + * @return #LLHTTP_CONTINUE_PARSING to continue parsing. */ -static int httpParserOnMessageBeginCallback( http_parser * pHttpParser ); +static int httpParserOnMessageBeginCallback( llhttp_t * pHttpParser ); /** - * @brief Callback invoked during http_parser_execute() when the HTTP response + * @brief Callback invoked during llhttp_execute() when the HTTP response * status-code and its associated reason-phrase are found. * * @param[in] pHttpParser Parsing object containing state and callback context. @@ -382,14 +382,14 @@ static int httpParserOnMessageBeginCallback( http_parser * pHttpParser ); * response message buffer. * @param[in] length Length of the HTTP response status code string. * - * @return #HTTP_PARSER_CONTINUE_PARSING to continue parsing. + * @return #LLHTTP_CONTINUE_PARSING to continue parsing. */ -static int httpParserOnStatusCallback( http_parser * pHttpParser, +static int httpParserOnStatusCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ); /** - * @brief Callback invoked during http_parser_execute() when an HTTP response + * @brief Callback invoked during llhttp_execute() when an HTTP response * header field is found. * * If only part of the header field was found, then parsing of the next part of @@ -400,14 +400,14 @@ static int httpParserOnStatusCallback( http_parser * pHttpParser, * message buffer. * @param[in] length Length of the header field. * - * @return #HTTP_PARSER_CONTINUE_PARSING to continue parsing. + * @return #LLHTTP_CONTINUE_PARSING to continue parsing. */ -static int httpParserOnHeaderFieldCallback( http_parser * pHttpParser, +static int httpParserOnHeaderFieldCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ); /** - * @brief Callback invoked during http_parser_execute() when an HTTP response + * @brief Callback invoked during llhttp_execute() when an HTTP response * header value is found. * * This header value corresponds to the header field that was found in the @@ -420,14 +420,14 @@ static int httpParserOnHeaderFieldCallback( http_parser * pHttpParser, * @param[in] pLoc Location of the header value in the response message buffer. * @param[in] length Length of the header value. * - * @return #HTTP_PARSER_CONTINUE_PARSING to continue parsing. + * @return #LLHTTP_CONTINUE_PARSING to continue parsing. */ -static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, +static int httpParserOnHeaderValueCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ); /** - * @brief Callback invoked during http_parser_execute() when the end of the + * @brief Callback invoked during llhttp_execute() when the end of the * headers are found. * * The end of the headers is signaled in a HTTP response message by another @@ -435,13 +435,13 @@ static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, * * @param[in] pHttpParser Parsing object containing state and callback context. * - * @return #HTTP_PARSER_CONTINUE_PARSING to continue parsing. - * #HTTP_PARSER_STOP_PARSING is returned if the response is for a HEAD request. + * @return #LLHTTP_CONTINUE_PARSING to continue parsing. + * #LLHTTP_STOP_PARSING_NO_BODY is returned if the response is for a HEAD request. */ -static int httpParserOnHeadersCompleteCallback( http_parser * pHttpParser ); +static int httpParserOnHeadersCompleteCallback( llhttp_t * pHttpParser ); /** - * @brief Callback invoked during http_parser_execute() when the HTTP response + * @brief Callback invoked during llhttp_execute() when the HTTP response * body is found. * * If only part of the response body was found, then parsing of the next part of @@ -480,14 +480,14 @@ static int httpParserOnHeadersCompleteCallback( http_parser * pHttpParser ); * @param[in] length - The length of the body found. * * @return Zero to continue parsing. All other return values will stop parsing - * and http_parser_execute() will return with status HPE_CB_body. + * and llhttp_execute() will return with the same value. */ -static int httpParserOnBodyCallback( http_parser * pHttpParser, +static int httpParserOnBodyCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ); /** - * @brief Callback invoked during http_parser_execute() to indicate the the + * @brief Callback invoked during llhttp_execute() to indicate the the * completion of an HTTP response message. * * When there is no response body, the end of the response message is when the @@ -495,20 +495,20 @@ static int httpParserOnBodyCallback( http_parser * pHttpParser, * * When there is response body, the end of the response message is when the * full "Content-Length" value is parsed following the end of the headers. If - * there is no Content-Length header, then http_parser_execute() expects a + * there is no Content-Length header, then llhttp_execute() expects a * zero length-ed parsing data to indicate the end of the response. * * For a "Transfer-Encoding: chunked" type of response message, the complete * response message is signaled by a terminating chunk header with length zero. * - * See https://github.com/nodejs/http-parser for more information. + * See https://github.com/nodejs/llhttp for more information. * * @param[in] pHttpParser Parsing object containing state and callback context. * * @return Zero to continue parsing. All other return values will stop parsing - * and http_parser_execute() will return with status HPE_CB_message_complete. + * and llhttp_execute() will return with the same value. */ -static int httpParserOnMessageCompleteCallback( http_parser * pHttpParser ); +static int httpParserOnMessageCompleteCallback( llhttp_t * pHttpParser ); /** * @brief When a complete header is found the HTTP response header count @@ -527,14 +527,13 @@ static void processCompleteHeader( HTTPParsingContext_t * pParsingContext ); /** * @brief When parsing is complete an error could be indicated in - * pHttpParser->http_errno. This function translates that error into a library + * pHttpParser->error. This function translates that error into a library * specific error code. * * @param[in] pHttpParser Third-party HTTP parsing context. * * @return One of the following: * - #HTTPSuccess - * - #HTTPSecurityAlertResponseHeadersSizeLimitExceeded * - #HTTPSecurityAlertExtraneousResponseData * - #HTTPSecurityAlertInvalidChunkHeader * - #HTTPSecurityAlertInvalidProtocolVersion @@ -543,7 +542,7 @@ static void processCompleteHeader( HTTPParsingContext_t * pParsingContext ); * - #HTTPSecurityAlertInvalidContentLength * - #HTTPParserInternalError */ -static HTTPStatus_t processHttpParserError( const http_parser * pHttpParser ); +static HTTPStatus_t processLlhttpError( const llhttp_t * pHttpParser ); /** * @brief Compares at most the first n bytes of str1 and str2 without case sensitivity @@ -638,7 +637,7 @@ static void processCompleteHeader( HTTPParsingContext_t * pParsingContext ) /*-----------------------------------------------------------*/ -static int httpParserOnMessageBeginCallback( http_parser * pHttpParser ) +static int httpParserOnMessageBeginCallback( llhttp_t * pHttpParser ) { HTTPParsingContext_t * pParsingContext = NULL; @@ -652,12 +651,12 @@ static int httpParserOnMessageBeginCallback( http_parser * pHttpParser ) LogDebug( ( "Response parsing: Found the start of the response message." ) ); - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ -static int httpParserOnStatusCallback( http_parser * pHttpParser, +static int httpParserOnStatusCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ) { @@ -683,7 +682,7 @@ static int httpParserOnStatusCallback( http_parser * pHttpParser, pParsingContext->pLastHeaderValue = NULL; pParsingContext->lastHeaderValueLen = 0U; - /* httpParserOnStatusCallback() is reached because http_parser_execute() has + /* httpParserOnStatusCallback() is reached because llhttp_execute() has * successfully read the HTTP response status code. */ pResponse->statusCode = ( uint16_t ) ( pHttpParser->status_code ); @@ -693,12 +692,12 @@ static int httpParserOnStatusCallback( http_parser * pHttpParser, ( int ) length, pLoc ) ); - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ -static int httpParserOnHeaderFieldCallback( http_parser * pHttpParser, +static int httpParserOnHeaderFieldCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ) { @@ -731,7 +730,7 @@ static int httpParserOnHeaderFieldCallback( http_parser * pHttpParser, processCompleteHeader( pParsingContext ); /* If httpParserOnHeaderFieldCallback() is invoked in succession, then the - * last time http_parser_execute() was called only part of the header field + * last time llhttp_execute() was called only part of the header field * was parsed. The indication of successive invocations is a non-NULL * pParsingContext->pLastHeaderField. */ if( pParsingContext->pLastHeaderField == NULL ) @@ -750,12 +749,12 @@ static int httpParserOnHeaderFieldCallback( http_parser * pHttpParser, ( int ) length, pLoc ) ); - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ -static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, +static int httpParserOnHeaderValueCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ) { @@ -771,7 +770,7 @@ static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, pParsingContext->pBufferCur = pLoc + length; /* If httpParserOnHeaderValueCallback() is invoked in succession, then the - * last time http_parser_execute() was called only part of the header field + * last time llhttp_execute() was called only part of the header field * was parsed. The indication of successive invocations is a non-NULL * pParsingContext->pLastHeaderField. */ if( pParsingContext->pLastHeaderValue == NULL ) @@ -786,8 +785,7 @@ static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, /* Given that httpParserOnHeaderFieldCallback() is ALWAYS invoked before * httpParserOnHeaderValueCallback() is invoked, then the last header field - * should never be NULL. This would indicate a bug in the http-parser - * library. */ + * should never be NULL. This would indicate a bug in the llhttp library. */ assert( pParsingContext->pLastHeaderField != NULL ); LogDebug( ( "Response parsing: Found a header value: " @@ -795,14 +793,14 @@ static int httpParserOnHeaderValueCallback( http_parser * pHttpParser, ( int ) length, pLoc ) ); - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ -static int httpParserOnHeadersCompleteCallback( http_parser * pHttpParser ) +static int httpParserOnHeadersCompleteCallback( llhttp_t * pHttpParser ) { - int shouldContinueParse = HTTP_PARSER_CONTINUE_PARSING; + int shouldContinueParse = LLHTTP_CONTINUE_PARSING; HTTPParsingContext_t * pParsingContext = NULL; HTTPResponse_t * pResponse = NULL; @@ -866,14 +864,14 @@ static int httpParserOnHeadersCompleteCallback( http_parser * pHttpParser ) pResponse->respFlags |= HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG; } - /* http_parser_execute() requires that callback implementations must + /* llhttp_execute() requires that callback implementations must * indicate that parsing stops on headers complete, if response is to a HEAD * request. A HEAD response will contain Content-Length, but no body. If * the parser is not stopped here, then it will try to keep parsing past the * end of the headers up to the Content-Length found. */ if( pParsingContext->isHeadResponse == 1U ) { - shouldContinueParse = HTTP_PARSER_STOP_PARSING; + shouldContinueParse = LLHTTP_STOP_PARSING_NO_BODY; } /* If headers are present in the response, then @@ -890,11 +888,11 @@ static int httpParserOnHeadersCompleteCallback( http_parser * pHttpParser ) /*-----------------------------------------------------------*/ -static int httpParserOnBodyCallback( http_parser * pHttpParser, +static int httpParserOnBodyCallback( llhttp_t * pHttpParser, const char * pLoc, size_t length ) { - int shouldContinueParse = HTTP_PARSER_CONTINUE_PARSING; + int shouldContinueParse = LLHTTP_CONTINUE_PARSING; HTTPParsingContext_t * pParsingContext = NULL; HTTPResponse_t * pResponse = NULL; char * pNextWriteLoc = NULL; @@ -967,7 +965,7 @@ static int httpParserOnBodyCallback( http_parser * pHttpParser, /*-----------------------------------------------------------*/ -static int httpParserOnMessageCompleteCallback( http_parser * pHttpParser ) +static int httpParserOnMessageCompleteCallback( llhttp_t * pHttpParser ) { HTTPParsingContext_t * pParsingContext = NULL; @@ -981,7 +979,7 @@ static int httpParserOnMessageCompleteCallback( http_parser * pHttpParser ) LogDebug( ( "Response parsing: Response message complete." ) ); - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ @@ -993,11 +991,21 @@ static void initializeParsingContextForFirstResponse( HTTPParsingContext_t * pPa assert( pRequestHeaders != NULL ); assert( pRequestHeaders->headersLen >= HTTP_MINIMUM_REQUEST_LINE_LENGTH ); + /* Initialize the callbacks that llhttp_execute will invoke. */ + llhttp_settings_init( &( pParsingContext->llhttpSettings ) ); + pParsingContext->llhttpSettings.on_message_begin = httpParserOnMessageBeginCallback; + pParsingContext->llhttpSettings.on_status = httpParserOnStatusCallback; + pParsingContext->llhttpSettings.on_header_field = httpParserOnHeaderFieldCallback; + pParsingContext->llhttpSettings.on_header_value = httpParserOnHeaderValueCallback; + pParsingContext->llhttpSettings.on_headers_complete = httpParserOnHeadersCompleteCallback; + pParsingContext->llhttpSettings.on_body = httpParserOnBodyCallback; + pParsingContext->llhttpSettings.on_message_complete = httpParserOnMessageCompleteCallback; + /* Initialize the third-party HTTP parser to parse responses. */ - http_parser_init( &( pParsingContext->httpParser ), HTTP_RESPONSE ); + llhttp_init( &( pParsingContext->llhttpParser ), HTTP_RESPONSE, &( pParsingContext->llhttpSettings ) ); /* The parser will return an error if this header size limit is exceeded. */ - http_parser_set_max_header_size( HTTP_MAX_RESPONSE_HEADERS_SIZE_BYTES ); + /*http_parser_set_max_header_size( HTTP_MAX_RESPONSE_HEADERS_SIZE_BYTES ); */ /* No response has been parsed yet. */ pParsingContext->state = HTTP_PARSING_NONE; @@ -1019,13 +1027,16 @@ static void initializeParsingContextForFirstResponse( HTTPParsingContext_t * pPa /*-----------------------------------------------------------*/ -static HTTPStatus_t processHttpParserError( const http_parser * pHttpParser ) +static HTTPStatus_t processLlhttpError( const llhttp_t * pHttpParser ) { HTTPStatus_t returnStatus = HTTPSuccess; assert( pHttpParser != NULL ); - switch( ( enum http_errno ) ( pHttpParser->http_errno ) ) + /* llhttp_get_err_pos() may be used to get exact error locations, which was + * not possible with the previous http-parser. */ + + switch( llhttp_get_errno( pHttpParser ) ) { case HPE_OK: /* There were no errors. */ @@ -1038,12 +1049,7 @@ static HTTPStatus_t processHttpParserError( const http_parser * pHttpParser ) * This case is already handled by checking HTTPParsingContext_t.state. */ break; - case HPE_HEADER_OVERFLOW: - LogError( ( "Response parsing error: Header byte limit " - "exceeded: HeaderByteLimit=%u", - HTTP_MAX_RESPONSE_HEADERS_SIZE_BYTES ) ); - returnStatus = HTTPSecurityAlertResponseHeadersSizeLimitExceeded; - break; + /* No header overflow in llhttp since no header size was set. */ case HPE_CLOSED_CONNECTION: LogError( ( "Response parsing error: Data received past complete " @@ -1066,6 +1072,7 @@ static HTTPStatus_t processHttpParserError( const http_parser * pHttpParser ) * character and location. */ LogError( ( "Response parsing error: Invalid character found in " "HTTP protocol version." ) ); + returnStatus = HTTPSecurityAlertInvalidProtocolVersion; break; @@ -1118,15 +1125,16 @@ static HTTPStatus_t processHttpParserError( const http_parser * pHttpParser ) /* All other error cases cannot be triggered and indicate an error in the * third-party parsing library if found. */ default: - LogError( ( "Error in third-party http-parser library." ) ); + LogError( ( "Error in third-party llhttp library: %s", llhttp_errno_name( llhttp_get_errno( pHttpParser ) ) ) ); returnStatus = HTTPParserInternalError; break; } - /* Errors with CB_ prepending are manual returns of non-zero in the + /* Errors with HPE_CB_ prepending are manual returns of non-zero in the * response parsing callback. */ - LogDebug( ( "http-parser errno description: %s", - http_errno_description( HTTP_PARSER_ERRNO( pHttpParser ) ) ) ); + LogDebug( ( "llhttp errno description: %s %s", + llhttp_errno_name( llhttp_get_errno( pHttpParser ) ), + llhttp_get_error_reason( pHttpParser ) ) ); return returnStatus; } @@ -1138,12 +1146,8 @@ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext, size_t parseLen ) { HTTPStatus_t returnStatus; - http_parser_settings parserSettings = { 0 }; - size_t bytesParsed = 0U; const char * parsingStartLoc = NULL; - - /* Disable unused variable warning. */ - ( void ) bytesParsed; + llhttp_errno_t parserStatus; assert( pParsingContext != NULL ); assert( pResponse != NULL ); @@ -1177,19 +1181,9 @@ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext, assert( pParsingContext->pResponse == pResponse ); } - /* Initialize the callbacks that http_parser_execute will invoke. */ - http_parser_settings_init( &parserSettings ); - parserSettings.on_message_begin = httpParserOnMessageBeginCallback; - parserSettings.on_status = httpParserOnStatusCallback; - parserSettings.on_header_field = httpParserOnHeaderFieldCallback; - parserSettings.on_header_value = httpParserOnHeaderValueCallback; - parserSettings.on_headers_complete = httpParserOnHeadersCompleteCallback; - parserSettings.on_body = httpParserOnBodyCallback; - parserSettings.on_message_complete = httpParserOnMessageCompleteCallback; - /* Setting this allows the parsing context and response to be carried to - * each of the callbacks that http_parser_execute() will invoke. */ - pParsingContext->httpParser.data = pParsingContext; + * each of the callbacks that llhttp_execute() will invoke. */ + pParsingContext->llhttpParser.data = pParsingContext; /* Save the starting response buffer location to parse. This is needed to * ensure that we move the next location to parse to exactly how many @@ -1199,21 +1193,12 @@ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext, /* This will begin the parsing. Each of the callbacks set in * parserSettings will be invoked as parts of the HTTP response are * reached. */ - bytesParsed = http_parser_execute( &( pParsingContext->httpParser ), - &parserSettings, - parsingStartLoc, - parseLen ); + parserStatus = llhttp_execute( &( pParsingContext->llhttpParser ), parsingStartLoc, parseLen ); /* The next location to parse will always be after what has already * been parsed. */ - pParsingContext->pBufferCur = parsingStartLoc + bytesParsed; - - LogDebug( ( "Parsed HTTP Response buffer: BytesParsed=%lu, " - "ExpectedBytesParsed=%lu", - ( unsigned long ) bytesParsed, - ( unsigned long ) parseLen ) ); - - returnStatus = processHttpParserError( &( pParsingContext->httpParser ) ); + pParsingContext->pBufferCur = parsingStartLoc + parseLen; + returnStatus = processLlhttpError( &( pParsingContext->llhttpParser ) ); return returnStatus; } @@ -2254,7 +2239,7 @@ HTTPStatus_t HTTPClient_Send( const TransportInterface_t * pTransport, /*-----------------------------------------------------------*/ -static int findHeaderFieldParserCallback( http_parser * pHttpParser, +static int findHeaderFieldParserCallback( llhttp_t * pHttpParser, const char * pFieldLoc, size_t fieldLen ) { @@ -2291,16 +2276,16 @@ static int findHeaderFieldParserCallback( http_parser * pHttpParser, /* Empty else for MISRA 15.7 compliance. */ } - return HTTP_PARSER_CONTINUE_PARSING; + return LLHTTP_CONTINUE_PARSING; } /*-----------------------------------------------------------*/ -static int findHeaderValueParserCallback( http_parser * pHttpParser, +static int findHeaderValueParserCallback( llhttp_t * pHttpParser, const char * pValueLoc, size_t valueLen ) { - int retCode = HTTP_PARSER_CONTINUE_PARSING; + int retCode = LLHTTP_CONTINUE_PARSING; findHeaderContext_t * pContext = NULL; assert( pHttpParser != NULL ); @@ -2343,7 +2328,7 @@ static int findHeaderValueParserCallback( http_parser * pHttpParser, /* As we have found the value associated with the header, we don't need * to parse the response any further. */ - retCode = HTTP_PARSER_STOP_PARSING; + retCode = LLHTTP_STOP_PARSING; } else { @@ -2355,7 +2340,7 @@ static int findHeaderValueParserCallback( http_parser * pHttpParser, /*-----------------------------------------------------------*/ -static int findHeaderOnHeaderCompleteCallback( http_parser * pHttpParser ) +static int findHeaderOnHeaderCompleteCallback( llhttp_t * pHttpParser ) { findHeaderContext_t * pContext = NULL; @@ -2375,8 +2360,14 @@ static int findHeaderOnHeaderCompleteCallback( http_parser * pHttpParser ) ( int ) ( pContext->fieldLen ), pContext->pField ) ); - /* No further parsing is required; thus, indicate the parser to stop parsing. */ - return HTTP_PARSER_STOP_PARSING; + /* No further parsing is required; thus, indicate the parser to stop parsing. + * The documentation for on_headers_complete states that this function can + * return 1 to indicate the response has no body, or -1 to indicate error. + * Returning 1 causes llhttp_execute() to exit with success, while -1 + * causes it to return the HPE_CB_HEADERS_COMPLETE error code (in strict + * mode) or success (in non-strict mode). We are fine with the success + * return value, as llhttp_execute will not be invoked again in the same call.*/ + return LLHTTP_STOP_PARSING_NO_HEADER; } /*-----------------------------------------------------------*/ @@ -2389,8 +2380,9 @@ static HTTPStatus_t findHeaderInResponse( const uint8_t * pBuffer, size_t * pValueLen ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser parser = { 0 }; - http_parser_settings parserSettings = { 0 }; + llhttp_t parser = { 0 }; + llhttp_settings_t parserSettings = { 0 }; + llhttp_errno_t parserErrno; findHeaderContext_t context = { 0 }; size_t numOfBytesParsed = 0U; @@ -2401,30 +2393,20 @@ static HTTPStatus_t findHeaderInResponse( const uint8_t * pBuffer, context.fieldFound = 0U; context.valueFound = 0U; - /* Disable unused variable warning. This variable is used only in logging. */ - ( void ) numOfBytesParsed; - - http_parser_init( &parser, HTTP_RESPONSE ); - - /* Set the context for the parser. */ - parser.data = &context; - /* The intention here to define callbacks just for searching the headers. We will - * need to create a private context in httpParser->data that has the field and + * need to create a private context in llhttp->data that has the field and * value to update and pass back. */ - http_parser_settings_init( &parserSettings ); + llhttp_settings_init( &( parserSettings ) ); parserSettings.on_header_field = findHeaderFieldParserCallback; parserSettings.on_header_value = findHeaderValueParserCallback; parserSettings.on_headers_complete = findHeaderOnHeaderCompleteCallback; + llhttp_init( &parser, HTTP_RESPONSE, &parserSettings ); - /* Start parsing for the header! */ - numOfBytesParsed = http_parser_execute( &parser, - &parserSettings, - ( const char * ) pBuffer, - bufferLen ); + /* Set the context for the parser. */ + parser.data = &context; - LogDebug( ( "Parsed response for header search: NumBytesParsed=%lu", - ( unsigned long ) numOfBytesParsed ) ); + /* Start parsing for the header! */ + parserErrno = llhttp_execute( &parser, ( const char * ) pBuffer, bufferLen ); if( context.fieldFound == 0U ) { @@ -2444,10 +2426,11 @@ static HTTPStatus_t findHeaderInResponse( const uint8_t * pBuffer, * in the ": \r\n" format of an HTTP header. */ LogError( ( "Unable to find header value in response: " "Response data is invalid: " - "RequestedHeader=%.*s, ParserError=%s", + "RequestedHeader=%.*s, ParserError=%s %s", ( int ) fieldLen, pField, - http_errno_description( HTTP_PARSER_ERRNO( &( parser ) ) ) ) ); + llhttp_errno_name( parserErrno ), + llhttp_get_error_reason( &parser ) ) ); returnStatus = HTTPInvalidResponse; } else @@ -2466,25 +2449,27 @@ static HTTPStatus_t findHeaderInResponse( const uint8_t * pBuffer, /* If the header field-value pair is found in response, then the return * value of "on_header_value" callback (related to the header value) should - * cause the http_parser.http_errno to be "CB_header_value". */ + * cause the llhttp.error to be "USER". */ if( ( returnStatus == HTTPSuccess ) && - ( parser.http_errno != ( unsigned int ) HPE_CB_header_value ) ) + ( parserErrno != HPE_USER ) ) { - LogError( ( "Header found in response but http-parser returned error: " - "ParserError=%s", - http_errno_description( HTTP_PARSER_ERRNO( &( parser ) ) ) ) ); + LogError( ( "Header found in response but llhttp returned error: " + "ParserError=%s %s", + llhttp_errno_name( parserErrno ), + llhttp_get_error_reason( &parser ) ) ); returnStatus = HTTPParserInternalError; } /* If header was not found, then the "on_header_complete" callback is - * expected to be called which should cause the http_parser.http_errno to be + * expected to be called which should cause the llhttp.error to be * "OK" */ else if( ( returnStatus == HTTPHeaderNotFound ) && - ( parser.http_errno != ( unsigned int ) ( HPE_OK ) ) ) + ( parserErrno != HPE_OK ) ) { - LogError( ( "Header not found in response: http-parser returned error: " - "ParserError=%s", - http_errno_description( HTTP_PARSER_ERRNO( &( parser ) ) ) ) ); + LogError( ( "Header not found in response: llhttp returned error: " + "ParserError=%s %s", + llhttp_errno_name( parserErrno ), + llhttp_get_error_reason( &parser ) ) ); returnStatus = HTTPInvalidResponse; } else diff --git a/source/dependency/3rdparty/http_parser b/source/dependency/3rdparty/http_parser deleted file mode 160000 index ec8b5ee6..00000000 --- a/source/dependency/3rdparty/http_parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ec8b5ee63f0e51191ea43bb0c6eac7bfbff3141d diff --git a/source/dependency/3rdparty/llhttp b/source/dependency/3rdparty/llhttp new file mode 160000 index 00000000..a4aa7a70 --- /dev/null +++ b/source/dependency/3rdparty/llhttp @@ -0,0 +1 @@ +Subproject commit a4aa7a70e8b9a67f378b53264c61bb044a224366 diff --git a/source/include/core_http_client.h b/source/include/core_http_client.h index 0f589a5b..2f005715 100644 --- a/source/include/core_http_client.h +++ b/source/include/core_http_client.h @@ -228,7 +228,7 @@ typedef enum HTTPStatus * #HTTP_MAX_RESPONSE_HEADERS_SIZE_BYTES. * * Functions that may return this value: - * - #HTTPClient_Send + * - None */ HTTPSecurityAlertResponseHeadersSizeLimitExceeded, diff --git a/source/include/core_http_client_private.h b/source/include/core_http_client_private.h index a0af6f9a..fc2ac28b 100644 --- a/source/include/core_http_client_private.h +++ b/source/include/core_http_client_private.h @@ -28,8 +28,17 @@ #ifndef CORE_HTTP_CLIENT_PRIVATE_H_ #define CORE_HTTP_CLIENT_PRIVATE_H_ -/* Third-party http-parser include. */ -#include "http_parser.h" +/** + * @cond DOXYGEN_IGNORE + * http-parser defaults this to 1, llhttp to 0. + */ +#ifndef LLHTTP_STRICT_MODE + #define LLHTTP_STRICT_MODE 0 +#endif +/** @endcond */ + +/* Third-party llhttp include. */ +#include "llhttp.h" /* *INDENT-OFF* */ #ifdef __cplusplus @@ -150,16 +159,31 @@ 1U /* Dash character '-' */ + MAX_INT32_NO_OF_DECIMAL_DIGITS ) /** - * @brief Return value for the http-parser registered callback to signal halting + * @brief Return value for llhttp registered callback to signal + * continuation of HTTP response parsing. Equal to HPE_OK. + */ +#define LLHTTP_CONTINUE_PARSING 0 + +/** + * @brief Return value for llhttp registered callback to signal halting * further execution. */ -#define HTTP_PARSER_STOP_PARSING 1 +#define LLHTTP_STOP_PARSING HPE_USER /** - * @brief Return value for http_parser registered callback to signal - * continuation of HTTP response parsing. + * @brief Return value for llhttp_t.on_headers_complete to signal + * that the HTTP response has no body and to halt further execution. */ -#define HTTP_PARSER_CONTINUE_PARSING 0 +#define LLHTTP_STOP_PARSING_NO_BODY 1 + +/** + * @brief Return value for llhttp_t.on_headers_complete to signal + * halting further execution. This is the same return value that + * indicates the HTTP response has no body, but unlike the -1 error + * code, gives consistent return values for llhttp_execute in both + * strict and non-strict modes. + */ +#define LLHTTP_STOP_PARSING_NO_HEADER 1 /** * @brief The minimum request-line in the headers has a possible one character @@ -261,16 +285,17 @@ typedef struct findHeaderContext */ typedef struct HTTPParsingContext { - http_parser httpParser; /**< Third-party http-parser context. */ - HTTPParsingState_t state; /**< The current state of the HTTP response parsed. */ - HTTPResponse_t * pResponse; /**< HTTP response associated with this parsing context. */ - uint8_t isHeadResponse; /**< HTTP response is for a HEAD request. */ - - const char * pBufferCur; /**< The current location of the parser in the response buffer. */ - const char * pLastHeaderField; /**< Holds the last part of the header field parsed. */ - size_t lastHeaderFieldLen; /**< The length of the last header field parsed. */ - const char * pLastHeaderValue; /**< Holds the last part of the header value parsed. */ - size_t lastHeaderValueLen; /**< The length of the last value field parsed. */ + llhttp_t llhttpParser; /**< Third-party llhttp context. */ + llhttp_settings_t llhttpSettings; /**< Third-party parser settings. */ + HTTPParsingState_t state; /**< The current state of the HTTP response parsed. */ + HTTPResponse_t * pResponse; /**< HTTP response associated with this parsing context. */ + uint8_t isHeadResponse; /**< HTTP response is for a HEAD request. */ + + const char * pBufferCur; /**< The current location of the parser in the response buffer. */ + const char * pLastHeaderField; /**< Holds the last part of the header field parsed. */ + size_t lastHeaderFieldLen; /**< The length of the last header field parsed. */ + const char * pLastHeaderValue; /**< Holds the last part of the header value parsed. */ + size_t lastHeaderValueLen; /**< The length of the last value field parsed. */ } HTTPParsingContext_t; /* *INDENT-OFF* */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 767b9363..d92a942e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -47,18 +47,18 @@ target_include_directories( coverity_analysis PUBLIC ${HTTP_INCLUDE_PUBLIC_DIRS} # ===================== Clone needed third-party libraries ====================== -# Define an http-paser resource path. -set( HTTP_PARSER_DIR ${MODULE_ROOT_DIR}/source/dependency/3rdparty/http_parser CACHE INTERNAL "http-parser library source directory." ) +# Define an llhttp paser resource path. +set( LLHTTP_DIR ${MODULE_ROOT_DIR}/source/dependency/3rdparty/llhttp CACHE INTERNAL "llhttp library source directory." ) -include( http_parser_build.cmake ) +include( llhttp_build.cmake ) -# Check if the http_parser source directory exists. -if( NOT EXISTS ${HTTP_PARSER_DIR}/http_parser.c ) - # Attempt to clone http_parser. +# Check if the llhttp_source directory exists. +if( NOT EXISTS ${LLHTTP_DIR}/src/llhttp.c ) + # Attempt to clone llhttp. if( ${BUILD_CLONE_SUBMODULES} ) - clone_http_parser() + clone_llhttp() else() - message( FATAL_ERROR "The required submodule http_parser does not exist. Either clone it manually, or set BUILD_CLONE_SUBMODULES to 1 to automatically clone it during build." ) + message( FATAL_ERROR "The required submodule llhttp does not exist. Either clone it manually, or set BUILD_CLONE_SUBMODULES to 1 to automatically clone it during build." ) endif() endif() diff --git a/test/http_parser_build.cmake b/test/http_parser_build.cmake deleted file mode 100644 index a5dd5609..00000000 --- a/test/http_parser_build.cmake +++ /dev/null @@ -1,19 +0,0 @@ -macro( clone_http_parser ) - find_package( Git REQUIRED ) - message( "Cloning submodule http_parser." ) - execute_process( COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive ${HTTP_PARSER_DIR} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE HTTP_PARSER_CLONE_RESULT ) - - if( NOT ${HTTP_PARSER_CLONE_RESULT} STREQUAL "0" ) - message( FATAL_ERROR "Failed to clone http_parser submodule." ) - endif() -endmacro() - -# http_parser library target. -add_library( http_parser - ${HTTP_PARSER_DIR}/http_parser.c ) - -# http_parser public include path. -target_include_directories( http_parser PUBLIC - ${HTTP_PARSER_DIR} ) diff --git a/test/llhttp_build.cmake b/test/llhttp_build.cmake new file mode 100644 index 00000000..3f04d0e4 --- /dev/null +++ b/test/llhttp_build.cmake @@ -0,0 +1,21 @@ +macro( clone_llhttp ) + find_package( Git REQUIRED ) + message( "Cloning submodule llhttp." ) + execute_process( COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive ${LLHTTP_DIR} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE LLHTTP_CLONE_RESULT ) + + if( NOT ${LLHTTP_CLONE_RESULT} STREQUAL "0" ) + message( FATAL_ERROR "Failed to clone llhttp submodule." ) + endif() +endmacro() + +# llhttp library target. +add_library( llhttp + ${LLHTTP_DIR}/src/api.c + ${LLHTTP_DIR}/src/http.c + ${LLHTTP_DIR}/src/llhttp.c ) + +# llhttp public include path. +target_include_directories( llhttp PUBLIC + ${LLHTTP_DIR}/include ) diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 838b891b..bf0baf5a 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -8,11 +8,11 @@ set(project_name "core_http") # list the files to mock here list(APPEND mock_list - ${HTTP_PARSER_DIR}/http_parser.h + ${LLHTTP_DIR}/include/llhttp.h ) # list the directories your mocks need list(APPEND mock_include_list - ${HTTP_PARSER_DIR} + ${LLHTTP_DIR}/include ) #list the definitions of your mocks to control what to be included list(APPEND mock_define_list @@ -41,7 +41,7 @@ list(APPEND test_include_directories # ============================= (end edit) =================================== -set(mock_name "http_parser_mock") +set(mock_name "llhttp_mock") set(real_name "${project_name}_real") create_mock_list(${mock_name} diff --git a/test/unit-test/core_http_send_utest.c b/test/unit-test/core_http_send_utest.c index 84e1e0bc..4ae0ae78 100644 --- a/test/unit-test/core_http_send_utest.c +++ b/test/unit-test/core_http_send_utest.c @@ -31,7 +31,7 @@ /* Private includes for internal macros. */ #include "core_http_client_private.h" -#include "mock_http_parser.h" +#include "mock_llhttp.h" /* Template HTTP request for a HEAD request. */ #define HTTP_TEST_REQUEST_HEAD_HEADERS \ @@ -217,12 +217,12 @@ static uint8_t recvCurrentCall = 0; /* The test sets this variable to indicate which call count count of transport * receive to return zero from. */ static uint8_t recvTimeoutCall = 0; -/* The count of times a mocked http_parser_execute callback has been invoked. */ +/* The count of times a mocked llhttp_execute callback has been invoked. */ static uint8_t httpParserExecuteCallCount; -/* The error to set to the parsing context when the http_parser_execute_error +/* The error to set to the parsing context when the llhttp_execute_error * callback is invoked. */ -static enum http_errno httpParsingErrno; +static enum llhttp_errno httpParsingErrno; /* Response shared among the tests. */ static HTTPResponse_t response = { 0 }; @@ -387,26 +387,47 @@ static int32_t transportRecvNetworkError( NetworkContext_t * pNetworkContext, return -1; } -/* Mocked http_parser_execute callback that sets the internal http_errno. */ -static size_t http_parser_execute_error( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +/* llhttp_init callback that sets the parser settings field. */ +static void llhttp_init_setup( llhttp_t * parser, + enum llhttp_type type, + const llhttp_settings_t * settings, + int cmock_num_calls ) +{ + ( void ) cmock_num_calls; + + parser->type = type; + /* Remove const qualifier. llhttp does this too. */ + parser->settings = ( llhttp_settings_t * ) settings; + parser->error = HPE_OK; +} + +/* llhttp_get_errno callback that returns the errno value. */ +llhttp_errno_t llhttp_get_errno_cb( const llhttp_t * parser, + int cmock_num_calls ) +{ + ( void ) cmock_num_calls; + + return parser->error; +} + +/* Mocked llhttp_execute callback that sets the internal errno. */ +static llhttp_errno_t llhttp_execute_error( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { - ( void ) pSettings; ( void ) pData; ( void ) len; ( void ) cmock_num_calls; - pParser->http_errno = httpParsingErrno; - return 0; + pParser->error = httpParsingErrno; + return httpParsingErrno; } /* Mock helper that parses the status line starting from pNext. */ static void helper_parse_status_line( const char ** pNext, - http_parser * pParser, - const http_parser_settings * pSettings ) + llhttp_t * pParser, + const llhttp_settings_t * pSettings ) { const char * pReasonPhraseStart = NULL; size_t reasonPhraseStartLen = 0; @@ -432,8 +453,8 @@ static void helper_parse_status_line( const char ** pNext, /* Mock helper that parses all of the headers starting from pNext. */ static void helper_parse_headers( const char ** pNext, - http_parser * pParser, - const http_parser_settings * pSettings ) + llhttp_t * pParser, + const llhttp_settings_t * pSettings ) { const char * pHeaderFieldStart = NULL; size_t headerFieldLen = 0; @@ -461,8 +482,8 @@ static void helper_parse_headers( const char ** pNext, /* Mock helper that parses the end of the headers starting from pNext. pNext * will point to the start of the body after this is finished. */ static void helper_parse_headers_finish( const char ** pNext, - http_parser * pParser, - const http_parser_settings * pSettings, + llhttp_t * pParser, + const llhttp_settings_t * pSettings, uint8_t * isHeadResponse ) { uint8_t isHeadResponseReturned = 0; @@ -491,8 +512,8 @@ static void helper_parse_headers_finish( const char ** pNext, /* Mock helper that parses the response body starting from pNext. */ static void helper_parse_body( const char ** pNext, - http_parser * pParser, - const http_parser_settings * pSettings, + llhttp_t * pParser, + const llhttp_settings_t * pSettings, uint8_t isHeadResponse, const char * pData, size_t len ) @@ -513,17 +534,17 @@ static void helper_parse_body( const char ** pNext, } } -/* Mocked http_parser_execute callback that expects a whole response to be in +/* Mocked llhttp_execute callback that expects a whole response to be in * the given data to parse. */ -static size_t http_parser_execute_whole_response( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +static llhttp_errno_t llhttp_execute_whole_response( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { ( void ) cmock_num_calls; const char * pNext = pData; uint8_t isHeadResponse = 0; + llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings; pSettings->on_message_begin( pParser ); @@ -535,24 +556,24 @@ static size_t http_parser_execute_whole_response( http_parser * pParser, pSettings->on_message_complete( pParser ); httpParserExecuteCallCount++; - return len; + return HPE_OK; } -/* Mocked http_parser_execute callback that will be called the first time on the +/* Mocked llhttp_execute callback that will be called the first time on the * response message up to the middle of the first header field, then the second * time on the response message from the middle of the first header field to the * end. */ -static size_t http_parser_execute_partial_header_field( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +static llhttp_errno_t llhttp_execute_partial_header_field( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { ( void ) cmock_num_calls; const char * pNext = pData; uint8_t isHeadResponse = 0; const char * pHeaderFieldStart = NULL; size_t headerFieldLen = 0; + llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings; if( httpParserExecuteCallCount == 0 ) { @@ -567,12 +588,12 @@ static size_t http_parser_execute_partial_header_field( http_parser * pParser, } else { - /* For testing of invoking http_parser_execute() with a parsing length + /* For testing of invoking llhttp_execute() with a parsing length * of zero, when data had been previously parsed. */ if( len == 0 ) { - pParser->http_errno = HPE_INVALID_EOF_STATE; - return 0; + pParser->error = HPE_INVALID_EOF_STATE; + return HPE_INVALID_EOF_STATE; } helper_parse_headers( &pNext, pParser, pSettings ); @@ -582,18 +603,17 @@ static size_t http_parser_execute_partial_header_field( http_parser * pParser, } httpParserExecuteCallCount++; - return len; + return HPE_OK; } -/* Mocked http_parser_execute callback that will be called the first time on the +/* Mocked llhttp_execute callback that will be called the first time on the * response message up to the middle of the first header value, then the second * time on the response message from the middle of the first header value to the * end. */ -static size_t http_parser_execute_partial_header_value( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +static llhttp_errno_t llhttp_execute_partial_header_value( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { ( void ) cmock_num_calls; @@ -603,6 +623,7 @@ static size_t http_parser_execute_partial_header_value( http_parser * pParser, size_t headerFieldLen = 0; const char * pHeaderValueStart = NULL; size_t headerValueLen = 0; + llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings; if( httpParserExecuteCallCount == 0 ) { @@ -625,7 +646,7 @@ static size_t http_parser_execute_partial_header_value( http_parser * pParser, } else { - /* In this second call to http_parser_execute mock, pData now starts + /* In this second call to llhttp_execute mock, pData now starts * at the partial header value. */ pHeaderValueStart = pNext; pNext = strstr( pNext, HTTP_HEADER_LINE_SEPARATOR ); @@ -642,21 +663,21 @@ static size_t http_parser_execute_partial_header_value( http_parser * pParser, } httpParserExecuteCallCount++; - return len; + return HPE_OK; } -/* Mocked http_parser_execute callback that will be called the first time on the +/* Mocked llhttp_execute callback that will be called the first time on the * response message up to the middle of the body, then the second time on the * response message from the middle of the body to the end. */ -static size_t http_parser_execute_partial_body( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +static llhttp_errno_t llhttp_execute_partial_body( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { ( void ) cmock_num_calls; const char * pNext = pData; + llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings; if( httpParserExecuteCallCount == 0 ) { @@ -676,16 +697,15 @@ static size_t http_parser_execute_partial_body( http_parser * pParser, } httpParserExecuteCallCount++; - return len; + return HPE_OK; } -/* Mocked http_parser_execute callback that will be on a response of type +/* Mocked llhttp_execute callback that will be on a response of type * transfer-encoding chunked. */ -static size_t http_parser_execute_chunked_body( http_parser * pParser, - const http_parser_settings * pSettings, - const char * pData, - size_t len, - int cmock_num_calls ) +static llhttp_errno_t llhttp_execute_chunked_body( llhttp_t * pParser, + const char * pData, + size_t len, + int cmock_num_calls ) { ( void ) cmock_num_calls; @@ -694,6 +714,7 @@ static size_t http_parser_execute_chunked_body( http_parser * pParser, const char * pBody = NULL; size_t bodyLen = 0; const char * pChunkHeader = NULL; + llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings; pSettings->on_message_begin( pParser ); @@ -725,7 +746,7 @@ static size_t http_parser_execute_chunked_body( http_parser * pParser, pSettings->on_message_complete( pParser ); httpParserExecuteCallCount++; - return len; + return HPE_OK; } /* ============================ UNITY FIXTURES ============================== */ @@ -765,10 +786,12 @@ void setUp( void ) response.pHeaderParsingCallback = &headerParsingCallback; /* Ignore third-party init functions that return void. */ - http_parser_init_Ignore(); - http_parser_settings_init_Ignore(); - http_parser_set_max_header_size_Ignore(); - http_errno_description_IgnoreAndReturn( "Dummy unit test print." ); + llhttp_init_Ignore(); + llhttp_init_Stub( llhttp_init_setup ); + llhttp_get_errno_Stub( llhttp_get_errno_cb ); + llhttp_settings_init_Ignore(); + llhttp_errno_name_IgnoreAndReturn( "Dummy" ); + llhttp_get_error_reason_IgnoreAndReturn( "Dummy unit test print." ); } /* ======================== Testing HTTPClient_Send ========================= */ @@ -779,7 +802,7 @@ void test_HTTPClient_Send_HEAD_request_parse_whole_response( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); returnStatus = HTTPClient_Send( &transportInterface, &requestHeaders, @@ -808,7 +831,7 @@ void test_HTTPClient_Send_PUT_request_parse_whole_response( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); checkContentLength = 1; memcpy( requestHeaders.pBuffer, @@ -847,7 +870,7 @@ void test_HTTPClient_Send_GET_request_parse_whole_response( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); memcpy( requestHeaders.pBuffer, HTTP_TEST_REQUEST_GET_HEADERS, @@ -883,7 +906,7 @@ void test_HTTPClient_Send_no_response_headers( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); pNetworkData = ( uint8_t * ) HTTP_TEST_RESPONSE_NO_HEADERS; networkDataLen = HTTP_TEST_RESPONSE_NO_HEADERS_LENGTH; @@ -916,7 +939,7 @@ void test_HTTPClient_Send_parse_partial_header_field( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_partial_header_field ); + llhttp_execute_Stub( llhttp_execute_partial_header_field ); firstPartBytes = HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_FIELD_LENGTH; returnStatus = HTTPClient_Send( &transportInterface, @@ -947,7 +970,7 @@ void test_HTTPClient_Send_parse_partial_header_value( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_partial_header_value ); + llhttp_execute_Stub( llhttp_execute_partial_header_value ); firstPartBytes = HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH; returnStatus = HTTPClient_Send( &transportInterface, @@ -978,7 +1001,7 @@ void test_HTTPClient_Send_parse_partial_body( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_partial_body ); + llhttp_execute_Stub( llhttp_execute_partial_body ); memcpy( requestHeaders.pBuffer, HTTP_TEST_REQUEST_GET_HEADERS, @@ -1013,7 +1036,7 @@ void test_HTTPClient_Send_parse_chunked_body( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_chunked_body ); + llhttp_execute_Stub( llhttp_execute_chunked_body ); memcpy( requestHeaders.pBuffer, HTTP_TEST_REQUEST_PUT_HEADERS, @@ -1048,7 +1071,7 @@ void test_HTTPClient_Send_timeout_recv_immediate( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_ExpectAnyArgsAndReturn( 0 ); + llhttp_execute_ExpectAnyArgsAndReturn( HPE_OK ); /* Return a zero on the first call. */ recvTimeoutCall = 1; @@ -1069,8 +1092,9 @@ void test_HTTPClient_Send_timeout_partial_response( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_partial_header_field ); - http_errno_description_IgnoreAndReturn( "Dummy unit test print." ); + llhttp_execute_Stub( llhttp_execute_partial_header_field ); + llhttp_errno_name_IgnoreAndReturn( "Dummy" ); + llhttp_get_error_reason_IgnoreAndReturn( "Dummy unit test print." ); firstPartBytes = HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH; /* Return a zero on the second transport receive call. */ @@ -1093,8 +1117,9 @@ void test_HTTPClient_Send_timeout_recv_retry( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); - http_errno_description_IgnoreAndReturn( "Dummy unit test print." ); + llhttp_execute_Stub( llhttp_execute_whole_response ); + llhttp_errno_name_IgnoreAndReturn( "Dummy" ); + llhttp_get_error_reason_IgnoreAndReturn( "Dummy unit test print." ); /* Set the optional time keeping function to retry the receive when zero * data is read from the network. */ @@ -1123,8 +1148,9 @@ void test_HTTPClient_Send_response_larger_than_buffer( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_partial_body ); - http_errno_description_IgnoreAndReturn( "Dummy unit test print." ); + llhttp_execute_Stub( llhttp_execute_partial_body ); + llhttp_errno_name_IgnoreAndReturn( "Dummy" ); + llhttp_get_error_reason_IgnoreAndReturn( "Dummy unit test print." ); requestHeaders.pBuffer = ( uint8_t * ) ( HTTP_TEST_REQUEST_GET_HEADERS ); requestHeaders.bufferLen = HTTP_TEST_REQUEST_GET_HEADERS_LENGTH; @@ -1218,7 +1244,7 @@ void test_HTTPClient_Send_timeout_send_retry( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); response.getTime = getTestTime; /* An zero is returned from the transport send on the first call. */ @@ -1241,7 +1267,7 @@ void test_HTTPClient_Send_timeout_send_retry_fail( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); /* By default a HEAD request is ready to be sent. */ transportInterface.send = transportSendSuccess; @@ -1269,7 +1295,7 @@ void test_HTTPClient_Send_less_bytes_request_headers( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); transportInterface.send = transportSendSuccess; /* Send the data partially in the first call to the transport send. */ @@ -1309,7 +1335,7 @@ void test_HTTPClient_Send_less_bytes_request_body( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_execute_Stub( http_parser_execute_whole_response ); + llhttp_execute_Stub( llhttp_execute_whole_response ); transportInterface.send = transportSendSuccess; @@ -1351,7 +1377,7 @@ void test_HTTPClient_Send_network_error_response( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_init_Ignore(); + llhttp_init_Ignore(); transportInterface.recv = transportRecvNetworkError; returnStatus = HTTPClient_Send( &transportInterface, @@ -1594,19 +1620,11 @@ void test_HTTPClient_Send_parsing_errors( void ) { HTTPStatus_t returnStatus = HTTPSuccess; - http_parser_init_Ignore(); - http_parser_settings_init_Ignore(); - http_parser_execute_Stub( http_parser_execute_error ); - http_errno_description_IgnoreAndReturn( "Dummy unit test print." ); - - httpParsingErrno = HPE_HEADER_OVERFLOW; - returnStatus = HTTPClient_Send( &transportInterface, - &requestHeaders, - NULL, - 0, - &response, - 0 ); - TEST_ASSERT_EQUAL( HTTPSecurityAlertResponseHeadersSizeLimitExceeded, returnStatus ); + llhttp_init_Ignore(); + llhttp_settings_init_Ignore(); + llhttp_execute_Stub( llhttp_execute_error ); + llhttp_errno_name_IgnoreAndReturn( "Dummy" ); + llhttp_get_error_reason_IgnoreAndReturn( "Dummy unit test print." ); httpParsingErrno = HPE_INVALID_CHUNK_SIZE; returnStatus = HTTPClient_Send( &transportInterface, @@ -1698,7 +1716,8 @@ void test_HTTPClient_Send_parsing_errors( void ) 0 ); TEST_ASSERT_EQUAL( HTTPSecurityAlertInvalidContentLength, returnStatus ); - httpParsingErrno = HPE_UNKNOWN; + /* Use -1 to indicate an unknown error. */ + httpParsingErrno = -1; returnStatus = HTTPClient_Send( &transportInterface, &requestHeaders, NULL, diff --git a/test/unit-test/core_http_utest.c b/test/unit-test/core_http_utest.c index fc928327..781f6820 100644 --- a/test/unit-test/core_http_utest.c +++ b/test/unit-test/core_http_utest.c @@ -30,8 +30,8 @@ /* Private includes for internal macros. */ #include "core_http_client_private.h" -/* Include mock implementation of http-parser dependency. */ -#include "mock_http_parser.h" +/* Include mock implementation of llhttp dependency. */ +#include "mock_llhttp.h" /* Default size for request buffer. */ #define HTTP_TEST_BUFFER_SIZE ( 100 ) @@ -175,8 +175,8 @@ static const size_t otherHeaderFieldInRespLoc = 98; static const size_t otherHeaderFieldInRespLen = sizeof( "header_not_in_buffer" ) - 1U; static const size_t headerValInRespLoc = 58; static const size_t headerValInRespLen = sizeof( "test-value1" ) - 1U; -static http_parser * pCapturedParser = NULL; -static http_parser_settings * pCapturedSettings = NULL; +static llhttp_t * pCapturedParser = NULL; +static llhttp_settings_t * pCapturedSettings = NULL; static const char * pExpectedBuffer = NULL; static size_t expectedBufferSize = 0U; static uint8_t invokeHeaderFieldCallback = 0U; @@ -192,12 +192,13 @@ static unsigned int parserErrNo = 0; /* ============================ Helper Functions ============================== */ /** - * @brief Callback that is passed to the mock of http_parse_init function + * @brief Callback that is passed to the mock of llhttp_init function * to set test expectations on input arguments sent by the HTTP API function under * test. */ -void parserInitExpectationCb( http_parser * parser, - enum http_parser_type type, +void parserInitExpectationCb( llhttp_t * parser, + enum llhttp_type type, + const llhttp_settings_t * settings, int cmock_num_calls ) { /* Disable unused parameter warning. */ @@ -205,16 +206,19 @@ void parserInitExpectationCb( http_parser * parser, TEST_ASSERT_NOT_NULL( parser ); pCapturedParser = parser; + TEST_ASSERT_EQUAL( pCapturedSettings, settings ); + /* Set the settings member expected by calls to llhttp_execute(). */ + parser->settings = ( llhttp_settings_t * ) settings; TEST_ASSERT_EQUAL( HTTP_RESPONSE, type ); } /** - * @brief Callback that is passed to the mock of http_parse_settings_init function + * @brief Callback that is passed to the mock of llhttp_settings_init function * to set test expectations on input arguments sent by the HTTP API function under * test. */ -void parserSettingsInitExpectationCb( http_parser_settings * settings, +void parserSettingsInitExpectationCb( llhttp_settings_t * settings, int cmock_num_calls ) { /* Disable unused parameter warning. */ @@ -225,53 +229,51 @@ void parserSettingsInitExpectationCb( http_parser_settings * settings, } /** - * @brief Callback that is passed to the mock of http_parse_execute() function + * @brief Callback that is passed to the mock of llhttp_execute() function * to set test expectations on input arguments, and inject behavior of invoking - * http-parser callbacks depending on test-case specific configuration of the + * llhttp callbacks depending on test-case specific configuration of the * function. */ -size_t parserExecuteExpectationsCb( http_parser * parser, - const http_parser_settings * settings, - const char * data, - size_t len, - int cmock_num_calls ) +llhttp_errno_t parserExecuteExpectationsCb( llhttp_t * parser, + const char * data, + size_t len, + int cmock_num_calls ) { /* Disable unused parameter warning. */ ( void ) cmock_num_calls; - TEST_ASSERT_NOT_NULL( settings ); TEST_ASSERT_EQUAL( pCapturedParser, parser ); TEST_ASSERT_NOT_NULL( parser ); - TEST_ASSERT_EQUAL( pCapturedSettings, settings ); + TEST_ASSERT_EQUAL( pCapturedSettings, parser->settings ); TEST_ASSERT_EQUAL( expectedBufferSize, len ); TEST_ASSERT_EQUAL( pExpectedBuffer, data ); if( invokeHeaderFieldCallback == 1U ) { - TEST_ASSERT_EQUAL( HTTP_PARSER_CONTINUE_PARSING, - settings->on_header_field( parser, - pFieldLocToReturn, - fieldLenToReturn ) ); + TEST_ASSERT_EQUAL( LLHTTP_CONTINUE_PARSING, + ( ( llhttp_settings_t * ) ( parser->settings ) )->on_header_field( parser, + pFieldLocToReturn, + fieldLenToReturn ) ); } if( invokeHeaderValueCallback == 1U ) { TEST_ASSERT_EQUAL( expectedValCbRetVal, - settings->on_header_value( parser, - pValueLocToReturn, - valueLenToReturn ) ); + ( ( llhttp_settings_t * ) ( parser->settings ) )->on_header_value( parser, + pValueLocToReturn, + valueLenToReturn ) ); } if( invokeHeaderCompleteCallback == 1U ) { - TEST_ASSERT_EQUAL( HTTP_PARSER_STOP_PARSING, - settings->on_headers_complete( parser ) ); + TEST_ASSERT_EQUAL( LLHTTP_STOP_PARSING_NO_HEADER, + ( ( llhttp_settings_t * ) ( parser->settings ) )->on_headers_complete( parser ) ); } /* Set the error value in the parser. */ - parser->http_errno = parserErrNo; - return len; + parser->error = parserErrNo; + return parserErrNo; } /** @@ -347,13 +349,14 @@ void setUp() testResponse.pBuffer = ( uint8_t * ) &pTestResponse[ 0 ]; testResponse.bufferLen = strlen( pTestResponse ); - /* Configure the http_parser mocks with their callbacks. */ - http_parser_init_AddCallback( parserInitExpectationCb ); - http_parser_settings_init_AddCallback( parserSettingsInitExpectationCb ); - http_parser_execute_AddCallback( parserExecuteExpectationsCb ); + /* Configure the llhttp mocks with their callbacks. */ + llhttp_settings_init_AddCallback( parserSettingsInitExpectationCb ); + llhttp_init_AddCallback( parserInitExpectationCb ); + llhttp_execute_AddCallback( parserExecuteExpectationsCb ); - /* Ignore the calls to http_errno_description. */ - http_errno_description_IgnoreAndReturn( "Mocked HTTP Parser Status" ); + /* Ignore the calls to llhttp_get_error_reason. */ + llhttp_errno_name_IgnoreAndReturn( "Mocked HTTP Parser Status" ); + llhttp_get_error_reason_IgnoreAndReturn( "Mocked HTTP Parser Status" ); } /* Called after each test method. */ @@ -1299,21 +1302,21 @@ void test_Http_ReadHeader_Invalid_Params( void ) */ void test_Http_ReadHeader_Header_Not_In_Response( void ) { - /* Add expectations for http_parser dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ + /* Configure the llhttp_execute mock. */ invokeHeaderFieldCallback = 1U; invokeHeaderValueCallback = 1U; pFieldLocToReturn = &pTestResponse[ headerFieldInRespLoc ]; fieldLenToReturn = headerFieldInRespLen; pValueLocToReturn = &pTestResponse[ headerValInRespLoc ]; valueLenToReturn = headerValInRespLen; - expectedValCbRetVal = HTTP_PARSER_CONTINUE_PARSING; + expectedValCbRetVal = LLHTTP_CONTINUE_PARSING; invokeHeaderCompleteCallback = 1U; parserErrNo = HPE_OK; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pTestResponse ) ); + llhttp_execute_ExpectAnyArgsAndReturn( HPE_OK ); /* Call the function under test. */ testResponse.bufferLen = strlen( pTestResponse ); @@ -1328,25 +1331,25 @@ void test_Http_ReadHeader_Header_Not_In_Response( void ) * Doing this allows us to take the branch where the actual contents * of the fields are compared rather than just the length. */ setUp(); - /* Add expectations for http_parser dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); /* Ensure that the header field does NOT match what we're searching. */ TEST_ASSERT_EQUAL( otherHeaderFieldInRespLen, HEADER_NOT_IN_BUFFER_LEN ); TEST_ASSERT_TRUE( memcmp( &pTestResponse[ otherHeaderFieldInRespLoc ], HEADER_NOT_IN_BUFFER, HEADER_NOT_IN_BUFFER_LEN ) != 0 ); - /* Configure the http_parser_execute mock. */ + /* Configure the llhttp_execute mock. */ invokeHeaderFieldCallback = 1U; invokeHeaderValueCallback = 1U; pFieldLocToReturn = &pTestResponse[ otherHeaderFieldInRespLoc ]; fieldLenToReturn = otherHeaderFieldInRespLen; pValueLocToReturn = &pTestResponse[ headerValInRespLoc ]; valueLenToReturn = headerValInRespLen; - expectedValCbRetVal = HTTP_PARSER_CONTINUE_PARSING; + expectedValCbRetVal = LLHTTP_CONTINUE_PARSING; invokeHeaderCompleteCallback = 1U; parserErrNo = HPE_OK; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pTestResponse ) ); + llhttp_execute_ExpectAnyArgsAndReturn( HPE_OK ); /* Call the function under test. */ testResponse.bufferLen = strlen( pTestResponse ); @@ -1369,17 +1372,17 @@ void test_Http_ReadHeader_Invalid_Response_Only_Header_Field_Found() "test-header0: test-value0\r\n" "test-header1:"; - /* Add expectations for http_parser init dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp init dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ + /* Configure the llhttp_execute mock. */ pExpectedBuffer = pResponseWithoutValue; expectedBufferSize = strlen( pResponseWithoutValue ); invokeHeaderFieldCallback = 1U; pFieldLocToReturn = &pTestResponse[ headerFieldInRespLoc ]; fieldLenToReturn = headerFieldInRespLen; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pResponseWithoutValue ) ); + llhttp_execute_ExpectAnyArgsAndReturn( HPE_OK ); /* Call the function under test. */ testResponse.pBuffer = ( uint8_t * ) &pResponseWithoutValue[ 0 ]; @@ -1405,15 +1408,16 @@ void test_Http_ReadHeader_Invalid_Response_No_Headers_Complete_Ending() tearDown(); - /* Add expectations for http_parser init dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp init dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ + /* Configure the llhttp_execute mock. */ pExpectedBuffer = &pResponseWithoutHeaders[ 0 ]; expectedBufferSize = strlen( pResponseWithoutHeaders ); - parserErrNo = HPE_UNKNOWN; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pResponseWithoutHeaders ) ); + /* Use -1 for an unknown error. */ + parserErrNo = -1; + llhttp_execute_ExpectAnyArgsAndReturn( -1 ); /* Call the function under test. */ testResponse.pBuffer = ( uint8_t * ) &pResponseWithoutHeaders[ 0 ]; testResponse.bufferLen = strlen( pResponseWithoutHeaders ); @@ -1426,26 +1430,26 @@ void test_Http_ReadHeader_Invalid_Response_No_Headers_Complete_Ending() } /** - * @brief Test when the header is present in response but http_parser_execute() - * does not set the expected errno value (of "CB_header_value") + * @brief Test when the header is present in response but llhttp_execute() + * does not set the expected errno value (of "HPE_USER") * due to an internal error. */ void test_Http_ReadHeader_With_HttpParser_Internal_Error() { - /* Add expectations for http_parser init dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp init dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ + /* Configure the llhttp_execute mock. */ invokeHeaderFieldCallback = 1U; invokeHeaderValueCallback = 1U; pFieldLocToReturn = &pTestResponse[ headerFieldInRespLoc ]; fieldLenToReturn = headerFieldInRespLen; pValueLocToReturn = &pTestResponse[ headerValInRespLoc ]; valueLenToReturn = headerValInRespLen; - expectedValCbRetVal = HTTP_PARSER_STOP_PARSING; - parserErrNo = HPE_CB_chunk_complete; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pTestResponse ) ); + expectedValCbRetVal = LLHTTP_STOP_PARSING; + parserErrNo = HPE_CB_CHUNK_COMPLETE; + llhttp_execute_ExpectAnyArgsAndReturn( HPE_CB_CHUNK_COMPLETE ); /* Call the function under test. */ retCode = HTTPClient_ReadHeader( &testResponse, @@ -1461,20 +1465,21 @@ void test_Http_ReadHeader_With_HttpParser_Internal_Error() */ void test_Http_ReadHeader_Happy_Path() { - /* Add expectations for http_parser init dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp init dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ - expectedValCbRetVal = HTTP_PARSER_STOP_PARSING; + /* Configure the llhttp_execute mock. */ + expectedValCbRetVal = LLHTTP_STOP_PARSING; pFieldLocToReturn = &pTestResponse[ headerFieldInRespLoc ]; fieldLenToReturn = headerFieldInRespLen; pValueLocToReturn = &pTestResponse[ headerValInRespLoc ]; valueLenToReturn = headerValInRespLen; invokeHeaderFieldCallback = 1U; invokeHeaderValueCallback = 1U; - parserErrNo = HPE_CB_header_value; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pTestResponse ) ); + /* Use HPE_USER to indicate the header value callback returns an error. */ + parserErrNo = HPE_USER; + llhttp_execute_ExpectAnyArgsAndReturn( HPE_USER ); /* Call the function under test. */ retCode = HTTPClient_ReadHeader( &testResponse, @@ -1493,22 +1498,23 @@ void test_Http_ReadHeader_Happy_Path() */ void test_Http_ReadHeader_EmptyHeaderValue() { - /* Add expectations for http_parser init dependencies. */ - http_parser_init_ExpectAnyArgs(); - http_parser_settings_init_ExpectAnyArgs(); + /* Add expectations for llhttp init dependencies. */ + llhttp_settings_init_ExpectAnyArgs(); + llhttp_init_ExpectAnyArgs(); - /* Configure the http_parser_execute mock. */ - expectedValCbRetVal = HTTP_PARSER_STOP_PARSING; + /* Configure the llhttp_execute mock. */ + expectedValCbRetVal = LLHTTP_STOP_PARSING; pFieldLocToReturn = &pTestResponseEmptyValue[ headerFieldInRespLoc ]; fieldLenToReturn = headerFieldInRespLen; /* Add two characters past the empty value to point to the next field. */ pValueLocToReturn = &pTestResponseEmptyValue[ headerValInRespLoc + HTTP_HEADER_LINE_SEPARATOR_LEN ]; - /* http-parser will pass in a value of zero for an empty value. */ + /* llhttp will pass in a value of zero for an empty value. */ valueLenToReturn = 0U; invokeHeaderFieldCallback = 1U; invokeHeaderValueCallback = 1U; - parserErrNo = HPE_CB_header_value; - http_parser_execute_ExpectAnyArgsAndReturn( strlen( pTestResponse ) ); + /* Use HPE_USER to indicate the header value callback returns an error. */ + parserErrNo = HPE_USER; + llhttp_execute_ExpectAnyArgsAndReturn( HPE_USER ); /* Call the function under test. */ retCode = HTTPClient_ReadHeader( &testResponse, diff --git a/tools/cmock/project.yml b/tools/cmock/project.yml index 8a90416b..1c01520f 100644 --- a/tools/cmock/project.yml +++ b/tools/cmock/project.yml @@ -24,3 +24,5 @@ :treat_externs: :exclude # Now the extern-ed functions will be mocked. :weak: __attribute__((weak)) :treat_externs: :include + :strippables: + - LLHTTP_EXPORT