Files
coco df489d5640 a
2026-07-03 16:05:30 +08:00

2043 lines
58 KiB
C++

#ifndef APP_GENSOFT_IMEJP_BUSINESSPLATFORM_INCLUDE_WINHTTP_HTTP_CLIENT_H
#define APP_GENSOFT_IMEJP_BUSINESSPLATFORM_INCLUDE_WINHTTP_HTTP_CLIENT_H
#include <Windows.h>
#include <winhttp.h>
#include <assert.h>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <memory>
#pragma comment(lib, "Winhttp.lib")
#define USER_AGENT L"BDI18N"
namespace mjz
{
class WinError
: public std::exception
{ // base of all logic-error exceptions
private:
std::string m_str; // the stored message string
public:
explicit WinError(const std::string& message)
: m_str(message)
{ // construct from message string
char buf[16] = {0};
_ultoa_s(::GetLastError(), buf, 16, 10);
m_str.append(" LastError:");
m_str.append(buf);
}
/*
virtual ~WinError() _THROW0()
{ // destroy the object
}
virtual const char *__CLR_OR_THIS_CALL what() const _THROW0()
{ // return pointer to message string
return (m_str.c_str());
}
*/
virtual ~WinError() throw()
{ // destroy the object
}
virtual const char *__CLR_OR_THIS_CALL what() const throw()
{ // return pointer to message string
return (m_str.c_str());
}
#if !_HAS_EXCEPTIONS
protected:
virtual void __CLR_OR_THIS_CALL _Doraise() const
{ // perform class-specific exception handling
_RAISE(*this);
}
#endif /* _HAS_EXCEPTIONS */
};
class RepeatCallError : public std::logic_error
{
public:
explicit RepeatCallError(const std::string& message)
: std::logic_error(message)
{ // construct from message string
}
virtual ~RepeatCallError() throw()
{ // destroy the object
}
};
class CLock
{
PCRITICAL_SECTION m_pCS;
public:
explicit CLock(PCRITICAL_SECTION pcs = NULL)
: m_pCS(NULL)
{
if (pcs) {
Lock(pcs);
}
}
~CLock()
{
if (m_pCS) {
LeaveCriticalSection(m_pCS);
}
}
void Lock(PCRITICAL_SECTION pSection)
{
if (m_pCS) {
throw RepeatCallError("CLock:Lock.");
}
m_pCS = pSection;
EnterCriticalSection(m_pCS);
}
};
template<typename T, typename Closer, T INVALID=NULL>
class HandleBase
{
public:
HandleBase()
: m_h(INVALID) {}
HandleBase(T handle)
: m_h(handle) {}
~HandleBase() {
Close();
}
BOOL IsValid() const {
return m_h!=INVALID;
}
operator T() const {
return m_h;
}
operator BOOL() const {
return m_h!=INVALID;
}
T* operator&() {
return &m_h;
}
void Close() {
if (IsValid()) {
Closer c;
c.operator()(m_h);
m_h=INVALID;
}
}
T Detach() {
T val = m_h;
m_h = INVALID;
return val;
}
void reset(T handle) {
if (m_h!=handle) {
Close();
m_h=handle;
}
}
T get() {
return m_h;
}
void operator=(T handle) {
assert(!IsValid());
reset(handle);
}
protected:
T m_h;
};
class HttpCloser
{
public:
void operator()(HINTERNET h) {
::WinHttpCloseHandle(h);
}
};
class NormalCloser
{
public:
void operator()(HANDLE h) {
::CloseHandle(h);
}
};
class GlobalFreeCloser
{
public:
void operator()(HLOCAL hMem) {
::GlobalFree(hMem);
}
};
typedef HandleBase<HINTERNET, HttpCloser, NULL> HttpHandle;
typedef HandleBase<HANDLE, NormalCloser, NULL> FileHandle;
//typedef HandleBase<HANDLE, NormalCloser, INVALID_HANDLE_VALUE> FileHandle;
typedef HandleBase<HANDLE, NormalCloser, NULL> EventHandle;
typedef HandleBase<HANDLE, GlobalFreeCloser, NULL> GlobalAllocHandle;
class RefHandle
{
volatile long m_lref;
public:
RefHandle(): m_lref(1) {}
void AddRef() {
InterlockedIncrement(&m_lref);
}
void Release() {
if (InterlockedDecrement(&m_lref) == 0) {
delete this;
}
}
protected:
virtual ~RefHandle() {}
};
class CriticalSection
{
protected:
CRITICAL_SECTION m_cs;
public:
CriticalSection() {
if (!::InitializeCriticalSectionAndSpinCount(&m_cs, 4000)) {
throw WinError("InitializeCriticalSectionAndSpinCount");
}
}
virtual ~CriticalSection() {
DeleteCriticalSection(&m_cs);
}
};
class HttpSession : public HttpHandle, public RefHandle
{
public:
inline HttpSession(BOOL bAsync=FALSE);
inline static VOID CALLBACK _AsyncCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength);
};
class ProxyResolver
{
std::vector<std::wstring> m_allProxy; //至少包括一个空代理
protected:
inline static void _GlobalFree(LPWSTR& lpPtr);
inline static BOOL _IsRecoverableAutoProxyError(DWORD dwError);
inline static BOOL _IsErrorValidForProxyFailover(DWORD dwError);
inline static DWORD _GetProxyForAutoSettings(
HINTERNET hSession,
PCWSTR pwszUrl,
PCWSTR pwszAutoConfigUrl,
std::wstring& wsProxy,
std::wstring& wsProxyBypass);
inline void _ParseProxy(const std::wstring& wsProxy, const std::wstring& wsScheme);
public:
ProxyResolver() {}
inline DWORD ResolveProxy(HttpSession* pSession, PCWSTR pwszUrl);
inline DWORD GetProxyServerNum() {return static_cast<DWORD>(m_allProxy.size());}
inline std::wstring GetProxyList();
inline void TakeFirst(LPCWSTR lpProxy);
inline DWORD SetNextProxySetting(HINTERNET hInternet, DWORD dwRequestError, DWORD& dwNextIndex);
};
class HttpRequest;
class HttpConnection : public HttpHandle, public RefHandle, public CriticalSection
{
ProxyResolver m_Proxy;
public:
friend HttpRequest;
inline HttpConnection(HttpSession* pSession, const std::wstring& wsUrl);
ProxyResolver& GetProxy() {return m_Proxy;}
};
enum {ERROR_USER_CANCEL=1000000};
class FileHelper
{
public:
inline static DWORD ReadFile(
HANDLE hFile,
HANDLE hCancelEvent,
ULARGE_INTEGER ullOffSet,
LPVOID lpBuf,
DWORD dwByteNumToRead,
DWORD& dwByteNumReaded);
inline static DWORD WriteFile(
HANDLE hFile,
HANDLE hCancelEvent,
ULARGE_INTEGER ullOffSet,
LPCVOID pBuf,
DWORD dwByteNum);
};
class StringHelper
{
public:
static DWORD HashKey(LPCWSTR Key, SIZE_T nLen = -1)
{
DWORD i = 0;
if (nLen == -1) {
nLen = wcslen(Key);
}
while (nLen-- > 0) {
i = (i << 5) + i + Key[nLen];
}
return i;
}
static void Trim(std::wstring& str)
{
while (!str.empty() && iswspace(*str.begin()))
{
str.erase(str.begin());
}
while (!str.empty() && iswspace(*str.rbegin()))
{
str.erase(str.size()-1);
}
}
};
template<typename T>
class SmartPtr
{
T* m_ptr;
public:
SmartPtr(T* ptr) : m_ptr(ptr) {}
~SmartPtr() {
release();
}
operator T* () {
return m_ptr;
}
T& operator*() {
return *m_ptr;
}
T** operator&() {
return &m_ptr;
}
T* operator->() {
return m_ptr;
}
T* get() {
return m_ptr;
}
T* detach() {
T* pTemp = m_ptr;
m_ptr = NULL;
return pTemp;
}
void release() {
T* pTemp = m_ptr;
if (pTemp) {
m_ptr = NULL;
pTemp->Release();
}
}
void reset(T* ptr) {
release();
m_ptr = ptr;
}
};
typedef bool (*PROGRESSPROC)(double);
#define VERB_GET L"GET"
#define VERB_POST L"POST"
class HttpRequest : public RefHandle, public CriticalSection
{
public:
static const DWORD WRITE_SIZE = 8092;
static const DWORD MAX_READ_BUF_SIZE = 1024*1024;
static const ULONGLONG INVALID_CONTENT_SIZE = -1;
public:
friend HttpSession;
friend HttpConnection;
inline HttpRequest(const std::wstring &url,
HttpSession* pSession=NULL,
HttpConnection* pConnection=NULL);
inline void SetProgressProc(PROGRESSPROC progressProc);
inline void SetAdditionalRequestHeaders(const std::wstring &additionalRequestHeaders);
inline void SetLocalFileToSaveResponse(const CString& file, BOOL bAllowPartialTranfer=FALSE);//get 接口
inline void SetAdditionalDataToSend(BYTE *data, DWORD dataSize);//POST 接口
inline void SetCookie(const std::wstring& wsCookie);
inline void SetCallBackMsg(HWND hWnd, DWORD dwMsg);
inline void SetCallBackEvent(HANDLE hEvent);
inline void SetCancelEvent(HANDLE hEvent);
inline void SetMaxTestRequestNum(SIZE_T nNum) {m_nMaxTestRequestNum = nNum;}
inline DWORD Send(LPCWSTR httpVerb = L"GET");
inline void Cancel();
inline BOOL WaitResult(DWORD dwTimeout = INFINITE);
inline std::wstring GetResponseHeader();
inline std::wstring GetResponseCookies();
inline DWORD GetStatusCode();
inline const std::string& GetResponseBuf();
inline std::wstring GetResponseCharset();
protected:
inline ~HttpRequest(void);
inline BOOL _LoadPartialTranferInfo();
inline DWORD _SavPartialTranferInfo();
inline DWORD _Finishing(DWORD dwErrorCode);
inline DWORD _Finished(DWORD dwErrorCode);
inline DWORD _Finished();
inline DWORD _ReSendRequestWithProxy(HINTERNET hRequest, DWORD dwLastRequestError);
inline DWORD _ReadData(DWORD dwDataSize);
inline DWORD _WriteData();
inline DWORD _OnReadComplete(DWORD dwByteNumReaded, DWORD dwError = ERROR_SUCCESS);
inline DWORD _OnWriteComplete(DWORD dwByteNumWritten);
inline DWORD _OnHeadersAvailable();
inline DWORD _SaveData(LPBYTE lpBuf, DWORD dwDataLength);
inline void _SetProgress();
inline DWORD _GetAdditionalDataSize();
inline LPBYTE _GetAdditionalDataBuf();
inline DWORD _QueryHeaderStringInfo(std::wstring& wsInfo, DWORD dwInfoLevel, LPDWORD lpdwIndex);
inline void _AttachRequest(HINTERNET hRequest);
inline void _ClearTestRequest(HINTERNET hRequest);
protected:
SmartPtr<HttpSession> m_pSession;
SmartPtr<HttpConnection> m_pConnection;
std::set<HINTERNET> m_testRequestSet;
SIZE_T m_nMaxTestRequestNum;
volatile HINTERNET m_hRequest;
DWORD m_dwProxyIndex;
std::wstring m_wsURL;
std::wstring m_wsHost;
std::wstring m_wsPath;
std::wstring m_wsExtraInfo;
std::wstring m_wsUserName;
std::wstring m_wsPassword;
INTERNET_SCHEME m_nScheme;
PROGRESSPROC m_pfProcessProc;
DWORD m_dwHttpError;
EventHandle m_hFinishedEvent;
EventHandle m_hCancelEvent;
std::string m_sReadBuf;
DWORD m_dwBufSize;
std::auto_ptr<BYTE> m_pAdditionalData;
std::wstring m_wsVerb;
std::wstring m_wsAdditionalRequestHeaders;
std::wstring m_wsAdditionalRequestCookies;
std::wstring m_wsResponseHeader;
std::wstring m_wsResponseCookies;
std::wstring m_wsLastModifyTime;
std::wstring m_sResponseCharset;
std::string m_sResponseBuf;
ULARGE_INTEGER m_ullContentBegin;
ULARGE_INTEGER m_ullContentEnd;
ULARGE_INTEGER m_ullContentTotalSize;
ULARGE_INTEGER m_ullDataTransfered;
DWORD m_dwStatusCode;
//std::wstring m_wsLocalFileToSaveResponse;
CString m_wsLocalFileToSaveResponse;
BOOL m_bAllowPartialTranfer;
BOOL m_bOnlyCheckModify;
FileHandle m_hFile;
FileHandle m_hInfoFile;
ULARGE_INTEGER m_ullFilePoint;
HWND m_hCallBackWnd;
DWORD m_dwCallBackMsg;
EventHandle m_hCallBackEvent;
};
HttpSession::HttpSession(BOOL bAsync/*=FALSE*/)
{
m_h = ::WinHttpOpen(USER_AGENT,
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
bAsync ? WINHTTP_FLAG_ASYNC : 0);
if (m_h == NULL) {
throw WinError("WinHttpOpen failed.");
}
if (bAsync && ::WinHttpSetStatusCallback(m_h,
(WINHTTP_STATUS_CALLBACK)_AsyncCallback,
WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
0) == WINHTTP_INVALID_STATUS_CALLBACK)
{
throw WinError("WinHttpSetStatusCallback failed.");
}
}
VOID CALLBACK HttpSession::_AsyncCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
)
{
DWORD dwError = ERROR_SUCCESS;
HttpRequest* pClient = (HttpRequest*)dwContext;
if (pClient == NULL)
{
return;
}
CLock lock1;
if (dwInternetStatus != WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
{
// If we're going to try use the request handle then we'd better lock it.
lock1.Lock(&pClient->m_cs);
}
switch (dwInternetStatus)
{
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
if (pClient->m_hRequest == NULL)
{
pClient->_AttachRequest(hInternet);
pClient->_ClearTestRequest(NULL);
if (pClient->m_wsVerb == VERB_GET)
{
if (!::WinHttpReceiveResponse(pClient->m_hRequest, NULL))
{
pClient->_Finishing(GetLastError());
}
}
else
{
pClient->_WriteData();
}
}
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
{
WINHTTP_ASYNC_RESULT*pWinhttpAsyncResult = (WINHTTP_ASYNC_RESULT*)lpvStatusInformation;
switch (pWinhttpAsyncResult->dwResult)
{
case API_SEND_REQUEST:
if (pClient->m_hRequest)
{
pClient->_ClearTestRequest(hInternet);
}
else if (ERROR_SUCCESS
!= pClient->_ReSendRequestWithProxy(hInternet, pWinhttpAsyncResult->dwError))
{
pClient->_ClearTestRequest(hInternet);
}
break;
case API_QUERY_DATA_AVAILABLE:
case API_READ_DATA:
pClient->_OnReadComplete(0, pWinhttpAsyncResult->dwError);
break;
case API_RECEIVE_RESPONSE:
case API_WRITE_DATA:
pClient->_Finishing(pWinhttpAsyncResult->dwError);
break;
default:
break;
}
}
break;
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
dwError = pClient->_OnHeadersAvailable();
break;
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
dwError = pClient->_ReadData(*(LPDWORD)lpvStatusInformation);
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
dwError = pClient->_OnReadComplete(dwStatusInformationLength);
break;
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
dwError = pClient->_OnWriteComplete(*(LPDWORD)lpvStatusInformation);
break;
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
// Garanted last callback this context will ever receive. Release
// context when we're done on behalf of all callbacks. (Balances the
// reference we took when we called WinHttpSendRequest)
if (pClient->m_hRequest == hInternet)
{
pClient->_Finished();
}
pClient->Release();
break;
default:
break;
}
}
HttpConnection::HttpConnection(HttpSession* pSession, const std::wstring& wsUrl)
{
URL_COMPONENTS url_component;
::ZeroMemory(&url_component, sizeof(URL_COMPONENTS));
url_component.dwStructSize = sizeof(URL_COMPONENTS);
wchar_t szHostName[256] = {0};
url_component.lpszHostName = szHostName;
url_component.dwHostNameLength = 256;
if (!::WinHttpCrackUrl(wsUrl.c_str(), (DWORD)wsUrl.length(), ICU_ESCAPE, &url_component))
{
throw WinError("WinHttpCrackUrl");
}
szHostName[url_component.dwHostNameLength] = 0;
m_h = ::WinHttpConnect(
pSession->get(),
szHostName,
url_component.nPort,
0);
if (m_h == NULL) {
throw WinError("WinHttpConnect");
}
m_Proxy.ResolveProxy(pSession, wsUrl.c_str());
}
void ProxyResolver::_GlobalFree(LPWSTR& lpPtr)
{
if (lpPtr) {
::GlobalFree(lpPtr);
lpPtr = NULL;
}
}
BOOL ProxyResolver::_IsRecoverableAutoProxyError(DWORD dwError)
{
BOOL fRecoverable = FALSE;
switch (dwError)
{
case ERROR_SUCCESS:
case ERROR_INVALID_PARAMETER:
case ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR:
case ERROR_WINHTTP_AUTODETECTION_FAILED:
case ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT:
case ERROR_WINHTTP_LOGIN_FAILURE:
case ERROR_WINHTTP_OPERATION_CANCELLED:
case ERROR_WINHTTP_TIMEOUT:
case ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT:
case ERROR_WINHTTP_UNRECOGNIZED_SCHEME:
fRecoverable = TRUE;
break;
default:
break;
}
return fRecoverable;
}
BOOL ProxyResolver::_IsErrorValidForProxyFailover(DWORD dwError)
{
BOOL fValid = FALSE;
switch (dwError)
{
case ERROR_WINHTTP_NAME_NOT_RESOLVED:
case ERROR_WINHTTP_CANNOT_CONNECT:
case ERROR_WINHTTP_CONNECTION_ERROR:
case ERROR_WINHTTP_TIMEOUT:
fValid = TRUE;
break;
default:
break;
}
return fValid;
}
DWORD ProxyResolver::_GetProxyForAutoSettings(
HINTERNET hSession,
PCWSTR pwszUrl,
PCWSTR pwszAutoConfigUrl,
std::wstring& wsProxy,
std::wstring& wsProxyBypass)
{
DWORD dwError = ERROR_SUCCESS;
WINHTTP_AUTOPROXY_OPTIONS waoOptions = {0};
WINHTTP_PROXY_INFO wpiProxyInfo = {0};
if (pwszAutoConfigUrl)
{
waoOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
waoOptions.lpszAutoConfigUrl = pwszAutoConfigUrl;
}
else
{
waoOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
waoOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
}
// First call with no autologon. Autologon prevents the
// session (in proc) or autoproxy service (out of proc) from caching
// the proxy script. This causes repetitive network traffic, so it is
// best not to do autologon unless it is required according to the
// result of WinHttpGetProxyForUrl.
if (!::WinHttpGetProxyForUrl(hSession,
pwszUrl,
&waoOptions,
&wpiProxyInfo))
{
dwError = ::GetLastError();
if (dwError != ERROR_WINHTTP_LOGIN_FAILURE)
{
goto quit;
}
// Enable autologon if challenged.
dwError = ERROR_SUCCESS;
waoOptions.fAutoLogonIfChallenged = TRUE;
if (!::WinHttpGetProxyForUrl(hSession,
pwszUrl,
&waoOptions,
&wpiProxyInfo))
{
dwError = ::GetLastError();
goto quit;
}
}
if (wpiProxyInfo.lpszProxy) {
wsProxy = wpiProxyInfo.lpszProxy;
}
if (wpiProxyInfo.lpszProxyBypass) {
wsProxyBypass = wpiProxyInfo.lpszProxyBypass;
}
quit:
_GlobalFree(wpiProxyInfo.lpszProxy);
_GlobalFree(wpiProxyInfo.lpszProxyBypass);
return dwError;
}
void ProxyResolver::_ParseProxy(const std::wstring& wsProxy, const std::wstring& wsScheme)
{
if (wsProxy.empty())
return;
wchar_t* pNextProxy = NULL;
wchar_t* pProxy = wcstok_s(const_cast<wchar_t*>(wsProxy.c_str()), L"; ", &pNextProxy);
LPCWSTR lpEqualPos = NULL;
while (pProxy != NULL)
{
std::wstring wsItem;
if (lpEqualPos = wcsstr(pProxy, L"="))
{
std::wstring wsSchemePre(pProxy, lpEqualPos-pProxy);
if (_wcsicmp(wsScheme.c_str(), wsSchemePre.c_str())==0)
{
wsItem = pProxy+wsSchemePre.length()+1;
}
} else {
wsItem = pProxy;
}
StringHelper::Trim(wsItem);
size_t i=0;
while (i < m_allProxy.size())
{
if (m_allProxy[i] == wsItem)
{
break;
}
++i;
}
if (i == m_allProxy.size())
{
m_allProxy.push_back(wsItem);
}
pProxy = wcstok_s(NULL, L"; ", &pNextProxy);
}
}
DWORD ProxyResolver::ResolveProxy(HttpSession* pSession, PCWSTR pwszUrl)
{
DWORD dwError = ERROR_SUCCESS;
m_allProxy.clear();
m_allProxy.push_back(L""); //空代理,表示不使用代理
std::wstring wsProxy;
std::wstring wsProxyBypass;
LPCWSTR lpSchemeEnd = wcsstr(pwszUrl, L"://");
if (lpSchemeEnd == NULL)
{
throw std::invalid_argument("url");
}
std::wstring wsScheme(pwszUrl, lpSchemeEnd-pwszUrl);
//获取winhttp 默认代理
WINHTTP_PROXY_INFO DefProxyInfo = {0};
if (::WinHttpGetDefaultProxyConfiguration(&DefProxyInfo))
{
if (DefProxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY && DefProxyInfo.lpszProxy)
{
_ParseProxy(DefProxyInfo.lpszProxy, wsScheme);
}
_GlobalFree(DefProxyInfo.lpszProxy);
_GlobalFree(DefProxyInfo.lpszProxyBypass);
}
//获取ie代理
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyConfig = {};
if (!::WinHttpGetIEProxyConfigForCurrentUser(&IEProxyConfig))
{
dwError = GetLastError();
if (dwError != ERROR_FILE_NOT_FOUND)
{
goto quit;
}
// No IE proxy settings found, just do autodetect.
IEProxyConfig.fAutoDetect = TRUE;
dwError = ERROR_SUCCESS;
}
// Begin processing the proxy settings in the following order:
// 1) Auto-Detect if configured.
// 2) Auto-Config URL if configured.
// 3) Static Proxy Settings if configured.
//
// In the event one mechanism fails with an expected error code it is
// required to fall back to the next mechanism. If the request fails
// after exhausting all detected proxies, there should be no attempt
// to discover additional proxies.
if (IEProxyConfig.fAutoDetect)
{
// Detect Proxy Settings
dwError = _GetProxyForAutoSettings(pSession->get(),
pwszUrl,
NULL,
wsProxy,
wsProxyBypass);
if (dwError == ERROR_SUCCESS)
{
_ParseProxy(wsProxy, wsScheme);
}
if (!_IsRecoverableAutoProxyError(dwError))
{
goto quit;
}
// Fall back to Autoconfig URL or Static settings. Application can
// optionally take some action such as logging, or creating a mechanism
// to expose multiple error codes in the class.
dwError = ERROR_SUCCESS;
}
if (IEProxyConfig.lpszAutoConfigUrl)
{
// Run autoproxy with AutoConfig URL.
dwError = _GetProxyForAutoSettings(pSession->get(),
pwszUrl,
IEProxyConfig.lpszAutoConfigUrl,
wsProxy,
wsProxyBypass);
if (dwError == ERROR_SUCCESS)
{
_ParseProxy(wsProxy, wsScheme);
}
if (!_IsRecoverableAutoProxyError(dwError))
{
goto quit;
}
// Fall back to Static Settings. Application can optionally take some
// action such as logging, or creating a mechanism to to expose multiple
// error codes in the class.
dwError = ERROR_SUCCESS;
}
// Static Proxy Config. Failover is not valid for static proxy since
// it is always either a single proxy or a list containing protocol
// specific proxies such as "proxy" or http=httpproxy;https=sslproxy
if (IEProxyConfig.lpszProxy) {
wsProxy = IEProxyConfig.lpszProxy;
}
if (IEProxyConfig.lpszProxyBypass) {
wsProxyBypass = IEProxyConfig.lpszProxyBypass;
}
_ParseProxy(wsProxy, wsScheme);
quit:
_GlobalFree(IEProxyConfig.lpszProxy);
_GlobalFree(IEProxyConfig.lpszAutoConfigUrl);
_GlobalFree(IEProxyConfig.lpszProxyBypass);
return dwError;
}
DWORD ProxyResolver::SetNextProxySetting(HINTERNET hInternet, DWORD dwRequestError, DWORD& dwNextIndex)
{
DWORD dwError = ERROR_SUCCESS;
PWSTR pwszCursor = NULL;
WINHTTP_PROXY_INFO NextProxyInfo = {0};
if (!_IsErrorValidForProxyFailover(dwRequestError))
{
return ERROR_NO_MORE_ITEMS;
}
if (m_allProxy.size() > dwNextIndex)
{
WINHTTP_PROXY_INFO NextProxyInfo = {0};
NextProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
if (!m_allProxy[dwNextIndex].empty())
{
NextProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
NextProxyInfo.lpszProxy = const_cast<wchar_t*>(m_allProxy[dwNextIndex].c_str());
}
if (!::WinHttpSetOption(hInternet,
WINHTTP_OPTION_PROXY,
&NextProxyInfo,
sizeof(NextProxyInfo)))
{
return ::GetLastError();
}
dwNextIndex ++;
return ERROR_SUCCESS;
}
else
{
return ERROR_NO_MORE_ITEMS;
}
}
void ProxyResolver::TakeFirst(LPCWSTR lpProxy)
{
//proxy set 172.12.22.22:8234
// lpProxy 172.12.22.22
size_t i= 0;
while (i < m_allProxy.size()) {
if (m_allProxy[i] == lpProxy)
{
if (i > 0)
{
m_allProxy.erase(m_allProxy.begin()+i);
m_allProxy.insert(m_allProxy.begin(), lpProxy);
}
break;
}
else if (*lpProxy != L'\0' && wcsstr(m_allProxy[i].c_str(), lpProxy) != NULL)
{
if (i > 0) {
std::wstring wsItem = m_allProxy[i];
m_allProxy.erase(m_allProxy.begin()+i);
m_allProxy.insert(m_allProxy.begin(), wsItem);
}
}
++ i;
}
}
std::wstring ProxyResolver::GetProxyList()
{
std::wstring wsOut;
std::vector<std::wstring>::iterator it = m_allProxy.begin();
while (it != m_allProxy.end())
{
if (!it->empty())
{
if (it != m_allProxy.begin()) {
wsOut += L"; ";
}
wsOut += *it;
}
++ it;
}
return wsOut;
}
DWORD FileHelper::ReadFile(
HANDLE hFile,
HANDLE hCancelEvent,
ULARGE_INTEGER ullOffSet,
LPVOID lpBuf,
DWORD dwByteNumToRead,
DWORD& dwByteNumReaded)
{
dwByteNumReaded = 0;
OVERLAPPED oa = {0};
oa.Offset = ullOffSet.LowPart;
oa.OffsetHigh = ullOffSet.HighPart;
oa.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
EventHandle h(oa.hEvent);
BOOL bRt = ::ReadFile(hFile, lpBuf, dwByteNumToRead, &dwByteNumReaded, &oa);
if (bRt)
{
return dwByteNumReaded>0 ? ERROR_SUCCESS : ERROR_HANDLE_EOF;
}
else
{
DWORD dwError = ::GetLastError();
if (dwError == ERROR_IO_PENDING)
{
HANDLE hWaitEvent[] = {hCancelEvent, oa.hEvent};
DWORD dwRt = ::WaitForMultipleObjects(ARRAYSIZE(hWaitEvent), hWaitEvent, FALSE, INFINITE);
if (dwRt == WAIT_OBJECT_0)
{
::CancelIo(hFile);
return ERROR_USER_CANCEL;
}
else if (dwRt == WAIT_OBJECT_0+1)
{
if (::GetOverlappedResult(hFile, &oa, &dwByteNumReaded, FALSE))
{
return dwByteNumReaded>0 ? ERROR_SUCCESS : ERROR_HANDLE_EOF;
}
}
}
}
return ::GetLastError();
}
DWORD FileHelper::WriteFile(
HANDLE hFile,
HANDLE hCancelEvent,
ULARGE_INTEGER ullOffSet,
LPCVOID pBuf,
DWORD dwByteNum)
{
DWORD dwNumOfByteToWrite = dwByteNum;
DWORD dwNumOfByteWritten = 0;
OVERLAPPED oa = {0};
oa.Offset = ullOffSet.LowPart;
oa.OffsetHigh = ullOffSet.HighPart;
oa.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
EventHandle event(oa.hEvent);
BOOL bRt = ::WriteFile(hFile, pBuf, dwNumOfByteToWrite, &dwNumOfByteWritten, &oa);
if (bRt && dwNumOfByteWritten == dwNumOfByteToWrite)
{
return ERROR_SUCCESS;
}
else if (!bRt && GetLastError() == ERROR_IO_PENDING)
{
HANDLE hWaitEvent[] = {hCancelEvent, oa.hEvent};
DWORD dwRt = ::WaitForMultipleObjects(ARRAYSIZE(hWaitEvent), hWaitEvent, FALSE, INFINITE);
if (dwRt == WAIT_OBJECT_0)
{
::CancelIo(hFile);
return ERROR_USER_CANCEL;
}
else if (dwRt == WAIT_OBJECT_0+1)
{
if (::GetOverlappedResult(hFile, &oa, &dwNumOfByteWritten, FALSE)
&& dwNumOfByteWritten==dwNumOfByteToWrite)
{
return ERROR_SUCCESS;
}
}
return ::GetLastError();
}
else
{
throw WinError("WriteFile error.");
}
}
HttpRequest::HttpRequest(
const std::wstring &wsUrl,
HttpSession* pSession/*=NULL*/,
HttpConnection* pConnection/*=NULL*/)
: m_pSession(pSession)
, m_pConnection(pConnection)
, m_hRequest(NULL)
, m_dwProxyIndex(0)
, m_wsURL(wsUrl)
, m_dwHttpError(ERROR_SUCCESS)
, m_dwStatusCode(-1)
, m_hCallBackWnd(NULL)
, m_dwCallBackMsg(NULL)
, m_bAllowPartialTranfer(FALSE)
, m_bOnlyCheckModify(FALSE)
, m_nMaxTestRequestNum(1)
{
m_ullFilePoint.QuadPart = 0;
m_ullContentBegin.QuadPart = INVALID_CONTENT_SIZE;
m_ullContentEnd.QuadPart = INVALID_CONTENT_SIZE;
m_ullContentTotalSize.QuadPart = INVALID_CONTENT_SIZE;
m_ullDataTransfered.QuadPart = 0;
m_pfProcessProc = NULL;
m_hFinishedEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_hCancelEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_pSession.get() == NULL) {
m_pSession.reset(new HttpSession(TRUE));
} else {
m_pSession->AddRef();
}
if (m_pConnection.get() == NULL) {
m_pConnection.reset(new HttpConnection(m_pSession, m_wsURL));
} else {
m_pConnection->AddRef();
}
URL_COMPONENTS url_component;
::ZeroMemory(&url_component, sizeof(URL_COMPONENTS));
url_component.dwStructSize = sizeof(URL_COMPONENTS);
wchar_t szHostName[256] = {0};
url_component.lpszHostName = szHostName;
url_component.dwHostNameLength = 256;
wchar_t szUrlPath[512] = {0};
url_component.lpszUrlPath = szUrlPath;
url_component.dwUrlPathLength = 512;
std::auto_ptr<wchar_t> pExtraInfo(new wchar_t[wsUrl.size()*4]);
url_component.lpszExtraInfo = pExtraInfo.get();
url_component.dwExtraInfoLength = (DWORD)wsUrl.size()*4;
wchar_t szUserName[256] = {0};
url_component.lpszUserName = szUserName;
url_component.dwUserNameLength = 256;
wchar_t szPassword[256] = {0};
url_component.lpszPassword = szPassword;
url_component.dwPasswordLength = 256;
if (!::WinHttpCrackUrl(wsUrl.c_str(), (DWORD)wsUrl.length(), ICU_ESCAPE, &url_component))
{
throw WinError("WinHttpCrackUrl");
}
m_wsHost.assign(url_component.lpszHostName, url_component.dwHostNameLength);
m_wsPath.assign(url_component.lpszUrlPath, url_component.dwUrlPathLength);
m_wsExtraInfo.assign(url_component.lpszExtraInfo, url_component.dwExtraInfoLength);
m_wsPassword.assign(url_component.lpszPassword, url_component.dwPasswordLength);
m_wsUserName.assign(url_component.lpszUserName, url_component.dwUserNameLength);
m_nScheme = url_component.nScheme;
}
HttpRequest::~HttpRequest(void)
{
}
DWORD HttpRequest::Send(LPCWSTR httpVerb /*= L"GET"*/)
{
if (_wcsicmp(httpVerb, VERB_GET) == 0)
{
m_wsVerb = VERB_GET;
}
else if (_wcsicmp(httpVerb, VERB_POST) == 0)
{
m_wsVerb = VERB_POST;
}
else
{
throw std::invalid_argument("verb error.");
}
DWORD dwOpenRequestFlag =
(m_nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
std::wstring wsObjectName = m_wsPath + m_wsExtraInfo;
CLock lock(&m_pConnection->m_cs);
for (DWORD i=0; i<m_pConnection->GetProxy().GetProxyServerNum(); ++i)
{
HttpHandle hRequest = ::WinHttpOpenRequest(
m_pConnection->get(),
m_wsVerb.c_str(),
wsObjectName.c_str(),
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
dwOpenRequestFlag);
if (!hRequest)
{
throw WinError("WinHttpOpenRequest");
}
if (!m_wsUserName.empty())
{
if (!::WinHttpSetCredentials(
hRequest,
WINHTTP_AUTH_TARGET_SERVER,
WINHTTP_AUTH_SCHEME_BASIC,
m_wsUserName.c_str(),
m_wsPassword.c_str(),
NULL))
{
throw WinError("WinHttpSetCredentials");
}
}
if (m_bOnlyCheckModify)
{
assert(!m_wsLastModifyTime.empty());
if (!m_wsLastModifyTime.empty())
{
std::wstring wsRange(L"If-Modified-Since: ");
wsRange += (m_wsLastModifyTime + L"\r\n");
if (!::WinHttpAddRequestHeaders(
hRequest,
wsRange.c_str(),
static_cast<DWORD>(wsRange.size()),
WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE))
{
throw WinError("WinHttpAddRequestHeaders");
}
}
}
else if (m_ullContentBegin.QuadPart != INVALID_CONTENT_SIZE)
{
assert(!m_wsLastModifyTime.empty());
//TCHAR szRange[256];
//swprintf_s(szRange, 256, _T(""), );
CString szRange;
szRange.Format(_T("Range: bytes=%I64u-\r\n"), m_ullContentBegin.QuadPart);
if (!::WinHttpAddRequestHeaders(
hRequest,
szRange.AllocSysString(),
-1,
WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE))
{
throw WinError("WinHttpAddRequestHeaders");
}
if (!m_wsLastModifyTime.empty())
{
std::wstring wsRange(L"Unless-Modified-Since: ");
wsRange += (m_wsLastModifyTime + L"\r\n");
if (!::WinHttpAddRequestHeaders(
hRequest,
wsRange.c_str(),
static_cast<DWORD>(wsRange.size()),
WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE))
{
throw WinError("WinHttpAddRequestHeaders");
}
}
}
if (!m_wsAdditionalRequestHeaders.empty())
{
if (!::WinHttpAddRequestHeaders(
hRequest,
m_wsAdditionalRequestHeaders.c_str(),
static_cast<DWORD>(m_wsAdditionalRequestHeaders.size()),
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
{
throw WinError("WinHttpAddRequestHeaders");
}
}
if (!m_wsAdditionalRequestCookies.empty())
{
std::wstring cookies = L"Cookie: ";
cookies += m_wsAdditionalRequestCookies;
if (!::WinHttpAddRequestHeaders(hRequest,
cookies.c_str(),
static_cast<DWORD>(cookies.size()),
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
{
throw WinError("WinHttpAddRequestHeaders");
}
}
if (ERROR_SUCCESS
!= m_pConnection->GetProxy().SetNextProxySetting(hRequest, ERROR_WINHTTP_TIMEOUT, m_dwProxyIndex))
{
break;
}
m_testRequestSet.insert(hRequest.Detach());
if (m_nMaxTestRequestNum <= m_testRequestSet.size())
{
break;
}
}
std::set<HINTERNET>::iterator it = m_testRequestSet.begin();
while (it != m_testRequestSet.end())
{
AddRef();
DWORD_PTR dwPtr = (DWORD_PTR)this;
DWORD dwTotalLength = _GetAdditionalDataSize();
if (!::WinHttpSendRequest(*it,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
dwTotalLength,
dwPtr))
{
throw WinError("WinHttpSendRequest");
}
++ it;
}
//异步操作,需要到_AsyncCallback去处理
return ERROR_SUCCESS;
}
DWORD HttpRequest::_ReSendRequestWithProxy(HINTERNET hRequest, DWORD dwLastRequestError)
{
DWORD dwError = m_pConnection->GetProxy().SetNextProxySetting(hRequest,
dwLastRequestError, m_dwProxyIndex);
if (dwError != ERROR_SUCCESS)
{
return dwLastRequestError;
}
else
{
DWORD dwTotalLength = _GetAdditionalDataSize();
if (!::WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
dwTotalLength,
NULL))
{
return dwLastRequestError;
}
return ERROR_SUCCESS;
}
}
BOOL HttpRequest::WaitResult(DWORD dwTimeout)
{
if (WAIT_OBJECT_0 != ::WaitForSingleObject(m_hFinishedEvent, dwTimeout))
{
Cancel();
::WaitForSingleObject(m_hFinishedEvent, INFINITE);
return FALSE;
}
if (m_wsVerb == L"GET")
{
return m_dwHttpError == ERROR_SUCCESS;
}
else
{
return m_dwHttpError == ERROR_SUCCESS;
}
}
void HttpRequest::Cancel()
{
CLock lock(&m_cs);
if (!::SetEvent(m_hCancelEvent))
{
throw WinError("SetEvent");
}
_Finishing(ERROR_USER_CANCEL);
}
DWORD HttpRequest::_Finishing(DWORD dwErrorCode)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hCancelEvent, 0))
{
m_dwHttpError = ERROR_USER_CANCEL;
} else {
m_dwHttpError = dwErrorCode;
}
if (m_hRequest) {
::WinHttpCloseHandle(m_hRequest); //这里不置0,需要保留原值,用于比对
}
return dwErrorCode;
}
std::wstring HttpRequest::GetResponseHeader()
{
CLock lock(&m_cs);
return m_wsResponseHeader;
}
DWORD HttpRequest::_Finished()
{
m_hFile.Close();
m_hInfoFile.Close();
::SetEvent(m_hFinishedEvent);
if (m_hCallBackEvent) {
::SetEvent(m_hCallBackEvent);
}
if (m_hCallBackWnd) {
::PostMessage(m_hCallBackWnd, m_dwCallBackMsg, (WPARAM)this, (LPARAM)WaitResult());
}
return m_dwHttpError;
}
DWORD HttpRequest::_Finished(DWORD dwErrorCode)
{
m_dwHttpError = dwErrorCode;
if (m_hRequest) {
::WinHttpCloseHandle(m_hRequest); //这里不置0,需要保留原值,用于比对
}
_Finished();
return dwErrorCode;
}
DWORD HttpRequest::_QueryHeaderStringInfo(
std::wstring& wsInfo,
DWORD dwInfoLevel,
LPDWORD lpdwIndex)
{
DWORD dwSize = 0;
::WinHttpQueryHeaders(m_hRequest,
dwInfoLevel,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER,
&dwSize,
lpdwIndex);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
wsInfo.resize(dwSize/2, 0);
if (::WinHttpQueryHeaders(m_hRequest,
dwInfoLevel,
WINHTTP_HEADER_NAME_BY_INDEX,
(LPWSTR)wsInfo.data(),
&dwSize,
lpdwIndex))
{
wsInfo.resize(dwSize/2, 0);
return ERROR_SUCCESS;
}
else
{
wsInfo.clear();
}
}
return ::GetLastError();
}
DWORD HttpRequest::_OnHeadersAvailable()
{
DWORD dwRt = _QueryHeaderStringInfo(
m_wsResponseHeader, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_NO_HEADER_INDEX);
if (dwRt != ERROR_SUCCESS)
{
return _Finishing(dwRt);
}
DWORD dwInfoLevel = WINHTTP_QUERY_FLAG_NUMBER | WINHTTP_QUERY_STATUS_CODE;
DWORD dwLength = sizeof(m_dwStatusCode);
if (!::WinHttpQueryHeaders(m_hRequest,
dwInfoLevel,
WINHTTP_HEADER_NAME_BY_INDEX,
&m_dwStatusCode,
&dwLength,
WINHTTP_NO_HEADER_INDEX))
{
return _Finishing(::GetLastError());
}
if (m_dwStatusCode < HTTP_STATUS_FIRST || m_dwStatusCode > HTTP_STATUS_LAST)
{
throw std::runtime_error("invalid status code");
}
if (m_wsVerb == VERB_GET)
{
//cookie
m_wsResponseCookies.clear();
DWORD dwIndex = 0;
do
{
std::wstring wsCookie;
dwRt = _QueryHeaderStringInfo(
wsCookie, WINHTTP_QUERY_SET_COOKIE, &dwIndex);
if (dwRt == ERROR_SUCCESS)
{
if (m_wsResponseCookies.size() > 0)
{
m_wsResponseCookies += L"; ";
}
StringHelper::Trim(wsCookie);
m_wsResponseCookies += wsCookie;
}
else if (m_wsResponseCookies.size()>0 && *m_wsResponseCookies.rbegin() != L';')
{
m_wsResponseCookies.append(1, L';');
}
} while (dwRt == ERROR_SUCCESS);
//last modify time
dwRt = _QueryHeaderStringInfo(
m_wsLastModifyTime, WINHTTP_QUERY_LAST_MODIFIED, WINHTTP_NO_HEADER_INDEX);
if (m_wsLastModifyTime.empty())
{
m_bAllowPartialTranfer = FALSE;
}
// charset
std::wstring wsContentType;
dwRt = _QueryHeaderStringInfo(
wsContentType, WINHTTP_QUERY_CONTENT_TYPE, WINHTTP_NO_HEADER_INDEX);
if (!wsContentType.empty() && wsContentType.find(L"charset=")!=std::wstring::npos)
{
wchar_t szType[256] = {0};
wchar_t szCharset[256] = {0};
swscanf_s(wsContentType.c_str(), L"%[^;]; charset=%s",
szType, ARRAYSIZE(szType), szCharset, ARRAYSIZE(szCharset));
m_sResponseCharset = szCharset;
}
if (m_dwStatusCode == HTTP_STATUS_OK) //200
{
m_ullFilePoint.QuadPart = 0;
m_ullContentBegin.QuadPart = 0;
dwInfoLevel = WINHTTP_QUERY_FLAG_NUMBER | WINHTTP_QUERY_CONTENT_LENGTH;
ULONGLONG uContentLength = 0;
dwLength = sizeof(uContentLength);
if (::WinHttpQueryHeaders(m_hRequest,
dwInfoLevel,
WINHTTP_HEADER_NAME_BY_INDEX,
&uContentLength,
&dwLength,
WINHTTP_NO_HEADER_INDEX))
{
m_ullContentTotalSize.QuadPart = uContentLength;
m_ullContentEnd.QuadPart = uContentLength-1;
}
}
else if (m_dwStatusCode == HTTP_STATUS_PARTIAL_CONTENT) //206
{
std::wstring wsContentRange;
dwRt = _QueryHeaderStringInfo(
wsContentRange, WINHTTP_QUERY_CONTENT_RANGE, WINHTTP_NO_HEADER_INDEX);
if (dwRt == ERROR_SUCCESS)
{
ULONGLONG uContentBegin=0, uContentEnd=0, uContentTotalSize=0;
if (3 == swscanf_s(wsContentRange.c_str(),
L"bytes %I64u-%I64u/%I64u",
&uContentBegin, &uContentEnd, &uContentTotalSize)
&& uContentBegin <= uContentEnd
&& uContentEnd < uContentTotalSize)
{
m_ullContentBegin.QuadPart = uContentBegin;
m_ullContentEnd.QuadPart = uContentEnd;
m_ullContentTotalSize.QuadPart = uContentTotalSize;
m_ullFilePoint.QuadPart = uContentBegin;
} else {
throw std::invalid_argument("Content Range");
}
} else {
return _Finishing(dwRt);
}
}
else if (m_dwStatusCode == HTTP_STATUS_NOT_MODIFIED) //304
{
assert(m_bOnlyCheckModify);
return _Finishing(ERROR_SUCCESS);
}
else
{
return _Finishing(m_dwStatusCode);
}
assert(HTTP_STATUS_OK == m_dwStatusCode || HTTP_STATUS_PARTIAL_CONTENT == m_dwStatusCode);
if (m_hFile.IsValid())
{
if (!::SetFilePointerEx(m_hFile, *(PLARGE_INTEGER)&m_ullFilePoint, NULL, FILE_BEGIN)) {
throw WinError("SetFilePointerEx");
}
if (!::SetEndOfFile(m_hFile)) {
throw WinError("SetEndOfFile");
}
}
if (!::WinHttpQueryDataAvailable(m_hRequest, NULL))
{
throw WinError("WinHttpQueryDataAvailable");
}
return ERROR_SUCCESS;
}
else
{
if (m_dwStatusCode == HTTP_STATUS_OK)
{
return _Finishing(ERROR_SUCCESS);
}
return _Finishing(m_dwStatusCode);
}
}
DWORD HttpRequest::_ReadData(DWORD dwDataSize)
{
if (dwDataSize == 0)
{
return _OnReadComplete(0);
}
if (m_sReadBuf.size() < dwDataSize)
{
m_sReadBuf.resize(dwDataSize);
}
if (!::WinHttpReadData(m_hRequest, const_cast<char*>(m_sReadBuf.data()), dwDataSize, NULL))
{
return _Finishing(::GetLastError());
}
return ERROR_SUCCESS;
}
DWORD HttpRequest::_OnReadComplete(DWORD dwByteNumReaded, DWORD dwError)
{
if (dwByteNumReaded != 0)
{
m_sResponseBuf.append(m_sReadBuf.c_str(), dwByteNumReaded);
}
if (!m_wsLocalFileToSaveResponse.IsEmpty())
{
if (!m_hFile)
{
m_hFile = ::CreateFile(m_wsLocalFileToSaveResponse.GetBuffer(),
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
if (!m_hFile)
{
return _Finishing(::GetLastError());
}
assert(m_ullFilePoint.QuadPart == 0);
assert(m_ullDataTransfered.QuadPart == 0);
assert(m_ullContentBegin.QuadPart == 0);
}
size_t nBufSize = m_sResponseBuf.size();
if (nBufSize > MAX_READ_BUF_SIZE || (dwByteNumReaded == 0 && nBufSize > 0))
{
DWORD dwRt = FileHelper::WriteFile(m_hFile, m_hCancelEvent, m_ullFilePoint, m_sResponseBuf.data(), nBufSize);
m_sResponseBuf.clear();
if (ERROR_SUCCESS == dwRt)
{
m_ullDataTransfered.QuadPart += nBufSize;
m_ullFilePoint.QuadPart += nBufSize;
if (m_bAllowPartialTranfer)
{
_SavPartialTranferInfo();
}
_SetProgress();
}
else
{
return _Finishing(dwRt);
}
}
}
else
{
m_ullDataTransfered.QuadPart += dwByteNumReaded;
_SetProgress();
}
if (dwByteNumReaded != 0)
{
assert(dwError == ERROR_SUCCESS);
if (!::WinHttpQueryDataAvailable(m_hRequest, NULL))
{
throw WinError("WinHttpQueryDataAvailable");
}
return ERROR_SUCCESS;
}
if (dwError != ERROR_SUCCESS)
{
return _Finishing(dwError);
}
//有时候dwByteNumReaded == 0,但是实际下载没有完成,所以做个check
//但是有时候下载网页,拿不到content-length,只有transfer-encoding:chunked,那么不知道m_ullContentEnd的值
//所以无法通过检查接收的数据大小来确认是否下载完成,那么只能依赖于dwByteNumReaded == 0
if (m_ullContentTotalSize.QuadPart != INVALID_CONTENT_SIZE
&& m_ullDataTransfered.QuadPart < m_ullContentEnd.QuadPart-m_ullContentBegin.QuadPart+1)
{
return _Finishing(ERROR_WINHTTP_INTERNAL_ERROR);
}
return _Finishing(ERROR_SUCCESS);
}
DWORD HttpRequest::_WriteData()
{
ULONGLONG ullTotalLength = _GetAdditionalDataSize();
if (ullTotalLength > 0)
{
if (ullTotalLength > m_ullDataTransfered.QuadPart)
{
ULONGLONG ullDataLen = WRITE_SIZE;
if (ullDataLen > ullTotalLength - m_ullDataTransfered.QuadPart)
{
ullDataLen = ullTotalLength - m_ullDataTransfered.QuadPart;
}
if (!::WinHttpWriteData(
m_hRequest,
_GetAdditionalDataBuf()+m_ullDataTransfered.QuadPart,
(DWORD)ullDataLen,
NULL))
{
return _Finishing(::GetLastError());
}
return ERROR_SUCCESS;
}
else if (ullTotalLength == m_ullDataTransfered.QuadPart)
{
if (!::WinHttpReceiveResponse(m_hRequest, NULL))
{
return _Finishing(GetLastError());
}
return ERROR_SUCCESS;
}
else
{
throw std::runtime_error("invalid transfer data size");
}
assert(FALSE);
}
throw std::runtime_error("invalid data size");
}
DWORD HttpRequest::_OnWriteComplete(DWORD dwByteNumWritten)
{
if (dwByteNumWritten != 0)
{
m_ullDataTransfered.QuadPart += dwByteNumWritten;
_SetProgress();
}
return _WriteData();
}
DWORD HttpRequest::_SaveData(LPBYTE lpBuf, DWORD dwDataLength)
{
if (m_wsLocalFileToSaveResponse.IsEmpty())
{
m_sResponseBuf.append((const char*)lpBuf, dwDataLength);
return ERROR_SUCCESS;
}
if (!m_hFile)
{
m_hFile = ::CreateFile(m_wsLocalFileToSaveResponse.GetBuffer(),
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
if (!m_hFile)
{
return _Finishing(::GetLastError());
}
assert(m_ullFilePoint.QuadPart == 0);
assert(m_ullDataTransfered.QuadPart == 0);
assert(m_ullContentBegin.QuadPart ==0);
}
DWORD dwRt = FileHelper::WriteFile(m_hFile, m_hCancelEvent, m_ullFilePoint, lpBuf, dwDataLength);
if (ERROR_SUCCESS == dwRt)
{
m_ullFilePoint.QuadPart += dwDataLength;
}
return dwRt;
}
void HttpRequest::SetLocalFileToSaveResponse(const CString& file, BOOL bAllowPartialTranfer/*=FALSE*/)
{
m_wsLocalFileToSaveResponse = file;
m_bAllowPartialTranfer = bAllowPartialTranfer;
if (m_bAllowPartialTranfer)
{
_LoadPartialTranferInfo();
}
}
void HttpRequest::SetAdditionalRequestHeaders(const std::wstring &additionalRequestHeaders)
{
m_wsAdditionalRequestHeaders = additionalRequestHeaders;
}
void HttpRequest::SetAdditionalDataToSend(BYTE *data, DWORD dataSize)
{
if (data == NULL || dataSize == 0)
{
return;
}
m_pAdditionalData.reset(new BYTE[dataSize+sizeof(dataSize)]);
memcpy(m_pAdditionalData.get(), &dataSize, sizeof(dataSize));
memcpy(m_pAdditionalData.get()+sizeof(dataSize), data, dataSize);
}
void HttpRequest::SetCallBackMsg(HWND hWnd, DWORD dwMsg)
{
m_hCallBackWnd = hWnd;
m_dwCallBackMsg =dwMsg;
}
void HttpRequest::SetCallBackEvent(HANDLE hEvent)
{
HANDLE hEventDup = NULL;
if (!::DuplicateHandle(::GetCurrentProcess(),
hEvent,
::GetCurrentProcess(),
&hEventDup,
EVENT_MODIFY_STATE,
FALSE,
0))
{
throw WinError("DuplicateHandle");
}
m_hCallBackEvent = hEventDup;
}
void HttpRequest::SetCancelEvent(HANDLE hEvent)
{
HANDLE hEventDup = NULL;
if (!::DuplicateHandle(::GetCurrentProcess(),
hEvent,
::GetCurrentProcess(),
&hEventDup,
EVENT_MODIFY_STATE,
FALSE,
0))
{
throw WinError("DuplicateHandle");
}
m_hCancelEvent.reset(hEventDup);
}
void HttpRequest::SetProgressProc(PROGRESSPROC progressProc)
{
m_pfProcessProc = progressProc;
}
void HttpRequest::_SetProgress()
{
if (m_pfProcessProc == NULL) {
return;
}
if (m_wsVerb == VERB_GET)
{
if (m_ullContentTotalSize.QuadPart != INVALID_CONTENT_SIZE)
{
ULONGLONG ullTotalDataTransfered = m_ullContentBegin.QuadPart + m_ullDataTransfered.QuadPart;
double dProgress = (double)ullTotalDataTransfered * 100 / m_ullContentTotalSize.QuadPart;
m_pfProcessProc(dProgress);
}
}
else if (m_wsVerb == VERB_POST && _GetAdditionalDataSize() > 0)
{
double dProgress = (double)m_ullDataTransfered.QuadPart * 100 / _GetAdditionalDataSize();
m_pfProcessProc(dProgress);
}
}
std::wstring HttpRequest::GetResponseCookies()
{
CLock lock(&m_cs);
return m_wsResponseCookies;
}
DWORD HttpRequest::GetStatusCode()
{
CLock lock(&m_cs);
return m_dwStatusCode;
}
std::wstring HttpRequest::GetResponseCharset()
{
CLock lock(&m_cs);
return m_sResponseCharset;
}
const std::string& HttpRequest::GetResponseBuf()
{
CLock lock(&m_cs);
return m_sResponseBuf;
}
BOOL HttpRequest::_LoadPartialTranferInfo()
{
CString strInfoFile;
strInfoFile.Format(_T("%s.info"), m_wsLocalFileToSaveResponse);
m_hInfoFile = ::CreateFile(strInfoFile.GetBuffer(),
GENERIC_READ|GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS,
FILE_FLAG_OVERLAPPED|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, NULL);
if (!m_hInfoFile || GetLastError() != ERROR_ALREADY_EXISTS) {
return FALSE;
}
BOOL bReadFileOk = FALSE;
DWORD dwSize = ::GetFileSize(m_hInfoFile, NULL);
std::auto_ptr<BYTE> pBuf;
if (dwSize > 10 && dwSize < 1024*1024)
{
pBuf.reset(new BYTE[dwSize+2]);
DWORD dwByteNumReaded = 0;
ULARGE_INTEGER ullOffset = {0};
if (ERROR_SUCCESS == FileHelper::ReadFile(m_hInfoFile, m_hCancelEvent, ullOffset, pBuf.get(), dwSize, dwByteNumReaded)
&& dwByteNumReaded == dwSize)
{
bReadFileOk = TRUE;
}
}
if (bReadFileOk)
{
wchar_t* lpBuf = (wchar_t*)(pBuf.get()+sizeof(DWORD)*2);
DWORD dwInfoSize = *(LPDWORD)(pBuf.get()+sizeof(DWORD));
if (dwInfoSize+sizeof(DWORD)*2 > dwSize)
{
return FALSE;
}
lpBuf[dwInfoSize/2] = 0;
DWORD dwHashKey = StringHelper::HashKey(lpBuf, dwInfoSize/2);
DWORD dwHashKey2 = *(LPDWORD)pBuf.get();
if (dwHashKey != dwHashKey2)
{
return FALSE;
}
wchar_t szLastModify[256] = {0};
std::auto_ptr<wchar_t> pUrl(new wchar_t[dwInfoSize/2]);
ULONGLONG ullTotalSize = 0;
ULONGLONG ullSize = 0;
int nRt = swscanf_s(lpBuf,
L"total_size=%I64u\nsize=%I64u\nurl=%[^\n]\nlast_modify=%[^\n]\n",
&ullTotalSize, &ullSize,
pUrl.get(), dwInfoSize/2,
szLastModify, 256);
if (4 > nRt)
{
return FALSE;
}
if (m_wsURL != pUrl.get())
{
//不是同一个url,从头下
m_ullFilePoint.QuadPart = 0;
return FALSE;
}
m_hFile = ::CreateFile(m_wsLocalFileToSaveResponse.GetBuffer(),
GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
if (!m_hFile)
{
_Finishing(::GetLastError());
return FALSE;
}
if (!::GetFileSizeEx(m_hFile, (PLARGE_INTEGER)&m_ullFilePoint))
{
throw WinError("GetFileSizeEx");
}
if (m_ullFilePoint.QuadPart < ullSize)
{
//源文件已经被破坏了,从头开始下载
m_ullFilePoint.QuadPart = 0;
return FALSE;
}
m_ullFilePoint.QuadPart = ullSize;
m_wsLastModifyTime = szLastModify;
m_ullContentBegin.QuadPart = ullSize;
m_ullContentTotalSize.QuadPart = ullTotalSize;
m_ullContentEnd.QuadPart = ullTotalSize-1;
if (ullSize == ullTotalSize)
{//已经下完了,去服务器看看是否有更新
m_bOnlyCheckModify = TRUE;
}
return TRUE;
}
return FALSE;
}
DWORD HttpRequest::_SavPartialTranferInfo()
{
if (!m_hInfoFile)
{
return ERROR_INVALID_HANDLE;
}
std::wostringstream os;
os << L"total_size=" << m_ullContentTotalSize.QuadPart << L"\n"
<< L"size=" << m_ullDataTransfered.QuadPart+m_ullContentBegin.QuadPart << L"\n"
<< L"url=" << m_wsURL << L"\n"
<< L"last_modify=" << m_wsLastModifyTime << L"\n";
std::wstring wsInfo = os.str();
DWORD nBufSize = (DWORD)wsInfo.size()*2+sizeof(DWORD)*2;
nBufSize = (nBufSize/4096+1)*4096;
std::auto_ptr<BYTE> pBuf(new BYTE[nBufSize]);
LPBYTE lpBuf = pBuf.get();
DWORD dwHashKey = StringHelper::HashKey(wsInfo.c_str(), wsInfo.size());
memcpy(lpBuf, &dwHashKey, sizeof(DWORD));
lpBuf += sizeof(DWORD);
DWORD dwInfoSize = static_cast<DWORD>(wsInfo.size())*2;
memcpy(lpBuf, &dwInfoSize, sizeof(DWORD));
lpBuf += sizeof(DWORD);
memcpy(lpBuf, wsInfo.c_str(), wsInfo.size()*2);
ULARGE_INTEGER ullOffset = {0};
return FileHelper::WriteFile(m_hInfoFile, m_hCancelEvent, ullOffset, pBuf.get(), nBufSize);
}
DWORD HttpRequest::_GetAdditionalDataSize()
{
DWORD dwTotalLength = 0;
if (m_pAdditionalData.get())
{
dwTotalLength = *(LPDWORD)m_pAdditionalData.get();
}
return dwTotalLength;
}
LPBYTE HttpRequest::_GetAdditionalDataBuf()
{
return m_pAdditionalData.get()+sizeof(DWORD);
}
void HttpRequest::_AttachRequest(HINTERNET hRequest)
{
WINHTTP_PROXY_INFO pi = {0};
DWORD dwLen = sizeof(pi);
if (::WinHttpQueryOption(hRequest, WINHTTP_OPTION_PROXY, &pi, &dwLen))
{
if (pi.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
{
CLock lock(&m_pConnection->m_cs);
m_pConnection->GetProxy().TakeFirst(L"");
}
else if (pi.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
CLock lock(&m_pConnection->m_cs);
m_pConnection->GetProxy().TakeFirst(pi.lpszProxy);
}
}
m_hRequest = hRequest;
m_testRequestSet.erase(hRequest);
}
void HttpRequest::_ClearTestRequest(HINTERNET hRequest)
{
if (hRequest == NULL)
{
std::set<HINTERNET>::iterator it = m_testRequestSet.begin();
while (it != m_testRequestSet.end())
{
::WinHttpCloseHandle(*it);
++it;
}
m_testRequestSet.clear();
}
else if (m_testRequestSet.find(hRequest) != m_testRequestSet.end())
{
::WinHttpCloseHandle(hRequest);
m_testRequestSet.erase(hRequest);
}
if (m_testRequestSet.empty() && m_hRequest == NULL)
{
_Finished(ERROR_WINHTTP_CANNOT_CONNECT);
}
}
}
#endif // APP_GENSOFT_IMEJP_BUSINESSPLATFORM_INCLUDE_WINHTTP_HTTP_CLIENT_H