Commit 4297a7b0 authored by Max Kellermann's avatar Max Kellermann

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.
parent 1bab6d0d
...@@ -46,7 +46,8 @@ ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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: ...@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment