From c24efa98216b7bfd25a59b6a1c3f363cc33e534d Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 30 Oct 2018 21:54:40 +0100 Subject: [PATCH] Implement additional storage targets --- include/wx/msw/webrequest_winhttp.h | 1 - include/wx/webrequest.h | 45 ++++++++++-- src/common/webrequest.cpp | 103 +++++++++++++++++++++++++++- src/msw/webrequest_winhttp.cpp | 15 ++-- tests/net/webrequest.cpp | 41 ++++++++++- 5 files changed, 188 insertions(+), 17 deletions(-) diff --git a/include/wx/msw/webrequest_winhttp.h b/include/wx/msw/webrequest_winhttp.h index b23d7e2dff..d11a1b327c 100644 --- a/include/wx/msw/webrequest_winhttp.h +++ b/include/wx/msw/webrequest_winhttp.h @@ -89,7 +89,6 @@ public: HINTERNET GetHandle() const { return m_request; } private: - wxWebSessionWinHTTP& m_session; wxString m_url; HINTERNET m_connect; HINTERNET m_request; diff --git a/include/wx/webrequest.h b/include/wx/webrequest.h index 50d5974564..60d874266d 100644 --- a/include/wx/webrequest.h +++ b/include/wx/webrequest.h @@ -15,6 +15,7 @@ #if wxUSE_WEBREQUEST #include "wx/event.h" +#include "wx/ffile.h" #include "wx/object.h" #include "wx/scopedptr.h" #include "wx/sharedptr.h" @@ -74,6 +75,8 @@ public: int GetId() const { return m_id; } + wxWebSession& GetSession() const { return m_session; } + State GetState() const { return m_state; } virtual wxFileOffset GetBytesSent() const = 0; @@ -84,6 +87,8 @@ public: virtual wxFileOffset GetBytesExpectedToReceive() const = 0; + void SetState(State state, const wxString& failMsg = ""); + protected: wxString m_method; Storage m_storage; @@ -91,18 +96,18 @@ protected: wxFileOffset m_dataSize; wxSharedPtr m_dataStream; - wxWebRequest(int id): + wxWebRequest(wxWebSession& session, int id): + m_session(session), m_id(id), m_state(State_Idle), m_ignoreServerErrorStatus(false), m_dataSize(0), m_storage(Storage_Memory) { } - void SetState(State state, const wxString& failMsg = ""); - bool CheckServerStatus(); private: + wxWebSession& m_session; int m_id; State m_state; bool m_ignoreServerErrorStatus; @@ -116,7 +121,7 @@ private: class WXDLLIMPEXP_NET wxWebResponse { public: - virtual ~wxWebResponse() { } + virtual ~wxWebResponse(); virtual wxInt64 GetContentLength() const = 0; @@ -136,12 +141,17 @@ public: wxString AsString(wxMBConv* conv = NULL) const; + void ReportDataCompleted(); + + bool Init(); + + virtual wxString GetFileName() const; + protected: wxWebRequest& m_request; size_t m_readSize; - wxWebResponse(wxWebRequest& request): - m_request(request), m_readSize(8 * 1024) { } + wxWebResponse(wxWebRequest& request); void* GetDataBuffer(size_t sizeNeeded); @@ -149,6 +159,7 @@ protected: private: wxMemoryBuffer m_readBuffer; + mutable wxFFile m_file; mutable wxScopedPtr m_stream; wxDECLARE_NO_COPY_CLASS(wxWebResponse); @@ -205,6 +216,10 @@ public: const wxWebRequestHeaderMap& GetHeaders() const { return m_headers; } + void SetTempDir(const wxString& dir) { m_tempDir = dir; } + + wxString GetTempDir() const; + static wxWebSession& GetDefault(); static wxWebSession* New(const wxString& backend = wxWebSessionBackendDefault); @@ -218,6 +233,7 @@ protected: private: wxWebRequestHeaderMap m_headers; + wxString m_tempDir; static wxScopedPtr ms_defaultSession; static wxStringWebSessionFactoryMap ms_factoryMap; @@ -232,7 +248,8 @@ public: wxWebRequestEvent(wxEventType type, int id, wxWebRequest::State state, wxWebResponse* response = NULL, const wxString& errorDesc = "") : wxEvent(id, type), - m_state(state), m_response(response), m_errorDescription(errorDesc) + m_state(state), m_response(response), m_data(NULL), m_dataSize(0), + m_errorDescription(errorDesc) { } wxWebRequest::State GetState() const { return m_state; } @@ -241,11 +258,25 @@ public: const wxString& GetErrorDescription() const { return m_errorDescription; } + const wxString& GetResponseFileName() const { return m_responseFileName; } + + void SetResponseFileName(const wxString& filename) { m_responseFileName = filename; } + + const void* GetDataBuffer() const { return m_data; } + + size_t GetDataSize() const { return m_dataSize; } + + void SetDataBuffer(const void* buffer, size_t size) + { m_data = buffer; m_dataSize = size; } + wxEvent* Clone() const wxOVERRIDE { return new wxWebRequestEvent(*this); } private: wxWebRequest::State m_state; wxWebResponse* m_response; + wxString m_responseFileName; + const void* m_data; + size_t m_dataSize; wxString m_errorDescription; }; diff --git a/src/common/webrequest.cpp b/src/common/webrequest.cpp index 997054aadc..276da34727 100644 --- a/src/common/webrequest.cpp +++ b/src/common/webrequest.cpp @@ -19,7 +19,10 @@ #include "wx/webrequest.h" #include "wx/mstream.h" #include "wx/uri.h" +#include "wx/filefn.h" #include "wx/filename.h" +#include "wx/stdpaths.h" +#include "wx/wfstream.h" #ifndef WX_PRECOMP #include "wx/app.h" @@ -105,9 +108,23 @@ void wxWebRequest::SetState(State state, const wxString & failMsg) void wxWebRequest::ProcessStateEvent(State state, const wxString& failMsg) { + wxString responseFileName; + wxWebRequestEvent evt(wxEVT_WEBREQUEST_STATE, GetId(), state, GetResponse(), failMsg); + if ( state == State_Completed && m_storage == Storage::Storage_File ) + { + responseFileName = GetResponse()->GetFileName(); + evt.SetResponseFileName(responseFileName); + } + ProcessEvent(evt); + + // Remove temporary file if it still exists + if ( state == State_Completed && m_storage == Storage::Storage_File && + wxFileExists(responseFileName) ) + wxRemove(responseFileName); + // Remove reference after the request is no longer active if (state == State_Completed || state == State_Failed || state == State_Cancelled) @@ -118,6 +135,43 @@ void wxWebRequest::ProcessStateEvent(State state, const wxString& failMsg) // // wxWebResponse // +wxWebResponse::wxWebResponse(wxWebRequest& request) : + m_request(request), + m_readSize(8 * 1024) +{ +} + +wxWebResponse::~wxWebResponse() +{ + if ( wxFileExists(m_file.GetName()) ) + wxRemove(m_file.GetName()); +} + +bool wxWebResponse::Init() +{ + if (m_request.GetStorage() == wxWebRequest::Storage_File) + { + wxFileName tmpPrefix; + tmpPrefix.AssignDir(m_request.GetSession().GetTempDir()); + if ( GetContentLength() > 0 ) + { + // Check available disk space + wxLongLong freeSpace; + if ( wxGetDiskSpace(tmpPrefix.GetFullPath(), NULL, &freeSpace) && + GetContentLength() > freeSpace ) + { + m_request.SetState(wxWebRequest::State_Failed, _("Not enough free disk space for download.")); + return false; + } + } + + tmpPrefix.SetName("wxd"); + wxFileName::CreateTempFileName(tmpPrefix.GetFullPath(), &m_file); + } + + return true; +} + wxString wxWebResponse::GetMimeType() const { return GetHeader("Mime-Type"); @@ -128,7 +182,20 @@ wxInputStream * wxWebResponse::GetStream() const if ( !m_stream.get() ) { // Create stream - m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen())); + switch (m_request.GetStorage()) + { + case wxWebRequest::Storage_Memory: + m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen())); + break; + case wxWebRequest::Storage_File: + m_stream.reset(new wxFFileInputStream(m_file)); + m_stream->SeekI(0); + break; + case wxWebRequest::Storage_None: + // No stream available + break; + } + } return m_stream.get(); @@ -155,10 +222,10 @@ wxString wxWebResponse::GetSuggestedFileName() const wxString wxWebResponse::AsString(wxMBConv * conv) const { // TODO: try to determine encoding type from content-type header - if (!conv) + if ( !conv ) conv = &wxConvUTF8; - if (m_request.GetStorage() == wxWebRequest::Storage_Memory) + if ( m_request.GetStorage() == wxWebRequest::Storage_Memory ) { size_t outLen = 0; return conv->cMB2WC((const char*)m_readBuffer.GetData(), m_readBuffer.GetDataLen(), &outLen); @@ -175,8 +242,30 @@ void* wxWebResponse::GetDataBuffer(size_t sizeNeeded) void wxWebResponse::ReportDataReceived(size_t sizeReceived) { m_readBuffer.UngetAppendBuf(sizeReceived); + + if ( m_request.GetStorage() == wxWebRequest::Storage_File ) + m_file.Write(m_readBuffer.GetData(), m_readBuffer.GetDataLen()); + else if ( m_request.GetStorage() == wxWebRequest::Storage_None ) + { + wxWebRequestEvent evt(wxEVT_WEBREQUEST_DATA, m_request.GetId(), wxWebRequest::State_Active); + evt.SetDataBuffer(m_readBuffer.GetData(), m_readBuffer.GetDataLen()); + m_request.ProcessEvent(evt); + } + + if ( m_request.GetStorage() != wxWebRequest::Storage_Memory ) + m_readBuffer.Clear(); } +wxString wxWebResponse::GetFileName() const +{ + return m_file.GetName(); +} + +void wxWebResponse::ReportDataCompleted() +{ + if ( m_request.GetStorage() == wxWebRequest::Storage_File ) + m_file.Close(); +} // // wxWebSession @@ -193,6 +282,14 @@ wxWebSession::wxWebSession() wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER)); } +wxString wxWebSession::GetTempDir() const +{ + if ( m_tempDir.empty() ) + return wxStandardPaths::Get().GetTempDir(); + else + return m_tempDir; +} + // static wxWebSession& wxWebSession::GetDefault() { diff --git a/src/msw/webrequest_winhttp.cpp b/src/msw/webrequest_winhttp.cpp index 394c56e2a8..55397aac54 100644 --- a/src/msw/webrequest_winhttp.cpp +++ b/src/msw/webrequest_winhttp.cpp @@ -143,8 +143,7 @@ static void CALLBACK wxRequestStatusCallback( // wxWebRequestWinHTTP::wxWebRequestWinHTTP(int id, wxWebSessionWinHTTP& session, const wxString& url): - wxWebRequest(id), - m_session(session), + wxWebRequest(session, id), m_url(url), m_connect(NULL), m_request(NULL), @@ -181,7 +180,10 @@ void wxWebRequestWinHTTP::HandleCallback(DWORD dwInternetStatus, SetFailedWithLastError(); } else + { + m_response->ReportDataCompleted(); SetState(State_Completed); + } break; case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: WriteData(); @@ -216,9 +218,11 @@ void wxWebRequestWinHTTP::WriteData() void wxWebRequestWinHTTP::CreateResponse() { - if (::WinHttpReceiveResponse(m_request, NULL)) + if ( ::WinHttpReceiveResponse(m_request, NULL) ) { m_response.reset(new wxWebResponseWinHTTP(*this)); + if ( !m_response->Init() ) + return; m_bytesExpectedToReceive = m_response->GetContentLength(); int status = m_response->GetStatus(); if ( status == 401 || status == 407) @@ -263,8 +267,9 @@ void wxWebRequestWinHTTP::Start() port = wxAtoi(uri.GetPort()); // Open a connction - m_connect = ::WinHttpConnect(m_session.GetHandle(), uri.GetServer().wc_str(), - port, 0); + m_connect = ::WinHttpConnect( + static_cast(GetSession()).GetHandle(), + uri.GetServer().wc_str(), port, 0); if ( m_connect == NULL ) { SetFailedWithLastError(); diff --git a/tests/net/webrequest.cpp b/tests/net/webrequest.cpp index f7e68f3f39..bb297c0ebc 100644 --- a/tests/net/webrequest.cpp +++ b/tests/net/webrequest.cpp @@ -22,6 +22,7 @@ #endif // WX_PRECOMP #include "wx/webrequest.h" +#include "wx/filename.h" #include "wx/wfstream.h" // This test requires the URL to an httpbin instance. @@ -38,7 +39,11 @@ class RequestFixture { public: - RequestFixture() { } + RequestFixture() + { + expectedFileSize = 0; + dataSize = 0; + } void Create(const wxString& subURL) { @@ -51,6 +56,7 @@ public: { request.reset(wxWebSession::GetDefault().CreateRequest(url)); request->Bind(wxEVT_WEBREQUEST_STATE, &RequestFixture::OnRequestState, this); + request->Bind(wxEVT_WEBREQUEST_DATA, &RequestFixture::OnData, this); } void OnRequestState(wxWebRequestEvent& evt) @@ -59,6 +65,12 @@ public: { case wxWebRequest::State_Unauthorized: case wxWebRequest::State_Completed: + if ( request->GetStorage() == wxWebRequest::Storage_File ) + { + wxFileName fn(evt.GetResponseFileName()); + REQUIRE( fn.GetSize() == expectedFileSize ); + } + wxFALLTHROUGH; case wxWebRequest::State_Failed: case wxWebRequest::State_Cancelled: loop.Exit(); @@ -66,6 +78,12 @@ public: } } + void OnData(wxWebRequestEvent& evt) + { + // Count all bytes recieved via data event for Storage_None + dataSize += evt.GetDataSize(); + } + void Run(wxWebRequest::State requiredState = wxWebRequest::State_Completed, int requiredStatus = 200) { @@ -79,6 +97,8 @@ public: wxEventLoop loop; wxObjectDataPtr request; + wxInt64 expectedFileSize; + wxInt64 dataSize; }; TEST_CASE_METHOD(RequestFixture, "WebRequest", "[net][.]") @@ -126,6 +146,25 @@ TEST_CASE_METHOD(RequestFixture, "WebRequest", "[net][.]") REQUIRE( request->GetResponse()->AsString() == "The quick brown fox jumps over the lazy dog" ); } + SECTION("GET 99KB to file") + { + expectedFileSize = 99 * 1024; + Create(wxString::Format("/bytes/%lld", expectedFileSize)); + request->SetStorage(wxWebRequest::Storage_File); + Run(); + REQUIRE( request->GetBytesReceived() == expectedFileSize ); + } + + SECTION("Process 99KB data") + { + int processingSize = 99 * 1024; + Create(wxString::Format("/bytes/%d", processingSize)); + request->SetStorage(wxWebRequest::Storage_None); + Run(); + REQUIRE( request->GetBytesReceived() == processingSize ); + REQUIRE( dataSize == processingSize ); + } + SECTION("PUT file data") { Create("/put");