handle server chunked response without number (#881)

This commit is contained in:
Victor Vazquez 2020-10-30 01:11:41 -07:00 committed by GitHub
parent 1fa314f19e
commit 75c3f85ca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 4 deletions

View File

@ -446,7 +446,18 @@ void CurlSession::ParseChunkSize(Context const& context)
if (i > 1 && this->m_readBuffer[index] == '\n')
{
// get chunk size. Chunk size comes in Hex value
this->m_chunkSize = static_cast<int64_t>(std::stoull(strChunkSize, nullptr, 16));
try
{
this->m_chunkSize = static_cast<int64_t>(std::stoull(strChunkSize, nullptr, 16));
}
catch (std::invalid_argument& err)
{
(void)err;
// Server can return something like `\n\r\n` for a chunk of zero length data. This is
// allowed by RFC. `stoull` will throw invalid_argument if there is not at least one hex
// digit to be parsed. For those cases, we consider the response as zero-lenght.
this->m_chunkSize = 0;
}
if (this->m_chunkSize == 0)
{ // Response with no content. end of chunk

View File

@ -3,10 +3,10 @@
/**
* @file
* @brief The base class for testing a curl session.
*
* @brief The base class for testing a curl session.
*
* @remark The curl connection mock is defined here.
*
*
*/
#include <gmock/gmock.h>
@ -39,6 +39,12 @@ namespace Azure { namespace Core { namespace Test {
SendBuffer,
(Context const& context, uint8_t const* buffer, size_t bufferSize),
(override));
/* This is a way to test we are calling the destructor
* Adding an extra mock method that is called from the destructor
*/
MOCK_METHOD(void, DestructObj, ());
virtual ~MockCurlNetworkConnection() { DestructObj(); }
};
}}} // namespace Azure::Core::Test

View File

@ -9,6 +9,7 @@
using ::testing::_;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArrayArgument;
namespace Azure { namespace Core { namespace Test {
@ -41,4 +42,40 @@ namespace Azure { namespace Core { namespace Test {
EXPECT_NO_THROW(session->Perform(Azure::Core::GetApplicationContext()));
}
TEST_F(CurlSession, chunkResponseSizeZero)
{
// chunked response with no content and no size
std::string response("HTTP/1.1 200 Ok\r\ntransfer-encoding: chunked\r\n\r\n\n\r\n");
std::string host("sample-host");
// Can't mock the curMock directly from a unique ptr, heap allocate it first and then make a
// unique ptr for it
MockCurlNetworkConnection* curlMock = new MockCurlNetworkConnection();
EXPECT_CALL(*curlMock, SendBuffer(_, _, _)).WillOnce(Return(CURLE_OK));
EXPECT_CALL(*curlMock, ReadFromSocket(_, _, _))
.WillOnce(DoAll(
SetArrayArgument<1>(response.data(), response.data() + response.size()),
Return(response.size())));
EXPECT_CALL(*curlMock, GetHost()).WillRepeatedly(ReturnRef(host));
EXPECT_CALL(*curlMock, updateLastUsageTime());
EXPECT_CALL(*curlMock, DestructObj());
// Create the unique ptr to take care about memory free at the end
std::unique_ptr<MockCurlNetworkConnection> uniqueCurlMock(curlMock);
// Simulate a request to be sent
Azure::Core::Http::Url url("http://microsoft.com");
Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url);
{
// Create the session inside scope so it is released and the connection is moved to the pool
auto session
= std::make_unique<Azure::Core::Http::CurlSession>(request, std::move(uniqueCurlMock));
EXPECT_NO_THROW(session->Perform(Azure::Core::GetApplicationContext()));
}
// Clear the connections from the pool to invoke clean routine
Azure::Core::Http::CurlConnectionPool::ConnectionPoolIndex.clear();
}
}}} // namespace Azure::Core::Test