Start wxWebRequestWinHTTP implementation

Can already run the GET request in the sample app
This commit is contained in:
Tobias Taschner
2018-10-20 19:21:35 +02:00
parent e07c1bf40c
commit a4279752f8
9 changed files with 676 additions and 26 deletions

View File

@@ -25,6 +25,10 @@ wx_add_library(net IS_BASE ${NET_FILES})
if(WIN32) if(WIN32)
wx_lib_link_libraries(net PRIVATE ws2_32) wx_lib_link_libraries(net PRIVATE ws2_32)
if(wxUSE_WEBREQUEST)
wx_lib_link_libraries(net PRIVATE Winhttp)
endif()
endif() endif()
wx_finalize_lib(net) wx_finalize_lib(net)

3
configure vendored
View File

@@ -23773,6 +23773,9 @@ fi
if test "$wxUSE_ACCESSIBILITY" = "yes" ; then if test "$wxUSE_ACCESSIBILITY" = "yes" ; then
LIBS="$LIBS -loleacc" LIBS="$LIBS -loleacc"
fi fi
if test "$wxUSE_WEBREQUEST" = "yes" ; then
LIBS="$LIBS -lwinhttp"
fi
case "${host}" in case "${host}" in
*-*-cygwin* ) *-*-cygwin* )

View File

@@ -2766,6 +2766,9 @@ if test "$USE_WIN32" = 1 ; then
if test "$wxUSE_ACCESSIBILITY" = "yes" ; then if test "$wxUSE_ACCESSIBILITY" = "yes" ; then
LIBS="$LIBS -loleacc" LIBS="$LIBS -loleacc"
fi fi
if test "$wxUSE_WEBREQUEST" = "yes" ; then
LIBS="$LIBS -lwinhttp"
fi
case "${host}" in case "${host}" in
*-*-cygwin* ) *-*-cygwin* )

View File

@@ -10,17 +10,102 @@
#ifndef _WX_MSW_WEBREQUEST_WINHTTP_H #ifndef _WX_MSW_WEBREQUEST_WINHTTP_H
#define _WX_MSW_WEBREQUEST_WINHTTP_H #define _WX_MSW_WEBREQUEST_WINHTTP_H
#include "wx/msw/wrapwin.h"
#include <winhttp.h>
#include "wx/buffer.h"
class wxWebSessionWinHTTP;
class wxWebRequestWinHTTP;
class WXDLLIMPEXP_NET wxWebResponseWinHTTP : public wxWebResponse
{
public:
wxWebResponseWinHTTP(wxWebRequestWinHTTP& request);
wxInt64 GetContentLength() const wxOVERRIDE { return m_contentLength; }
wxString GetURL() const wxOVERRIDE;
wxString GetHeader(const wxString& name) const wxOVERRIDE;
int GetStatus() const wxOVERRIDE;
wxString GetStatusText() const wxOVERRIDE;
wxInputStream* GetStream() const wxOVERRIDE;
wxString AsString(wxMBConv* conv = NULL) const wxOVERRIDE;
bool ReadData();
bool ReportAvailableData(DWORD dataLen);
void ReportDataComplete();
private:
wxWebRequestWinHTTP& m_request;
wxInt64 m_contentLength;
long m_readSize;
wxMemoryBuffer m_readBuffer;
wxScopedPtr<wxInputStream> m_stream;
wxDECLARE_NO_COPY_CLASS(wxWebResponseWinHTTP);
};
class WXDLLIMPEXP_NET wxWebRequestWinHTTP : public wxWebRequest
{
public:
wxWebRequestWinHTTP(int id, wxWebSessionWinHTTP& session, const wxString& url);
~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;
wxWebResponse* GetResponse() wxOVERRIDE;
void HandleCallback(DWORD dwInternetStatus, LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength);
HINTERNET GetHandle() const { return m_request; }
private:
wxWebSessionWinHTTP& m_session;
wxString m_url;
HINTERNET m_connect;
HINTERNET m_request;
wxScopedPtr<wxWebResponseWinHTTP> m_response;
void SetFailedWithLastError();
wxDECLARE_NO_COPY_CLASS(wxWebRequestWinHTTP);
};
class WXDLLIMPEXP_NET wxWebSessionWinHTTP: public wxWebSession class WXDLLIMPEXP_NET wxWebSessionWinHTTP: public wxWebSession
{ {
public: public:
wxWebSessionWinHTTP() { } wxWebSessionWinHTTP();
~wxWebSessionWinHTTP();
wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) wxOVERRIDE; wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) wxOVERRIDE;
void SetHeader(const wxString& name, const wxString& value) wxOVERRIDE; HINTERNET GetHandle() const { return m_handle; }
private: private:
bool m_initialized;
HINTERNET m_handle;
void Init();
wxDECLARE_NO_COPY_CLASS(wxWebSessionWinHTTP);
}; };
class WXDLLIMPEXP_NET wxWebSessionFactoryWinHTTP: public wxWebSessionFactory class WXDLLIMPEXP_NET wxWebSessionFactoryWinHTTP: public wxWebSessionFactory

View File

@@ -18,15 +18,29 @@
#include "wx/object.h" #include "wx/object.h"
#include "wx/scopedptr.h" #include "wx/scopedptr.h"
#include "wx/sharedptr.h" #include "wx/sharedptr.h"
#include "wx/stream.h"
#include "wx/vector.h" #include "wx/vector.h"
class wxWebResponse; class wxWebResponse;
class wxWebSession; class wxWebSession;
WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap);
class WXDLLIMPEXP_NET wxWebRequest : public wxEvtHandler, public wxRefCounter class WXDLLIMPEXP_NET wxWebRequest : public wxEvtHandler, public wxRefCounter
{ {
public: public:
virtual void SetHeader(const wxString& name, const wxString& value) = 0; enum State
{
State_Idle,
State_Active,
State_Ready,
State_Failed
};
virtual ~wxWebRequest() { }
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) = 0;
@@ -34,21 +48,50 @@ public:
virtual void SetData(const wxInputStream& dataStream, const wxString& contentType) = 0; virtual void SetData(const wxInputStream& dataStream, const wxString& contentType) = 0;
virtual void SetIgnoreServerErrorStatus(bool ignore) = 0; void SetIgnoreServerErrorStatus(bool ignore) { m_ignoreServerErrorStatus = ignore; }
virtual void Start() = 0; virtual void Start() = 0;
virtual void Cancel() = 0; virtual void Cancel() = 0;
virtual const wxWebResponse* GetResponse() const = 0; virtual wxWebResponse* GetResponse() = 0;
int GetId() const { return m_id; }
State GetState() const { return m_state; }
protected:
wxWebRequestHeaderMap m_headers;
wxWebRequest(int id):
m_id(id),
m_state(State_Idle),
m_ignoreServerErrorStatus(false) { }
void SetState(State state, const wxString& failMsg = "");
bool CheckServerStatus();
private: private:
int m_id;
State m_state;
wxString m_failMessage;
bool m_ignoreServerErrorStatus;
void ProcessReadyEvent();
void ProcessFailedEvent();
wxDECLARE_NO_COPY_CLASS(wxWebRequest); wxDECLARE_NO_COPY_CLASS(wxWebRequest);
}; };
class WXDLLIMPEXP_NET wxWebResponse class WXDLLIMPEXP_NET wxWebResponse
{ {
public: public:
virtual ~wxWebResponse() { }
virtual wxInt64 GetContentLength() const = 0;
virtual wxString GetURL() const = 0; virtual wxString GetURL() const = 0;
virtual wxString GetHeader(const wxString& name) const = 0; virtual wxString GetHeader(const wxString& name) const = 0;
@@ -57,10 +100,13 @@ public:
virtual wxString GetStatusText() const = 0; virtual wxString GetStatusText() const = 0;
virtual wxInputStream& GetStream() const = 0; virtual wxInputStream* GetStream() const = 0;
virtual wxString AsString(wxMBConv* conv = NULL) const = 0; virtual wxString AsString(wxMBConv* conv = NULL) const = 0;
protected:
wxWebResponse() { }
private: private:
wxDECLARE_NO_COPY_CLASS(wxWebResponse); wxDECLARE_NO_COPY_CLASS(wxWebResponse);
}; };
@@ -69,6 +115,8 @@ class WXDLLIMPEXP_NET wxWebSessionFactory
{ {
public: public:
virtual wxWebSession* Create() = 0; virtual wxWebSession* Create() = 0;
virtual ~wxWebSessionFactory() { }
}; };
WX_DECLARE_STRING_HASH_MAP(wxSharedPtr<wxWebSessionFactory>, wxStringWebSessionFactoryMap); WX_DECLARE_STRING_HASH_MAP(wxSharedPtr<wxWebSessionFactory>, wxStringWebSessionFactoryMap);
@@ -85,7 +133,10 @@ public:
virtual wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) = 0; virtual wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) = 0;
virtual void SetHeader(const wxString& name, const wxString& value) = 0; virtual void SetHeader(const wxString& name, const wxString& value)
{ m_headers[name] = value; }
const wxWebRequestHeaderMap& GetHeaders() const { return m_headers; }
static wxWebSession& GetDefault(); static wxWebSession& GetDefault();
@@ -96,9 +147,10 @@ public:
static bool IsBackendAvailable(const wxString& backend); static bool IsBackendAvailable(const wxString& backend);
protected: protected:
wxWebSession() { } wxWebSession();
private: private:
wxWebRequestHeaderMap m_headers;
static wxScopedPtr<wxWebSession> ms_defaultSession; static wxScopedPtr<wxWebSession> ms_defaultSession;
static wxStringWebSessionFactoryMap ms_factoryMap; static wxStringWebSessionFactoryMap ms_factoryMap;
@@ -120,6 +172,8 @@ public:
const wxString& GetErrorDescription() const { return m_errorDescription; } const wxString& GetErrorDescription() const { return m_errorDescription; }
wxEvent* Clone() const wxOVERRIDE { return new wxWebRequestEvent(*this); }
private: private:
wxWebResponse* m_response; wxWebResponse* m_response;
wxString m_errorDescription; wxString m_errorDescription;

View File

@@ -31,7 +31,7 @@
// Bind events // Bind events
request->Bind(wxEVT_WEBREQUEST_READY, [](wxWebRequestEvent& evt) { request->Bind(wxEVT_WEBREQUEST_READY, [](wxWebRequestEvent& evt) {
wxImage logoImage(evt->GetResponse()->GetStream()); wxImage logoImage(*evt->GetResponse()->GetStream());
if (logoImage.IsOK()) if (logoImage.IsOK())
wxLogInfo("Image loaded"); wxLogInfo("Image loaded");
}); });
@@ -192,6 +192,11 @@ public:
@c NULL. @c NULL.
*/ */
const wxWebResponse* GetResponse() const; const wxWebResponse* GetResponse() const;
/**
Returns the id specified while creating this request.
*/
int GetId() const;
}; };
/** /**
@@ -235,7 +240,7 @@ public:
/** /**
Returns a stream which represents the response data sent by the server. Returns a stream which represents the response data sent by the server.
*/ */
wxInputStream& GetStream() const; wxInputStream* GetStream() const;
/** /**
Returns all response data as a string. Returns all response data as a string.

View File

@@ -55,12 +55,12 @@ public:
getLoadButton->Bind(wxEVT_BUTTON, &WebRequestFrame::OnGetLoadButton, this); getLoadButton->Bind(wxEVT_BUTTON, &WebRequestFrame::OnGetLoadButton, this);
getSizer->Add(getLoadButton, wxSizerFlags().Border()); getSizer->Add(getLoadButton, wxSizerFlags().Border());
wxStaticBoxSizer* getImageBox = m_getImageBox =
new wxStaticBoxSizer(wxVERTICAL, getPanel, "Image"); new wxStaticBoxSizer(wxVERTICAL, getPanel, "Image");
m_getStaticBitmap = new wxStaticBitmap(getImageBox->GetStaticBox(), m_getStaticBitmap = new wxStaticBitmap(m_getImageBox->GetStaticBox(),
wxID_ANY, wxArtProvider::GetBitmap(wxART_MISSING_IMAGE)); wxID_ANY, wxArtProvider::GetBitmap(wxART_MISSING_IMAGE));
getImageBox->Add(m_getStaticBitmap, wxSizerFlags(1).Expand()); m_getImageBox->Add(m_getStaticBitmap, wxSizerFlags(1).Expand());
getSizer->Add(getImageBox, wxSizerFlags(1).Expand().Border()); getSizer->Add(m_getImageBox, wxSizerFlags(1).Expand().Border());
getPanel->SetSizer(getSizer); getPanel->SetSizer(getSizer);
notebook->AddPage(getPanel, "GET Image", true); notebook->AddPage(getPanel, "GET Image", true);
@@ -132,8 +132,10 @@ public:
void OnGetWebRequestReady(wxWebRequestEvent& evt) void OnGetWebRequestReady(wxWebRequestEvent& evt)
{ {
wxImage img(evt.GetResponse()->GetStream()); wxImage img(*evt.GetResponse()->GetStream());
m_getStaticBitmap->SetBitmap(img); m_getStaticBitmap->SetBitmap(img);
m_getImageBox->Layout();
GetStatusBar()->SetStatusText(wxString::Format("Loaded %lld bytes image data", evt.GetResponse()->GetContentLength()));
} }
void OnWebRequestFailed(wxWebRequestEvent& evt) void OnWebRequestFailed(wxWebRequestEvent& evt)
@@ -149,6 +151,7 @@ public:
private: private:
wxTextCtrl* m_getURLTextCtrl; wxTextCtrl* m_getURLTextCtrl;
wxStaticBoxSizer* m_getImageBox;
wxStaticBitmap* m_getStaticBitmap; wxStaticBitmap* m_getStaticBitmap;
wxTextCtrl* m_postURLTextCtrl; wxTextCtrl* m_postURLTextCtrl;

View File

@@ -18,6 +18,11 @@
#include "wx/webrequest.h" #include "wx/webrequest.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/translation.h"
#endif
#if defined(__WINDOWS__) #if defined(__WINDOWS__)
#include "wx/msw/webrequest_winhttp.h" #include "wx/msw/webrequest_winhttp.h"
#endif #endif
@@ -34,13 +39,86 @@ wxDEFINE_EVENT(wxEVT_WEBREQUEST_READY, wxWebRequestEvent);
wxDEFINE_EVENT(wxEVT_WEBREQUEST_FAILED, wxWebRequestEvent); wxDEFINE_EVENT(wxEVT_WEBREQUEST_FAILED, wxWebRequestEvent);
wxDEFINE_EVENT(wxEVT_WEBREQUEST_AUTH_REQUIRED, wxWebRequestEvent); wxDEFINE_EVENT(wxEVT_WEBREQUEST_AUTH_REQUIRED, wxWebRequestEvent);
//
// wxWebRequest
//
bool wxWebRequest::CheckServerStatus()
{
const wxWebResponse* resp = GetResponse();
if ( resp && resp->GetStatus() >= 400 && !m_ignoreServerErrorStatus )
{
SetState(State_Failed, wxString::Format(_("Error: %s (%d)"),
resp->GetStatusText(), resp->GetStatus()));
return false;
}
else
return true;
}
void wxWebRequest::SetState(State state, const wxString & failMsg)
{
switch (state)
{
case State_Active:
// Add a reference while the request is active
if ( m_state != State_Active )
{
IncRef();
m_state = state;
}
break;
case State_Ready:
// Trigger the ready event in main thread
CallAfter(&wxWebRequest::ProcessReadyEvent);
break;
case State_Failed:
m_failMessage = failMsg;
// Trigger the failed event in main thread
CallAfter(&wxWebRequest::ProcessFailedEvent);
break;
}
}
void wxWebRequest::ProcessReadyEvent()
{
wxWebRequestEvent evt(wxEVT_WEBREQUEST_READY, GetId(), GetResponse());
ProcessEvent(evt);
// Remove reference after the request is no longer active
if ( m_state == State_Active )
DecRef();
m_state = State_Ready;
}
void wxWebRequest::ProcessFailedEvent()
{
wxWebRequestEvent evt(wxEVT_WEBREQUEST_FAILED, GetId(), NULL,
m_failMessage);
ProcessEvent(evt);
// Remove reference after the request is no longer active
if ( m_state == State_Active )
DecRef();
m_state = State_Failed;
}
//
// wxWebSession
//
wxScopedPtr<wxWebSession> wxWebSession::ms_defaultSession; wxScopedPtr<wxWebSession> wxWebSession::ms_defaultSession;
wxStringWebSessionFactoryMap wxWebSession::ms_factoryMap; wxStringWebSessionFactoryMap wxWebSession::ms_factoryMap;
wxWebSession::wxWebSession()
{
// Initialize the user-Agent header with a reasonable default
SetHeader("User-Agent", wxString::Format("%s/1 wxWidgets/%d.%d.%d",
wxTheApp->GetAppName(),
wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER));
}
// static // static
wxWebSession& wxWebSession::GetDefault() wxWebSession& wxWebSession::GetDefault()
{ {
if (ms_defaultSession == NULL) if ( ms_defaultSession == NULL )
ms_defaultSession.reset(wxWebSession::New()); ms_defaultSession.reset(wxWebSession::New());
return *ms_defaultSession; return *ms_defaultSession;
@@ -49,11 +127,11 @@ wxWebSession& wxWebSession::GetDefault()
// static // static
wxWebSession* wxWebSession::New(const wxString& backend) wxWebSession* wxWebSession::New(const wxString& backend)
{ {
if (ms_factoryMap.empty()) if ( ms_factoryMap.empty() )
InitFactoryMap(); InitFactoryMap();
wxStringWebSessionFactoryMap::iterator factory = ms_factoryMap.find(backend); wxStringWebSessionFactoryMap::iterator factory = ms_factoryMap.find(backend);
if (factory != ms_factoryMap.end()) if ( factory != ms_factoryMap.end() )
return factory->second->Create(); return factory->second->Create();
else else
return NULL; return NULL;
@@ -77,13 +155,11 @@ void wxWebSession::InitFactoryMap()
// static // static
bool wxWebSession::IsBackendAvailable(const wxString& backend) bool wxWebSession::IsBackendAvailable(const wxString& backend)
{ {
if (ms_factoryMap.empty()) if ( ms_factoryMap.empty() )
InitFactoryMap(); InitFactoryMap();
wxStringWebSessionFactoryMap::iterator factory = ms_factoryMap.find(backend); wxStringWebSessionFactoryMap::iterator factory = ms_factoryMap.find(backend);
return factory != ms_factoryMap.end(); return factory != ms_factoryMap.end();
} }
#endif // wxUSE_WEBREQUEST #endif // wxUSE_WEBREQUEST

View File

@@ -18,16 +18,433 @@
#if wxUSE_WEBREQUEST #if wxUSE_WEBREQUEST
#include "wx/mstream.h"
#include "wx/uri.h"
#include "wx/msw/webrequest_winhttp.h" #include "wx/msw/webrequest_winhttp.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/utils.h"
#include "wx/translation.h"
#endif
// For MSVC we can link in the required library explicitly, for the other
// compilers (e.g. MinGW) this needs to be done at makefiles level.
#ifdef __VISUALC__
#pragma comment(lib, "Winhttp")
#endif
// Define constants potentially missing in old SDKs
#ifndef WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
#endif
#ifndef WINHTTP_PROTOCOL_FLAG_HTTP2
#define WINHTTP_PROTOCOL_FLAG_HTTP2 0x1
#endif
#ifndef WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL
#define WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL 133
#endif
#ifndef WINHTTP_DECOMPRESSION_FLAG_ALL
#define WINHTTP_DECOMPRESSION_FLAG_GZIP 0x00000001
#define WINHTTP_DECOMPRESSION_FLAG_DEFLATE 0x00000002
#define WINHTTP_DECOMPRESSION_FLAG_ALL ( \
WINHTTP_DECOMPRESSION_FLAG_GZIP | \
WINHTTP_DECOMPRESSION_FLAG_DEFLATE)
#endif
#ifndef WINHTTP_OPTION_DECOMPRESSION
#define WINHTTP_OPTION_DECOMPRESSION 118
#endif
#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1
#define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200
#endif
#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
#define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800
#endif
// Helper functions
static wxString wxWinHTTPErrorToString(DWORD errorCode)
{
wxString errorString;
LPVOID msgBuf;
if ( FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle(TEXT("WINHTTP")),
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msgBuf,
0, NULL) )
{
errorString.assign((LPWSTR)msgBuf);
LocalFree(msgBuf);
// Truncate trailing \n\r
if ( errorString.size() > 2 )
errorString.resize(errorString.size());
}
return errorString;
}
static wxString wxWinHTTPQueryHeaderString(HINTERNET hRequest, DWORD dwInfoLevel,
LPCWSTR pwszName = WINHTTP_HEADER_NAME_BY_INDEX)
{
wxString result;
DWORD bufferLen = 0;
::WinHttpQueryHeaders(hRequest, dwInfoLevel, pwszName, NULL, &bufferLen,
WINHTTP_NO_HEADER_INDEX);
if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
wxWCharBuffer resBuf(bufferLen);
if ( ::WinHttpQueryHeaders(hRequest, dwInfoLevel, pwszName,
resBuf.data(), &bufferLen, WINHTTP_NO_HEADER_INDEX) )
result.assign(resBuf);
}
return result;
}
static wxString wxWinHTTPQueryOptionString(HINTERNET hInternet, DWORD dwOption)
{
wxString result;
DWORD bufferLen = 0;
::WinHttpQueryOption(hInternet, dwOption, NULL, &bufferLen);
if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
wxWCharBuffer resBuf(bufferLen);
if ( ::WinHttpQueryOption(hInternet, dwOption, resBuf.data(), &bufferLen) )
result.assign(resBuf);
}
return result;
}
static void CALLBACK wxRequestStatusCallback(
HINTERNET WXUNUSED(hInternet),
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
)
{
if ( dwContext )
{
wxWebRequestWinHTTP* request =
reinterpret_cast<wxWebRequestWinHTTP*>(dwContext);
request->HandleCallback(dwInternetStatus, lpvStatusInformation,
dwStatusInformationLength);
}
}
//
// wxWebRequestWinHTTP
//
wxWebRequestWinHTTP::wxWebRequestWinHTTP(int id, wxWebSessionWinHTTP& session, const wxString& url):
wxWebRequest(id),
m_session(session),
m_url(url),
m_connect(NULL),
m_request(NULL)
{
m_headers = session.GetHeaders();
}
wxWebRequestWinHTTP::~wxWebRequestWinHTTP()
{
if ( m_request )
::WinHttpCloseHandle(m_request);
if ( m_connect )
::WinHttpCloseHandle(m_connect);
}
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;
}
}
else
handleLastError = true;
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
if ( dwStatusInformationLength > 0 )
{
if ( !m_response->ReportAvailableData(dwStatusInformationLength) )
handleLastError = true;
}
else
{
m_response->ReportDataComplete();
SetState(State_Ready);
}
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
{
LPWINHTTP_ASYNC_RESULT asyncResult = reinterpret_cast<LPWINHTTP_ASYNC_RESULT>(lpvStatusInformation);
SetState(State_Failed, wxWinHTTPErrorToString(asyncResult->dwError));
break;
}
}
if (handleLastError)
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
return;
// Parse the URL
wxURI uri(m_url);
bool isSecure = uri.GetScheme().IsSameAs("HTTPS", false);
int port;
if ( !uri.HasPort() )
port = (isSecure) ? 443 : 80;
else
port = wxAtoi(uri.GetPort());
// Open a connction
m_connect = ::WinHttpConnect(m_session.GetHandle(), uri.GetServer().wc_str(),
port, 0);
if ( m_connect == NULL )
{
SetFailedWithLastError();
return;
}
// Open a request
m_request = ::WinHttpOpenRequest(m_connect,
L"GET", uri.GetPath().wc_str(),
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
(isSecure) ? WINHTTP_FLAG_SECURE : 0);
if ( m_request == NULL )
{
SetFailedWithLastError();
return;
}
// Register callback
if ( ::WinHttpSetStatusCallback(m_request,
(WINHTTP_STATUS_CALLBACK)wxRequestStatusCallback,
WINHTTP_CALLBACK_FLAG_READ_COMPLETE |
WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE |
WINHTTP_CALLBACK_FLAG_REQUEST_ERROR,
0) == WINHTTP_INVALID_STATUS_CALLBACK )
{
SetFailedWithLastError();
return;
}
// Combine all headers to a string
wxString allHeaders;
for (wxWebRequestHeaderMap::const_iterator header = m_headers.begin(); header != m_headers.end(); ++header)
allHeaders.append(wxString::Format("%s: %s\n", header->first, header->second));
// Send request
if ( WinHttpSendRequest(m_request,
allHeaders.wc_str(), allHeaders.length(),
NULL, 0, 0,
(DWORD_PTR)this) )
{
SetState(State_Active);
}
else
SetFailedWithLastError();
}
void wxWebRequestWinHTTP::Cancel()
{
// TODO: implement
}
wxWebResponse* wxWebRequestWinHTTP::GetResponse()
{
return m_response.get();
}
//
// wxWebResponseWinHTTP
//
wxWebResponseWinHTTP::wxWebResponseWinHTTP(wxWebRequestWinHTTP& request):
m_request(request),
m_readSize(8 * 1024)
{
wxString contentLengthStr = wxWinHTTPQueryHeaderString(m_request.GetHandle(),
WINHTTP_QUERY_CONTENT_LENGTH);
if ( contentLengthStr.empty() ||
!contentLengthStr.ToLongLong(&m_contentLength) )
m_contentLength = -1;
}
wxString wxWebResponseWinHTTP::GetURL() const
{
return wxWinHTTPQueryOptionString(m_request.GetHandle(), WINHTTP_OPTION_URL);
}
wxString wxWebResponseWinHTTP::GetHeader(const wxString& name) const
{
return wxWinHTTPQueryHeaderString(m_request.GetHandle(),
WINHTTP_QUERY_CUSTOM, name.wc_str());
}
int wxWebResponseWinHTTP::GetStatus() const
{
DWORD status = 0;
DWORD statusSize = sizeof(status);
if ( !::WinHttpQueryHeaders(m_request.GetHandle(),
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &status, &statusSize, 0) )
{
status = 0;
wxLogLastError("WinHttpQueryHeaders/status");
}
return status;
}
wxString wxWebResponseWinHTTP::GetStatusText() const
{
return wxWinHTTPQueryHeaderString(m_request.GetHandle(), WINHTTP_QUERY_STATUS_TEXT);
}
wxInputStream* wxWebResponseWinHTTP::GetStream() const
{
return m_stream.get();
}
wxString wxWebResponseWinHTTP::AsString(wxMBConv* conv) const
{
// TODO: try to determine encoding type from content-type header
if ( !conv )
conv = &wxConvUTF8;
size_t outLen = 0;
return conv->cMB2WC((const char*) m_readBuffer.GetData(), m_readBuffer.GetDataLen(), &outLen);
}
bool wxWebResponseWinHTTP::ReadData()
{
if ( ::WinHttpReadData(m_request.GetHandle(),
m_readBuffer.GetAppendBuf(m_readSize), m_readSize, NULL) )
return true;
else
return false;
}
bool wxWebResponseWinHTTP::ReportAvailableData(DWORD dataLen)
{
m_readBuffer.UngetAppendBuf(dataLen);
return ReadData();
}
void wxWebResponseWinHTTP::ReportDataComplete()
{
m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen()));
}
//
// wxWebSessionWinHTTP
//
wxWebSessionWinHTTP::wxWebSessionWinHTTP():
m_initialized(false),
m_handle(NULL)
{
}
wxWebSessionWinHTTP::~wxWebSessionWinHTTP()
{
if ( m_handle != INVALID_HANDLE_VALUE )
::WinHttpCloseHandle(m_handle);
}
void wxWebSessionWinHTTP::Init()
{
DWORD accessType;
if ( wxCheckOsVersion(6, 3) )
accessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;
else
accessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
m_handle = ::WinHttpOpen(GetHeaders().find("User-Agent")->second.wc_str(), accessType,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS,
WINHTTP_FLAG_ASYNC);
if ( m_handle != NULL )
{
// Try to enable HTTP/2 (available since Win 10 1607)
DWORD protFlags = WINHTTP_PROTOCOL_FLAG_HTTP2;
::WinHttpSetOption(m_handle, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL,
&protFlags, sizeof(protFlags));
// Try to enable GZIP and DEFLATE (available since Win 8.1)
DWORD decompressFlags = WINHTTP_DECOMPRESSION_FLAG_ALL;
::WinHttpSetOption(m_handle, WINHTTP_OPTION_DECOMPRESSION,
&decompressFlags, sizeof(decompressFlags));
// Try to enable modern TLS for older Windows versions
if ( !wxCheckOsVersion(6, 3) )
{
DWORD securityFlags = WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
::WinHttpSetOption(m_handle, WINHTTP_OPTION_SECURE_PROTOCOLS,
&securityFlags, sizeof(securityFlags));
}
}
else
wxLogLastError("WinHttpOpen");
m_initialized = true;
}
wxWebRequest* wxWebSessionWinHTTP::CreateRequest(const wxString& url, int id) wxWebRequest* wxWebSessionWinHTTP::CreateRequest(const wxString& url, int id)
{ {
return NULL; if ( !m_initialized )
} Init();
void wxWebSessionWinHTTP::SetHeader(const wxString& name, const wxString& value)
{
return new wxWebRequestWinHTTP(id, *this, url);
} }
#endif // wxUSE_WEBREQUEST #endif // wxUSE_WEBREQUEST