From 668db4b3faf92a9dc96c2e3c62c716d649be0071 Mon Sep 17 00:00:00 2001 From: V <36897290+cryi@users.noreply.github.com> Date: Thu, 9 Nov 2023 00:01:47 +0100 Subject: [PATCH] Expose Internal Functions for Enhanced Usability (#167) This exposes the internal functions sendHttpHeaders, sendHttpData, and receiveAndParseHttpResponse. Exposing these functions aims to provide developers with more control over the HTTP communication process, enabling the support for chunked body reads and writes, streaming, and other advanced HTTP functionalities which are essential for building more sophisticated applications atop coreHTTP. --- docs/doxygen/include/size_table.md | 4 +- source/core_http_client.c | 120 ++++++++-------------- source/include/core_http_client.h | 91 ++++++++++++++++ test/cbmc/proofs/HTTPClient_Send/Makefile | 4 +- 4 files changed, 139 insertions(+), 80 deletions(-) diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md index fed45453..2170e484 100644 --- a/docs/doxygen/include/size_table.md +++ b/docs/doxygen/include/size_table.md @@ -10,7 +10,7 @@ core_http_client.c
3.2K
-
2.5K
+
2.6K
api.c (llhttp) @@ -30,6 +30,6 @@ Total estimates
24.0K
-
20.7K
+
20.8K
diff --git a/source/core_http_client.c b/source/core_http_client.c index f5693eac..995143ff 100644 --- a/source/core_http_client.c +++ b/source/core_http_client.c @@ -43,43 +43,18 @@ */ static uint32_t getZeroTimestampMs( void ); -/** - * @brief Send HTTP bytes over the transport send interface. - * - * @param[in] pTransport Transport interface. - * @param[in] getTimestampMs Function to retrieve a timestamp in milliseconds. - * @param[in] pData HTTP request data to send. - * @param[in] dataLen HTTP request data length. - * - * @return #HTTPSuccess if successful. If there was a network error or less - * bytes than what were specified were sent, then #HTTPNetworkError is - * returned. - */ -static HTTPStatus_t sendHttpData( const TransportInterface_t * pTransport, - HTTPClient_GetCurrentTimeFunc_t getTimestampMs, - const uint8_t * pData, - size_t dataLen ); -/** - * @brief Send the HTTP headers over the transport send interface. - * - * @param[in] pTransport Transport interface. - * @param[in] getTimestampMs Function to retrieve a timestamp in milliseconds. - * @param[in] pRequestHeaders Request headers to send, it includes the buffer - * and length. - * @param[in] reqBodyLen The length of the request body to be sent. This is - * used to generated a Content-Length header. - * @param[in] sendFlags Application provided flags to #HTTPClient_Send. - * - * @return #HTTPSuccess if successful. If there was a network error or less - * bytes than what were specified were sent, then #HTTPNetworkError is - * returned. - */ -static HTTPStatus_t sendHttpHeaders( const TransportInterface_t * pTransport, - HTTPClient_GetCurrentTimeFunc_t getTimestampMs, - HTTPRequestHeaders_t * pRequestHeaders, - size_t reqBodyLen, - uint32_t sendFlags ); +HTTPStatus_t HTTPClient_SendHttpData( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + const uint8_t * pData, + size_t dataLen ); + + +HTTPStatus_t HTTPClient_SendHttpHeaders( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + HTTPRequestHeaders_t * pRequestHeaders, + size_t reqBodyLen, + uint32_t sendFlags ); /** * @brief Adds the Content-Length header field and value to the @@ -187,20 +162,10 @@ static HTTPStatus_t getFinalResponseStatus( HTTPParsingState_t parsingState, size_t totalReceived, size_t responseBufferLen ); -/** - * @brief Receive the HTTP response from the network and parse it. - * - * @param[in] pTransport Transport interface. - * @param[in] pResponse Response message to receive data from the network. - * @param[in] pRequestHeaders Request headers for the corresponding HTTP request. - * - * @return Returns #HTTPSuccess if successful. #HTTPNetworkError for a transport - * receive error. Please see #parseHttpResponse and #getFinalResponseStatus for - * other statuses returned. - */ -static HTTPStatus_t receiveAndParseHttpResponse( const TransportInterface_t * pTransport, - HTTPResponse_t * pResponse, - const HTTPRequestHeaders_t * pRequestHeaders ); + +HTTPStatus_t HTTPClient_ReceiveAndParseHttpResponse( const TransportInterface_t * pTransport, + HTTPResponse_t * pResponse, + const HTTPRequestHeaders_t * pRequestHeaders ); /** * @brief Send the HTTP request over the network. @@ -212,7 +177,7 @@ static HTTPStatus_t receiveAndParseHttpResponse( const TransportInterface_t * pT * @param[in] reqBodyBufLen Length of the request body buffer. * @param[in] sendFlags Application provided flags to #HTTPClient_Send. * - * @return Returns #HTTPSuccess if successful. Please see #sendHttpHeaders and + * @return Returns #HTTPSuccess if successful. Please see #HTTPClient_SendHttpHeaders and * #sendHttpBody for other statuses returned. */ static HTTPStatus_t sendHttpRequest( const TransportInterface_t * pTransport, @@ -833,6 +798,9 @@ static int httpParserOnHeadersCompleteCallback( llhttp_t * pHttpParser ) assert( pResponse != NULL ); assert( pParsingContext->pBufferCur != NULL ); + /* Flag indicating that the headers have been completely signed - useful for libraries built on top of corehttp. */ + pResponse->areHeadersComplete = 1; + /* The current location to parse was updated in previous callbacks and MUST * always be within the response buffer. */ assert( pParsingContext->pBufferCur >= ( const char * ) ( pResponse->pBuffer ) ); @@ -1796,10 +1764,10 @@ HTTPStatus_t HTTPClient_AddRangeHeader( HTTPRequestHeaders_t * pRequestHeaders, /*-----------------------------------------------------------*/ -static HTTPStatus_t sendHttpData( const TransportInterface_t * pTransport, - HTTPClient_GetCurrentTimeFunc_t getTimestampMs, - const uint8_t * pData, - size_t dataLen ) +HTTPStatus_t HTTPClient_SendHttpData( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + const uint8_t * pData, + size_t dataLen ) { HTTPStatus_t returnStatus = HTTPSuccess; const uint8_t * pIndex = pData; @@ -1908,11 +1876,11 @@ static HTTPStatus_t addContentLengthHeader( HTTPRequestHeaders_t * pRequestHeade /*-----------------------------------------------------------*/ -static HTTPStatus_t sendHttpHeaders( const TransportInterface_t * pTransport, - HTTPClient_GetCurrentTimeFunc_t getTimestampMs, - HTTPRequestHeaders_t * pRequestHeaders, - size_t reqBodyLen, - uint32_t sendFlags ) +HTTPStatus_t HTTPClient_SendHttpHeaders( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + HTTPRequestHeaders_t * pRequestHeaders, + size_t reqBodyLen, + uint32_t sendFlags ) { HTTPStatus_t returnStatus = HTTPSuccess; uint8_t shouldSendContentLength = 0U; @@ -1935,10 +1903,10 @@ static HTTPStatus_t sendHttpHeaders( const TransportInterface_t * pTransport, { LogDebug( ( "Sending HTTP request headers: HeaderBytes=%lu", ( unsigned long ) ( pRequestHeaders->headersLen ) ) ); - returnStatus = sendHttpData( pTransport, - getTimestampMs, - pRequestHeaders->pBuffer, - pRequestHeaders->headersLen ); + returnStatus = HTTPClient_SendHttpData( pTransport, + getTimestampMs, + pRequestHeaders->pBuffer, + pRequestHeaders->headersLen ); } return returnStatus; @@ -1960,7 +1928,7 @@ static HTTPStatus_t sendHttpBody( const TransportInterface_t * pTransport, /* Send the request body. */ LogDebug( ( "Sending the HTTP request body: BodyBytes=%lu", ( unsigned long ) reqBodyBufLen ) ); - returnStatus = sendHttpData( pTransport, getTimestampMs, pRequestBodyBuf, reqBodyBufLen ); + returnStatus = HTTPClient_SendHttpData( pTransport, getTimestampMs, pRequestBodyBuf, reqBodyBufLen ); return returnStatus; } @@ -2014,9 +1982,9 @@ static HTTPStatus_t getFinalResponseStatus( HTTPParsingState_t parsingState, /*-----------------------------------------------------------*/ -static HTTPStatus_t receiveAndParseHttpResponse( const TransportInterface_t * pTransport, - HTTPResponse_t * pResponse, - const HTTPRequestHeaders_t * pRequestHeaders ) +HTTPStatus_t HTTPClient_ReceiveAndParseHttpResponse( const TransportInterface_t * pTransport, + HTTPResponse_t * pResponse, + const HTTPRequestHeaders_t * pRequestHeaders ) { HTTPStatus_t returnStatus = HTTPSuccess; size_t totalReceived = 0U; @@ -2149,11 +2117,11 @@ static HTTPStatus_t sendHttpRequest( const TransportInterface_t * pTransport, assert( getTimestampMs != NULL ); /* Send the headers, which are at one location in memory. */ - returnStatus = sendHttpHeaders( pTransport, - getTimestampMs, - pRequestHeaders, - reqBodyBufLen, - sendFlags ); + returnStatus = HTTPClient_SendHttpHeaders( pTransport, + getTimestampMs, + pRequestHeaders, + reqBodyBufLen, + sendFlags ); /* Send the body, which is at another location in memory. */ if( returnStatus == HTTPSuccess ) @@ -2269,9 +2237,9 @@ HTTPStatus_t HTTPClient_Send( const TransportInterface_t * pTransport, if( returnStatus == HTTPSuccess ) { - returnStatus = receiveAndParseHttpResponse( pTransport, - pResponse, - pRequestHeaders ); + returnStatus = HTTPClient_ReceiveAndParseHttpResponse( pTransport, + pResponse, + pRequestHeaders ); } return returnStatus; diff --git a/source/include/core_http_client.h b/source/include/core_http_client.h index 96f2d1bd..8dc2a098 100644 --- a/source/include/core_http_client.h +++ b/source/include/core_http_client.h @@ -528,6 +528,13 @@ typedef struct HTTPResponse */ size_t headerCount; + /** + * @brief Indicates whether the HTTP response headers have been fully received. + * + * This variable is set to 1 after all headers have been received and processed by #HTTPClient_Send. + */ + uint8_t areHeadersComplete; + /** * @brief Flags of useful headers found in the response. * @@ -730,6 +737,69 @@ HTTPStatus_t HTTPClient_AddRangeHeader( HTTPRequestHeaders_t * pRequestHeaders, int32_t rangeEnd ); /* @[declare_httpclient_addrangeheader] */ +/** + * @brief Send the request headers in @p pRequestHeaders over the transport. + * + * If #HTTP_SEND_DISABLE_CONTENT_LENGTH_FLAG is not set in parameter @p sendFlags, + * then the Content-Length to be sent to the server is automatically written to + * @p pRequestHeaders. The Content-Length will not be written when there is + * no request body. If there is not enough room in the buffer to write the + * Content-Length then #HTTPInsufficientMemory is returned. Please see + * #HTTP_MAX_CONTENT_LENGTH_HEADER_LENGTH for the maximum Content-Length header + * field and value that could be written to the buffer. + * + * The application should close the connection with the server if any of the + * following errors are returned: + * - #HTTPSecurityAlertExtraneousResponseData + * - #HTTPSecurityAlertInvalidChunkHeader + * - #HTTPSecurityAlertInvalidProtocolVersion + * - #HTTPSecurityAlertInvalidStatusCode + * - #HTTPSecurityAlertInvalidCharacter + * - #HTTPSecurityAlertInvalidContentLength + * + * + * @param[in] pTransport Transport interface, see #TransportInterface_t for + * more information. + * @param[in] getTimestampMs Function to retrieve a timestamp in milliseconds. + * @param[in] pRequestHeaders Request configuration containing the buffer of headers to + * send. + * @param[in] reqBodyLen The length of the request entity in bytes. + * @param[in] sendFlags Flags which modify the behavior of this function. Please see @ref + * http_send_flags for more information. + * + * @return #HTTPSuccess if successful. If there was a network error or less + * bytes than what were specified were sent, then #HTTPNetworkError is + * returned. + * + */ +/* @[declare_httpclient_sendhttpheaders] */ +HTTPStatus_t HTTPClient_SendHttpHeaders( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + HTTPRequestHeaders_t * pRequestHeaders, + size_t reqBodyLen, + uint32_t sendFlags ); +/* @[declare_httpclient_sendhttpheaders] */ + +/** + * @brief Send the request body in @p pRequestBodyBuf over the transport. + * + * @param[in] pTransport Transport interface, see #TransportInterface_t for + * more information. + * @param[in] getTimestampMs Function to retrieve a timestamp in milliseconds. + * @param[in] pData HTTP request data to send. + * @param[in] dataLen HTTP request data length. + * + * @return #HTTPSuccess if successful. If there was a network error or less + * bytes than what were specified were sent, then #HTTPNetworkError is + * returned. + */ +/* @[declare_httpclient_sendhttpdata] */ +HTTPStatus_t HTTPClient_SendHttpData( const TransportInterface_t * pTransport, + HTTPClient_GetCurrentTimeFunc_t getTimestampMs, + const uint8_t * pData, + size_t dataLen ); +/* @[declare_httpclient_sendhttpdata] */ + /** * @brief Send the request headers in #HTTPRequestHeaders_t.pBuffer and request * body in @p pRequestBodyBuf over the transport. The response is received in @@ -832,6 +902,27 @@ HTTPStatus_t HTTPClient_Send( const TransportInterface_t * pTransport, uint32_t sendFlags ); /* @[declare_httpclient_send] */ +/** + * @brief Receive the HTTP response from the network and parse it. + * + * @param[in] pTransport Transport interface, see #TransportInterface_t for more + * information. + * @param[in] pResponse The response message and some notable response parameters will be + * returned here on success. + * @param[in] pRequestHeaders Request configuration containing the buffer of headers to + * send. + * + * @return Returns #HTTPSuccess if successful. #HTTPNetworkError for a transport + * receive error. Please see #parseHttpResponse and #getFinalResponseStatus for + * other statuses returned. + * + */ +/* @[declare_httpclient_receiveandparsehttpresponse] */ +HTTPStatus_t HTTPClient_ReceiveAndParseHttpResponse( const TransportInterface_t * pTransport, + HTTPResponse_t * pResponse, + const HTTPRequestHeaders_t * pRequestHeaders ); +/* @[declare_httpclient_receiveandparsehttpresponse] */ + /** * @brief Read a header from a buffer containing a complete HTTP response. * This will return the location of the response header value in the diff --git a/test/cbmc/proofs/HTTPClient_Send/Makefile b/test/cbmc/proofs/HTTPClient_Send/Makefile index 931729ad..064330a3 100644 --- a/test/cbmc/proofs/HTTPClient_Send/Makefile +++ b/test/cbmc/proofs/HTTPClient_Send/Makefile @@ -57,8 +57,8 @@ REMOVE_FUNCTION_BODY += __CPROVER_file_local_core_http_client_c_httpHeaderStrncp # than the total possible iterations in the int32_t to ASCII converation. UNWINDSET += __CPROVER_file_local_core_http_client_c_convertInt32ToAscii.0:11 UNWINDSET += __CPROVER_file_local_core_http_client_c_convertInt32ToAscii.1:11 -UNWINDSET += __CPROVER_file_local_core_http_client_c_receiveAndParseHttpResponse.0:10 -UNWINDSET += __CPROVER_file_local_core_http_client_c_sendHttpData.0:10 +UNWINDSET += HTTPClient_ReceiveAndParseHttpResponse.0:10 +UNWINDSET += HTTPClient_SendHttpData.0:10 # strncmp is used to find if there exists "\r\n\r\n" at the end of the header # buffer. Therefore, we need to unwind strncmp to the length of "\r\n\r\n" + 1.