#ifndef APP_GENSOFT_IMEJP_BUSINESSPLATFORM_INCLUDE_WINHTTP_HTTP_CLIENT_H #define APP_GENSOFT_IMEJP_BUSINESSPLATFORM_INCLUDE_WINHTTP_HTTP_CLIENT_H #include #include #include #include #include #include #include #include #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 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 HttpHandle; typedef HandleBase FileHandle; //typedef HandleBase FileHandle; typedef HandleBase EventHandle; typedef HandleBase 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 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(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 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 m_pSession; SmartPtr m_pConnection; std::set 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 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(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(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::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 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; iGetProxy().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(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(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(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(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::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(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 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 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 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(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::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