diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md
index 2170e484..4f22c250 100644
--- a/docs/doxygen/include/size_table.md
+++ b/docs/doxygen/include/size_table.md
@@ -9,7 +9,7 @@
core_http_client.c |
- 3.2K |
+ 3.3K |
2.6K |
@@ -29,7 +29,7 @@
Total estimates |
- 24.0K |
+ 24.1K |
20.8K |
diff --git a/source/core_http_client.c b/source/core_http_client.c
index 84c87b72..c4a4533a 100644
--- a/source/core_http_client.c
+++ b/source/core_http_client.c
@@ -912,6 +912,13 @@ static int httpParserOnHeadersCompleteCallback( llhttp_t * pHttpParser )
LogDebug( ( "Response parsing: Found the end of the headers." ) );
+ /* If there is HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG opt-in we should stop
+ * parsing here. */
+ if( ( pResponse->respOptionFlags & HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG ) != 0U )
+ {
+ shouldContinueParse = LLHTTP_PAUSE_PARSING;
+ }
+
return shouldContinueParse;
}
@@ -1145,6 +1152,11 @@ static HTTPStatus_t processLlhttpError( const llhttp_t * pHttpParser )
returnStatus = HTTPSecurityAlertInvalidContentLength;
break;
+ case HPE_PAUSED:
+ LogInfo( ( "User intervention: Parsing stopped by user." ) );
+ returnStatus = HTTPParserPaused;
+ break;
+
/* All other error cases cannot be triggered and indicate an error in the
* third-party parsing library if found. */
default:
@@ -1223,9 +1235,17 @@ static HTTPStatus_t parseHttpResponse( HTTPParsingContext_t * pParsingContext,
llhttp_resume_after_upgrade( &( pParsingContext->llhttpParser ) );
}
- /* The next location to parse will always be after what has already
- * been parsed. */
- pParsingContext->pBufferCur = parsingStartLoc + parseLen;
+ if( eReturn == HPE_PAUSED )
+ {
+ /* The next location to parse is where the parser was paused. */
+ pParsingContext->pBufferCur = pParsingContext->llhttpParser.error_pos;
+ }
+ else
+ {
+ /* The next location to parse is after what has already been parsed. */
+ pParsingContext->pBufferCur = parsingStartLoc + parseLen;
+ }
+
returnStatus = processLlhttpError( &( pParsingContext->llhttpParser ) );
return returnStatus;
@@ -2129,6 +2149,17 @@ HTTPStatus_t HTTPClient_ReceiveAndParseHttpResponse( const TransportInterface_t
( totalReceived < pResponse->bufferLen ) ) ? 1U : 0U;
}
+ if( ( returnStatus == HTTPParserPaused ) &&
+ ( ( pResponse->respOptionFlags & HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG ) != 0U ) )
+ {
+ returnStatus = HTTPSuccess;
+
+ /* There may be dangling data if we parse with do not parse body flag.
+ * We expose this data through body to let the applications access it. */
+ pResponse->pBody = ( const uint8_t * ) parsingContext.pBufferCur;
+ pResponse->bodyLen = totalReceived - ( size_t ) ( ( ( uintptr_t ) pResponse->pBody ) - ( ( uintptr_t ) pResponse->pBuffer ) );
+ }
+
if( returnStatus == HTTPSuccess )
{
/* If there are errors in receiving from the network or during parsing,
@@ -2650,6 +2681,10 @@ const char * HTTPClient_strerror( HTTPStatus_t status )
str = "HTTPSecurityAlertInvalidContentLength";
break;
+ case HTTPParserPaused:
+ str = "HTTPParserPaused";
+ break;
+
case HTTPParserInternalError:
str = "HTTPParserInternalError";
break;
diff --git a/source/include/core_http_client.h b/source/include/core_http_client.h
index 8dc2a098..5239c3a1 100644
--- a/source/include/core_http_client.h
+++ b/source/include/core_http_client.h
@@ -153,6 +153,27 @@
*/
#define HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG 0x2U
+/**
+ * @defgroup http_response_option_flags HTTPResponse_t Flags
+ * @brief Flags for #HTTPResponse_t.respOptionFlags.
+ * These flags control the behavior of response parsing.
+ *
+ * Flags should be bitwise-ORed with each other to change the behavior of
+ * #HTTPClient_ReceiveAndParseHttpResponse and #HTTPClient_Send.
+ */
+
+/**
+ * @ingroup http_response_option_flags
+ * @brief Set this flag to indicate that the response body should not be parsed.
+ *
+ * Setting this will cause parser to stop after parsing the headers. Portion of
+ * the raw body may be available in #HTTPResponse_t.pBody and
+ * #HTTPResponse_t.bodyLen.
+ *
+ * This flag is valid only for #HTTPResponse_t.respOptionFlags.
+ */
+#define HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG 0x1U
+
/**
* @ingroup http_constants
* @brief Flag that represents End of File byte in the range specification of
@@ -165,7 +186,7 @@
* - When the requested range is for the last N bytes of the file.
* In both cases, this value should be used for the "rangeEnd" parameter.
*/
-#define HTTP_RANGE_REQUEST_END_OF_FILE -1
+#define HTTP_RANGE_REQUEST_END_OF_FILE -1
/**
* @ingroup http_enum_types
@@ -291,6 +312,17 @@ typedef enum HTTPStatus
*/
HTTPSecurityAlertInvalidContentLength,
+ /**
+ * @brief Represents the paused state of the HTTP parser.
+ *
+ * This state indicates that the parser has encountered a pause condition
+ * and is waiting for further input.
+ *
+ * @see HTTPClient_Send
+ * @see HTTPClient_ReceiveAndParseHttpResponse
+ */
+ HTTPParserPaused,
+
/**
* @brief An error occurred in the third-party parsing library.
*
@@ -535,6 +567,13 @@ typedef struct HTTPResponse
*/
uint8_t areHeadersComplete;
+ /**
+ * @brief Flags to control the behavior of response parsing.
+ *
+ * Please see @ref http_response_option_flags for more information.
+ */
+ uint32_t respOptionFlags;
+
/**
* @brief Flags of useful headers found in the response.
*
diff --git a/source/include/core_http_client_private.h b/source/include/core_http_client_private.h
index 680e1894..3ded00cc 100644
--- a/source/include/core_http_client_private.h
+++ b/source/include/core_http_client_private.h
@@ -166,6 +166,12 @@
*/
#define LLHTTP_STOP_PARSING HPE_USER
+/**
+ * @brief Return value for llhttp registered callback to signal to pause
+ * further execution.
+ */
+#define LLHTTP_PAUSE_PARSING HPE_PAUSED
+
/**
* @brief Return value for llhttp_t.on_headers_complete to signal
* that the HTTP response has no body and to halt further execution.
diff --git a/test/cbmc/stubs/HTTPClient_Send_llhttp_execute.c b/test/cbmc/stubs/HTTPClient_Send_llhttp_execute.c
index 8ceb72dd..74b29bb7 100644
--- a/test/cbmc/stubs/HTTPClient_Send_llhttp_execute.c
+++ b/test/cbmc/stubs/HTTPClient_Send_llhttp_execute.c
@@ -80,5 +80,35 @@ llhttp_errno_t llhttp_execute( llhttp_t * parser,
pParsingContext->lastHeaderValueLen = 0U;
}
+ /* The body pointer is set by the httpParserOnBodyCallback. But since we are
+ * removing that from CBMC proof execution, the body has to be set here. */
+ size_t bodyOffset;
+
+ if( pParsingContext->pResponse->bufferLen == 0U )
+ {
+ bodyOffset = 0U;
+ }
+ else
+ {
+ /* Body offset can be anything as long as it doesn't exceed the buffer length
+ * and the length of the current data packet. */
+ __CPROVER_assume( bodyOffset < pParsingContext->pResponse->bufferLen );
+ __CPROVER_assume( bodyOffset < len );
+ }
+
+ pParsingContext->pResponse->pBody = pParsingContext->pBufferCur + bodyOffset;
+
+ if( parser->error == HPE_PAUSED )
+ {
+ /* When the parser is paused ensure that the error_pos member points to
+ * a valid location in the response buffer. */
+ size_t errorPosOffset;
+
+ __CPROVER_assume( errorPosOffset < pParsingContext->pResponse->bufferLen );
+ __CPROVER_assume( errorPosOffset < len );
+
+ parser->error_pos = pParsingContext->pResponse->pBuffer + errorPosOffset;
+ }
+
return parser->error;
}
diff --git a/test/unit-test/core_http_utest.c b/test/unit-test/core_http_utest.c
index 04809c2f..ec22b1c2 100644
--- a/test/unit-test/core_http_utest.c
+++ b/test/unit-test/core_http_utest.c
@@ -1662,6 +1662,10 @@ void test_HTTPClient_strerror( void )
str = HTTPClient_strerror( status );
TEST_ASSERT_EQUAL_STRING( "HTTPSecurityAlertInvalidContentLength", str );
+ status = HTTPParserPaused;
+ str = HTTPClient_strerror( status );
+ TEST_ASSERT_EQUAL_STRING( "HTTPParserPaused", str );
+
status = HTTPParserInternalError;
str = HTTPClient_strerror( status );
TEST_ASSERT_EQUAL_STRING( "HTTPParserInternalError", str );