Skip to content

Commit fcfea62

Browse files
SergeyRyabininSergey Ryabinin
authored andcommitted
Basic error handling for curl multi
1 parent 7e85c62 commit fcfea62

File tree

3 files changed

+58
-21
lines changed

3 files changed

+58
-21
lines changed

src/aws-cpp-sdk-core/include/aws/core/http/curl-multi/CurlEasyHandleContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ namespace Http
6969
/* SDK side */
7070
const CurlMultiHttpClient* m_client = nullptr;
7171
ExecutionPolicy m_execPolicy;
72-
std::function<void()> m_onCurlDoneFn;
72+
std::function<void()> m_onCurlDoneFn; // called in a main multi loop => must be lightweight.
7373
Aws::Utils::DateTime startTransmissionTime;
7474

7575
/* Curl calls the SDK back */

src/aws-cpp-sdk-core/include/aws/core/http/curl-multi/CurlMultiHttpClient.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <aws/core/client/ClientConfiguration.h>
1313
#include <aws/core/utils/memory/stl/AWSString.h>
1414
#include <aws/core/utils/memory/stl/AWSQueue.h>
15+
#include <aws/core/utils/memory/stl/AWSSet.h>
1516
#include <atomic>
1617
#include <thread>
1718
#include <queue>
@@ -86,10 +87,11 @@ class AWS_CORE_API CurlMultiHttpClient: public HttpClient
8687
virtual void OverrideOptionsOnConnectionHandle(CURL*) const {}
8788

8889
private:
89-
void SubmitTask(Curl::CurlEasyHandleContext* pEasyHandleCtx) const;
90+
bool SubmitTask(Curl::CurlEasyHandleContext* pEasyHandleCtx) const;
9091

9192
static std::shared_ptr<HttpResponse> HandleCurlResponse(Curl::CurlEasyHandleContext* pEasyHandleCtx);
9293
static void CurlMultiPerformThread(CurlMultiHttpClient* pClient);
94+
void CurlMultiPerformReset();
9395

9496
std::thread m_multiHandleThread;
9597
std::atomic<bool> m_isRunning;
@@ -99,7 +101,8 @@ class AWS_CORE_API CurlMultiHttpClient: public HttpClient
99101
// mutable std::mutex m_tasksMutex;
100102
mutable std::atomic<size_t> m_tasksQueued;
101103
mutable std::mutex m_tasksMutex;
102-
mutable Aws::UnorderedMap<CURL*, std::shared_ptr<Curl::CurlEasyHandleContext>> m_tasks;
104+
// used to track tasks sent to multi handle, for handling multi perform errors
105+
mutable Aws::UnorderedSet<Curl::CurlEasyHandleContext*> m_multiTasks;
103106

104107
CurlMultiHttpClientConfig m_config;
105108

src/aws-cpp-sdk-core/source/http/curl-multi/CurlMultiHttpClient.cpp

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,24 @@ int CurlMultiDebugCallback(CURL *handle, curl_infotype type, char *data, size_t
274274
return 0;
275275
}
276276

277+
void CurlMultiHttpClient::CurlMultiPerformReset()
278+
{
279+
// TODO: refactor
280+
std::unique_lock<std::mutex> lockGuard(m_signalMutex);
281+
std::unique_lock<std::mutex> lock(m_tasksMutex);
282+
AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Removing all easy handles from a multi handle and triggering callbacks.");
283+
284+
for(Curl::CurlEasyHandleContext* handleCtx : m_multiTasks)
285+
{
286+
curl_multi_remove_handle(m_curlMultiHandleContainer.AccessCurlMultiHandle(), handleCtx->m_curlEasyHandle);
287+
handleCtx->curlResultMsg = nullptr;
288+
handleCtx->curlResult = static_cast<CURLcode>(-1);
289+
handleCtx->m_onCurlDoneFn();
290+
m_multiTasks.erase(handleCtx);
291+
}
292+
m_multiTasks.clear();
293+
}
294+
277295
void CurlMultiHttpClient::CurlMultiPerformThread(CurlMultiHttpClient* pClient)
278296
{
279297
assert(pClient && pClient->m_curlMultiHandleContainer.AccessCurlMultiHandle());
@@ -297,12 +315,18 @@ void CurlMultiHttpClient::CurlMultiPerformThread(CurlMultiHttpClient* pClient)
297315
}
298316
if(!pClient->m_isRunning.load())
299317
{
300-
break;
318+
break;
301319
}
302320

303321
pClient->m_tasksQueued = 0;
304322

305323
CURLMcode mc = curl_multi_perform(multi_handle, &stillRunning);
324+
if(mc != CURLM_OK)
325+
{
326+
AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Curl curl_multi_perform returned error code " << mc
327+
<< " resetting multi handle.");
328+
pClient->CurlMultiPerformReset();
329+
}
306330
int msgQueue = 0;
307331
do {
308332

@@ -326,12 +350,14 @@ void CurlMultiHttpClient::CurlMultiPerformThread(CurlMultiHttpClient* pClient)
326350
} while(msgQueue > 0);
327351

328352
if(!mc && stillRunning)
329-
/* wait for activity, timeout or "nothing" */
330-
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
331-
332-
if(mc) {
333-
fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mc);
334-
break;
353+
{
354+
/* wait for activity, timeout or "nothing" */
355+
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
356+
if(mc)
357+
{
358+
AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Curl curl_multi_poll returned error code " << mc);
359+
break;
360+
}
335361
}
336362
};
337363
}
@@ -705,6 +731,10 @@ std::shared_ptr<HttpResponse> CurlMultiHttpClient::HandleCurlResponse(Curl::Curl
705731
}
706732

707733
curl_multi_remove_handle(client->m_curlMultiHandleContainer.AccessCurlMultiHandle(), connectionHandle);
734+
{
735+
std::unique_lock<std::mutex> lock(client->m_tasksMutex);
736+
client->m_multiTasks.erase(pEasyHandleCtx);
737+
}
708738

709739
std::shared_ptr<HttpResponse> res = std::move(pEasyHandleCtx->writeContext.m_response);
710740
pEasyHandleCtx->writeContext.m_response.reset();
@@ -721,19 +751,27 @@ std::shared_ptr<HttpResponse> CurlMultiHttpClient::HandleCurlResponse(Curl::Curl
721751
return res;
722752
}
723753

724-
void CurlMultiHttpClient::SubmitTask(Curl::CurlEasyHandleContext* pEasyHandleCtx) const
754+
bool CurlMultiHttpClient::SubmitTask(Curl::CurlEasyHandleContext* pEasyHandleCtx) const
725755
{
726756
assert(pEasyHandleCtx);
727-
AWS_UNREFERENCED_PARAM(pEasyHandleCtx);
757+
CURLMcode curlMultiResponseCode = curl_multi_add_handle(m_curlMultiHandleContainer.AccessCurlMultiHandle(),
758+
pEasyHandleCtx->m_curlEasyHandle);
759+
if (CURLM_OK != curlMultiResponseCode)
760+
{
761+
return false;
762+
}
763+
728764
{
729765
std::unique_lock<std::mutex> lock(m_tasksMutex);
766+
m_multiTasks.insert(pEasyHandleCtx);
730767
m_tasksQueued++;
731768
}
732769
{
733770
std::unique_lock<std::mutex> lockGuard(m_signalMutex);
734771
m_signalRunning.notify_one();
735772
curl_multi_wakeup(m_curlMultiHandleContainer.AccessCurlMultiHandle());
736773
}
774+
return true;
737775
}
738776

739777
// Blocking
@@ -774,18 +812,14 @@ std::shared_ptr<HttpResponse> CurlMultiHttpClient::MakeRequest(const std::shared
774812

775813
easyHandleContext->startTransmissionTime = Aws::Utils::DateTime::Now();
776814

777-
CURLMcode curlMultiResponseCode = curl_multi_add_handle(m_curlMultiHandleContainer.AccessCurlMultiHandle(),
778-
easyHandleContext->m_curlEasyHandle);
779-
if (CURLM_OK != curlMultiResponseCode)
815+
if(!SubmitTask(easyHandleContext))
780816
{
781-
response->SetClientErrorType(CoreErrors::NETWORK_CONNECTION);
782-
response->SetClientErrorMessage("Failed to add curl_easy_handle to curl_multi_handle.");
783-
AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Failed to add curl_easy_handle to curl_multi_handle.");
784-
return response;
817+
response->SetClientErrorType(CoreErrors::NETWORK_CONNECTION);
818+
response->SetClientErrorMessage("Failed to add curl_easy_handle to curl_multi_handle.");
819+
AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Failed to add curl_easy_handle to curl_multi_handle.");
820+
return response;
785821
}
786822

787-
SubmitTask(easyHandleContext);
788-
789823
// Task submitted, wait for it's completion
790824
std::unique_lock<std::mutex> lockGuard(taskMutex);
791825
signal.wait(lockGuard,

0 commit comments

Comments
 (0)