Track active transfers in wxWebSessionCURL class

At various points in a transfer being managed by the wxWebSessionCURL
class we need to perform operations on a wxWebRequestCURL object.
However it’s possible for the request object to be deleted while the
transfer is in progress.

To ensure that the request objects are valid, keep track of the request
objects with a hash map. Objects are added to the map when a transfer is
started and removed when the transfer is complete or in the request’s
destructor.
This commit is contained in:
New Pagodi
2021-02-07 00:48:02 -06:00
parent b0b53e8db3
commit 774a752bed
2 changed files with 48 additions and 9 deletions

View File

@@ -17,6 +17,7 @@
#include "wx/thread.h" #include "wx/thread.h"
#include "wx/vector.h" #include "wx/vector.h"
#include "wx/timer.h" #include "wx/timer.h"
#include "wx/hashmap.h"
#include "curl/curl.h" #include "curl/curl.h"
@@ -151,6 +152,8 @@ public:
void CancelRequest(wxWebRequestCURL* request); void CancelRequest(wxWebRequestCURL* request);
void RequestHasTerminated(wxWebRequestCURL* request);
static bool CurlRuntimeAtLeastVersion(unsigned int, unsigned int, static bool CurlRuntimeAtLeastVersion(unsigned int, unsigned int,
unsigned int); unsigned int);
@@ -165,6 +168,11 @@ private:
void ProcessSocketPollerResult(wxThreadEvent&); void ProcessSocketPollerResult(wxThreadEvent&);
void CheckForCompletedTransfers(); void CheckForCompletedTransfers();
WX_DECLARE_HASH_MAP(CURL*, wxWebRequestCURL*, wxPointerHash, \
wxPointerEqual, TransferSet);
TransferSet m_activeTransfers;
SocketPoller* m_socketPoller; SocketPoller* m_socketPoller;
wxTimer m_timeoutTimer; wxTimer m_timeoutTimer;
CURLM* m_handle; CURLM* m_handle;

View File

@@ -240,8 +240,6 @@ wxWebRequestCURL::wxWebRequestCURL(wxWebSession & session,
// Set error buffer to get more detailed CURL status // Set error buffer to get more detailed CURL status
m_errorBuffer[0] = '\0'; m_errorBuffer[0] = '\0';
curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer); curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
// Set this request in the private pointer
curl_easy_setopt(m_handle, CURLOPT_PRIVATE, static_cast<void*>(this));
// Set URL to handle: note that we must use wxURI to escape characters not // Set URL to handle: note that we must use wxURI to escape characters not
// allowed in the URLs correctly (URL API is only available in libcurl // allowed in the URLs correctly (URL API is only available in libcurl
// since the relatively recent v7.62.0, so we don't want to rely on it). // since the relatively recent v7.62.0, so we don't want to rely on it).
@@ -266,8 +264,7 @@ wxWebRequestCURL::wxWebRequestCURL(wxWebSession & session,
wxWebRequestCURL::~wxWebRequestCURL() wxWebRequestCURL::~wxWebRequestCURL()
{ {
DestroyHeaderList(); DestroyHeaderList();
m_sessionImpl.RequestHasTerminated(this);
curl_easy_cleanup(m_handle);
} }
void wxWebRequestCURL::Start() void wxWebRequestCURL::Start()
@@ -1004,6 +1001,7 @@ bool wxWebSessionCURL::StartRequest(wxWebRequestCURL & request)
if ( code == CURLM_OK ) if ( code == CURLM_OK )
{ {
request.SetState(wxWebRequest::State_Active); request.SetState(wxWebRequest::State_Active);
m_activeTransfers[curl] = &request;
// Report a timeout to curl to initiate this transfer. // Report a timeout to curl to initiate this transfer.
int runningHandles; int runningHandles;
@@ -1020,10 +1018,37 @@ bool wxWebSessionCURL::StartRequest(wxWebRequestCURL & request)
void wxWebSessionCURL::CancelRequest(wxWebRequestCURL* request) void wxWebSessionCURL::CancelRequest(wxWebRequestCURL* request)
{ {
curl_multi_remove_handle(m_handle, request->GetHandle()); CURL* curl = request->GetHandle();
TransferSet::iterator it = m_activeTransfers.find(curl);
if ( it != m_activeTransfers.end() )
{
m_activeTransfers.erase(it);
}
curl_multi_remove_handle(m_handle, curl);
request->SetState(wxWebRequest::State_Cancelled); request->SetState(wxWebRequest::State_Cancelled);
} }
void wxWebSessionCURL::RequestHasTerminated(wxWebRequestCURL* request)
{
CURL* curl = request->GetHandle();
TransferSet::iterator it = m_activeTransfers.find(curl);
if ( it != m_activeTransfers.end() )
{
// The transfer the CURL handle is performing is still in progress, but
// the web request object it belongs to is being deleted. Since the
// next step will call curl_easy_cleanup and any calls on the CURL
// handle after cleanup are illegal, remove it from the CURLM
// multihandle now.
curl_multi_remove_handle(m_handle, curl);
m_activeTransfers.erase(it);
}
curl_easy_cleanup(curl);
}
wxVersionInfo wxWebSessionCURL::GetLibraryVersionInfo() wxVersionInfo wxWebSessionCURL::GetLibraryVersionInfo()
{ {
const curl_version_info_data* vi = curl_version_info(CURLVERSION_NOW); const curl_version_info_data* vi = curl_version_info(CURLVERSION_NOW);
@@ -1196,10 +1221,16 @@ void wxWebSessionCURL::CheckForCompletedTransfers()
{ {
if ( msg->msg == CURLMSG_DONE ) if ( msg->msg == CURLMSG_DONE )
{ {
wxWebRequestCURL* request; CURL* curl = msg->easy_handle;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &request); TransferSet::iterator it = m_activeTransfers.find(curl);
curl_multi_remove_handle(m_handle, msg->easy_handle);
if ( it != m_activeTransfers.end() )
{
wxWebRequestCURL* request = it->second;
curl_multi_remove_handle(m_handle, curl);
request->HandleCompletion(); request->HandleCompletion();
m_activeTransfers.erase(it);
}
} }
} }
} }