Allow reusing POST data when using wxWebRequestCURL

Set seek callback to allow libcurl to reuse the data being uploaded when
it has to redo the HTTP request, e.g. because of redirection after a 301
or resubmitting it with correct authentication data after a 401.

Add a test checking that this works correctly, although it has to be
disabled for WinHTTP for now as it doesn't handle 307/308 redirects
automatically (see #26046).
This commit is contained in:
Vadim Zeitlin
2025-12-22 13:37:23 +01:00
parent 1fd12d7bd6
commit c3cb1c1d1b
3 changed files with 67 additions and 5 deletions
+2 -1
View File
@@ -89,8 +89,9 @@ public:
wxString GetError() const;
// Method called from libcurl callback
// These functions implement libcurl callbacks.
size_t CURLOnRead(char* buffer, size_t size);
int CURLOnSeek(curl_off_t offset, int origin);
private:
// Common initialization for sync and async requests performed when the
+41 -4
View File
@@ -50,6 +50,10 @@
#define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
#endif
#if wxUSE_LOG_TRACE
constexpr const char* TRACE_CURL = "curl";
#endif
// Define libcurl timeout constants
static constexpr int LIBCURL_DEFAULT_CONNECT_TIMEOUT = 300000; // 5m in ms.
@@ -335,6 +339,13 @@ static size_t wxCURLRead(char *buffer, size_t size, size_t nitems, void *userdat
return static_cast<wxWebRequestCURL*>(userdata)->CURLOnRead(buffer, size * nitems);
}
static int wxCURLSeek(void* userdata, curl_off_t offset, int origin)
{
wxCHECK_MSG( userdata, CURL_SEEKFUNC_CANTSEEK, "invalid curl seek callback data" );
return static_cast<wxWebRequestCURL*>(userdata)->CURLOnSeek(offset, origin);
}
wxWebRequestCURL::wxWebRequestCURL(wxWebSession & session,
wxWebSessionCURL& sessionImpl,
wxEvtHandler* handler,
@@ -377,6 +388,8 @@ void wxWebRequestCURL::DoStartPrepare(const wxString& url)
wxCURLSetOpt(m_handle, CURLOPT_HEADERFUNCTION, wxCURLHeader);
wxCURLSetOpt(m_handle, CURLOPT_READFUNCTION, wxCURLRead);
wxCURLSetOpt(m_handle, CURLOPT_READDATA, this);
wxCURLSetOpt(m_handle, CURLOPT_SEEKFUNCTION, wxCURLSeek);
wxCURLSetOpt(m_handle, CURLOPT_SEEKDATA, this);
// Enable gzip, etc decompression
wxCURLSetOpt(m_handle, CURLOPT_ACCEPT_ENCODING, "");
// Enable redirection handling
@@ -618,6 +631,34 @@ size_t wxWebRequestCURL::CURLOnRead(char* buffer, size_t size)
return 0;
}
int wxWebRequestCURL::CURLOnSeek(curl_off_t offset, int origin)
{
wxSeekMode mode = wxFromStart;
switch ( origin )
{
case SEEK_SET:
mode = wxFromStart;
break;
case SEEK_CUR:
mode = wxFromCurrent;
break;
case SEEK_END:
mode = wxFromEnd;
break;
default:
wxLogTrace(TRACE_CURL, "Seek function: unknown origin %d", origin);
return CURL_SEEKFUNC_CANTSEEK;
}
if ( m_dataStream->SeekI(offset, mode) == wxInvalidOffset )
return CURL_SEEKFUNC_CANTSEEK;
return CURL_SEEKFUNC_OK;
}
wxFileOffset wxWebRequestCURL::GetBytesSent() const
{
return m_bytesSent;
@@ -875,10 +916,6 @@ using SocketPollerBase = WinSock1SocketPoller;
#else
#if wxUSE_LOG_TRACE
constexpr const char* TRACE_CURL = "curl";
#endif
// SocketPollerSourceHandler - a source handler used by the SocketPoller class.
class SourceSocketPoller;
+24
View File
@@ -1064,6 +1064,30 @@ TEST_CASE_METHOD(SyncRequestFixture,
CHECK( response.GetStatus() == 200 );
}
TEST_CASE_METHOD(SyncRequestFixture,
"WebRequest::Sync::PostAfterRedirect", "[net][webrequest][sync]")
{
if ( !InitBaseURL() )
return;
// We can't test this when using WinHTTP because we need to use either 307
// or 308 redirect status code to preserve the POST method across the
// redirect (all backends switch to GET for 301 and 302, although it would
// be possible to configure this to preserve POST when using libcurl) and
// WinHTTP doesn't handle them automatically.
const auto& versionInfo = wxWebSession::GetDefault().GetLibraryVersionInfo();
if ( versionInfo.GetName() == "WinHTTP" )
{
WARN("Skipping POST with redirect test with WinHTTP backend");
return;
}
Create("redirect-to?url=post&status_code=307");
request.SetData("app=WebRequestRedirect&version=1", "application/x-www-form-urlencoded");
REQUIRE( Execute() );
CHECK( response.GetStatus() == 200 );
}
TEST_CASE_METHOD(SyncRequestFixture,
"WebRequest::Sync::Put", "[net][webrequest][sync]")
{