diff --git a/include/wx/private/webrequest_curl.h b/include/wx/private/webrequest_curl.h index ae184061e3..e05dc48828 100644 --- a/include/wx/private/webrequest_curl.h +++ b/include/wx/private/webrequest_curl.h @@ -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 diff --git a/src/common/webrequest_curl.cpp b/src/common/webrequest_curl.cpp index 6ad4d18828..a913474158 100644 --- a/src/common/webrequest_curl.cpp +++ b/src/common/webrequest_curl.cpp @@ -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(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(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; diff --git a/tests/net/webrequest.cpp b/tests/net/webrequest.cpp index 65a6cf446d..a2f90a0368 100644 --- a/tests/net/webrequest.cpp +++ b/tests/net/webrequest.cpp @@ -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]") {