diff --git a/Build/libHttpClient.GDK/libHttpClient.GDK.def b/Build/libHttpClient.GDK/libHttpClient.GDK.def index 0d2d7a96..112095fa 100644 --- a/Build/libHttpClient.GDK/libHttpClient.GDK.def +++ b/Build/libHttpClient.GDK/libHttpClient.GDK.def @@ -57,6 +57,12 @@ EXPORTS HCHttpCallResponseSetResponseBodyBytes HCHttpCallResponseSetResponseBodyWriteFunction HCHttpCallResponseSetStatusCode + HCHttpCallRequestSetDynamicSize + HCHttpCallRequestAddDynamicBytesWritten + HCHttpCallRequestGetDynamicBytesWritten + HCHttpCallResponseSetDynamicSize + HCHttpCallResponseAddDynamicBytesWritten + HCHttpCallResponseGetDynamicBytesWritten HCHttpCallSetContext HCHttpCallSetTracing HCHttpDisableAssertsForSSLValidationInDevSandboxes diff --git a/Build/libHttpClient.Win32/libHttpClient.Win32.def b/Build/libHttpClient.Win32/libHttpClient.Win32.def index cff0099e..a82f615b 100644 --- a/Build/libHttpClient.Win32/libHttpClient.Win32.def +++ b/Build/libHttpClient.Win32/libHttpClient.Win32.def @@ -57,6 +57,12 @@ EXPORTS HCHttpCallResponseSetResponseBodyBytes HCHttpCallResponseSetResponseBodyWriteFunction HCHttpCallResponseSetStatusCode + HCHttpCallRequestSetDynamicSize + HCHttpCallRequestAddDynamicBytesWritten + HCHttpCallRequestGetDynamicBytesWritten + HCHttpCallResponseSetDynamicSize + HCHttpCallResponseAddDynamicBytesWritten + HCHttpCallResponseGetDynamicBytesWritten HCHttpCallSetContext HCHttpCallSetTracing HCInitialize diff --git a/Include/httpClient/httpClient.h b/Include/httpClient/httpClient.h index 09391b50..62888402 100644 --- a/Include/httpClient/httpClient.h +++ b/Include/httpClient/httpClient.h @@ -328,6 +328,29 @@ STDAPI HCHttpCallRequestSetUrl( _In_z_ const char* url ) noexcept; +/// +/// Mark the HTTP call as having a dynamic size request body for progress reporting. Report the bytes written in the custom callback using +/// HCHttpCallRequestAddDynamicBytesWritten. +/// +/// The handle of the HTTP call. +/// The length in bytes to use for reporting. +/// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. +STDAPI HCHttpCallRequestSetDynamicSize( + _In_ HCCallHandle call, + _In_ uint64_t dynamicBodySize +) noexcept; + +/// +/// Report a custom amount of bytes written when the body size is dynamic. HCHttpCallRequestSetDynamicSize must be set. +/// +/// The handle of the HTTP call. +/// The number of bytes written. +/// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. +STDAPI HCHttpCallRequestAddDynamicBytesWritten( + _In_ HCCallHandle call, + _In_ uint64_t bytesWritten +) noexcept; + /// /// Set the request body bytes of the HTTP call. This API operation is mutually exclusive with /// HCHttpCallRequestSetRequestBodyReadFunction and will result in any custom read callbacks that were diff --git a/Include/httpClient/httpProvider.h b/Include/httpClient/httpProvider.h index a2ba1034..f6ae9924 100644 --- a/Include/httpClient/httpProvider.h +++ b/Include/httpClient/httpProvider.h @@ -150,6 +150,19 @@ STDAPI HCHttpCallRequestGetRequestBodyReadFunction( _Out_ void** context ) noexcept; +/// +/// Get the custom bytes written and total body size for an HTTP call with a dynamic body size. Use standard request body info if dynamicBodySize is 0. +/// +/// The handle of the HTTP call. +/// The custom size to use for reporting +/// The custom bytes written to use for reporting +/// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. +STDAPI HCHttpCallRequestGetDynamicBytesWritten( + _In_ HCCallHandle call, + _Out_ size_t* dynamicBodySize, + _Out_ size_t* dynamicBodyBytesWritten +) noexcept; + /// /// Get the function used by the HTTP call to get progress updates /// @@ -316,10 +329,46 @@ STDAPI HCHttpCallResponseGetResponseBodyWriteFunction( _Out_ void** context ) noexcept; +/// +/// Get the custom bytes written and total body size for an HTTP call with a dynamic body size. Use standard response body info if dynamicBodySize is 0. +/// +/// The handle of the HTTP call. +/// The custom size to use for reporting +/// The custom bytes written to use for reporting +/// +STDAPI HCHttpCallResponseGetDynamicBytesWritten( + _In_ HCCallHandle call, + _Out_ size_t* dynamicBodySize, + _Out_ size_t* dynamicBodyBytesWritten +) noexcept; + ///////////////////////////////////////////////////////////////////////////////////////// // HttpCallResponse Set APIs // +/// +/// Mark the HTTP call as having a dynamic size response body for progress reporting. Report the bytes written in the custom callback using +/// HCHttpCallResponseAddDynamicBytesWritten. +/// +/// The handle of the HTTP call. +/// The length in bytes of the body being set. +/// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. +STDAPI HCHttpCallResponseSetDynamicSize( + _In_ HCCallHandle call, + _In_ uint64_t dynamicBodySize +) noexcept; + +/// +/// Report a custom amount of bytes written when the body size is dynamic. HCHttpCallRequestSetDynamicSize must be set. +/// +/// The handle of the HTTP call. +/// The number of bytes written. +/// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. +STDAPI HCHttpCallResponseAddDynamicBytesWritten( + _In_ HCCallHandle call, + _In_ uint64_t bytesWritten +) noexcept; + /// /// Set the response body byte buffer of the HTTP call. If a custom write callback was previously set /// on this call handle using HCHttpCallResponseSetResponseBodyWriteFunction, this operation will fail diff --git a/Source/HTTP/Curl/CurlEasyRequest.cpp b/Source/HTTP/Curl/CurlEasyRequest.cpp index c41facc9..ddc75b34 100644 --- a/Source/HTTP/Curl/CurlEasyRequest.cpp +++ b/Source/HTTP/Curl/CurlEasyRequest.cpp @@ -267,12 +267,24 @@ size_t CurlEasyRequest::ReadCallback(char* buffer, size_t size, size_t nitems, v return 1; } + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallRequestGetDynamicBytesWritten(request->m_hcCallHandle, &dynamicBodySize, &dynamicBodyBytesWritten); + + uint64_t reportBytesWritten = request->m_requestBodyOffset; + uint64_t reportTotalBytes = bodySize; + if (dynamicBodySize > 0) + { + reportBytesWritten = dynamicBodyBytesWritten; + reportTotalBytes = dynamicBodySize; + } + ReportProgress( request->m_hcCallHandle, uploadProgressReportFunction, request->m_hcCallHandle->uploadMinimumProgressReportInterval, - request->m_requestBodyOffset, - bodySize, + reportBytesWritten, + reportTotalBytes, uploadProgressReportCallbackContext, &request->m_hcCallHandle->uploadLastProgressReport ); @@ -405,12 +417,24 @@ size_t CurlEasyRequest::WriteDataCallback(char* buffer, size_t size, size_t nmem return 1; } + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallResponseGetDynamicBytesWritten(request->m_hcCallHandle, &dynamicBodySize, &dynamicBodyBytesWritten); + + uint64_t reportBytesWritten = request->m_responseBodySize - request->m_responseBodyRemainingToRead; + uint64_t reportTotalBytes = request->m_responseBodySize; + if (dynamicBodySize > 0) + { + reportBytesWritten = dynamicBodyBytesWritten; + reportTotalBytes = dynamicBodySize; + } + ReportProgress( request->m_hcCallHandle, downloadProgressReportFunction, request->m_hcCallHandle->downloadMinimumProgressReportInterval, - request->m_responseBodySize - request->m_responseBodyRemainingToRead, - request->m_responseBodySize, + reportBytesWritten, + reportTotalBytes, downloadProgressReportCallbackContext, &request->m_hcCallHandle->downloadLastProgressReport ); @@ -486,12 +510,24 @@ int CurlEasyRequest::ProgressReportCallback(void* context, curl_off_t dltotal, c return 1; } + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallRequestGetDynamicBytesWritten(request->m_hcCallHandle, &dynamicBodySize, &dynamicBodyBytesWritten); + + uint64_t reportBytesWritten = ulnow; + uint64_t reportTotalBytes = ultotal; + if (dynamicBodySize > 0) + { + reportBytesWritten = dynamicBodyBytesWritten; + reportTotalBytes = dynamicBodySize; + } + ReportProgress( request->m_hcCallHandle, uploadProgressReportFunction, request->m_hcCallHandle->uploadMinimumProgressReportInterval, - ulnow, - ultotal, + reportBytesWritten, + reportTotalBytes, uploadProgressReportCallbackContext, &request->m_hcCallHandle->uploadLastProgressReport ); @@ -509,12 +545,24 @@ int CurlEasyRequest::ProgressReportCallback(void* context, curl_off_t dltotal, c return 1; } + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallResponseGetDynamicBytesWritten(request->m_hcCallHandle, &dynamicBodySize, &dynamicBodyBytesWritten); + + uint64_t reportBytesWritten = ulnow; + uint64_t reportTotalBytes = ultotal; + if (dynamicBodySize > 0) + { + reportBytesWritten = dynamicBodyBytesWritten; + reportTotalBytes = dynamicBodySize; + } + ReportProgress( request->m_hcCallHandle, downloadProgressReportFunction, request->m_hcCallHandle->downloadMinimumProgressReportInterval, - dlnow, - dltotal, + reportBytesWritten, + reportTotalBytes, downloadProgressReportCallbackContext, &request->m_hcCallHandle->downloadLastProgressReport); } diff --git a/Source/HTTP/WinHttp/winhttp_connection.cpp b/Source/HTTP/WinHttp/winhttp_connection.cpp index f8703196..f5e360ab 100644 --- a/Source/HTTP/WinHttp/winhttp_connection.cpp +++ b/Source/HTTP/WinHttp/winhttp_connection.cpp @@ -525,13 +525,39 @@ void WinHttpConnection::ReportProgress(_In_ WinHttpConnection* pRequestContext, if (isUpload) { - current = pRequestContext->m_requestBodyOffset; + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallRequestGetDynamicBytesWritten(pRequestContext->m_call, &dynamicBodySize, &dynamicBodyBytesWritten); + + if (dynamicBodySize > 0) + { + bodySize = dynamicBodySize; + current = dynamicBodyBytesWritten; + } + else + { + current = bodySize - pRequestContext->m_responseBodyRemainingToRead; + } + lastProgressReport = pRequestContext->m_call->uploadLastProgressReport; minimumProgressReportIntervalInMs = static_cast(pRequestContext->m_call->uploadMinimumProgressReportInterval * 1000); } else { - current = bodySize - pRequestContext->m_responseBodyRemainingToRead; + uint64_t dynamicBodySize{}; + uint64_t dynamicBodyBytesWritten{}; + HCHttpCallResponseGetDynamicBytesWritten(pRequestContext->m_call, &dynamicBodySize, &dynamicBodyBytesWritten); + + if (dynamicBodySize > 0) + { + bodySize = dynamicBodySize; + current = dynamicBodyBytesWritten; + } + else + { + current = bodySize - pRequestContext->m_responseBodyRemainingToRead; + } + lastProgressReport = pRequestContext->m_call->downloadLastProgressReport; minimumProgressReportIntervalInMs = static_cast(pRequestContext->m_call->downloadMinimumProgressReportInterval * 1000); } diff --git a/Source/HTTP/httpcall.h b/Source/HTTP/httpcall.h index 7883ab0b..5fdf2a07 100644 --- a/Source/HTTP/httpcall.h +++ b/Source/HTTP/httpcall.h @@ -95,6 +95,11 @@ struct HC_CALL std::chrono::steady_clock::time_point downloadLastProgressReport{}; void* downloadProgressReportFunctionContext{ nullptr }; + // Dynamic size properties + uint64_t dynamicRequestBodySize{ 0 }; + uint64_t dynamicRequestBodyBytesWritten{ 0 }; + uint64_t dynamicResponseBodySize{ 0 }; + uint64_t dynamicResponseBodyBytesWritten{ 0 }; static HRESULT CALLBACK ReadRequestBody( _In_ HCCallHandle call, diff --git a/Source/HTTP/httpcall_request.cpp b/Source/HTTP/httpcall_request.cpp index ed05b7b0..d8a2d5c2 100644 --- a/Source/HTTP/httpcall_request.cpp +++ b/Source/HTTP/httpcall_request.cpp @@ -65,6 +65,64 @@ try } CATCH_RETURN() +STDAPI +HCHttpCallRequestSetDynamicSize( + _In_ HCCallHandle call, + _In_ uint64_t dynamicBodySize +) noexcept +try +{ + if (call == nullptr || dynamicBodySize == 0) + { + return E_INVALIDARG; + } + RETURN_IF_PERFORM_CALLED(call); + + auto httpSingleton = get_http_singleton(); + if (nullptr == httpSingleton) + { + return E_HC_NOT_INITIALISED; + } + + call->dynamicRequestBodySize = dynamicBodySize; + + if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallRequestSetDynamicSize [ID %llu]: dynamicBodySize=%llu", TO_ULL(call->id), TO_ULL(dynamicBodySize)); } + + return S_OK; +} +CATCH_RETURN() + +STDAPI +HCHttpCallRequestAddDynamicBytesWritten( + _In_ HCCallHandle call, + _In_ uint64_t bytesWritten +) noexcept +try +{ + if (call == nullptr) + { + return E_INVALIDARG; + } + + if (call->dynamicRequestBodySize == 0) + { + return E_UNEXPECTED; + } + + call->dynamicRequestBodyBytesWritten += bytesWritten; + + if (call->dynamicRequestBodyBytesWritten > call->dynamicRequestBodySize) + { + HC_TRACE_WARNING(HTTPCLIENT, "HCHttpCallRequestAddDynamicBytesWritten [ID %llu]: Reducing excessive bytesWritten=%llu to dynamicBodySize=%llu", TO_ULL(call->id), TO_ULL(call->dynamicRequestBodyBytesWritten), TO_ULL(call->dynamicRequestBodySize)); + call->dynamicRequestBodyBytesWritten = call->dynamicRequestBodySize; + } + + if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallRequestAddDynamicBytesWritten [ID %llu]: bytesWritten=%llu", TO_ULL(call->id), TO_ULL(bytesWritten)); } + + return S_OK; +} +CATCH_RETURN() + STDAPI HCHttpCallRequestSetRequestBodyBytes( _In_ HCCallHandle call, @@ -319,6 +377,24 @@ try } CATCH_RETURN() +STDAPI HCHttpCallRequestGetDynamicBytesWritten( + _In_ HCCallHandle call, + _Out_ size_t* dynamicBodySize, + _Out_ size_t* dynamicBodyBytesWritten +) noexcept +try +{ + if (call == nullptr || dynamicBodySize == nullptr || dynamicBodyBytesWritten == nullptr) + { + return E_INVALIDARG; + } + + *dynamicBodySize = call->dynamicRequestBodySize; + *dynamicBodyBytesWritten = call->dynamicRequestBodyBytesWritten; + + return S_OK; +} +CATCH_RETURN() STDAPI HCHttpCallRequestGetProgressReportFunction( diff --git a/Source/HTTP/httpcall_response.cpp b/Source/HTTP/httpcall_response.cpp index 2c69dd6a..6d41661a 100644 --- a/Source/HTTP/httpcall_response.cpp +++ b/Source/HTTP/httpcall_response.cpp @@ -27,6 +27,26 @@ try } CATCH_RETURN() +STDAPI HCHttpCallResponseGetDynamicBytesWritten( + _In_ HCCallHandle call, + _Out_ size_t* dynamicBodySize, + _Out_ size_t* dynamicBodyBytesWritten +) noexcept +try +{ + if (call == nullptr || dynamicBodySize == nullptr || dynamicBodyBytesWritten == nullptr) + { + return E_INVALIDARG; + } + + *dynamicBodySize = call->dynamicResponseBodySize; + *dynamicBodyBytesWritten = call->dynamicResponseBodyBytesWritten; + + return S_OK; + +} +CATCH_RETURN() + STDAPI HCHttpCallResponseSetResponseBodyWriteFunction( _In_ HCCallHandle call, @@ -141,6 +161,64 @@ try } CATCH_RETURN() +STDAPI +HCHttpCallResponseSetDynamicSize( + _In_ HCCallHandle call, + _In_ uint64_t dynamicBodySize +) noexcept +try +{ + if (call == nullptr || dynamicBodySize == 0) + { + return E_INVALIDARG; + } + RETURN_IF_PERFORM_CALLED(call); + + auto httpSingleton = get_http_singleton(); + if (nullptr == httpSingleton) + { + return E_HC_NOT_INITIALISED; + } + + call->dynamicResponseBodySize = dynamicBodySize; + + if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallResponseSetDynamicSize [ID %llu]: dynamicBodySize=%llu", TO_ULL(call->id), TO_ULL(dynamicBodySize)); } + + return S_OK; +} +CATCH_RETURN() + +STDAPI +HCHttpCallResponseAddDynamicBytesWritten( + _In_ HCCallHandle call, + _In_ uint64_t bytesWritten +) noexcept +try +{ + if (call == nullptr) + { + return E_INVALIDARG; + } + + if (call->dynamicResponseBodySize == 0) + { + return E_UNEXPECTED; + } + + call->dynamicResponseBodyBytesWritten += bytesWritten; + + if (call->dynamicResponseBodyBytesWritten > call->dynamicResponseBodySize) + { + HC_TRACE_WARNING(HTTPCLIENT, "HCHttpCallResponseAddDynamicBytesWritten [ID %llu]: Reducing excessive bytesWritten=%llu to dynamicBodySize=%llu", TO_ULL(call->id), TO_ULL(call->dynamicResponseBodyBytesWritten), TO_ULL(call->dynamicResponseBodySize)); + call->dynamicResponseBodyBytesWritten = call->dynamicResponseBodySize; + } + + if (call->traceCall) { HC_TRACE_INFORMATION(HTTPCLIENT, "HCHttpCallResponseAddDynamicBytesWritten [ID %llu]: bytesWritten=%llu", TO_ULL(call->id), TO_ULL(bytesWritten)); } + + return S_OK; +} +CATCH_RETURN() + STDAPI HCHttpCallResponseSetResponseBodyBytes( _In_ HCCallHandle call,