From d7dee7019e94138643ae9805320fcf34aaa7fd19 Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Mon, 22 Oct 2018 21:39:30 +0200 Subject: [PATCH] Implement sending request data with wxWebRequest --- include/wx/msw/webrequest_winhttp.h | 12 ++-- include/wx/webrequest.h | 13 +++-- samples/webrequest/webrequest.cpp | 17 ++++-- src/common/webrequest.cpp | 27 +++++++++ src/msw/webrequest_winhttp.cpp | 91 ++++++++++++++++++----------- 5 files changed, 110 insertions(+), 50 deletions(-) diff --git a/include/wx/msw/webrequest_winhttp.h b/include/wx/msw/webrequest_winhttp.h index 493c984bf6..09b51fb3f8 100644 --- a/include/wx/msw/webrequest_winhttp.h +++ b/include/wx/msw/webrequest_winhttp.h @@ -59,12 +59,6 @@ public: ~wxWebRequestWinHTTP(); - void SetMethod(const wxString& method) wxOVERRIDE; - - void SetData(const wxString& text, const wxString& contentType) wxOVERRIDE; - - void SetData(const wxInputStream& dataStream, const wxString& contentType) wxOVERRIDE; - void Start() wxOVERRIDE; void Cancel() wxOVERRIDE; @@ -82,6 +76,12 @@ private: HINTERNET m_connect; HINTERNET m_request; wxScopedPtr m_response; + wxMemoryBuffer m_dataWriteBuffer; + wxFileOffset m_dataWritten; + + void WriteData(); + + void CreateResponse(); void SetFailedWithLastError(); diff --git a/include/wx/webrequest.h b/include/wx/webrequest.h index c72c1a21f9..032f5436b5 100644 --- a/include/wx/webrequest.h +++ b/include/wx/webrequest.h @@ -42,11 +42,11 @@ public: virtual void SetHeader(const wxString& name, const wxString& value) { m_headers[name] = value; } - virtual void SetMethod(const wxString& method) = 0; + virtual void SetMethod(const wxString& method) { m_method = method; } - virtual void SetData(const wxString& text, const wxString& contentType) = 0; + void SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv = wxConvUTF8); - virtual void SetData(const wxInputStream& dataStream, const wxString& contentType) = 0; + void SetData(wxSharedPtr dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset); void SetIgnoreServerErrorStatus(bool ignore) { m_ignoreServerErrorStatus = ignore; } @@ -61,12 +61,16 @@ public: State GetState() const { return m_state; } protected: + wxString m_method; wxWebRequestHeaderMap m_headers; + wxFileOffset m_dataSize; + wxSharedPtr m_dataStream; wxWebRequest(int id): m_id(id), m_state(State_Idle), - m_ignoreServerErrorStatus(false) { } + m_ignoreServerErrorStatus(false), + m_dataSize(0) { } void SetState(State state, const wxString& failMsg = ""); @@ -77,6 +81,7 @@ private: State m_state; wxString m_failMessage; bool m_ignoreServerErrorStatus; + wxCharBuffer m_dataText; void ProcessReadyEvent(); diff --git a/samples/webrequest/webrequest.cpp b/samples/webrequest/webrequest.cpp index b09992089b..d6225ee74b 100644 --- a/samples/webrequest/webrequest.cpp +++ b/samples/webrequest/webrequest.cpp @@ -77,15 +77,16 @@ public: textSizer->Add(m_postCheckBox, wxSizerFlags().Border()); m_postCheckBox->Bind(wxEVT_CHECKBOX, &WebRequestFrame::OnPostCheckBox, this); - m_postRequestTextCtrl = new wxTextCtrl(textPanel, wxID_ANY, "", - wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); + m_postRequestTextCtrl = new wxTextCtrl(textPanel, wxID_ANY, + "app=WebRequestSample&version=1", + wxDefaultPosition, wxSize(-1, FromDIP(60)), wxTE_MULTILINE); textSizer->Add(m_postRequestTextCtrl, - wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT)); + wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT)); textSizer->Add(new wxStaticText(textPanel, wxID_ANY, "Request body content type:"), wxSizerFlags().Border()); m_postContentTypeTextCtrl = new wxTextCtrl(textPanel, wxID_ANY, - "application/json"); + "application/x-www-form-urlencoded"); textSizer->Add(m_postContentTypeTextCtrl, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT)); @@ -142,10 +143,16 @@ public: switch (m_notebook->GetSelection()) { case Page_Image: + // Reset static bitmap image + m_imageStaticBitmap->SetBitmap(wxArtProvider::GetBitmap(wxART_MISSING_IMAGE)); // Bind completion event to response as image request->Bind(wxEVT_WEBREQUEST_READY, &WebRequestFrame::OnImageRequestReady, this); break; case Page_Text: + // Reset response text control + m_textResponseTextCtrl->Clear(); + + // Set postdata if checked if (m_postCheckBox->IsChecked()) { request->SetData(m_postRequestTextCtrl->GetValue(), @@ -214,7 +221,7 @@ public: defaultURL = "https://www.wxwidgets.org/downloads/logos/blocks.png"; break; case Page_Text: - defaultURL = "https://api.github.com"; + defaultURL = "https://httpbin.org/post"; break; case Page_Download: defaultURL = "https://www.wxwidgets.com/download.zip"; diff --git a/src/common/webrequest.cpp b/src/common/webrequest.cpp index 10c5e701ef..8ab49bd37e 100644 --- a/src/common/webrequest.cpp +++ b/src/common/webrequest.cpp @@ -17,6 +17,7 @@ #if wxUSE_WEBREQUEST #include "wx/webrequest.h" +#include "wx/mstream.h" #ifndef WX_PRECOMP #include "wx/app.h" @@ -55,6 +56,32 @@ bool wxWebRequest::CheckServerStatus() return true; } +void wxWebRequest::SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv) +{ + m_dataText = text.mb_str(conv); + SetData(wxSharedPtr(new wxMemoryInputStream(m_dataText, m_dataText.length())), contentType); +} + +void wxWebRequest::SetData(wxSharedPtr dataStream, const wxString& contentType, wxFileOffset dataSize) +{ + m_dataStream = dataStream; + if ( m_dataStream.get() ) + { + if ( dataSize == wxInvalidOffset ) + { + // Determine data size + m_dataSize = m_dataStream->SeekI(0, wxFromEnd); + m_dataStream->SeekI(0); + } + else + m_dataSize = dataSize; + } + else + m_dataSize = 0; + + SetHeader("Content-Type", contentType); +} + void wxWebRequest::SetState(State state, const wxString & failMsg) { switch (state) diff --git a/src/msw/webrequest_winhttp.cpp b/src/msw/webrequest_winhttp.cpp index 118cacde06..b644700c16 100644 --- a/src/msw/webrequest_winhttp.cpp +++ b/src/msw/webrequest_winhttp.cpp @@ -163,30 +163,19 @@ wxWebRequestWinHTTP::~wxWebRequestWinHTTP() void wxWebRequestWinHTTP::HandleCallback(DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) { - bool handleLastError = false; - static const int readSize = 8 * 1024; - switch ( dwInternetStatus ) { case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: - if ( ::WinHttpReceiveResponse(m_request, NULL) ) - { - m_response.reset(new wxWebResponseWinHTTP(*this)); - if ( CheckServerStatus() ) - { - // Start reading the response - if ( !m_response->ReadData() ) - handleLastError = true; - } - } + if ( m_dataSize ) + WriteData(); else - handleLastError = true; + CreateResponse(); break; case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: if ( dwStatusInformationLength > 0 ) { if ( !m_response->ReportAvailableData(dwStatusInformationLength) ) - handleLastError = true; + SetFailedWithLastError(); } else { @@ -194,6 +183,9 @@ void wxWebRequestWinHTTP::HandleCallback(DWORD dwInternetStatus, SetState(State_Ready); } break; + case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: + WriteData(); + break; case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: { LPWINHTTP_ASYNC_RESULT asyncResult = reinterpret_cast(lpvStatusInformation); @@ -201,32 +193,49 @@ void wxWebRequestWinHTTP::HandleCallback(DWORD dwInternetStatus, break; } } - - if (handleLastError) - SetFailedWithLastError(); } +void wxWebRequestWinHTTP::WriteData() +{ + int dataWriteSize = 8 * 1024; + if ( m_dataWritten + dataWriteSize > m_dataSize ) + dataWriteSize = m_dataSize - m_dataWritten; + if ( dataWriteSize ) + { + m_dataWriteBuffer.Clear(); + m_dataWriteBuffer.GetWriteBuf(dataWriteSize); + m_dataStream->Read(m_dataWriteBuffer.GetData(), dataWriteSize); + + if ( !::WinHttpWriteData(m_request, m_dataWriteBuffer.GetData(), dataWriteSize, NULL) ) + SetFailedWithLastError(); + m_dataWritten += dataWriteSize; + } + else + CreateResponse(); +} + +void wxWebRequestWinHTTP::CreateResponse() +{ + if (::WinHttpReceiveResponse(m_request, NULL)) + { + m_response.reset(new wxWebResponseWinHTTP(*this)); + if (CheckServerStatus()) + { + // Start reading the response + if (!m_response->ReadData()) + SetFailedWithLastError(); + } + } + else + SetFailedWithLastError(); +} + void wxWebRequestWinHTTP::SetFailedWithLastError() { wxString failMessage = wxWinHTTPErrorToString(::GetLastError()); SetState(State_Failed, failMessage); } -void wxWebRequestWinHTTP::SetMethod(const wxString& method) -{ - // TODO: implement -} - -void wxWebRequestWinHTTP::SetData(const wxString& text, const wxString& contentType) -{ - // TODO: implement -} - -void wxWebRequestWinHTTP::SetData(const wxInputStream& dataStream, const wxString& contentType) -{ - // TODO: implement -} - void wxWebRequestWinHTTP::Start() { if ( GetState() != State_Idle ) // Completed requests can not be restarted @@ -251,9 +260,17 @@ void wxWebRequestWinHTTP::Start() return; } + wxString method; + if ( !m_method.empty() ) + method = m_method; + else if ( m_dataSize ) + method = "POST"; + else + method = "GET"; + // Open a request m_request = ::WinHttpOpenRequest(m_connect, - L"GET", uri.GetPath().wc_str(), + method.wc_str(), uri.GetPath().wc_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, (isSecure) ? WINHTTP_FLAG_SECURE : 0); @@ -267,6 +284,7 @@ void wxWebRequestWinHTTP::Start() if ( ::WinHttpSetStatusCallback(m_request, (WINHTTP_STATUS_CALLBACK)wxRequestStatusCallback, WINHTTP_CALLBACK_FLAG_READ_COMPLETE | + WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE | WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE | WINHTTP_CALLBACK_FLAG_REQUEST_ERROR, 0) == WINHTTP_INVALID_STATUS_CALLBACK ) @@ -280,10 +298,13 @@ void wxWebRequestWinHTTP::Start() for (wxWebRequestHeaderMap::const_iterator header = m_headers.begin(); header != m_headers.end(); ++header) allHeaders.append(wxString::Format("%s: %s\n", header->first, header->second)); + if ( m_dataSize ) + m_dataWritten = 0; + // Send request if ( WinHttpSendRequest(m_request, allHeaders.wc_str(), allHeaders.length(), - NULL, 0, 0, + NULL, 0, m_dataSize, (DWORD_PTR)this) ) { SetState(State_Active);