lib/curl/Request: move exception handling out of the WRITEFUNCTION
libcurl's WRITEFUNCTION is pretty fragile; if we destroy the CURL* instance or even unregister it using curl_multi_remove_handle(), libcurl will crash instantly. But still we need to be able to handle exceptions from inside the WRITEFUNCTION, and call CurlResponseHandler::OnError(), which may destroy the whole thing. As a workaround, I use DeferredMonitor to postpone the OnError() call into a stack frame which is allowed to destroy the request.
This commit is contained in:
parent
1bab6d0dd7
commit
4297a7b0a4
@ -46,7 +46,8 @@
|
|||||||
|
|
||||||
CurlRequest::CurlRequest(CurlGlobal &_global, const char *url,
|
CurlRequest::CurlRequest(CurlGlobal &_global, const char *url,
|
||||||
CurlResponseHandler &_handler)
|
CurlResponseHandler &_handler)
|
||||||
:global(_global), handler(_handler)
|
:DeferredMonitor(_global.GetEventLoop()),
|
||||||
|
global(_global), handler(_handler)
|
||||||
{
|
{
|
||||||
error_buffer[0] = 0;
|
error_buffer[0] = 0;
|
||||||
|
|
||||||
@ -226,8 +227,11 @@ CurlRequest::DataReceived(const void *ptr, size_t received_size)
|
|||||||
return CURL_WRITEFUNC_PAUSE;
|
return CURL_WRITEFUNC_PAUSE;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
state = State::CLOSED;
|
state = State::CLOSED;
|
||||||
handler.OnError(std::current_exception());
|
/* move the CurlResponseHandler::OnError() call into a
|
||||||
return 0;
|
"safe" stack frame */
|
||||||
|
postponed_error = std::current_exception();
|
||||||
|
DeferredMonitor::Schedule();
|
||||||
|
return CURL_WRITEFUNC_PAUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -243,3 +247,11 @@ CurlRequest::WriteFunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
|||||||
|
|
||||||
return c.DataReceived(ptr, size);
|
return c.DataReceived(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlRequest::RunDeferred()
|
||||||
|
{
|
||||||
|
assert(postponed_error);
|
||||||
|
|
||||||
|
handler.OnError(postponed_error);
|
||||||
|
}
|
||||||
|
@ -31,15 +31,17 @@
|
|||||||
#define CURL_REQUEST_HXX
|
#define CURL_REQUEST_HXX
|
||||||
|
|
||||||
#include "Easy.hxx"
|
#include "Easy.hxx"
|
||||||
|
#include "event/DeferredMonitor.hxx"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
struct StringView;
|
struct StringView;
|
||||||
class CurlGlobal;
|
class CurlGlobal;
|
||||||
class CurlResponseHandler;
|
class CurlResponseHandler;
|
||||||
|
|
||||||
class CurlRequest {
|
class CurlRequest final : DeferredMonitor {
|
||||||
CurlGlobal &global;
|
CurlGlobal &global;
|
||||||
|
|
||||||
CurlResponseHandler &handler;
|
CurlResponseHandler &handler;
|
||||||
@ -55,6 +57,16 @@ class CurlRequest {
|
|||||||
|
|
||||||
std::multimap<std::string, std::string> headers;
|
std::multimap<std::string, std::string> headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception caught by DataReceived(), which will be
|
||||||
|
* forwarded into a "safe" stack frame by
|
||||||
|
* DeferredMonitor::RunDeferred(). This works around the
|
||||||
|
* problem that libcurl crashes if you call
|
||||||
|
* curl_multi_remove_handle() from within the WRITEFUNCTION
|
||||||
|
* (i.e. DataReceived()).
|
||||||
|
*/
|
||||||
|
std::exception_ptr postponed_error;
|
||||||
|
|
||||||
/** error message provided by libcurl */
|
/** error message provided by libcurl */
|
||||||
char error_buffer[CURL_ERROR_SIZE];
|
char error_buffer[CURL_ERROR_SIZE];
|
||||||
|
|
||||||
@ -129,6 +141,9 @@ private:
|
|||||||
/** called by curl when new data is available */
|
/** called by curl when new data is available */
|
||||||
static size_t WriteFunction(void *ptr, size_t size, size_t nmemb,
|
static size_t WriteFunction(void *ptr, size_t size, size_t nmemb,
|
||||||
void *stream);
|
void *stream);
|
||||||
|
|
||||||
|
/* virtual methods from class DeferredMonitor */
|
||||||
|
void RunDeferred() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user