#pragma once
/*
作者：hardeyhuang
本代码可以任意传播使用。如果代码有bug，请邮件通知hardey@qq.com。

代码主要是封装了Winhttp的相关接口，实现了Http相关请求和下载。
为了方便拷贝复用，所有的实现都放在这一个hpp文件里面，3000多行代码。

HttpSession，HttpConnection, HttpRequest三个类直接对应Winhttp的API封装。
HttpRequest支持常见的GET、POST、HEAD三种动词，其他动词有需要可以自己扩展。
它内部会自动使用IE代理，走代理不成功，会尝试不走代理重试一次，详见HttpRequest和IEProxyProvider。
如果是https失败，可以通过IsSecureFailure()接口获知失败是否是因为ssl证书问题。
win7以下系统默认关闭了证书校验（因为现在新的数字证书都不支持xp了），无需考虑证书问题。
win7及以上系统，如果不在乎安全问题，因证书导致访问失败，可以考虑SetValidSSLCert(false)，Send()重试一次。
具体用法参考demo中的TestHtmlRequest()。
但是https访问，SetValidSSLCert(false)也不一定会成功。例如winhttp在xp系统只能支持tls1.0版本，
如果服务器不支持tls1.0，那么https访问必定失败（例如https://pay.qq.com/)。

DataDownloader支持多线程下载和断点续传，支持4g以上的大文件，支持MD5校验。
既可以落地到磁盘文件，也可以只下载到内存中。
但下载到内存中的文件最大不能超过100M，否则会失败。而磁盘文件大小则没有限制，取决于磁盘剩余空间。
具体逻辑如下：
1，本地没有下载记录，那么开始全新下载。
2，本地有未完成的下载记录，那么跟服务器校验本地文件信息，通过的话，开启断点续传下载，不通过，全新下载。
3，本地有已经完成的下载文件：
3.1，有MD5时，直接用MD5校验文件，校验ok，返回成功，不ok，全新下载。
3.2，没有MD5时，向服务器问询文件是否有更新，有更新，重新下载，没有更新，返回成功。

没有DataUploader, 要上传文件，直接使用HttpRequest::SetAdditionalDataToSend()设置要上传的数据。

另外几个辅助类也很有用，MD5Cryptor支持计算内存的md5，FileMD5Cryptor使用异步IO支持大文件计算MD5。
(测试发现，FileMD5Cryptor在SSD+I9处理器的配置下，单线程每秒能计算650M的数据，超乎我的想象）

为了让异步回调发送到发起请求的线程，代码中通过窗口进行异步消息派发，所以调用线程中要有消息循环。
但并不局限于在ui线程中使用，MsgDispatcher提供了一个函数RunMessageLoop来创建消息循环。
*/

#include <shlwapi.h>
#include <winhttp.h>
#include <Shlobj.h>

#include <string>
#include <set>
#include <list>
#include <map>
#include <vector>
#include <memory>
#include <functional>
#include <process.h>
#include <iostream>
#pragma comment(lib, "Winhttp.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "version.lib")

namespace mjz
{
	//----------------------------------------------一些参数定义----------------------------------------------------------//
#define DEFAULT_DOWNLOAD_REQ_NUM                    3                          //并行下载同时发起的最大请求数(默认)
#define MAX_PARTION_NUM                             100                        //分片下载的最大拆分片数
#define INIT_PART_SIZE                              (1024*1024)                //初始分片大小
#define MAX_MEM_FILE_SIZE                           (100*1024*1024)            //允许下载到内存中的文件最大size
#define MAX_READ_WRITE_NET_DATA_SIZE                (64*1024)                  //单次网络读写最大数据长度
#define MIN_PROGRESS_NOTIFY_TIME                    100                        //进度通知的时间间隔（毫秒）
#define SAVE_DWONLOAD_INFO_TIME                     2000                       //存储断点续传信息文件的间隔（毫秒）
#define DEFAULT_USERAGENT                           L"QQGameMicro"              //http user agent

	//-------------------------------------------其他定义-----------------------------------------------------------------//
#define THROW_THIS_IF_FALSE(rt)                     {if(!(rt)) throw this;}
#define THROW_LASTERR_IF_FALSE(rt)                  {if(!(rt)) throw GetLastError();}
#define THROW_LASTERR_IF_NULL(rt)                   {if((rt) == NULL) throw GetLastError();}
#define MAKE_ULONGLONG(L,H)                         ((((ULONGLONG)(H))<<32)|(ULONGLONG)(L))
#define MD5_BUF_LEN                                 16

	//-------------------------------------------------------------------------------------------------------------------//
	//以下两个类主要是封装系统CRITICAL_SECTION及其调用
	class CriticalSection
	{
	protected:
		CRITICAL_SECTION m_cs;

	public:
		CriticalSection()
		{
			//Windows Server 2003 and Windows XP:  If the function succeeds, the return value is nonzero.
			//If the function fails, the return value is zero (0). To get extended error information, call GetLastError. 
			//Starting with Windows Vista, the InitializeCriticalSectionAndSpinCount function always succeeds, even in low memory situations.
			THROW_LASTERR_IF_FALSE(::InitializeCriticalSectionAndSpinCount(&m_cs, 4000));
		}

		virtual ~CriticalSection()
		{
			::DeleteCriticalSection(&m_cs);
		}

		operator PCRITICAL_SECTION() { return &m_cs; }

		void Lock()
		{
			::EnterCriticalSection(&m_cs);
		}

		void Unlock()
		{
			::LeaveCriticalSection(&m_cs);
		}
	};

	class Locker
	{
		CriticalSection* m_pcs;
	public:
		Locker(CriticalSection* cs = NULL)
			: m_pcs(cs)
		{
			if (m_pcs) m_pcs->Lock();
		}

		~Locker()
		{
			Detach();
		}

		void Attach(CriticalSection* cs)
		{
			m_pcs = cs;
			m_pcs->Lock();
		}
		void Detach()
		{
			if (m_pcs) m_pcs->Unlock();
			m_pcs = NULL;
		}
	};

	//-------------------------------------------------------------------------------------------------------------------//
	//主要提供一些帮助函数：打印日志，判断系统版本，以及验证url格式是否正确等
	class Helper
	{
	public:
		static std::wstring FormatErrorMsg(DWORD lasterr)
		{
			std::wstring error_msg;
			wchar_t* lpmsg = NULL;
			DWORD flag = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
			LPCVOID lpSource = NULL;

			if (lasterr > WINHTTP_ERROR_BASE && lasterr < WINHTTP_ERROR_LAST)
			{
				flag = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS;
				lpSource = GetModuleHandleW(L"winhttp.dll");
			}
			DWORD dwLaunguageID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
			DWORD rt = ::FormatMessageW(flag, lpSource, lasterr, dwLaunguageID, (LPWSTR)&lpmsg, 0, NULL);
			if (rt > 0)
			{
				error_msg.reserve(rt);
				for (DWORD i = 0; i < rt; ++i)
				{
					if (lpmsg[i] != L'\r' && lpmsg[i] != L'\n')
					{
						error_msg.push_back(lpmsg[i]);
					}
				}
				::LocalFree(lpmsg);
			}
			else
			{
				wchar_t def_msg[64];
				swprintf_s(def_msg, L"error[%d] not defined.", lasterr);
				error_msg = def_msg;
			}
			return error_msg;
		}

		static std::wstring FormatMsg(LPCWSTR fmt, ...)
		{
			va_list args;
			va_start(args, fmt);

			std::wstring msg;
			int len = _vscwprintf(fmt, args);
			if (len > 0) //not include the terminating null character
			{
				msg.resize(len, L'x');
				_vsnwprintf_s(&msg[0], len + 1, _TRUNCATE, fmt, args);
			}
			va_end(args);
			return msg;
		}

		static void OutPut(const std::wstring& msg)
		{
			OutputDebugStringW((msg + L"\r\n").c_str());
		}

		static void OutPut(LPCWSTR fmt, ...)
		{
			va_list args;
			va_start(args, fmt);

			wchar_t buffer[1024];
			wchar_t * msg = buffer;
			int len = _vsnwprintf_s(buffer, ARRAYSIZE(buffer) - 2, _TRUNCATE, fmt, args); //留2个字节给\r\n
			if (len < 0)
			{
				len = _vscwprintf(fmt, args);
				msg = new wchar_t[len + 3]; //3个字节\r\n\0
				len = _vsnwprintf_s(msg, len + 3, _TRUNCATE, fmt, args);
			}
			_ASSERT(len >= 0);
			va_end(args);
			msg[len++] = '\r';
			msg[len++] = '\n';
			msg[len] = '\0';
			OutputDebugStringW(msg);
			if (msg != buffer) delete msg;
		}

		static BOOL IsEqualOrGreater(DWORD dwMajor, DWORD dwMinor)
		{
			OSVERSIONINFOEXW osvi = { sizeof(osvi), dwMajor, dwMinor, 0, 0, { 0 }, 0, 0 };
			DWORDLONG dwlConditionMask = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
			dwlConditionMask = ::VerSetConditionMask(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
			return ::VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask);
		}

		//PathIsURL()有bug，辨识不了“http:/www.baidu.com/"这样的错误url，只能自己封装一个
		static BOOL PathIsHttpUrl(const std::wstring& url)
		{
			URL_COMPONENTSW url_component = { sizeof(URL_COMPONENTSW) };
			url_component.dwUrlPathLength = 1;
			url_component.dwHostNameLength = 1;
			url_component.dwExtraInfoLength = 1;
			return ::WinHttpCrackUrl(url.c_str(), (DWORD)url.length(), 0, &url_component);
		}

		typedef enum
		{
			NAME_ONLY,                  //只要文件名
			NAME_WITH_PATH,             //文件名带路径
			NAME_WITH_PATH_AND_HOST,    //文件名带路径和域名
		} FILE_NAME_FORMAT;

		//从url中提取文件名
		static std::wstring GetFileNameFromUrl(const std::wstring& url, FILE_NAME_FORMAT fmt)
		{
			//url                                       |fmt=NAME_ONLY|fmt=NAME_WITH_PATH|fmt=NAME_WITH_PATH_AND_HOST
			//http://minigame.qq.com                    |index.html   |index.html        |minigame.qq.com\index.html
			//http://minigame.qq.com/nqg/index.html     |index.html   |nqg\index.html    |minigame.qq.com\nqg\index.html
			//http://minigame.qq.com/nqg/index.html?a=b |index.html   |nqg\index.html    |minigame.qq.com\nqg\index.html
			//hdd://dddd(非法http链接)                   |             |                  |

			std::wstring path;
			URL_COMPONENTSW url_component = { sizeof(URL_COMPONENTSW) };
			url_component.dwUrlPathLength = 1;
			url_component.dwHostNameLength = 1;
			url_component.dwExtraInfoLength = 1; //这个值为1，那么dwUrlPathLength就不会包含附加信息的长度
			if (::WinHttpCrackUrl(url.c_str(), (DWORD)url.length(), 0, &url_component))
			{
				if (fmt == NAME_WITH_PATH_AND_HOST)
				{
					path.assign(url_component.lpszHostName, url_component.dwHostNameLength);
				}
				path.append(url_component.lpszUrlPath, url_component.dwUrlPathLength);

				size_t pos = path.rfind(L'/');
				if (pos == path.npos) //url=http://minigame.qq.com
				{
					path += L"/index.html";
				}
				else if (pos == path.length() - 1) //url=http://minigame.qq.com/
				{
					path += L"index.html";
				}
				if (fmt == NAME_ONLY)
				{
					return path.substr(path.rfind(L'/') + 1);
				}
				pos = 0;
				while ((pos = path.find(L'/', pos)) != path.npos)
				{
					path[pos] = L'\\';
				}
				if (fmt == NAME_WITH_PATH)
				{
					return path.substr(1);
				}
			}
			return path;
		}

		static BOOL IsDebugViewPPExists()
		{
			DWORD dwCurrent = GetTickCount();
			if (dwCurrent - s_dwLastCheckTime < 500) {
				if (s_hDebugViewPPMainWnd) {
					if (IsWindow(s_hDebugViewPPMainWnd)) {
						return TRUE;
					}
					s_hDebugViewPPMainWnd = NULL;
				}
				return FALSE;
			}
			s_dwLastCheckTime = dwCurrent;
			//注意，我们基于DebugViewPP的开源代码，特殊定制了一个debugview, 主窗口名字
			//为{D9F0EA1A-1F39-47FF-9A8D-F767A82ABC20}，这样就不会把日志输出到其他debugview里面了。
			s_hDebugViewPPMainWnd = FindWindowW(L"{D9F0EA1A-1F39-47FF-9A8D-F767A82ABC20}", NULL);
			return  s_hDebugViewPPMainWnd != NULL;
		}
	private:
		static DWORD s_dwLastCheckTime;
		static HWND  s_hDebugViewPPMainWnd;
	};

	__declspec(selectany) DWORD Helper::s_dwLastCheckTime = 0;
	__declspec(selectany) HWND Helper::s_hDebugViewPPMainWnd = NULL;

#define MJZOutPutInfo(...)  do { if(Helper::IsDebugViewPPExists()) Helper::OutPut(__VA_ARGS__); } while(0)

	//-------------------------------------------------------------------------------------------------------------------//
	//MsgDispatcher这个类主要提供3个能力。
	//1. 从其他线程中将消息派发到类构造时的线程（一般是主线程）
	//2. 提供消息循环函数，方便在非ui线程中构造消息派发系统
	//3. 创建工作线程，执行耗时较久的任务

	class MsgDispatcher
	{
#define WM_DISPATCHER_MSG                                    (WM_USER + 100)
#define WM_DELAY_DISPATCHER_MSG                              (WM_USER + 101)
#define WM_KILL_TIMER                                        (WM_USER + 102)
#define WM_IO_COMPLETE                                       (WM_USER + 103)
#define WM_DESTROY_WND                                       (WM_USER + 104)
#define TIMER_IO_COMPLETE_ID                                 1
#define TIMER_DELAY_MSG_ID_BEGIN                             1000
#define TIMER_DELAY_MSG_ID_END                               2000000000 //20亿

#define MSGDISPATHER_CLASS                                   L"{8DDAE87F-D88E-4CE7-BD59-680E9C5ADF45}"
		typedef LARGE_INTEGER WND_ATTR;                              //HighPart-->WND, LowPart-->引用计数

		static CriticalSection s_cs_for_msg;                         //用于消息派发的同步
		static std::map<DWORD, WND_ATTR> s_thread_msg_wnd;           //线程ID映射窗口句柄
		static UINT_PTR s_msg_id_seq;                                //累加，保证消息ID不重复
		static std::map<UINT_PTR, std::function<void()>> s_msg_map;  //消息ID映射消息函数

	protected:
		DWORD m_thread_id = 0;                                       //记录MsgDispatcher构造时的线程ID
		std::vector<UINT_PTR> m_msg_id;                              //本MsgDispatcher负责派发的消息ID集合

		struct thread_attr
		{
			HANDLE handle;
			unsigned int id;

			bool operator<(const thread_attr& other) const { return id < other.id; }
			bool operator==(const thread_attr& other) const { return id == other.id; }
		};
		//工作线程相关
		std::map<thread_attr, std::function<void()>> m_work_thead_fun_map;

	public:
		MsgDispatcher()
		{
			m_thread_id = GetCurrentThreadId();
			Locker lk(&s_cs_for_msg);
			//一条线程只创建一个用于消息派发的窗口，用引用计数控制其销毁。
			if (s_thread_msg_wnd.find(m_thread_id) == s_thread_msg_wnd.end())
			{
				WNDCLASSEXW wndclass = { 0 };
				wndclass.cbSize = sizeof(WNDCLASSEXW);
				HINSTANCE module = NULL;
				DWORD dwFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
				::GetModuleHandleExW(dwFlags, (LPCWSTR)WinProc, &module);

				if (!::GetClassInfoExW(module, MSGDISPATHER_CLASS, &wndclass))
				{
					wndclass.lpfnWndProc = WinProc;
					wndclass.hInstance = module;
					wndclass.lpszClassName = MSGDISPATHER_CLASS;
					THROW_LASTERR_IF_FALSE(::RegisterClassExW(&wndclass));
				}

				HWND hwnd = ::CreateWindowExW(0, MSGDISPATHER_CLASS, NULL, 0, 0, 0, 1, 1, NULL, NULL, module, NULL);
				THROW_LASTERR_IF_FALSE(hwnd);

				s_thread_msg_wnd[m_thread_id].HighPart = (LONG)hwnd;
				s_thread_msg_wnd[m_thread_id].LowPart = (LONG)1; //引用计数
			}
			else
			{
				++s_thread_msg_wnd[m_thread_id].LowPart;
			}
		}

		~MsgDispatcher()
		{
			CancelMsg();
			TerminateAllWorkThreadIfTimeout(10 * 1000);
			Locker lk(&s_cs_for_msg);
			auto it = s_thread_msg_wnd.find(m_thread_id);
			if (it != s_thread_msg_wnd.end())
			{
				--it->second.LowPart;

				if (it->second.LowPart == 0)
				{
					HWND hWnd = (HWND)it->second.HighPart;
					//MSDN提过，不能跨线程DestroyWindow(),所以这里需要做下处理。
					if (GetCurrentThreadId() == m_thread_id)
					{
						DestroyWindow(hWnd);
					}
					else {
						//此处不用SendMessage，防止窗口所在线程消息循环已经退出导致卡死
						PostMessageW(hWnd, WM_DESTROY_WND, 0, 0);
					}
					s_thread_msg_wnd.erase(it);
				}
			}
		}

		//这个函数可以在任何线程调用，将消息派发到m_thread_id对应的线程中执行
		//msDelay表示延迟执行的时间，单位是毫秒。注意，即使是msDelay==0，也是异步派发的。
		BOOL DispatchMsg(DWORD msDelay, std::function<void()>&& msg)
		{
			Locker lk(&s_cs_for_msg);
			ClearHandledMsgID();
			UINT_PTR msg_id = GetNextMsgID();
			s_msg_map[msg_id] = std::move(msg);
			auto it = s_thread_msg_wnd.find(m_thread_id);
			if (PostMessageW((HWND)it->second.HighPart, WM_DISPATCHER_MSG, msg_id, msDelay))
			{
				m_msg_id.push_back(msg_id);
				return TRUE;
			}
			s_msg_map.erase(msg_id);
			return FALSE;
		}

		//这个函数可以在任何线程调用，将已经派发但尚未执行的消息取消
		//注意，已经在执行中的消息无法取消
		void CancelMsg()
		{
			Locker lk(&s_cs_for_msg);
			auto it = s_thread_msg_wnd.find(m_thread_id);
			HWND hWnd = (HWND)it->second.HighPart;
			for (auto id : m_msg_id)
			{
				if (s_msg_map.erase(id) > 0)
				{
					if (GetCurrentThreadId() == m_thread_id)
						KillTimer(hWnd, id);
					else
						PostMessageW(hWnd, WM_KILL_TIMER, id, 0);
				}
			}
			m_msg_id.clear();
		}

		//这个函数可以在任何线程调用，触发m_thread_id所在线程起定时器去调用SleepEx执行IO完成。
		void AsyncIOComplete()
		{
			HWND hWnd = NULL;
			{
				Locker lk(&s_cs_for_msg);
				auto it = s_thread_msg_wnd.find(m_thread_id);
				hWnd = (HWND)it->second.HighPart;
			}
			PostMessageW(hWnd, WM_IO_COMPLETE, 0, 0);
		}

		//取得本MsgDispatcher未执行的消息数量，非全局
		DWORD GetUnhandledMsgNum()
		{
			Locker lk(&s_cs_for_msg);
			ClearHandledMsgID();
			return m_msg_id.size();
		}

		//创建一条工作线程去执行函数functor，返回句柄外部不要关闭
		HANDLE RunInWorkThread(std::function<void()>&& functor)
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());
			thread_attr ta = { 0 };
			ta.handle = (HANDLE)_beginthreadex(nullptr, 0, WorkThreadProc, this, CREATE_SUSPENDED, &ta.id);
			if (ta.handle == 0)
				throw GetLastError();
			{
				Locker lk(&s_cs_for_msg);
				m_work_thead_fun_map[ta] = std::move(functor);
			}
			ResumeThread(ta.handle);
			return ta.handle;
		}

		void TerminateAllWorkThreadIfTimeout(DWORD msTimeout)
		{
			std::vector<HANDLE> threadHandle;
			{
				Locker lk(&s_cs_for_msg);
				for (auto& thread : m_work_thead_fun_map)
					threadHandle.push_back((HANDLE)thread.first.handle);
			}
			if (!threadHandle.empty())
			{
				WaitForMultipleObjectsEx(threadHandle.size(), &threadHandle[0], TRUE, msTimeout, FALSE);
				for (auto& handle : threadHandle)
				{
					if (WaitForSingleObject(handle, 0) == WAIT_TIMEOUT)
					{
						_ASSERT(false);
						TerminateThread(handle, 1);
					}
					CloseHandle(handle);
				}
				Locker lk(&s_cs_for_msg);
				m_work_thead_fun_map.clear();
			}
		}

		static int RunMessageLoop()
		{
			MSG msg;
			while (GetMessageW(&msg, NULL, 0, 0))
			{
				TranslateMessage(&msg);
				DispatchMessageW(&msg);
			}
			return (int)msg.wParam;
		}

	protected:
		static UINT_PTR GetNextMsgID()
		{
			UINT_PTR msg_id = 0;
			do
			{
				msg_id = ++s_msg_id_seq;
				if (msg_id > TIMER_DELAY_MSG_ID_END)
					msg_id = TIMER_DELAY_MSG_ID_BEGIN;

			} while (s_msg_map.find(msg_id) != s_msg_map.end());
			return msg_id;
		}

		void ClearHandledMsgID()
		{
			auto it = m_msg_id.begin();
			while (it != m_msg_id.end())
			{
				if (s_msg_map.find(*it) == s_msg_map.end())
					it = m_msg_id.erase(it);
				else
					++it;
			}
		}

		static void ProcMsg(UINT_PTR msg_id)
		{
			std::function<void()> msg;
			{
				Locker lk(&s_cs_for_msg);
				auto it = s_msg_map.find(msg_id);
				if (it != s_msg_map.end())
				{
					msg = std::move(it->second);
					s_msg_map.erase(it);
				}
			}
			if (msg)
			{
				msg();
			}
		}

		static LRESULT CALLBACK WinProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
		{
			switch (nMessage)
			{
			case WM_DISPATCHER_MSG:
				if (lParam == 0) //0表示立即派发
					ProcMsg(wParam);
				else
					SetTimer(hWnd, wParam, (UINT)lParam, NULL);
				break;

			case WM_IO_COMPLETE:
			{
				//启动一个timer去触发SleepEx实现io完成。并且通过窗口prop记录触发的时间。
				DWORD dwIOCompTimer = (DWORD)GetPropW(hWnd, L"COMPLETE_TIMER");
				SetPropW(hWnd, L"IO_TIME", (HANDLE)GetTickCount());
				if (dwIOCompTimer != TIMER_IO_COMPLETE_ID)
				{
					SetTimer(hWnd, TIMER_IO_COMPLETE_ID, 10, NULL);
					SetPropW(hWnd, L"COMPLETE_TIMER", (HANDLE)TIMER_IO_COMPLETE_ID);
				}
				break;
			}

			case WM_TIMER:
			{
				if (wParam == TIMER_IO_COMPLETE_ID)
				{
					//如果没有io完成事件，并且超过5秒没有新的WM_IO_COMPLETE消息，那么停止timer
					if (SleepEx(0, TRUE) != WAIT_IO_COMPLETION)
					{
						DWORD dwTime = (DWORD)GetPropW(hWnd, L"IO_TIME");
						if (GetTickCount() - dwTime > 5000)
						{
							KillTimer(hWnd, wParam);
							RemovePropW(hWnd, L"COMPLETE_TIMER");
							RemovePropW(hWnd, L"IO_TIME");
						}
					}
				}
				else {
					KillTimer(hWnd, wParam);
					ProcMsg((DWORD)wParam);
				}
				break;
			}

			case WM_KILL_TIMER:
				KillTimer(hWnd, wParam);
				break;

			case WM_DESTROY_WND:
				DestroyWindow(hWnd);
				break;

			case WM_DESTROY:
			{
				RemovePropW(hWnd, L"COMPLETE_TIMER");
				RemovePropW(hWnd, L"IO_TIME");
				//break; //no break
			}
			default:
				return ::DefWindowProcW(hWnd, nMessage, wParam, lParam);
			}
			return 0;
		}

		static unsigned int __stdcall WorkThreadProc(void* param)
		{
			MsgDispatcher* msg = (MsgDispatcher*)param;
			msg->RunWorkThreadFunc();
			return 0;
		}

		void RunWorkThreadFunc()
		{
			unsigned int id = GetCurrentThreadId();
			thread_attr ta = { 0, id };
			std::function<void()> functor;
			{
				Locker lk(&s_cs_for_msg);
				auto it = m_work_thead_fun_map.find(ta);
				functor = std::move(it->second);
			}
			functor();
		}
	};

	__declspec(selectany) CriticalSection MsgDispatcher::s_cs_for_msg;
	__declspec(selectany) std::map<DWORD, MsgDispatcher::WND_ATTR> MsgDispatcher::s_thread_msg_wnd;
	__declspec(selectany) std::map<UINT_PTR, std::function<void()>> MsgDispatcher::s_msg_map;
	__declspec(selectany) UINT_PTR MsgDispatcher::s_msg_id_seq = TIMER_DELAY_MSG_ID_BEGIN;

	//------------------------------------------------------------------------------------------------//
	//辅助类，用于某些只需要初始化一次的场所
	template<class T>
	class CallOnce
	{
		static volatile LONG s_nCallOnceFlag;
	public:
		static BOOL IfFirstCall()
		{
			if (s_nCallOnceFlag < 2)
			{
				if (InterlockedCompareExchange(&s_nCallOnceFlag, 1, 0) == 0)
				{
					return TRUE;
				}
				else {
					while (s_nCallOnceFlag == 1) Sleep(0);
				}
			}
			return FALSE;
		}

		static VOID EndFirstCall()
		{
			s_nCallOnceFlag = 2;
		}
	};
	template <class T> __declspec(selectany) volatile LONG CallOnce<T>::s_nCallOnceFlag = 0;

	//辅助类，计算md5
	class MD5Cryptor : public CallOnce<MD5Cryptor>
	{
	protected:
		typedef struct {
			unsigned long i[2];
			unsigned long buf[4];
			unsigned char in[64];
			unsigned char digest[MD5_BUF_LEN];
		} MD5_CTX;
		typedef void (WINAPI *PMD5Init)(MD5_CTX* context);
		typedef void (WINAPI *PMD5Update)(MD5_CTX* context, LPCVOID input, unsigned int inlen);
		typedef void (WINAPI *PMD5Final)(MD5_CTX* context);
		static PMD5Final MD5Init;
		static PMD5Update MD5Update;
		static PMD5Init MD5Final;
		static volatile LONG s_nCryptDllInited;
		MD5_CTX m_md5ctx;
		wchar_t m_szMD5[36];

	public:
		MD5Cryptor()
		{
			m_szMD5[0] = 0;
			if (IfFirstCall())
			{
				HMODULE hDll = LoadLibraryW(L"Cryptdll.dll");
				MD5Init = (PMD5Init)GetProcAddress(hDll, "MD5Init");
				MD5Update = (PMD5Update)GetProcAddress(hDll, "MD5Update");
				MD5Final = (PMD5Final)GetProcAddress(hDll, "MD5Final");
				EndFirstCall();
			}
		}

		wchar_t* operator()(LPCVOID buffer, size_t len)
		{
			MD5Init(&m_md5ctx);
			MD5Update(&m_md5ctx, buffer, len);
			MD5Final(&m_md5ctx);
			ToHex(m_md5ctx, m_szMD5);
			return m_szMD5;
		}

	protected:
		static void ToHex(MD5_CTX& md5ctx, wchar_t md5[33])
		{
			const char* HEX_TABLE = "0123456789ABCDEF";
			for (int i = 0; i < 16; ++i)
			{
				md5[i * 2] = HEX_TABLE[md5ctx.digest[i] / 16];
				md5[i * 2 + 1] = HEX_TABLE[md5ctx.digest[i] % 16];
			}
			md5[32] = 0;
		}
	};

	__declspec(selectany) volatile LONG MD5Cryptor::s_nCryptDllInited = 0;
	__declspec(selectany) MD5Cryptor::PMD5Init MD5Cryptor::MD5Init = NULL;
	__declspec(selectany) MD5Cryptor::PMD5Update MD5Cryptor::MD5Update = NULL;
	__declspec(selectany) MD5Cryptor::PMD5Final MD5Cryptor::MD5Final = NULL;

	//辅助类，异步IO实现大文件的MD5计算
	class FileMD5Cryptor : private MD5Cryptor, public MsgDispatcher
	{
#define MAX_BUFFER_SIZE 256 * 1024

	private:
		volatile BOOL m_bExitFlag = FALSE;
		HANDLE m_hExitEvent = NULL;
		HANDLE m_hThread = NULL;
		HANDLE m_hOverlappedFileHandle = INVALID_HANDLE_VALUE;
		BOOL   m_bNeedCloseFileHandle = FALSE;
		std::function<void(FileMD5Cryptor*, bool end)> m_fnProgressCallback;
		wchar_t m_szMD5[36];
		OVERLAPPED m_ovRead;
		std::unique_ptr<char>  m_bufFront, m_bufBackend;
		LARGE_INTEGER m_liFilePos;
		LARGE_INTEGER m_liLastFilePos;
		LARGE_INTEGER m_liFileSize;
		DWORD m_dwLastProgressTime = 0;
		DWORD m_dwProgress = 0;
		DWORD m_dwSpeed = 0;

	public:
		FileMD5Cryptor(HANDLE hOverlappedHandle, std::function<void(FileMD5Cryptor*, bool)>&& progresscb)
			: m_hOverlappedFileHandle(hOverlappedHandle)
			, m_fnProgressCallback(std::move(progresscb))
		{
			m_szMD5[0] = 0;
			m_liFileSize.QuadPart = 0;
			if (m_hOverlappedFileHandle != INVALID_HANDLE_VALUE)
			{
				m_hExitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
				if (m_hExitEvent)
					m_hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, this, 0, NULL);
			}

			if (m_hThread == NULL)
				THROW_LASTERR_IF_FALSE(DispatchMsg(0, [this]{ m_fnProgressCallback(this, true); }));
		}

		FileMD5Cryptor(LPCWSTR lpFile, std::function<void(FileMD5Cryptor*, bool)>&& progresscb)
			: FileMD5Cryptor(CreateFileW(lpFile, GENERIC_READ, FILE_SHARE_READ, NULL,
			OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL)
			, std::move(progresscb))
		{
			m_bNeedCloseFileHandle = TRUE;
		}

		wchar_t* operator()()
		{
			return m_szMD5;
		}

		DWORD GetProgress(DWORD wRatio = 1000)
		{
			return m_dwProgress * wRatio / 100;
		}

		DWORD GetSpeed()
		{
			return m_dwSpeed;
		}

		~FileMD5Cryptor()
		{
			if (m_hExitEvent)
			{
				if (m_hThread)
				{
					SetEvent(m_hExitEvent);
					while (!m_bExitFlag) {
						//这里不等待线程句柄，防止在dll中卡死
						Sleep(0);
					}
					CloseHandle(m_hThread);
				}
				CloseHandle(m_hExitEvent);
			}

			if (m_bNeedCloseFileHandle && m_hOverlappedFileHandle != INVALID_HANDLE_VALUE)
				CloseHandle(m_hOverlappedFileHandle);
		}

	private:
		static VOID CALLBACK IOCompRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
		{
			FileMD5Cryptor* cryptor = (FileMD5Cryptor*)lpOverlapped->hEvent;
			if (cryptor)
			{
				cryptor->OnReadComplete(dwErrorCode, dwNumberOfBytesTransfered, lpOverlapped);
			}
		}

		void OnReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
		{
			_ASSERT(&m_ovRead == lpOverlapped);
			if (dwErrorCode == ERROR_SUCCESS && dwNumberOfBytesTransfered > 0 && dwNumberOfBytesTransfered <= MAX_BUFFER_SIZE)
			{
				m_liFilePos.QuadPart += dwNumberOfBytesTransfered;
				m_bufBackend.swap(m_bufFront);
				if (m_liFilePos.QuadPart < m_liFileSize.QuadPart)
				{
					m_ovRead.Offset = m_liFilePos.LowPart;
					m_ovRead.OffsetHigh = m_liFilePos.HighPart;
					if (!ReadFileEx(m_hOverlappedFileHandle, m_bufFront.get(), MAX_BUFFER_SIZE, &m_ovRead, IOCompRoutine))
					{
						SetEvent(m_hExitEvent); //失败退出
						return;
					}
				}
				else
				{
					SetEvent(m_hExitEvent); //成功退出
				}

				MD5Update(&m_md5ctx, m_bufBackend.get(), dwNumberOfBytesTransfered);

				m_dwProgress = (DWORD)(m_liFilePos.QuadPart * 100 / m_liFileSize.QuadPart);

				DWORD dwTime = GetTickCount();
				DWORD dwInterval = dwTime - m_dwLastProgressTime;
				if (dwInterval > MIN_PROGRESS_NOTIFY_TIME)
				{
					m_dwSpeed = (DWORD)((m_liFilePos.QuadPart - m_liLastFilePos.QuadPart) * 1000 / dwInterval);
					m_dwLastProgressTime = dwTime;
					m_liLastFilePos.QuadPart = m_liFilePos.QuadPart;
					DispatchMsg(0, [this]{ m_fnProgressCallback(this, false); });
				}
			}
		}

		void Run()
		{
			do
			{
				MD5Init(&m_md5ctx);

				if (!GetFileSizeEx(m_hOverlappedFileHandle, &m_liFileSize) || m_liFileSize.QuadPart == 0)
					break;

				m_bufFront.reset(new char[MAX_BUFFER_SIZE]);
				m_bufBackend.reset(new char[MAX_BUFFER_SIZE]);
				m_liFilePos.QuadPart = 0;
				m_liLastFilePos.QuadPart = 0;
				ZeroMemory(&m_ovRead, sizeof(m_ovRead));
				m_ovRead.hEvent = (HANDLE)this;
				if (!ReadFileEx(m_hOverlappedFileHandle, m_bufFront.get(), MAX_BUFFER_SIZE, &m_ovRead, IOCompRoutine))
					break;

				while (WaitForSingleObjectEx(m_hExitEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION);

				if (m_liFilePos.QuadPart == m_liFileSize.QuadPart)
				{
					MD5Final(&m_md5ctx);
					ToHex(m_md5ctx, m_szMD5);
				}
			} while (FALSE);

			THROW_LASTERR_IF_FALSE(DispatchMsg(0, [this]{ m_fnProgressCallback(this, true); }));

			m_bExitFlag = TRUE;
		}

		static unsigned int __stdcall ThreadProc(void* param)
		{
			FileMD5Cryptor* cryptor = (FileMD5Cryptor*)param;
			cryptor->Run();
			return 0;
		}

	};


	//--------------------------------------------------------------------------------------------------------//
	class HttpHandle : public CriticalSection
	{
	protected:
		HINTERNET m_handle;

	public:
		HttpHandle(HINTERNET handle = NULL) : m_handle(handle) {}
		virtual ~HttpHandle() { Close(); }

		operator BOOL() { return m_handle != NULL; }

		operator HINTERNET() { return m_handle; }

		virtual void Close()
		{
			Locker lk(this);
			if (m_handle)
			{
				WinHttpCloseHandle(m_handle);
				m_handle = NULL;
			}
		}
	};

	class HttpParentHandle : public HttpHandle
	{
	protected:
		std::set<HttpHandle*> m_children;

	public:
		HttpParentHandle(HINTERNET handle = NULL) : HttpHandle(handle) {}
		virtual void Close() override
		{
			Locker lk(this);

			std::set<HttpHandle*>::iterator it = m_children.begin();
			while (it != m_children.end())
			{
				(*it)->Close();
				++it;
			}
			HttpHandle::Close();
		}

		void AddChild(HttpHandle* pChild)
		{
			Locker lk(this);
			m_children.insert(pChild);
		}

		void RemoveChild(HttpHandle* pChild)
		{
			Locker lk(this);
			m_children.erase(pChild);
		}

	};

	class HttpConnection;
	class HttpRequest;
	class DataDownloader;

	class HttpSession : public HttpParentHandle, public CallOnce<HttpSession>
	{
		friend HttpConnection;
		friend HttpRequest;
		static std::shared_ptr<HttpSession> s_defSession;

		HttpSession(LPCWSTR lpUserAgent)
		{
			m_handle = ::WinHttpOpen(lpUserAgent ? lpUserAgent : DEFAULT_USERAGENT,
				WINHTTP_ACCESS_TYPE_NO_PROXY,
				WINHTTP_NO_PROXY_NAME,
				WINHTTP_NO_PROXY_BYPASS,
				WINHTTP_FLAG_ASYNC);
			THROW_LASTERR_IF_FALSE(m_handle);

			::WinHttpSetStatusCallback(m_handle, AsyncCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
		}

	public:
		static std::shared_ptr<HttpSession> Create(LPCWSTR lpUserAgent = NULL)
		{
			return std::shared_ptr<HttpSession>(new HttpSession(lpUserAgent));
		}

		static std::shared_ptr<HttpSession> GetDefSession()
		{
			if (IfFirstCall())
			{
				s_defSession = Create(NULL);
				EndFirstCall();
			}
			return s_defSession;
		}

	private:
		static VOID CALLBACK AsyncCallback(
			HINTERNET hInternet,
			DWORD_PTR dwContext,
			DWORD dwInternetStatus,
			LPVOID lpvStatusInformation,
			DWORD dwStatusInformationLength);
	};
	__declspec(selectany) std::shared_ptr<HttpSession> HttpSession::s_defSession;

	class HttpConnection : public HttpParentHandle
	{
		friend HttpRequest;

		std::shared_ptr<HttpSession> m_session;
		std::wstring m_wsHost;
		INTERNET_PORT m_nPort;

		HttpConnection(
			std::shared_ptr<HttpSession> pSession,
			const std::wstring& wsHost,
			INTERNET_PORT port)
			: m_session(pSession)
			, m_wsHost(wsHost)
			, m_nPort(port)
		{
			if (!m_session)
			{
				m_session = HttpSession::GetDefSession();
			}
			m_session->AddChild(this);

			m_handle = ::WinHttpConnect(*m_session, wsHost.c_str(), port, 0);

			THROW_LASTERR_IF_FALSE(m_handle);
		}

	public:
		static std::shared_ptr<HttpConnection> Create(
			std::shared_ptr<HttpSession> pSession,
			const std::wstring& wsHost,
			INTERNET_PORT port = INTERNET_DEFAULT_PORT)
		{
			return std::shared_ptr<HttpConnection>(new HttpConnection(pSession, wsHost, port));
		}

		~HttpConnection()
		{
			m_session->RemoveChild(this);
		}

		std::shared_ptr<HttpSession> GetSession() { return m_session; }

		std::wstring GetHost() { return m_wsHost; }

		INTERNET_PORT GetPort() { return m_nPort; }
	};

	enum HTTP_VERB
	{
		VERB_GET = 1,
		VERB_POST = 2,
		VERB_HEAD = 3
	};

	struct Timeout
	{
		int nResolveTimeout = 30000;
		int nConnectTimeout = 30000;
		int nSendTimeout = 30000;
		int nReceiveTimeout = 30000;
	};

	enum RequestStatus
	{
		StatusPreparing,
		StatusSending,
		StatusDone,
	};

	struct WinHttpProxyInfo : public WINHTTP_PROXY_INFO
	{
		WinHttpProxyInfo()
		{
			dwAccessType = 0;
			lpszProxy = NULL;
			lpszProxyBypass = NULL;
		}

		~WinHttpProxyInfo()
		{
			if (lpszProxyBypass != NULL)
				::GlobalFree(lpszProxyBypass);
			if (lpszProxy != NULL)
				::GlobalFree(lpszProxy);
		}

		WinHttpProxyInfo& operator = (WinHttpProxyInfo&& other)
		{
			if (this != &other) {
				dwAccessType = other.dwAccessType;
				lpszProxy = other.lpszProxy;
				lpszProxyBypass = other.lpszProxyBypass;

				other.lpszProxy = NULL;
				other.lpszProxyBypass = NULL;
			}
			return *this;
		}

	private:
		WinHttpProxyInfo(const WinHttpProxyInfo&);
		WinHttpProxyInfo& operator = (const WinHttpProxyInfo&);
	};

	//封装IE代理的异步提供者
	class IEProxyProvider : public CriticalSection, public CallOnce<IEProxyProvider>
	{
		volatile bool m_bQuitFlag = false;
		volatile bool m_bThreadFunctionExited = false;
		HANDLE m_hProxyEvent = NULL;
		HANDLE m_hThread = NULL;
		std::list<HttpRequest*> m_req_list;
		WINHTTP_CURRENT_USER_IE_PROXY_CONFIG m_proxyConfig;
		static std::shared_ptr<IEProxyProvider> s_IEProxyProvider;

		IEProxyProvider()
		{
			//实现比较简单，不支持中途修改代理的情况
			memset(&m_proxyConfig, 0, sizeof(m_proxyConfig));
			::WinHttpGetIEProxyConfigForCurrentUser(&m_proxyConfig);

			if (m_proxyConfig.lpszAutoConfigUrl)
			{
				//这里开线程的原因是WinHttpGetProxyForUrl在某些情况下会卡住。
				//测试同学设置了错误的系统dns（模拟dns劫持，网络无法访问），会卡很久,设置session的超时都没有作用。
				m_hProxyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
				THROW_LASTERR_IF_FALSE(m_hProxyEvent);
				m_hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, this, 0, NULL);
			}
			else if (m_proxyConfig.fAutoDetect)
			{
				//感觉没有什么公司用这个，用户误配置的概率更高，会延迟很高，不做处理。
				//如果需要，可以自己实现。windows sdk中有关于IE代理的例子。
			}
		}

	public:
		~IEProxyProvider()
		{
			if (m_hThread)
			{
				m_bQuitFlag = true;
				SetEvent(m_hProxyEvent);

				//此处不能等待线程句柄，因为在dllmain函数中会造成死锁。
				while (!m_bThreadFunctionExited) {
					Sleep(0);
				}
				CloseHandle(m_hThread);
				m_hThread = NULL;
				CloseHandle(m_hProxyEvent);
				m_hProxyEvent = NULL;
			}
			if (m_proxyConfig.lpszAutoConfigUrl)
				GlobalFree(m_proxyConfig.lpszAutoConfigUrl);
			if (m_proxyConfig.lpszProxy)
				GlobalFree(m_proxyConfig.lpszProxy);
			if (m_proxyConfig.lpszProxyBypass)
				GlobalFree(m_proxyConfig.lpszProxyBypass);
		}

		static IEProxyProvider* GetInst()
		{
			if (IfFirstCall())
			{
				s_IEProxyProvider.reset(new IEProxyProvider());
				EndFirstCall();
			}
			return s_IEProxyProvider.get();
		}

		bool IsValid()
		{
			return m_proxyConfig.lpszProxy || m_proxyConfig.lpszAutoConfigUrl;
		}

		//Send,Cancel, Run三个函数需要放到HttpRequest后面实现，因为调用了HttpRequest的方法，否则编译不过。
		void Send(HttpRequest* req);

		void Cancel(HttpRequest* req);

		void Run();

		static unsigned int __stdcall ThreadProc(void* param)
		{
			IEProxyProvider* provider = (IEProxyProvider*)param;
			provider->Run();
			return 0;
		}
	};
	__declspec(selectany) std::shared_ptr<IEProxyProvider> IEProxyProvider::s_IEProxyProvider;

	struct ContentRange
	{
		//[ullBegin, ullEnd) //前闭后开区间（与http header中的Content-Range闭区间有点区别）
		ULONGLONG ullBegin = 0;
		ULONGLONG ullEnd = 0;
		ULONGLONG ullTotal = 0;
	};

	//分片下载的信息
	struct PartialDownloadInfo : public ContentRange
	{
		std::wstring wsLastModifyTime;
		BOOL IsValid() const
		{
			return (!wsLastModifyTime.empty()) && ullBegin < ullEnd && ullEnd <= ullTotal;
		}
	};

	class IDataDownloader
	{
	public:
		virtual void _OnDataReaded(HttpRequest* pReq, ULONGLONG dataPos, LPCVOID lpBuf, size_t len) = 0;
	};

	class HttpRequest : public HttpHandle, public MsgDispatcher
	{
		friend HttpSession;
		friend IEProxyProvider;
		friend DataDownloader;

		struct UploadDataPackage
		{
			std::string                 buffer1;
			std::shared_ptr<char>       buffer2;
			DWORD                       dwBuffer2Len = 0;
			DWORD                       dwUploadedDataLength = 0; //已经发送的数据的长度

		};

		struct DownloadDataPackage
		{
			std::string                    rawResponseData;
			IDataDownloader*            pDataDownloader = NULL;
			ULONGLONG                    ullDownloadedDataLength = 0;
			std::string                    sReadBuffer;
		};

		std::shared_ptr<HttpConnection>             m_pConnection;
		volatile RequestStatus                      m_nStatus = StatusPreparing;

		std::wstring                                m_wsUrl;                  //它是格式校验正确后才赋值的
		std::wstring                                m_wsPathWithExtraInfo;    //包括“?”后面的参数等
		INTERNET_SCHEME                             m_nScheme = INTERNET_SCHEME_HTTP;
		HTTP_VERB                                   m_nVerb = VERB_GET;
		std::wstring                                m_wsReferer;
		std::wstring                                m_wsUserAgent;
		std::wstring                                m_wsAdditionalRequestHeaders;
		std::wstring                                m_wsProxy;
		std::wstring                                m_wsProxyBypass;
		BOOL                                        m_bFromIEProxy = FALSE; //是否来自用户设置的IE代理
		PartialDownloadInfo                         m_PartialDownloadInfo;
		Timeout                                     m_Timeout;
		BOOL                                        m_bAutoRedirect = TRUE;
		BOOL                                        m_bValidSSLCert = Helper::IsEqualOrGreater(6, 1); //win7
		BOOL                                        m_bSetProxy = FALSE;
		std::function<void(HttpRequest*, bool)>     m_fnProgressCallback;   //外部设置进度回调函数，第二个参数表示是否完成
		DWORD                                       m_dwLastCallbackTime = 0;

		BOOL                                        m_bSuccess = FALSE;
		std::wstring                                m_wsLastErrorString;
		DWORD                                       m_dwWinHttpError = ERROR_SUCCESS;
		DWORD                                       m_dwSecureErrorCode = ERROR_SUCCESS;

		std::wstring                                m_wsResponseHeader;
		DWORD                                       m_dwResponseCode = 0;
		ContentRange                                m_ContentRange; //Content-Length 也转为Content-Range
		BOOL                                        m_bHasContentRange = FALSE;
		BOOL                                        m_bAcceptRangesBytes = FALSE;
		std::wstring                                m_wsLastModifyTime;

		UploadDataPackage                           m_UploadDataPackage;    //需要发送的数据包
		DownloadDataPackage                         m_DownloadDataPackage;  //负责接收数据

		static CriticalSection                      s_cs_for_request;
		static std::set<HttpRequest*>               s_RequestSet;

	private:
		HttpRequest(const std::wstring& url)
		{
			{
				Locker lk(&s_cs_for_request);
				s_RequestSet.insert(this);
			}
			SetPath(url);
			//MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Constructed.", this);
		}

		HttpRequest(std::shared_ptr<HttpConnection> pConnection, INTERNET_SCHEME scheme, const std::wstring& path)
		{
			{
				Locker lk(&s_cs_for_request);
				s_RequestSet.insert(this);
			}
			SetPath(pConnection, scheme, path);
			//MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Constructed.", this);
		}

	public:
		static std::shared_ptr<HttpRequest> Create(const std::wstring& url)
		{
			return std::shared_ptr<HttpRequest>(new HttpRequest(url));
		}

		static std::shared_ptr<HttpRequest> Create(std::shared_ptr<HttpConnection> pConnection, INTERNET_SCHEME scheme, const std::wstring& path)
		{
			return std::shared_ptr<HttpRequest>(new HttpRequest(pConnection, scheme, path));
		}

		~HttpRequest(void)
		{
			{
				//主要是保证异步回调安全。具体实现可见HttpSession::AsyncCallback()
				Locker lk(&s_cs_for_request);
				s_RequestSet.erase(this);
			}
			Close();
			//MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Destructed.", this);
		}

		virtual void Close() override
		{
			IEProxyProvider::GetInst()->Cancel(this);

			Locker lk(this);
			if (m_handle)
			{
				::WinHttpCloseHandle(m_handle);
				m_handle = NULL;
			}
		}

		std::shared_ptr<HttpConnection> GetConnection()
		{
			return m_pConnection;
		}

		BOOL SetPath(const std::wstring& url)
		{
			Locker lk(this);
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			URL_COMPONENTSW url_component = { sizeof(URL_COMPONENTSW) };
			url_component.dwHostNameLength = 1;
			url_component.dwUrlPathLength = 1;
			if (!::WinHttpCrackUrl(url.c_str(), (DWORD)url.length(), 0, &url_component))
			{
				m_dwWinHttpError = GetLastError();
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]WinHttpCrackUrl failed. LastError:[%u]%s, url:%s",
					this, m_dwWinHttpError, Helper::FormatErrorMsg(m_dwWinHttpError).c_str(), url.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
				return FALSE;
			}
			m_dwWinHttpError = ERROR_SUCCESS;
			m_wsUrl = url;
			std::wstring wsHost(url_component.lpszHostName, url_component.dwHostNameLength);
			m_wsPathWithExtraInfo.assign(url_component.lpszUrlPath); //包括？后面的参数等
			m_nScheme = url_component.nScheme;
			m_pConnection = HttpConnection::Create(NULL, wsHost, url_component.nPort);
			return TRUE;
		}

		BOOL SetPath(std::shared_ptr<HttpConnection> pConnection, INTERNET_SCHEME scheme, const std::wstring& wsPathWithExtraInfo)
		{
			Locker lk(this);
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_wsPathWithExtraInfo = wsPathWithExtraInfo;

			if (m_wsPathWithExtraInfo.empty())
			{
				m_wsPathWithExtraInfo.append(L"/");
			}
			else if (m_wsPathWithExtraInfo[0] != L'/')
			{
				m_dwWinHttpError = ERROR_WINHTTP_INVALID_URL;
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]SetPath param invalid. wsPathWithExtraInfo:%s",
					this, wsPathWithExtraInfo.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				return FALSE;
			}

			std::wstring wsHost = pConnection->GetHost();
			INTERNET_PORT nPort = pConnection->GetPort();
			m_wsUrl = (scheme == INTERNET_SCHEME_HTTP) ? L"http://" : L"https://";
			m_wsUrl += wsHost;
			if (nPort != INTERNET_DEFAULT_PORT
				&& ((scheme == INTERNET_SCHEME_HTTP && nPort != INTERNET_DEFAULT_HTTP_PORT)
				|| (scheme == INTERNET_SCHEME_HTTPS && nPort != INTERNET_DEFAULT_HTTPS_PORT)))
			{
				m_wsUrl.append(L":").append(std::to_wstring(nPort));
			}
			m_wsUrl += m_wsPathWithExtraInfo;
			if (!Helper::PathIsHttpUrl(m_wsUrl))
			{
				m_dwWinHttpError = ERROR_WINHTTP_INVALID_URL;
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]SetPath param invalid. url:%s",
					this, m_wsUrl.c_str());
				m_wsUrl.clear();
				m_wsPathWithExtraInfo.clear();
				return FALSE;
			}

			m_dwWinHttpError = ERROR_SUCCESS;
			m_pConnection = pConnection;
			m_nScheme = scheme;
			return TRUE;
		}

		void SetScheme(INTERNET_SCHEME scheme)
		{
			Locker lk(this);
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_nScheme = scheme;
			if (scheme == INTERNET_SCHEME_HTTP && _wcsicmp(m_wsUrl.substr(0, 8).c_str(), L"https://") == 0)
			{
				m_wsUrl.replace(0, 5, L"http");
			}
			else if (scheme == INTERNET_SCHEME_HTTPS && _wcsicmp(m_wsUrl.substr(0, 7).c_str(), L"http://") == 0)
			{
				m_wsUrl.replace(0, 4, L"https");
			}
		}

		void SetVerb(HTTP_VERB verb)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_nVerb = verb;
		}

		void SetAdditionalRequestHeaders(const std::wstring &additionalRequestHeaders)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_wsAdditionalRequestHeaders = additionalRequestHeaders;
		}

		void SetAdditionalDataToSend(std::string && data)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_UploadDataPackage.buffer1 = std::move(data);
			m_UploadDataPackage.buffer2.reset();
		}

		void SetAdditionalDataToSend(const std::string& data)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_UploadDataPackage.buffer1 = data;
			m_UploadDataPackage.buffer2.reset();
		}

		void SetAdditionalDataToSend(std::shared_ptr<char>& data, DWORD len)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_UploadDataPackage.buffer1.clear();
			m_UploadDataPackage.buffer2 = data;
			m_UploadDataPackage.dwBuffer2Len = len;
		}

		void SetTimeout(const Timeout& to)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_Timeout = to;
		}

		void SetReferer(const std::wstring& referer)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			m_wsReferer = referer;
		}

		void SetUserAgent(const std::wstring& agent)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			_ASSERT(agent.length() < 200);
			m_wsUserAgent = agent;
		}

		void SetProxy(LPCWSTR lpProxy, LPCWSTR lpBypass)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			_InternalSetProxy(lpProxy, lpBypass, FALSE);
		}

		BOOL SetAutoRedirect(BOOL enabled)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			BOOL oldValue = m_bAutoRedirect;
			m_bAutoRedirect = enabled;
			return oldValue;
		}

		BOOL SetValidSSLCert(BOOL valid)
		{
			THROW_THIS_IF_FALSE(m_nStatus != StatusSending);
			BOOL oldValue = m_bValidSSLCert;
			m_bValidSSLCert = valid;
			return oldValue;
		}

		void Send(std::function<void(HttpRequest*, bool)>&& progresscb)
		{
			Locker lk(this);
			m_fnProgressCallback = std::move(progresscb);
			Send();
		}

		void Send(HTTP_VERB verb, std::function<void(HttpRequest*, bool)>&& progresscb)
		{
			Locker lk(this);
			m_nVerb = verb;
			m_fnProgressCallback = std::move(progresscb);
			Send();
		}

		void Send()
		{
			Locker lk(this);
			THROW_THIS_IF_FALSE(InterlockedExchange((volatile ULONG*)&m_nStatus, StatusSending) != StatusSending);

			if (m_dwWinHttpError == ERROR_WINHTTP_INVALID_URL)
			{
				//url 有问题，直接回调失败
				_ResetRequestStatus();
				_Done();
				return;
			}

			if (IEProxyProvider::GetInst()->IsValid())
			{
				IEProxyProvider::GetInst()->Send(this);
			}
			else
			{
				_InternalSend();
			}
		}

		BOOL IsSuccess()
		{
			_ASSERT(m_nStatus == StatusDone);
			return m_bSuccess;
		}

		BOOL IsFinished()
		{
			return m_nStatus == StatusDone;
		}

		//获取完整的header信息
		std::wstring GetResponseHeader()
		{
			Locker lk(this);
			return m_wsResponseHeader;
		}

		//这个函数直接提取响应体的数据，注意不是拷贝
		void GetRawResponseBody(std::string& res)
		{
			Locker lk(this);
			res.swap(m_DownloadDataPackage.rawResponseData);
			m_DownloadDataPackage.rawResponseData.clear();
		}

		//这个函数获取一份响应体的拷贝
		std::string GetRawResponseBody()
		{
			Locker lk(this);
			return m_DownloadDataPackage.rawResponseData;
		}

		//获取响应状态码，例如200,404
		DWORD GetResponseCode()
		{
			return m_dwResponseCode;
		}

		//获取已经上传的数据长度
		DWORD GetUploadedDataLength()
		{
			return m_UploadDataPackage.dwUploadedDataLength;
		}

		//获取上传进度0~dwRatio
		DWORD GetUploadProgress(DWORD dwRatio = 100)
		{
			Locker lk(this);
			DWORD dwTotalLength = _GetUploadDataTotalLen();
			if (dwTotalLength == 0) return dwRatio;
			return (DWORD)(((ULONGLONG)m_UploadDataPackage.dwUploadedDataLength) * dwRatio / dwTotalLength);
		}

		//获取已经下载的数据长度
		ULONGLONG GetDownloadedDataLength()
		{
			return m_DownloadDataPackage.ullDownloadedDataLength;
		}

		//获取下载进度，0~dwRatio,只是当前请求的进度，不是全局进度
		//例如分片下载时0-990/10000，文件有10000字节，但本请求只下载0-999共1000个字节， 下载200个字节后，Progress就是20
		DWORD GetDownloadProgress(DWORD dwRatio = 100)
		{
			Locker lk(this);
			if (!m_bHasContentRange) return 0;
			ULONGLONG uPartialSize = m_ContentRange.ullEnd - m_ContentRange.ullBegin;
			if (uPartialSize == 0) return dwRatio;
			return (DWORD)(m_DownloadDataPackage.ullDownloadedDataLength * dwRatio / uPartialSize);
		}

		//获取WinHttp的错误码
		//注意，错误码为0并不表示成功，是否成功由IsSuccess()判断
		DWORD GetWinHttpLastError()
		{
			return m_dwWinHttpError;
		}

		//获取文本化的错误信息
		std::wstring GetLastErrorString()
		{
			Locker lk(this);
			return m_wsLastErrorString;
		}

		std::wstring GetUrl()
		{
			Locker lk(this);
			return m_wsUrl;
		}

		std::wstring GetPathWithExtraInfo()
		{
			Locker lk(this);
			return m_wsPathWithExtraInfo;
		}

		INTERNET_SCHEME GetScheme()
		{
			return m_nScheme;
		}

		//获取文件最后修改时间
		std::wstring GetLastModifyTime()
		{
			Locker lk(this);
			return m_wsLastModifyTime;
		}

		BOOL IsSecureFailure()
		{
			return m_dwSecureErrorCode != ERROR_SUCCESS;
		}

	private:
		void _ResetRequestStatus()
		{
			m_bSuccess = FALSE;
			m_bHasContentRange = FALSE;
			m_bAcceptRangesBytes = FALSE;
			m_wsLastModifyTime.clear();
			m_UploadDataPackage.dwUploadedDataLength = 0;
			m_DownloadDataPackage.ullDownloadedDataLength = 0;
			m_DownloadDataPackage.rawResponseData.clear();
			m_dwResponseCode = 0;
			m_ContentRange.ullBegin = m_ContentRange.ullEnd = m_ContentRange.ullTotal = 0;
			m_wsResponseHeader.clear();
			m_dwSecureErrorCode = ERROR_SUCCESS;
		}

		//_SetPartialDownloadInfo()和_SetDataDownloader()接口只开放给DataDownloader,
		//有特殊处理
		void _SetPartialDownloadInfo(const PartialDownloadInfo& info)
		{
			if (!info.IsValid())
			{
				_ASSERT(false);
				return;
			}
			m_PartialDownloadInfo = info;
		}

		void _SetDataDownloader(IDataDownloader* pDataDownloader)
		{
			m_DownloadDataPackage.pDataDownloader = pDataDownloader;
		}

		BOOL _HasContentRange()
		{
			return m_bHasContentRange;
		}

		BOOL _AcceptRangesBytes()
		{
			return m_bAcceptRangesBytes;
		}

		ContentRange _GetContentRange()
		{
			return m_ContentRange;
		}

		void _Done()
		{
			if (InterlockedExchange((volatile ULONG*)&m_nStatus, StatusDone) != StatusDone)
			{
				if (m_fnProgressCallback)
				{
					DispatchMsg(0, [this]{ m_fnProgressCallback(this, true); });
				}
				MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Done %s.", this, m_bSuccess ? L"success" : L"failed");
			}
		}

		void _NotifyProgress()
		{
			if (m_fnProgressCallback)
			{
				DWORD dwCurrent = GetTickCount();
				if (dwCurrent - m_dwLastCallbackTime < MIN_PROGRESS_NOTIFY_TIME)
				{
					return;
				}
				m_dwLastCallbackTime = dwCurrent;
				DispatchMsg(0, [this]{ m_fnProgressCallback(this, false); });
			}
		}

		static LPCWSTR _GetVerb(HTTP_VERB v)
		{
			if (v == VERB_GET) return L"GET";
			if (v == VERB_POST) return L"POST";
			if (v == VERB_HEAD) return L"HEAD";
			throw v;
		}

		void _InternalSetProxy(LPCWSTR lpProxy, LPCWSTR lpProxyBypass, BOOL bFromIEProxy)
		{
			Locker lk(this);
			m_wsProxy = lpProxy ? lpProxy : L"";
			m_wsProxyBypass = lpProxyBypass ? lpProxyBypass : L"";
			m_bSetProxy = !m_wsProxy.empty();
			m_bFromIEProxy = bFromIEProxy;
		}

		void _InternalSend()
		{
			Locker lk(this);

			//有些状态需要重置，因为可能多次Send
			_ResetRequestStatus();
			//把callback事件清理掉，防止上一次请求回调事件的干扰
			MsgDispatcher::CancelMsg();
			//清理错误码
			m_wsLastErrorString.clear();
			m_dwWinHttpError = ERROR_SUCCESS;

			DWORD dwOpenRequestFlag =
				(m_nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;

			_ASSERT(m_handle == NULL);
			m_handle = ::WinHttpOpenRequest(
				*m_pConnection,
				_GetVerb(m_nVerb),
				m_wsPathWithExtraInfo.c_str(),
				NULL,
				m_wsReferer.empty() ? WINHTTP_NO_REFERER : m_wsReferer.c_str(),
				WINHTTP_DEFAULT_ACCEPT_TYPES,
				dwOpenRequestFlag);

			if (m_handle == NULL)
			{
				m_dwWinHttpError = GetLastError();
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]WinHttpOpenRequest failed. LastError:[%u]%s, url:%s",
					this, m_dwWinHttpError, Helper::FormatErrorMsg(m_dwWinHttpError).c_str(), m_wsUrl.c_str());
				MJZOutPutInfo(m_wsLastErrorString);

				_Done();
				return;
			}

			::WinHttpSetTimeouts(m_handle,
				m_Timeout.nResolveTimeout,
				m_Timeout.nConnectTimeout,
				m_Timeout.nSendTimeout,
				m_Timeout.nReceiveTimeout);

			if (m_nScheme == INTERNET_SCHEME_HTTPS)
			{
				if (!m_bValidSSLCert)
				{
					//不验证证书的正确性，此处降低了安全性，但是有利于成功率
					DWORD options = SECURITY_FLAG_IGNORE_CERT_CN_INVALID
						| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
						| SECURITY_FLAG_IGNORE_UNKNOWN_CA
						| SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
					::WinHttpSetOption(m_handle,
						WINHTTP_OPTION_SECURITY_FLAGS,
						(LPVOID)&options,
						sizeof(DWORD));
				}
			}

			if (!m_bAutoRedirect)
			{
				DWORD dwDisableFeature = WINHTTP_DISABLE_REDIRECTS;
				::WinHttpSetOption(m_handle, WINHTTP_OPTION_DISABLE_FEATURE, &dwDisableFeature, sizeof(dwDisableFeature));
			}

			if (!m_wsAdditionalRequestHeaders.empty())
			{
				::WinHttpAddRequestHeaders(
					m_handle,
					m_wsAdditionalRequestHeaders.c_str(),
					static_cast<DWORD>(m_wsAdditionalRequestHeaders.size()),
					WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON);
			}

			WINHTTP_PROXY_INFO wpi = { WINHTTP_ACCESS_TYPE_NO_PROXY };
			if (m_bSetProxy)
			{
				wpi.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
				wpi.lpszProxy = &m_wsProxy[0];
				wpi.lpszProxyBypass = m_wsProxyBypass.empty() ? NULL : &m_wsProxyBypass[0];
			}
			::WinHttpSetOption(m_handle, WINHTTP_OPTION_PROXY, &wpi, sizeof(wpi));

			DWORD dwModifiers = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
			wchar_t szHeader[256] = { 0 };
			if (!m_PartialDownloadInfo.wsLastModifyTime.empty())
			{
				//ullEnd是开区间，而range是闭区间，所以要-1
				swprintf_s(szHeader, L"Range: bytes=%I64u-%I64u\r\n", m_PartialDownloadInfo.ullBegin, m_PartialDownloadInfo.ullEnd - 1);
				::WinHttpAddRequestHeaders(m_handle, szHeader, -1, dwModifiers);

				swprintf_s(szHeader, L"Unless-Modified-Since: %s\r\n", m_PartialDownloadInfo.wsLastModifyTime.c_str());
				::WinHttpAddRequestHeaders(m_handle, szHeader, -1, dwModifiers);
			}

			if (!m_wsUserAgent.empty())
			{
				swprintf_s(szHeader, L"User-Agent: %s\r\n", m_wsUserAgent.c_str());
				::WinHttpAddRequestHeaders(m_handle, szHeader, -1, dwModifiers);
			}

			DWORD dwPostDataLen = _GetUploadDataTotalLen();
			if (!::WinHttpSendRequest(m_handle,
				WINHTTP_NO_ADDITIONAL_HEADERS,
				0,
				WINHTTP_NO_REQUEST_DATA,
				0,
				dwPostDataLen,
				(DWORD_PTR)this))
			{
				//正常来说，异步发送的话，这个地方不应该失败,但是msdn又没说清楚，所以这里还是处理下。
				WINHTTP_ASYNC_RESULT war = { API_SEND_REQUEST, GetLastError() };
				_OnRequestError(&war);
			}
			else {
				m_nStatus = StatusSending;
				MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]WinHttpSendRequest. url=%s, proxy=%s, postDataLen=%u",
					this, m_wsUrl.c_str(), wpi.lpszProxy, dwPostDataLen);
			}
		}

		DWORD _GetUploadDataTotalLen()
		{
			DWORD dwTotalLength = m_UploadDataPackage.buffer1.length();
			if (dwTotalLength == 0)
				dwTotalLength = m_UploadDataPackage.dwBuffer2Len;
			return dwTotalLength;
		}

		DWORD _GetUploadDataLeft()
		{
			DWORD dwDataTotal = _GetUploadDataTotalLen();
			THROW_THIS_IF_FALSE(m_UploadDataPackage.dwUploadedDataLength <= dwDataTotal);
			return dwDataTotal - m_UploadDataPackage.dwUploadedDataLength;
		}

		const void* _GetUploadDataPtr()
		{
			const void* data_ptr = NULL;
			if (!m_UploadDataPackage.buffer1.empty())
			{
				data_ptr = m_UploadDataPackage.buffer1.data() + m_UploadDataPackage.dwUploadedDataLength;
			}
			else if (m_UploadDataPackage.buffer2)
			{
				data_ptr = m_UploadDataPackage.buffer2.get() + m_UploadDataPackage.dwUploadedDataLength;
			}
			return data_ptr;
		}

		void _WriteData()
		{
			DWORD dwDataLeft = _GetUploadDataLeft();
			if (dwDataLeft == 0)
			{
				_ReceiveResponse();
			}
			else
			{
				if (dwDataLeft > MAX_READ_WRITE_NET_DATA_SIZE) dwDataLeft = MAX_READ_WRITE_NET_DATA_SIZE;

				const void* data_ptr = _GetUploadDataPtr();

				if (!::WinHttpWriteData(m_handle, data_ptr, dwDataLeft, NULL))
				{
					WINHTTP_ASYNC_RESULT war = { API_WRITE_DATA, GetLastError() };
					_OnRequestError(&war);
				}
			}
		}

		void _OnSendRequestCompleted()
		{
			_WriteData();
		}

		void _OnConnectingToServer(LPCWSTR ip)
		{
			//可能因为ip连接不上或者302跳转，会多次回调这个函数
			//也可能因为底层复用了socket连接，不会回调这个函数
			MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Connecting to server:%s.", this, ip);
		}

		void _OnConnectedToServer(LPCWSTR ip)
		{
			MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Connected to server:%s.", this, ip);
		}

		void _OnSecureFailure(DWORD errorcode)
		{
			m_dwSecureErrorCode = errorcode;
		}

		static DWORD _ConvertSecurityErrorCode(DWORD dwSecurityFailureError)
		{
			//dwSecurityError可能是多个标志位or组合的，xp下测试有个错误48，实际是
			//WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID|WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID

			DWORD rt = ERROR_WINHTTP_SECURE_FAILURE;
			if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
				rt = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
				rt = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)
				rt = ERROR_WINHTTP_SECURE_INVALID_CERT;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
				rt = ERROR_WINHTTP_SECURE_INVALID_CA;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)
				rt = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)
				rt = ERROR_WINHTTP_SECURE_CERT_REVOKED;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE)
				rt = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
			else if (dwSecurityFailureError & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)
				rt = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
			return rt;
		}

		void _OnRequestError(WINHTTP_ASYNC_RESULT* pWinhttpAsyncResult)
		{
			LPCWSTR ErrorApi = L"UnkownApi";
			switch (pWinhttpAsyncResult->dwResult)
			{
			case API_SEND_REQUEST:
				ErrorApi = L"WinHttpSendRequest";
				break;
			case API_RECEIVE_RESPONSE:
				ErrorApi = L"WinHttpReceiveResponse";
				break;
			case API_QUERY_DATA_AVAILABLE:
				ErrorApi = L"WinHttpQueryDataAvailable";
				break;
			case API_READ_DATA:
				ErrorApi = L"WinHttpReadData";
				break;
			case API_WRITE_DATA:
				ErrorApi = L"WinHttpWriteData";
				break;
			}

			m_dwWinHttpError = pWinhttpAsyncResult->dwError;
			if (m_dwWinHttpError == ERROR_WINHTTP_SECURE_FAILURE)
			{
				//将SECURE_FAILURE转换为具体的错误原因
				m_dwWinHttpError = _ConvertSecurityErrorCode(m_dwSecureErrorCode);
			}
			//错误码并不一定都是ERROR_WINHTTP_*这种。
			//xp系统访问https://pay.qq.com/会返回SEC_E_ILLEGAL_MESSAGE(0x80090326L)这个错误码。
			m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]%s failed. LastError:[0x%0x][%u]%s, url:%s", this,
				ErrorApi, m_dwWinHttpError, m_dwWinHttpError, Helper::FormatErrorMsg(m_dwWinHttpError).c_str(), m_wsUrl.c_str());
			MJZOutPutInfo(m_wsLastErrorString);

			Close();
			//有些用户可能设置了IE代理访问国外网站，如果用了这个代理访问国内网站，很可能会失败。
			//所以不用代理再试一次。
			if (m_bSetProxy && m_bFromIEProxy)
			{
				m_bSetProxy = FALSE;
				_InternalSend();
				return;
			}
			_Done();
		}

		void _OnWriteComplete(DWORD dwByteNumWritten)
		{
			m_UploadDataPackage.dwUploadedDataLength += dwByteNumWritten;
			_NotifyProgress();
			_WriteData();
		}

		std::wstring _QueryHeaderStringInfo(DWORD dwInfoLevel, LPDWORD lpdwIndex)
		{
			std::wstring wsInfo;
			DWORD dwSize = 128;
			wsInfo.resize(dwSize);

			if (::WinHttpQueryHeaders(m_handle, dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &wsInfo[0], &dwSize, lpdwIndex))
			{
				wsInfo.resize(dwSize / 2);
			}
			else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
			{
				wsInfo.resize(dwSize / 2);
				::WinHttpQueryHeaders(m_handle, dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &wsInfo[0], &dwSize, lpdwIndex);
			}
			else {
				wsInfo.clear();
			}
			return wsInfo;
		}

		void _OnHeadersAvailable()
		{
			m_wsResponseHeader = _QueryHeaderStringInfo(WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_NO_HEADER_INDEX);

			MJZOutPutInfo(L"[HttpRequest][0x%0x][Info]Response header: %s", this, m_wsResponseHeader.c_str());

			m_dwResponseCode = 0;
			std::wstring res_code = _QueryHeaderStringInfo(WINHTTP_QUERY_STATUS_CODE, WINHTTP_NO_HEADER_INDEX);
			if (!res_code.empty())
			{
				m_dwResponseCode = (DWORD)_wtol(res_code.c_str());
			}

			m_wsLastModifyTime = _QueryHeaderStringInfo(WINHTTP_QUERY_LAST_MODIFIED, WINHTTP_NO_HEADER_INDEX);

			std::wstring ranges = _QueryHeaderStringInfo(WINHTTP_QUERY_ACCEPT_RANGES, WINHTTP_NO_HEADER_INDEX);
			m_bAcceptRangesBytes = (_wcsicmp(ranges.c_str(), L"bytes") == 0);

			m_bHasContentRange = FALSE;
			if (m_dwResponseCode == HTTP_STATUS_OK) //200
			{
				std::wstring wsContentLength = _QueryHeaderStringInfo(WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_NO_HEADER_INDEX);
				if (!wsContentLength.empty())
				{
					//Content-Length也转为Content-Range，方便统一处理
					ULONGLONG ullContentLength = (ULONGLONG)_wtoi64(wsContentLength.c_str());
					m_bHasContentRange = TRUE;
					m_ContentRange.ullBegin = 0;
					m_ContentRange.ullTotal = ullContentLength;
					m_ContentRange.ullEnd = ullContentLength;
				}

				if (m_PartialDownloadInfo.IsValid())
				{
					//请求是分片下载，结果服务器返回200，说明文件发生了改变。
					//此时，服务器返回200，在http规范中并没有任何问题，但是我们在处理时认为是个“错误”,不继续下载。
					//原因是分片下载这个接口只是开给DataDownloader使用的，而DataDownloader中遇到这种情况需要重新发起请求。
					MJZOutPutInfo(L"[HttpRequest][0x%0x][Error]Partial download refused. url:%s", this, m_wsUrl.c_str());
					Close();
					_Done();
				}
				else
				{
					if (m_DownloadDataPackage.pDataDownloader == NULL && m_nVerb != VERB_HEAD)
						m_DownloadDataPackage.rawResponseData.reserve((size_t)m_ContentRange.ullTotal);

					_QueryDataAvailable();
				}
			}
			else if (m_dwResponseCode == HTTP_STATUS_PARTIAL_CONTENT) //206
			{
				std::wstring wsContentRange = _QueryHeaderStringInfo(WINHTTP_QUERY_CONTENT_RANGE, WINHTTP_NO_HEADER_INDEX);
				ULONGLONG uContentBegin = 0, uContentEnd = 0, uContentTotalSize = 0;
				if (3 == swscanf_s(wsContentRange.c_str(),
					L"bytes %I64u-%I64u/%I64u",
					&uContentBegin, &uContentEnd, &uContentTotalSize))
				{
					uContentEnd += 1;//闭区间改为开区间
					m_ContentRange.ullBegin = uContentBegin;
					m_ContentRange.ullEnd = uContentEnd;
					m_ContentRange.ullTotal = uContentTotalSize;
					m_bHasContentRange = TRUE;

					//m_PartialDownloadInfo如果有效，那么必须确保服务器返回的Content-Range与之匹配
					if ((!m_PartialDownloadInfo.IsValid())
						|| (uContentBegin == m_PartialDownloadInfo.ullBegin
						&& uContentEnd == m_PartialDownloadInfo.ullEnd
						&& uContentTotalSize == m_PartialDownloadInfo.ullTotal))
					{
						if (m_DownloadDataPackage.pDataDownloader == NULL && m_nVerb != VERB_HEAD)
							m_DownloadDataPackage.rawResponseData.reserve((size_t)(uContentEnd - uContentBegin));

						_QueryDataAvailable();
						return;
					}
				}
				//服务器返回出错了
				m_dwWinHttpError = ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]Header's Content-Range invalid:%s, url:%s",
					this, m_wsResponseHeader.c_str(), m_wsUrl.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				Close();
				_Done();
			}
			else if (m_dwResponseCode == HTTP_STATUS_NOT_MODIFIED) //304
			{
				m_bSuccess = TRUE;
				Close();
				_Done();
			}
			else if (m_dwResponseCode >= HTTP_STATUS_BAD_REQUEST) //400
			{
				m_dwWinHttpError = ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
				m_wsLastErrorString = Helper::FormatMsg(L"[HttpRequest][0x%0x][Error]Response bad. code:%u, url:%s",
					this, m_dwResponseCode, m_wsUrl.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				Close();
				_Done();
			}
			else
			{
				_QueryDataAvailable();
			}
		}

		void _ReceiveResponse()
		{
			if (!::WinHttpReceiveResponse(m_handle, NULL))
			{
				WINHTTP_ASYNC_RESULT war = { API_RECEIVE_RESPONSE, GetLastError() };
				_OnRequestError(&war);
			}
		}

		void _QueryDataAvailable()
		{
			if (!::WinHttpQueryDataAvailable(m_handle, NULL))
			{
				WINHTTP_ASYNC_RESULT war = { API_QUERY_DATA_AVAILABLE, GetLastError() };
				_OnRequestError(&war);
			}
		}

		void _OnDataAvailable(DWORD len)
		{
			if (len == 0)
			{
				m_bSuccess = true;
				Close();
				_Done();
			}
			else
			{
				len = (len > MAX_READ_WRITE_NET_DATA_SIZE ? MAX_READ_WRITE_NET_DATA_SIZE : len);

				if (m_DownloadDataPackage.sReadBuffer.size() < len)
					m_DownloadDataPackage.sReadBuffer.resize(len);

				if (!::WinHttpReadData(m_handle, &m_DownloadDataPackage.sReadBuffer[0], len, NULL))
				{
					WINHTTP_ASYNC_RESULT war = { API_READ_DATA, GetLastError() };
					_OnRequestError(&war);
				}
			}
		}

		void _OnReadComplete(char* buffer, DWORD len)
		{
			ULONGLONG ullBegin = m_PartialDownloadInfo.IsValid() ? m_PartialDownloadInfo.ullBegin : 0;
			ULONGLONG ullPos = m_DownloadDataPackage.ullDownloadedDataLength + ullBegin;
			m_DownloadDataPackage.ullDownloadedDataLength += len;
			if (m_DownloadDataPackage.pDataDownloader)
			{
				m_DownloadDataPackage.pDataDownloader->_OnDataReaded(this, ullPos, buffer, len);
			}
			else
			{
				m_DownloadDataPackage.rawResponseData.append(buffer, len);
				_NotifyProgress();
			}
			_QueryDataAvailable();
		}
	};

	__declspec(selectany) CriticalSection HttpRequest::s_cs_for_request;
	__declspec(selectany) std::set<HttpRequest*> HttpRequest::s_RequestSet;

	//---------------------------------------------------------------------------------------------------------------------------//
	//这些函数必须放在HttpRequest后面，因为它们内部调用了HttpRequest的方法
	inline VOID CALLBACK HttpSession::AsyncCallback(
		HINTERNET hInternet,
		DWORD_PTR dwContext,
		DWORD dwInternetStatus,
		LPVOID lpvStatusInformation,
		DWORD dwStatusInformationLength)
	{
		if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
		{
			return;
		}

		if (dwContext == NULL) return;

		HttpRequest* pRequest = (HttpRequest*)dwContext;
		Locker lk(&HttpRequest::s_cs_for_request);
		if (HttpRequest::s_RequestSet.find(pRequest) == HttpRequest::s_RequestSet.end())
		{
			return;
		}

		Locker lk2(pRequest);
		lk.Detach();
		if (pRequest->m_handle != hInternet)
			return;

		switch (dwInternetStatus)
		{
		case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
			pRequest->_OnConnectingToServer((LPWSTR)lpvStatusInformation);
			break;

		case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
			pRequest->_OnConnectedToServer((LPWSTR)lpvStatusInformation);
			break;

		case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
			pRequest->_OnSecureFailure(*(DWORD*)lpvStatusInformation);
			break;

		case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
			pRequest->_OnSendRequestCompleted();
			break;

		case  WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
			pRequest->_OnRequestError((WINHTTP_ASYNC_RESULT*)lpvStatusInformation);
			break;

		case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
			pRequest->_OnHeadersAvailable();
			break;
		case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
			pRequest->_OnDataAvailable(*(LPDWORD)lpvStatusInformation);
			break;

		case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
			pRequest->_OnReadComplete((char*)lpvStatusInformation, dwStatusInformationLength);
			break;

		case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
			pRequest->_OnWriteComplete(*(LPDWORD)lpvStatusInformation);
			break;
		}
	}

	inline void IEProxyProvider::Run()
	{
		while (WaitForSingleObject(m_hProxyEvent, INFINITE) == WAIT_OBJECT_0)
		{
			if (m_bQuitFlag)
				break;

			std::wstring url;
			HttpRequest* pReq = NULL;
			do
			{
				pReq = NULL;
				{
					Locker lk(this);
					if (!m_req_list.empty())
					{
						pReq = *m_req_list.begin();
						url = pReq->GetUrl();
					}
				}

				if (pReq)
				{
					_ASSERT(Helper::PathIsHttpUrl(url));
					WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions = { 0 };
					autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
					autoProxyOptions.lpszAutoConfigUrl = m_proxyConfig.lpszAutoConfigUrl;
					autoProxyOptions.fAutoLogonIfChallenged = FALSE;

					WinHttpProxyInfo info;
					BOOL success = ::WinHttpGetProxyForUrl(*HttpSession::GetDefSession(), url.c_str(), &autoProxyOptions, &info);
					if (!success && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE)
					{
						autoProxyOptions.fAutoLogonIfChallenged = TRUE;
						success = ::WinHttpGetProxyForUrl(*HttpSession::GetDefSession(), url.c_str(), &autoProxyOptions, &info);
					}

					if (!success)
					{
						//这个失败，一般是代理不可用。为了减少后续请求浪费的时间，这里把所有的请求发送出去，然后退出线程。
						{
							Locker lk(this);
							m_proxyConfig.lpszAutoConfigUrl = NULL;
							for (auto req : m_req_list)
							{
								req->_InternalSend();
							}
							m_req_list.clear();
						}

						m_bQuitFlag = true;
						SetEvent(m_hProxyEvent);
						pReq = NULL;
					}
					else
					{
						Locker lk(this);
						//获取代理的过程中，锁已经释放，所以需要重新校验pReq指向的对象是否被释放。
						//不仅要比较指针，还需比较url，防止对象释放后，新对象的指针复用。
						if ((!m_req_list.empty()) && (*m_req_list.begin()) == pReq && url == pReq->GetUrl())
						{
							if (info.lpszProxy && wcslen(info.lpszProxy)) //即使成功，也可能代理不支持这个host，返回空
							{
								pReq->_InternalSetProxy(info.lpszProxy, info.lpszProxyBypass, TRUE);
							}
							pReq->_InternalSend();
							m_req_list.pop_front();
						}
					}
				}

			} while (pReq);
		}
		m_bThreadFunctionExited = true;
	}

	inline void IEProxyProvider::Send(HttpRequest* pReq)
	{
		Locker lk(this);
		if (m_proxyConfig.lpszProxy)
		{
			pReq->_InternalSetProxy(m_proxyConfig.lpszProxy, m_proxyConfig.lpszProxyBypass, TRUE);
			pReq->_InternalSend();
		}
		else if (m_proxyConfig.lpszAutoConfigUrl)
		{
			if (std::find(m_req_list.begin(), m_req_list.end(), pReq) == m_req_list.end())
			{
				m_req_list.push_back(pReq);
				SetEvent(m_hProxyEvent);
			}
			else {
				_ASSERT(false);
			}
		}
		else {
			pReq->_InternalSend();
		}
	}

	inline void IEProxyProvider::Cancel(HttpRequest* pReq)
	{
		Locker lk(this);
		auto it = std::find(m_req_list.begin(), m_req_list.end(), pReq);
		if (it != m_req_list.end())
		{
			m_req_list.erase(it);
		}
	}
	//---------------------------------------------------------------------------------------------------------------------------//

	enum DataDownloaderErrorType
	{
		DataDownloaderNoError = 0,
		DataDownloaderParamError,
		DataDownloaderFileOperationFailed,
		DataDownloaderMemOperationFailed,
		DataDownloaderHttpRequestFailed,
		DataDownloaderFileMD5VerifyFailed,
	};

	//接口仿std::string,用来缓存下载数据
	class DataBuffer
	{
		char*  m_ptr = NULL;
		size_t m_capacity = 0;
		size_t m_size = 0;
		ULONGLONG m_pos = 0; //这个pos是指本段数据在整体数据中的位置，由外部设置

		DataBuffer(const DataBuffer&);
		DataBuffer& operator = (const DataBuffer&);
	public:
		DataBuffer() {}
		~DataBuffer() { clear(); }
		DataBuffer(DataBuffer&& other)
		{
			operator=(std::move(other));
		}

		DataBuffer& operator=(DataBuffer&& other)
		{
			memcpy_s(this, sizeof(DataBuffer), &other, sizeof(DataBuffer));
			ZeroMemory(&other, sizeof(DataBuffer));
			return *this;
		}

		void reserve(size_t len)
		{
			if (len > m_capacity) {
				char* ptr = (char*)realloc(m_ptr, len);
				if (ptr == nullptr)
					throw std::bad_alloc();
				m_capacity = len;
				m_ptr = ptr;
			}
		}

		void resize(size_t len)
		{
			reserve(len);
			m_size = len; //这里不会在新增内存上填0
		}

		void clear()
		{
			free(m_ptr);
			m_ptr = NULL;
			m_capacity = 0;
			m_size = 0;
			m_pos = 0;
		}
		void append(void* data, size_t len)
		{
			if (len + m_size > m_capacity)
			{
				reserve(expand_capacity(len + m_size));
			}
			memcpy_s(m_ptr + m_size, len, data, len);
			m_size += len;
		}

		void swap(DataBuffer& other)
		{
			char temp[sizeof(DataBuffer)];
			memcpy_s(temp, sizeof(DataBuffer), this, sizeof(DataBuffer));
			memcpy_s(this, sizeof(DataBuffer), &other, sizeof(DataBuffer));
			memcpy_s(&other, sizeof(DataBuffer), temp, sizeof(DataBuffer));
		}

		void* data(size_t offset = 0) { return m_ptr + offset; }
		size_t length() { return m_size; }
		size_t size() { return m_size; }
		size_t capacity() { return m_capacity; }
		bool empty() { return m_size == 0; }
		void setpos(ULONGLONG pos) { m_pos = pos; }
		ULONGLONG getpos() { return m_pos; }

	private:
		size_t expand_capacity(size_t cap)
		{
			size_t new_cap = cap;
			if (new_cap >= m_capacity * 2)
				new_cap = (new_cap + 1023) / 1024 * 1024;
			else
				new_cap = m_capacity * 2;

			return new_cap;
		}
	};

	//文件下载类，支持分片下载、断点续传，具体用法参考文件末尾的例子
	class DataDownloader
		: public IDataDownloader
		, public CriticalSection
		, public MsgDispatcher
	{
		friend HttpRequest;

		//并行下载用来记录信息的结构体
		struct ParallelDownloadInfo
		{
			//开闭区间[ullBegin, ullEnd)
			ULONGLONG ullBegin = 0;
			ULONGLONG ullEnd = 0;
			ULONGLONG ullCurPos = 0;

			BOOL IsValid()
			{
				return (ullBegin <= ullCurPos && ullCurPos <= ullEnd);
			}

			BOOL IsFinished()
			{
				return (ullBegin <= ullCurPos && ullCurPos == ullEnd);
			}

			BOOL CanAppend(ULONGLONG ullPosBegin, ULONGLONG ullPosEnd)
			{
				return ullBegin <= ullPosBegin
					&& ullPosBegin <= ullCurPos //小于表示允许覆写
					&& ullPosEnd <= ullEnd;
			}
			BOOL IsInside(ULONGLONG ullPosBegin)
			{
				return ullBegin <= ullPosBegin && ullPosBegin < ullEnd;
			}
		};

		struct TotalDownloadInfo
		{
#define TOTALDOWNLOAD_INFO_VERSION 2            //文件信息字段发生变化时，一定要修改这个值
			char                    md5[MD5_BUF_LEN];       //本结构体的md5，防止信息被篡改
			DWORD                   dwVerison;              //断点续传信息文件的版本，防止版本不匹配。
			BOOL                    bHasContentRange;       //Http header中有没有提供数据长度的字段，例如Content-Length，Content-Range等
			BOOL                    bAcceptRangesBytes;     //是否可以分片下载，来自Http Header， Accept-Ranges: bytes
			DWORD                   dwParallelPartNum;      //由于最小分片的设置，可能为1, 但是即使为1，也认为是并行下载，因为它支持断点续传
			ULONGLONG               ullDownloadedSize;      //累计下载数据大小总量
			ULONGLONG               ullTotalSize;           //这个总大小来自于HTTP header中，有些网页(bChunked=TRUE)没有这个字段，所以为0 
			wchar_t                 szLastModifyTime[128];  //文件最后修改时间，用于断点续传和分片下载
			ParallelDownloadInfo    pdInfo[MAX_PARTION_NUM];//分片下载各片信息

			TotalDownloadInfo()
			{
				ZeroMemory(this, sizeof(TotalDownloadInfo));
			}

			BOOL IsValid()
			{
				if (dwVerison != TOTALDOWNLOAD_INFO_VERSION)
					return FALSE;

				MD5Cryptor crypt;
				if (memcmp(crypt((const char*)this + MD5_BUF_LEN, sizeof(TotalDownloadInfo) - MD5_BUF_LEN), md5, MD5_BUF_LEN) != 0)
				{
					return FALSE;
				}

				for (DWORD i = 0; i < dwParallelPartNum; ++i)
				{
					if (!pdInfo[i].IsValid())
					{
						return FALSE;
					}
				}
				return TRUE;
			}

			BOOL HasLastModifyTime()
			{
				return szLastModifyTime[0] != 0;
			}

			BOOL HasContentRange()
			{
				return bHasContentRange;
			}

			BOOL CanContinueFromBreakPoint()
			{
				return bHasContentRange && szLastModifyTime[0] && bAcceptRangesBytes && dwParallelPartNum > 0;
			}

			BOOL CanParallelDownload()
			{
				return (bHasContentRange && szLastModifyTime[0] && bAcceptRangesBytes && ullTotalSize > 0);
			}

			BOOL IsFinished()
			{
				return (bHasContentRange && ullTotalSize == ullDownloadedSize);
			}

			void Update()
			{
				dwVerison = TOTALDOWNLOAD_INFO_VERSION;
				MD5Cryptor crypt;
				memcpy_s(md5, MD5_BUF_LEN, crypt((const char*)this + MD5_BUF_LEN, sizeof(TotalDownloadInfo) - MD5_BUF_LEN), MD5_BUF_LEN);
			}
		};

		enum DownloadStatus
		{
			NotStart = 0,
			MD5VerifyFirst,
			HttpDownload,
			MD5VerifySecond,
			DownloadEnd
		};
	private:
		std::wstring                                                        m_wsUrl;                            //下载链接
		std::wstring                                                        m_wsMD5;                            //文件Md5值，用于校验
		wchar_t                                                             m_szFileName[MAX_PATH];             //本地存储文件名
		wchar_t                                                             m_szFileTempName[MAX_PATH];         //临时文件名
		wchar_t                                                             m_szPartialInfoFile[MAX_PATH];      //分片下载的信息记录文件名
		size_t                                                              m_nMaxDownloadRequestNum;           //并行下载最大请求数
		std::function<void(DataDownloader*, bool end)>                      m_cbProgress;                       //外部设置的进度回调通知函数

		TotalDownloadInfo                                                   m_TotalDownloadInfo;                //下载文件信息
		DataBuffer                                                          m_rawDataBuffer;                    //内存buffer

		std::shared_ptr<HttpRequest>                                        m_HeadInfoReq;                      //请求文件header
		std::map<std::shared_ptr<HttpRequest>, DWORD>                       m_DownloadReqSet;                   //下载请求集合
		std::map<HttpRequest*, DataBuffer>                                  m_DownloadCache;                    //并行下载分片cache
		std::map<std::shared_ptr<OVERLAPPED>, std::shared_ptr<DataBuffer>>  m_AsyncWriteInfo;                   //异步写文件的信息
		std::vector<std::pair<ULONGLONG, ULONGLONG>>                        m_unUpdateDataRanges;               //异步写文件未更新的数据区段

		BOOL                                                                m_bSuccess = FALSE;
		HANDLE                                                              m_hFile = INVALID_HANDLE_VALUE;     //本地存储文件句柄
		HANDLE                                                              m_hPartialInfoFile = INVALID_HANDLE_VALUE; //分片下载的信息记录文件句柄
		BOOL                                                                m_bTempFile = FALSE;                //是否是临时文件（.part结尾）
		DownloadStatus                                                      m_DownloadStatus = NotStart;

		DataDownloaderErrorType                                             m_DataDownloaderErrorType = DataDownloaderNoError;
		std::wstring                                                        m_wsLastErrorString;

		ULONGLONG                                                           m_ullLastDownloadedSize = 0;        //上一轮下载的字节数
		DWORD                                                               m_dwLastCallbackTime = 0;           //上一次回调的时间
		ULONGLONG                                                           m_ullLastProgressSize = 0;          //上一次报告进度时的大小
		DWORD                                                               m_dwCurrentDownloadSpeed = 0;       //下载速度，每秒多少字节
		DWORD                                                               m_dwLastWritePatialInfoTime = 0;    //上一次写断点续传信息的时间
		DWORD                                                               m_dwInitThreadID = 0;               //初始化时的线程ID

		std::shared_ptr<FileMD5Cryptor>                                     m_FileMD5Cryptor;

		DataDownloader(const std::wstring& url, const std::wstring& file_name, const std::wstring& md5, size_t nMaxDownloadRequestNum,
			std::function<void(DataDownloader*, bool end)>&& cbProgress)
			: m_wsUrl(url)
			, m_wsMD5(md5)
			, m_nMaxDownloadRequestNum(nMaxDownloadRequestNum)
			, m_cbProgress(std::move(cbProgress))
			, m_dwInitThreadID(GetCurrentThreadId())
		{
			if (!file_name.empty())
			{
				wcscpy_s(m_szFileName, file_name.c_str());
				wcscpy_s(m_szPartialInfoFile, m_szFileName);
				wcscat_s(m_szPartialInfoFile, L".inf");
				wcscpy_s(m_szFileTempName, m_szFileName);
				wcscat_s(m_szFileTempName, L".part");
			}
			if (m_nMaxDownloadRequestNum == 0)
				m_nMaxDownloadRequestNum = DEFAULT_DOWNLOAD_REQ_NUM;

			_Start();
		}

	public:
		static std::shared_ptr<DataDownloader> Create(
			const std::wstring& url,        //下载链接。http下载失败时，可能切换成https下载
			const std::wstring& file_name,  //下载文件保存路径（绝对路径）,如果为空，则下载到内存中不落地
			const std::wstring& md5,        //文件md5，用于下载完成后的校验和本地缓存的校验。可以为空，不校验md5
			DWORD nMaxDownloadRequestNum,   //最大并行下载的请求数（默认为5个）
			std::function<void(DataDownloader*, bool end)>&& cbProgress) //下载进度回调通知
		{
			return std::shared_ptr<DataDownloader>(new DataDownloader(url, file_name, md5,
				nMaxDownloadRequestNum, std::move(cbProgress)));
		}

		~DataDownloader()
		{
			Locker lk(this);
			_CloseResource();
		}

		std::wstring GetFileName()
		{
			Locker lk(this);
			return m_szFileName;
		}

		std::wstring GetUrl()
		{
			Locker lk(this);
			return m_wsUrl;
		}

		//0~dwRatio
		//注意，这个函数返回的进度可能走到99又回退到0（文件校验不过，重新下载）
		DWORD GetProgress(DWORD dwRatio = 100)
		{
			Locker lk(this);
			if (m_DownloadStatus == NotStart)
			{
				return 0;
			}
			else if (m_DownloadStatus == MD5VerifyFirst || m_DownloadStatus == MD5VerifySecond)
			{
				if (m_FileMD5Cryptor)
					return m_FileMD5Cryptor->GetProgress(dwRatio);
			}
			else if (m_DownloadStatus == HttpDownload)
			{
				if (!m_TotalDownloadInfo.bHasContentRange)
					return 0;

				if (m_TotalDownloadInfo.ullTotalSize == 0)
					return dwRatio;

				return (DWORD)(m_TotalDownloadInfo.ullDownloadedSize * dwRatio / m_TotalDownloadInfo.ullTotalSize);
			}
			else if (m_DownloadStatus == DownloadEnd)
			{
				return dwRatio;
			}
			return 0;
		}

		DWORD GetDownloadSpeed()
		{
			Locker lk(this);
			if (m_DownloadStatus == NotStart || m_DownloadStatus == DownloadEnd)
			{
				return 0;
			}
			else if (m_DownloadStatus == MD5VerifyFirst || m_DownloadStatus == MD5VerifySecond)
			{
				if (m_FileMD5Cryptor)
					return m_FileMD5Cryptor->GetSpeed();
			}
			else if (m_DownloadStatus == HttpDownload)
			{
				return m_dwCurrentDownloadSpeed;
			}
			_ASSERT(FALSE);
			return 0;
		}

		ULONGLONG GetDownloadedSize()
		{
			Locker lk(this);
			return m_TotalDownloadInfo.ullDownloadedSize;
		}

		ULONGLONG GetTotalSize()
		{
			Locker lk(this);
			_ASSERT(m_TotalDownloadInfo.bHasContentRange);
			return m_TotalDownloadInfo.ullTotalSize;
		}

		BOOL IsSuccess()
		{
			_ASSERT(m_DownloadStatus == DownloadEnd);
			return m_bSuccess;
		}

		DataDownloaderErrorType GetDataDownloaderErrorType()
		{
			return m_DataDownloaderErrorType;
		}

		std::wstring GetLastErrorString()
		{
			Locker lk(this);
			return m_wsLastErrorString;
		}

		void GetRawDownloadedData(DataBuffer& data)
		{
			Locker lk(this);
			_ASSERT(data.empty());
			data.swap(m_rawDataBuffer);
		}

		void Retry()
		{
			Locker lk(this);
			_ASSERT(m_dwInitThreadID == GetCurrentThreadId());
			if (m_DownloadStatus != DownloadEnd)
			{
				_ASSERT(FALSE);
				return;
			}
			_Start();
		}

	private:
		void _CloseResource()
		{
			_ASSERT(m_dwInitThreadID == GetCurrentThreadId());

			if (m_hFile != INVALID_HANDLE_VALUE)
			{
				CancelIo(m_hFile);
				CloseHandle(m_hFile);

				//可能还有已经完成但是未进入完成回调的io事件，但是到这一步都丢弃掉，防止_OnWriteComplete的逻辑出现问题
				m_hFile = INVALID_HANDLE_VALUE;
				//All I/O operations that are canceled complete with the error ERROR_OPERATION_ABORTED, 
				//and all completion notifications for the I/O operations occur normally.
				//这里把所有Cancel error处理掉,防止干扰到下一次请求
				SleepEx(0, TRUE);//_OnWriteComplete函数会处理io丢失事件
				_ASSERT(m_AsyncWriteInfo.empty());
			}
			if (m_hPartialInfoFile != INVALID_HANDLE_VALUE)
			{
				CloseHandle(m_hPartialInfoFile);
				m_hPartialInfoFile = INVALID_HANDLE_VALUE;
			}

			m_HeadInfoReq.reset();
			m_DownloadReqSet.clear();
			m_DownloadCache.clear();
			m_AsyncWriteInfo.clear();
			//不清理m_TotalDownloadInfo和m_rawDataBuffer，方便断点续传
		}

		void _Done()
		{
			_CloseResource();

			if (InterlockedExchange((volatile ULONG*)&m_DownloadStatus, DownloadEnd) < DownloadEnd)
			{
				if (m_bSuccess)
				{
					if (m_bTempFile && (!MoveFileExW(m_szFileTempName, m_szFileName, MOVEFILE_REPLACE_EXISTING)))
					{
						DWORD dwLastError = GetLastError();
						m_bSuccess = FALSE;
						m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
						m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]MoveFileExW failed. LastError:[%u]%s, file:%s",
							this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), m_szFileTempName);
						MJZOutPutInfo(m_wsLastErrorString);
					}
				}
				//有md5校验可以删除断点续传信息文件
				if (m_szPartialInfoFile[0] && (!m_wsMD5.empty()) && m_TotalDownloadInfo.CanContinueFromBreakPoint())
				{
					DeleteFileW(m_szPartialInfoFile);
				}
				THROW_LASTERR_IF_FALSE(MsgDispatcher::DispatchMsg(0, [this]{ m_cbProgress(this, true); }));
			}
		}

		void _Start()
		{
			_ASSERT(m_dwInitThreadID == GetCurrentThreadId());
			//将上一轮未处理的消息全部清除
			MsgDispatcher::CancelMsg();

			//重置一些状态
			m_DataDownloaderErrorType = DataDownloaderNoError;
			m_wsLastErrorString.clear();
			m_bSuccess = FALSE;
			m_bTempFile = FALSE;
			m_DownloadStatus = NotStart;
			m_dwLastCallbackTime = 0;
			m_dwLastWritePatialInfoTime = 0;
			m_ullLastProgressSize = m_TotalDownloadInfo.ullDownloadedSize;
			m_dwCurrentDownloadSpeed = 0;

			//判断放在这里，而不是构造函数中，原因是用户可能在回调函数中调用Retry()又发起了请求。
			//这里是发起请求的统一入口。
			if (!Helper::PathIsHttpUrl(m_wsUrl))
			{
				_ASSERT(FALSE);
				m_DownloadStatus = DownloadEnd;
				m_DataDownloaderErrorType = DataDownloaderParamError;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]Url invalid:%s", this, m_wsUrl.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				if (m_cbProgress) {
					THROW_LASTERR_IF_FALSE(MsgDispatcher::DispatchMsg(0, [this]{ m_cbProgress(this, true); }));
				}
			}
			else
			{
				_ASSERT(m_hFile == INVALID_HANDLE_VALUE);
				_ASSERT(m_hPartialInfoFile == INVALID_HANDLE_VALUE);

				if (m_szFileName[0] == 0)
					_CheckMemInfo();
				else
					_CheckFileInfo();
			}
		}

		void _CheckMemInfo()
		{
			if ((!m_wsMD5.empty()) && (!m_rawDataBuffer.empty())
				&& m_TotalDownloadInfo.IsValid()
				&& m_TotalDownloadInfo.IsFinished())
			{
				MD5Cryptor cryptor;
				if (_wcsicmp(m_wsMD5.c_str(), cryptor(m_rawDataBuffer.data(), m_rawDataBuffer.length())) == 0)
				{
					m_bSuccess = TRUE;
					_Done();
					return;
				}
				m_rawDataBuffer.clear();
				ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));
			}
			_StartRequestHeaderInfo();
		}

		void _CheckFileInfo()
		{
			THROW_THIS_IF_FALSE(m_hFile == INVALID_HANDLE_VALUE);
			//先以只读权限尝试打开本地文件（并不创建）
			m_hFile = CreateFileW(m_szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
			if (m_hFile != INVALID_HANDLE_VALUE)
			{
				//本地有已下载完成的文件,如果配置了md5，那么直接开启文件md5校验
				m_bTempFile = FALSE;
				if (!m_wsMD5.empty())
				{
					m_FileMD5Cryptor.reset(new FileMD5Cryptor(m_hFile,
						[this](FileMD5Cryptor* cryptor, bool end){ _OnMD5Progress(cryptor, end); }));
					m_DownloadStatus = MD5VerifyFirst;
					return;
				}
				//没有配置MD5,那么后续跟服务器确认下文件是否发生了更新
			}
			else
			{
				//本地没有下载完成的文件，那么创建可写的临时文件（.part结尾），失败则结束下载
				DWORD  dwLastError = GetLastError();
				if (dwLastError == ERROR_PATH_NOT_FOUND)
				{
					//文件夹不存在返回ERROR_PATH_NOT_FOUND，仅仅文件不存在返回ERROR_FILE_NOT_FOUND
					wchar_t dir[MAX_PATH] = { 0 };
					wcscpy_s(dir, m_szFileName);
					PathRemoveFileSpecW(dir);
					SHCreateDirectory(NULL, dir);
				}
				if (dwLastError == ERROR_PATH_NOT_FOUND || dwLastError == ERROR_FILE_NOT_FOUND)
				{
					if (!_CreateFile(m_hFile, m_szFileTempName))
					{
						_Done();
						return;
					}
					m_bTempFile = TRUE;
				}
				else
				{
					_ASSERT(PathFileExistsW(m_szFileName));
					m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
					m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]CreateFileW failed. LastError:[%u]%s, file:%s",
						this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), m_szFileName);
					MJZOutPutInfo(m_wsLastErrorString);
					_Done();
					return;
				}
			}
			//加载分片下载断点续传信息
			_LoadPartialInfo();

			//向服务器请求资源文件的HEADER信息
			_StartRequestHeaderInfo();
		}

		void _OnMD5Progress(FileMD5Cryptor* cryptor, bool end)
		{
			Locker lk(this);
			if (end)
			{
				std::wstring md5 = (*cryptor)();
				m_FileMD5Cryptor.reset();

				if (_wcsicmp(m_wsMD5.c_str(), md5.c_str()) == 0)
				{
					m_bSuccess = TRUE;
					_Done();
					return;
				}

				BOOL bCanSwitchHttps = FALSE;
				if (m_DownloadStatus == MD5VerifyFirst
					|| (bCanSwitchHttps = (_wcsnicmp(m_wsUrl.c_str(), L"http://", 7) == 0)))
				{
					_CloseResource();

					//因为是md5校验失败，重试下载需要将已经下载的文件内容清除
					if (_DeleteFileIfExists(m_szFileName)
						&& _DeleteFileIfExists(m_szPartialInfoFile)
						&& _DeleteFileIfExists(m_szFileTempName))
					{
						if (bCanSwitchHttps) {
							m_wsUrl.replace(0, 4, L"https");
						}
						ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));
						_Start();
					}
					else {
						//_DeleteFileIfExists函数中有设置ErrorType和ErrorString,这里无需设置。
						_Done();
					}
				}
				else {
					m_DataDownloaderErrorType = DataDownloaderFileMD5VerifyFailed;
					m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]MD5 verify failed. file:%s, md5:%s",
						this, m_szFileTempName, md5.c_str());
					MJZOutPutInfo(m_wsLastErrorString);
					_Done();
				}
			}
			else
			{
				_NotifyProgress();
			}
		}

		BOOL _CreateFile(HANDLE& hFile, const wchar_t* file)
		{
			hFile = CreateFileW(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
			if (hFile == INVALID_HANDLE_VALUE)
			{
				DWORD  dwLastError = GetLastError();
				m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]CreateFileW failed. LastError:[%u]%s, file:%s",
					this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), file);
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
				return FALSE;
			}
			return TRUE;
		}

		BOOL _DeleteFileIfExists(const wchar_t* file)
		{
			if (DeleteFileW(file))
			{
				return TRUE;
			}
			DWORD  dwLastError = GetLastError();
			if (dwLastError == ERROR_FILE_NOT_FOUND || dwLastError == ERROR_PATH_NOT_FOUND)
				return TRUE;

			m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
			m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]DeleteFileW failed. LastError:[%u]%s, file:%s",
				this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), file);
			MJZOutPutInfo(m_wsLastErrorString);
			_ASSERT(FALSE);
			return FALSE;
		}

		//加载本地分片下载断点续传的记录，并简单校验信息的完整性，以及未完成的下载是否匹配
		VOID _LoadPartialInfo()
		{
			THROW_THIS_IF_FALSE(m_hPartialInfoFile == INVALID_HANDLE_VALUE);
			ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));
			m_hPartialInfoFile = CreateFileW(m_szPartialInfoFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (m_hPartialInfoFile != INVALID_HANDLE_VALUE)
			{
				LARGE_INTEGER liFileSize = { 0 };
				THROW_LASTERR_IF_FALSE(GetFileSizeEx(m_hPartialInfoFile, &liFileSize));
				if (liFileSize.QuadPart == sizeof(TotalDownloadInfo))
				{
					DWORD dwBytesReaded = 0;
					LARGE_INTEGER liPoint = { 0 };
					THROW_LASTERR_IF_FALSE(SetFilePointerEx(m_hPartialInfoFile, liPoint, NULL, FILE_BEGIN));
					THROW_LASTERR_IF_FALSE(ReadFile(m_hPartialInfoFile, &m_TotalDownloadInfo, liFileSize.LowPart, &dwBytesReaded, NULL));
				}
				if (!m_TotalDownloadInfo.IsValid())
				{
					_ASSERT(false);
					ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));
				}
				else
				{
					//此处校验文件信息是否匹配，简单起见，目前只验证文件大小
					THROW_LASTERR_IF_FALSE(GetFileSizeEx(m_hFile, &liFileSize));
					if (m_TotalDownloadInfo.ullTotalSize != liFileSize.QuadPart)
					{
						_ASSERT(false);
						ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));
					}
				}
				CloseHandle(m_hPartialInfoFile);
				m_hPartialInfoFile = INVALID_HANDLE_VALUE;

				if (!m_TotalDownloadInfo.IsValid())
				{
					MJZOutPutInfo(L"[DataDownloader][0x%0x][Error]Load partial info failed. file:%s", this, m_szPartialInfoFile);
				}
				else {
					m_ullLastDownloadedSize = m_TotalDownloadInfo.ullDownloadedSize;
				}
			}
			else {
				DWORD  dwLastError = GetLastError();
				if (!(dwLastError == ERROR_FILE_NOT_FOUND || dwLastError == ERROR_PATH_NOT_FOUND))
				{
					//非关键错误, 不影响下载，只会导致无法断点续传
					MJZOutPutInfo(L"[DataDownloader][0x%0x][Error]CreateFileW failed. LastError:[%u]%s, file:%s",
						this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), m_szPartialInfoFile);
				}
			}
		}

		//开始请求http头部信息
		void _StartRequestHeaderInfo()
		{
			m_DownloadStatus = HttpDownload;
			m_HeadInfoReq = HttpRequest::Create(m_wsUrl);
			m_HeadInfoReq->Send(VERB_HEAD, [this](HttpRequest* pReq, bool done) { if (done) _OnRequestDone(pReq); });
		}

		void _OnRequestDone(HttpRequest* pReq)
		{
			Locker lk(this);
			_ASSERT(m_thread_id == GetCurrentThreadId());

			if (m_HeadInfoReq.get() == pReq)
			{
				_OnHeadInfoReqDone();
			}
			else
			{
				_OnDownloadReqDone(pReq);
			}
		}

		//根据头部信息来确定文件大小以及是否支持分片下载断点续传等
		void _OnHeadInfoReqDone()
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());

			DWORD code = m_HeadInfoReq->GetResponseCode();
			if ((!m_HeadInfoReq->IsSuccess()) || (code != HTTP_STATUS_OK))
			{
				//http失败时，用https再试一次。外网的确出现过http失败但https成功的情况。
				if (_wcsnicmp(m_wsUrl.c_str(), L"http://", 7) == 0)
				{
					m_wsUrl.replace(0, 4, L"https");
					_StartRequestHeaderInfo();
					return;
				}
				std::wstring reqError;
				DWORD dwLastError = m_HeadInfoReq->GetWinHttpLastError();
				if (dwLastError != ERROR_SUCCESS)
				{
					reqError = m_HeadInfoReq->GetLastErrorString();
				}
				else if (code != HTTP_STATUS_OK)
				{
					reqError = Helper::FormatMsg(L"response bad:[%u],url:%s", code);
				}
				m_DataDownloaderErrorType = DataDownloaderHttpRequestFailed;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]Get header failed. %s",
					this, reqError.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
				_Done();
				return;
			}

			std::wstring wsLastModifyTime = m_HeadInfoReq->GetLastModifyTime();
			BOOL bHasContentRange = m_HeadInfoReq->_HasContentRange();
			BOOL bAcceptRangesBytes = m_HeadInfoReq->_AcceptRangesBytes();
			ContentRange cr;
			if (bHasContentRange) cr = m_HeadInfoReq->_GetContentRange();

			if (m_TotalDownloadInfo.IsValid())
			{
				//文件未发生改变
				if ((!wsLastModifyTime.empty()) && wsLastModifyTime == m_TotalDownloadInfo.szLastModifyTime)
				{
					if (m_TotalDownloadInfo.IsFinished())
					{
						_ASSERT(m_hFile != INVALID_HANDLE_VALUE);
						if (m_bTempFile && (!m_wsMD5.empty()))
						{
							m_FileMD5Cryptor.reset(new FileMD5Cryptor(m_hFile,
								[this](FileMD5Cryptor* cryptor, bool end){ _OnMD5Progress(cryptor, end); }));
							m_DownloadStatus = MD5VerifyFirst;
						}
						else
						{
							//相当于304
							m_bSuccess = TRUE;
							_Done();
						}
						return;
					}
					if (m_TotalDownloadInfo.CanContinueFromBreakPoint() && bAcceptRangesBytes && bHasContentRange)
					{
						//断点续传
						if (!_ResetSize(m_TotalDownloadInfo.ullTotalSize))
						{
							//_ResetSize函数中有设置ErrorType和ErrorString,这里无需设置
							_Done();
							return;
						}
						_ASSERT(m_DownloadReqSet.empty());
						_MakeParallelDownloadRequest();
						_ASSERT(!m_DownloadReqSet.empty());
						return;
					}
				}
			}

			//全新下载
			m_TotalDownloadInfo.dwParallelPartNum = 0;
			m_TotalDownloadInfo.bHasContentRange = bHasContentRange;
			m_TotalDownloadInfo.bAcceptRangesBytes = bAcceptRangesBytes;
			m_TotalDownloadInfo.ullDownloadedSize = 0;
			m_ullLastDownloadedSize = 0;
			m_TotalDownloadInfo.ullTotalSize = bHasContentRange ? cr.ullTotal : 0;
			wcscpy_s(m_TotalDownloadInfo.szLastModifyTime, wsLastModifyTime.c_str());
			m_TotalDownloadInfo.Update();

			if (!_ResetSize(m_TotalDownloadInfo.ullTotalSize))
			{
				//_ResetSize函数中有设置ErrorType和ErrorString,这里无需设置
				_Done();
				return;
			}
			_ASSERT(m_DownloadReqSet.empty());
			if (m_TotalDownloadInfo.CanParallelDownload())
			{
				_MakeParallelDownloadInfo();
				_MakeParallelDownloadRequest();
			}
			else
			{
				_MakeSingleDownloadRequest();
			}
			_ASSERT(!m_DownloadReqSet.empty());
		}

		void _OnDownloadReqDone(HttpRequest* pReq)
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());

			//不管成功还是失败，把pReq已经下载下来的缓存数据刷到文件中
			if (m_hFile != INVALID_HANDLE_VALUE)
			{
				auto& cache = m_DownloadCache[pReq];
				if (!cache.empty())
				{
					std::shared_ptr<DataBuffer> temp(new DataBuffer(std::move(cache)));
					_WriteDataToFile(temp);
				}
				m_DownloadCache.erase(pReq);
			}

			BOOL bSuccess = pReq->IsSuccess();
			std::wstring wsLastReqError = pReq->GetLastErrorString();

			//把完成的请求从集合中清除，因为后续调用的_MakeParallelDownloadRequest()函数会根据集合中的请求数量来判断
			//是否需要新增一个请求
			m_DownloadReqSet.erase(std::shared_ptr<HttpRequest>(pReq, [](HttpRequest*){}));

			if (bSuccess && m_TotalDownloadInfo.CanParallelDownload())
			{
				//下载过程中，服务器文件发生了变化（分片下载结果返回200），那么后续所有的断点续传都会失败。
				//HttpRequest::_OnHeadersAvailable()函数中有解释说明。
				//所以这里只有当成功完成一个请求，才继续发送其他请求，防止失败陷入死循环。
				//
				//想象这样一个场景：
				//要下载的文件在不同服务器上有不同版本（现实中可能不会出现），恰好cdn服务器做了负载均衡，
				//我们多个请求会分配到不同服务器，那么可然出现部分请求返回200而不是206（LastModifyTime不匹配），
				//根据下载策略，返回200的请求都在HttpRequest中被当做错误处理，到了这里就被丢弃了。
				//失败的请求不会触发新的请求，所以刚开始并行5个请求因为其中2个请求返回失败，只有3个请求可以继续下载。
				//而剩下的3个请求只要有一个请求成功，那么就会进入_MakeParallelDownloadRequest()函数，又重新补齐5个请求。
				//这补齐的5个请求又有几个因为返回200被丢弃了，周而复始，有可能最终能够成功下载某个版本的文件,
				//也有可能因为全部返回200而失败。

				//失败后会自动发起重试，第一步请求HEAD信息，如果返回的LastModifyTime没有发生变化，就在上次的基础上，
				//发起下载请求。如果发生了变化，就丢弃上次下载的数据，然后根据新的LastModifyTime从头向服务器发起下载请求。
				_MakeParallelDownloadRequest();
			}

			if (m_DownloadReqSet.empty())
			{
				//最后一个请求失败了，说明文件下载必然失败
				if (!bSuccess)
				{
					//什么样的失败场景下可以自动发起重试？
					//本轮下载字节数大于上轮，说明有新的下载内容，可以重试
					if (m_ullLastDownloadedSize < m_TotalDownloadInfo.ullDownloadedSize)
					{
						m_ullLastDownloadedSize = m_TotalDownloadInfo.ullDownloadedSize;
						_CloseResource(); //重试只需关闭一些文件，然后重新加载就行，无需将已经下载的部分清除
						_Start();
						return;
					}

					m_DataDownloaderErrorType = DataDownloaderHttpRequestFailed;
					m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x]Request failed. %s", this, wsLastReqError.c_str());
					MJZOutPutInfo(m_wsLastErrorString);
					_ASSERT(false);
					_Done();
					return;
				}

				//下载完成
				if (m_hFile != INVALID_HANDLE_VALUE)
				{
					//异步写文件都完成了，则开始文件校验，否则冗余触发主线程执行IO完成
					if (m_AsyncWriteInfo.empty())
						_FileVerify();
					else
						MsgDispatcher::AsyncIOComplete();
				}
				else {
					_MemBufferVerify();//内存下载可以直接开始校验
				}
			}
		}

		//创建单线程下载的http请求
		void _MakeSingleDownloadRequest()
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());
			_ASSERT(m_DownloadReqSet.empty());
			auto req = HttpRequest::Create(m_HeadInfoReq->GetConnection(),
				m_HeadInfoReq->GetScheme(),
				m_HeadInfoReq->GetPathWithExtraInfo());
			m_DownloadReqSet[req] = 0;
			req->_SetDataDownloader(this);
			req->Send(VERB_GET, [this](HttpRequest* pReq, bool done) { if (done) _OnRequestDone(pReq); });
		}

		//创建多线程下载的http请求
		void _MakeParallelDownloadRequest()
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());
			//从分片好的信息中，取一些既不在下载中也不在写文件中的信息，创建一批下载请求
			//总请求数不超过m_nMaxDownloadRequestNum规定。
			for (DWORD i = 0; i < m_TotalDownloadInfo.dwParallelPartNum; ++i)
			{
				auto& pdInfo = m_TotalDownloadInfo.pdInfo[i];
				if (!pdInfo.IsFinished())
				{
					BOOL bInDownloading = FALSE;
					for (auto& kv : m_DownloadReqSet)
					{
						if (kv.second == i)
						{
							bInDownloading = TRUE;
							break;
						}
					}
					if (!bInDownloading)
					{
						BOOL bInWriting = FALSE;
						for (auto& kv : m_AsyncWriteInfo)
						{
							if (pdInfo.IsInside(MAKE_ULONGLONG(kv.first->Offset, kv.first->OffsetHigh)))
							{
								bInWriting = TRUE;
								break;
							}
						}
						if (!bInWriting)
						{
							PartialDownloadInfo info;
							info.ullBegin = pdInfo.ullCurPos;
							info.ullEnd = pdInfo.ullEnd;
							info.ullTotal = m_TotalDownloadInfo.ullTotalSize;
							info.wsLastModifyTime = m_TotalDownloadInfo.szLastModifyTime;
							_ASSERT(info.IsValid());

							auto req = HttpRequest::Create(m_HeadInfoReq->GetConnection(),
								m_HeadInfoReq->GetScheme(),
								m_HeadInfoReq->GetPathWithExtraInfo());
							m_DownloadReqSet[req] = i;
							req->_SetPartialDownloadInfo(info);
							req->_SetDataDownloader(this);
							req->Send(VERB_GET,
								[this](HttpRequest* pReq, bool done) {
								if (done) _OnRequestDone(pReq);
							});
							if (m_DownloadReqSet.size() >= m_nMaxDownloadRequestNum)
								break;
						}
					}
				}
			}
		}

		//根据header信息中的远端文件大小，重置本地文件的size
		BOOL _ResetSize(LONGLONG ullSize)
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());
			//m_hFile 可能是只读权限打开的句柄，往下要转成临时文件去写
			if (m_hFile != INVALID_HANDLE_VALUE && (!m_bTempFile))
			{
				CloseHandle(m_hFile);
				m_hFile = INVALID_HANDLE_VALUE;

				if ((!_DeleteFileIfExists(m_szFileName)) || (!_CreateFile(m_hFile, m_szFileTempName)))
				{
					//_DeleteFileIfExists和_CreateFile函数中有设置ErrorType和ErrorString,这里无需设置
					return FALSE;
				}
				m_bTempFile = TRUE;
			}

			//重置文件的size
			if (m_hFile != INVALID_HANDLE_VALUE)
			{
				_ASSERT(m_bTempFile);
				LARGE_INTEGER liFileSize = { 0 };
				if (GetFileSizeEx(m_hFile, &liFileSize)
					&& liFileSize.QuadPart == ullSize)
				{
					return TRUE;
				}

				LARGE_INTEGER liPt;
				liPt.QuadPart = ullSize;
				if (!(SetFilePointerEx(m_hFile, liPt, NULL, FILE_BEGIN)
					&& SetEndOfFile(m_hFile))) //extend file size
				{
					DWORD  dwLastError = GetLastError();
					m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
					m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]Resize file failed. LastError:[%u]%s, size:%I64u, file:%s",
						this, dwLastError, Helper::FormatErrorMsg(dwLastError).c_str(), ullSize, m_szFileTempName);
					MJZOutPutInfo(m_wsLastErrorString);
					_ASSERT(FALSE);
					return FALSE;
				}
				return TRUE;
			}

			//重置内存buffer的size
			try
			{
				if (ullSize > MAX_MEM_FILE_SIZE)
					throw 1;

				m_rawDataBuffer.resize((size_t)ullSize);
			}
			catch (...)
			{
				m_DataDownloaderErrorType = DataDownloaderMemOperationFailed;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]Data size too large or memory not enough. size:%I64u",
					this, ullSize);
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
				return FALSE;
			}
			return TRUE;
		}

		//根据一定的规则，将文件分割成多片，制作并行下载的信息结构体
		void _MakeParallelDownloadInfo()
		{
			_ASSERT(m_thread_id == GetCurrentThreadId());

			//分片数量是总size开根号。0~4M分1片，4~9M分2片，9~16M分3片，16~25M分4片，...
			double num = (double)(m_TotalDownloadInfo.ullTotalSize / INIT_PART_SIZE);
			ULONGLONG partnum = (ULONGLONG)sqrt(num);
			if (partnum == 0)
				partnum = 1;
			else if (partnum > MAX_PARTION_NUM)
				partnum = MAX_PARTION_NUM;

			//计算每一片的大小，并向上对齐到64k
			ULONGLONG partsize = m_TotalDownloadInfo.ullTotalSize / partnum;
			partsize = (partsize + MAX_READ_WRITE_NET_DATA_SIZE - 1)
				/ MAX_READ_WRITE_NET_DATA_SIZE * MAX_READ_WRITE_NET_DATA_SIZE;

			m_TotalDownloadInfo.dwParallelPartNum = (DWORD)partnum;
			for (ULONGLONG i = 0; i < partnum; ++i)
			{
				auto& pdInfo = m_TotalDownloadInfo.pdInfo[i];
				pdInfo.ullCurPos = pdInfo.ullBegin = partsize *  i;

				if (i + 1 == partnum)
				{
					pdInfo.ullEnd = m_TotalDownloadInfo.ullTotalSize;
				}
				else {
					pdInfo.ullEnd = partsize * (i + 1);
				}
				_ASSERT(pdInfo.IsValid());
			}
			m_TotalDownloadInfo.ullDownloadedSize = 0;
			m_TotalDownloadInfo.Update();
		}

		//将缓存的下载数据写到文件
		BOOL _WriteDataToFile(std::shared_ptr<DataBuffer>& cache)
		{
			Locker lk(this);
			_ASSERT(m_thread_id == GetCurrentThreadId());
			ULONGLONG pos = cache->getpos();
			std::shared_ptr<OVERLAPPED> lpOV(new OVERLAPPED);
			lpOV->Internal = lpOV->InternalHigh = 0;
			lpOV->OffsetHigh = (DWORD)((pos >> 32) & 0xffffffff);
			lpOV->Offset = (DWORD)(pos);
			//The WriteFileEx function ignores the OVERLAPPED structure's hEvent member. 
			//An application is free to use that member for its own purposes in the context of a WriteFileEx call. 
			lpOV->hEvent = (HANDLE)this;

			if (WriteFileEx(m_hFile, cache->data(), cache->size(), lpOV.get(), FileIOCompletionRoutine))
			{
				m_AsyncWriteInfo.emplace(lpOV, cache);
				MsgDispatcher::AsyncIOComplete();
				return TRUE;
			}
			DWORD dwErrorCode = GetLastError();
			m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
			m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]WriteFileEx failed. LastError:[%u]%s, file:%s",
				this, dwErrorCode, Helper::FormatErrorMsg(dwErrorCode).c_str(), m_szFileTempName);
			MJZOutPutInfo(m_wsLastErrorString);
			_ASSERT(FALSE);
			_Done();
			return FALSE;
		}

		//不落地内存的校验
		void _MemBufferVerify()
		{
			m_TotalDownloadInfo.Update();
			if (m_TotalDownloadInfo.HasContentRange())
				_ASSERT(m_TotalDownloadInfo.IsFinished());

			if (m_wsMD5.empty())
			{
				m_bSuccess = TRUE;
				_Done();
				return;
			}
			m_DownloadStatus = MD5VerifySecond;
			MD5Cryptor cryptor;
			std::wstring md5 = cryptor(m_rawDataBuffer.data(), m_rawDataBuffer.length());
			if (_wcsicmp(md5.c_str(), m_wsMD5.c_str()) == 0)
			{
				m_bSuccess = TRUE;
			}
			else
			{
				if (_wcsnicmp(m_wsUrl.c_str(), L"http://", 7) == 0)
				{
					//因为是md5校验失败，重试需要将已经下载的文件内容清除
					m_rawDataBuffer.clear();
					ZeroMemory(&m_TotalDownloadInfo, sizeof(TotalDownloadInfo));

					m_wsUrl.replace(0, 4, L"https");
					_Start();
					return;
				}
				m_DataDownloaderErrorType = DataDownloaderFileMD5VerifyFailed;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]MD5 verify failed. md5:%s", this, md5.c_str());
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
			}
			_Done();
		}

		//落地文件的校验
		void _FileVerify()
		{
			m_TotalDownloadInfo.Update();
			if (m_TotalDownloadInfo.HasContentRange())
			{
				_ASSERT(m_TotalDownloadInfo.IsFinished());
			}

			_SaveTotalDownloadInfo(TRUE);

			if (m_wsMD5.empty())
			{
				m_bSuccess = TRUE;
				_Done();
				return;
			}

			m_FileMD5Cryptor.reset(new FileMD5Cryptor(m_hFile,
				[this](FileMD5Cryptor* cryptor, bool end){ _OnMD5Progress(cryptor, end); }));
			m_DownloadStatus = MD5VerifySecond;
		}

		static VOID CALLBACK FileIOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytes, LPOVERLAPPED lpOverlapped)
		{
			DataDownloader* dd = (DataDownloader*)lpOverlapped->hEvent;
			if (dd)
			{
				dd->_OnWriteComplete(dwErrorCode, dwNumberOfBytes, lpOverlapped);
			}
		}

		void _OnWriteComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
		{
			Locker lk(this);
			auto ptr = std::shared_ptr<OVERLAPPED>(lpOverlapped, [](LPOVERLAPPED){});
			auto it = m_AsyncWriteInfo.find(ptr);
			THROW_THIS_IF_FALSE(it != m_AsyncWriteInfo.end());

			if (m_hFile == INVALID_HANDLE_VALUE)
			{
				//不管dwErrorCode是成功还是失败，这里都丢弃io事件，防止后续逻辑出现问题。见_CloseResource中的逻辑
				m_AsyncWriteInfo.erase(it);
				return;
			}

			if (dwErrorCode == ERROR_SUCCESS)
			{
				size_t totalLength = it->second->length();
				LARGE_INTEGER liPos = { 0 };
				liPos.LowPart = ptr->Offset;
				liPos.HighPart = ptr->OffsetHigh;

				m_AsyncWriteInfo.erase(it);

				THROW_THIS_IF_FALSE(totalLength == dwNumberOfBytesTransfered);
				_UpdateTotalDownloadInfo(liPos.QuadPart, liPos.QuadPart + dwNumberOfBytesTransfered);
				_NotifyProgress();

				if (m_AsyncWriteInfo.empty() && m_DownloadReqSet.empty())
				{
					//下载完成，并且数据都写入了文件。保存下载信息，并开始文件校验。
					_SaveTotalDownloadInfo(TRUE);
					_FileVerify();
				}
				else
				{
					_SaveTotalDownloadInfo(FALSE);
				}
			}
			else
			{
				m_AsyncWriteInfo.erase(it);
				m_DataDownloaderErrorType = DataDownloaderFileOperationFailed;
				m_wsLastErrorString = Helper::FormatMsg(L"[DataDownloader][0x%0x][Error]WriteFileEx failed. LastError:[%u]%s, file:%s",
					this, dwErrorCode, Helper::FormatErrorMsg(dwErrorCode).c_str(), m_szFileTempName);
				MJZOutPutInfo(m_wsLastErrorString);
				_ASSERT(FALSE);
				_Done();
			}
		}

		//将断点续传信息写入到文件
		void _SaveTotalDownloadInfo(BOOL bEnd)
		{
			if (m_szPartialInfoFile[0] && m_TotalDownloadInfo.CanContinueFromBreakPoint())
			{
				DWORD dwCurrentTime = GetTickCount();
				//除非结束，否则间隔x秒
				if (bEnd || dwCurrentTime - m_dwLastWritePatialInfoTime > SAVE_DWONLOAD_INFO_TIME)
				{
					m_dwLastWritePatialInfoTime = dwCurrentTime;
					if (m_hPartialInfoFile == INVALID_HANDLE_VALUE)
					{
						m_hPartialInfoFile = CreateFileW(m_szPartialInfoFile, GENERIC_READ | GENERIC_WRITE,
							0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
					}
					if (m_hPartialInfoFile != INVALID_HANDLE_VALUE)
					{
						m_TotalDownloadInfo.Update();
						LARGE_INTEGER liPoint = { 0 };
						SetFilePointerEx(m_hPartialInfoFile, liPoint, NULL, FILE_BEGIN);
						DWORD dwNumOfBytesWritten = 0;
						WriteFile(m_hPartialInfoFile, &m_TotalDownloadInfo, sizeof(TotalDownloadInfo), &dwNumOfBytesWritten, NULL);
						SetEndOfFile(m_hPartialInfoFile);
					}
					else
					{
						//非关键信息，失败了也无所谓
					}
				}
			}
		}

		//从HttpRequest中回调过来的接口，接收到新的下载数据了
		virtual void _OnDataReaded(HttpRequest* pReq, ULONGLONG dataPos, LPCVOID lpBuf, size_t len) override
		{
			//这个函数会被Winhttp从工作线程调用(并且是多个线程)，所以需要加锁
			Locker lk(this);
			if (m_szFileName[0])
			{
				//文件句柄无效，说明下载被终止了，这里直接返回
				if (m_hFile == INVALID_HANDLE_VALUE)
					return;

				//分片下载有点随机写的意思，package过来的数据一般是8k字节。这里不直接写入磁盘，而是先缓存一下，优化写文件的性能。
				//这个优化在机械硬盘上可能会有点效果（在ssd我估计没啥意义）。
				size_t MAX_PARALLEL_CACHE_SIZE = 1024 * 256;
				auto& cache = m_DownloadCache[pReq];
				if (cache.empty())
				{
					cache.reserve(MAX_PARALLEL_CACHE_SIZE);
					cache.setpos(dataPos);
				}
				if (cache.size() + len > cache.capacity())
				{
					std::shared_ptr<DataBuffer> temp(new DataBuffer(std::move(cache)));
					cache.reserve(MAX_PARALLEL_CACHE_SIZE);
					cache.setpos(dataPos);
					cache.append((char*)lpBuf, len);
					lk.Detach();

					DWORD dwSleepTicks = 0;
					while (GetUnhandledMsgNum() > 20) {
						Sleep(++dwSleepTicks);//主线程消息太多，这里停下来等一会
					}
					DispatchMsg(0, [this, temp]() mutable { _WriteDataToFile(temp); });
				}
				else
				{
					cache.append((char*)lpBuf, len);
				}
			}
			else
			{
				if (m_rawDataBuffer.size() == dataPos)
				{
					m_rawDataBuffer.append((char*)lpBuf, len);
				}
				else if (m_rawDataBuffer.size() >= dataPos + len)
				{
					//_ResetSize()函数中设定了buffer的size了
					memcpy_s(m_rawDataBuffer.data((DWORD)dataPos), len, lpBuf, len);
				}
				else {
					throw this;
				}
				_UpdateTotalDownloadInfo(dataPos, dataPos + len);
				_NotifyProgress();
			}
		}

		//更新下载信息。
		//主要是应对IO完成没有按照写文件的顺序来回调，后写的数据可能先完成，需要缓存先完成的range。
		//有点像tcp的窗口，测试中偶然会出现，不知道是代码bug还是系统IO完成本身特性。
		void _UpdateTotalDownloadInfo(ULONGLONG dataBegin, ULONGLONG dataEnd)
		{
			_ASSERT(m_unUpdateDataRanges.size() < 20);
			auto it = m_unUpdateDataRanges.begin();
			while (it != m_unUpdateDataRanges.end())
			{
				if (dataBegin > it->second)
				{
					++it;
					continue;
				}
				if (dataEnd < it->first)
					break;

				if (dataBegin >= it->first && dataEnd <= it->second)
				{
					_ASSERT(false);
					return;
				}
				if (dataBegin <= it->first && dataEnd >= it->first)
				{
					if (dataEnd < it->second)
						dataEnd = it->second;
					it = m_unUpdateDataRanges.erase(it);
					continue;
				}
				if (dataBegin <= it->second && dataEnd >= it->second)
				{
					if (dataBegin > it->first)
						dataBegin = it->first;
					it = m_unUpdateDataRanges.erase(it);
					continue;
				}
			}

			BOOL bAppend = FALSE;
			if (m_TotalDownloadInfo.dwParallelPartNum > 0)
			{
				for (DWORD i = 0; i < m_TotalDownloadInfo.dwParallelPartNum; ++i)
				{
					auto& pdInfo = m_TotalDownloadInfo.pdInfo[i];
					if (pdInfo.CanAppend(dataBegin, dataEnd))
					{
						_ASSERT(dataBegin == pdInfo.ullCurPos);
						ULONGLONG ullOldPos = pdInfo.ullCurPos;
						pdInfo.ullCurPos = dataEnd;
						ULONGLONG ullIncreaseSize = pdInfo.ullCurPos - ullOldPos;
						m_TotalDownloadInfo.ullDownloadedSize += ullIncreaseSize;
						bAppend = TRUE;
						break;
					}
				}
			}
			else
			{
				if (dataBegin <= m_TotalDownloadInfo.ullDownloadedSize)
				{
					m_TotalDownloadInfo.ullDownloadedSize = dataEnd;
					bAppend = TRUE;
				}
			}
			if (!bAppend)
			{
				auto it = m_unUpdateDataRanges.begin();
				while (it != m_unUpdateDataRanges.end())
				{
					if (dataBegin > it->second)
					{
						++it;
						continue;
					}
					if (dataEnd < it->first)
					{
						break;
					}
				}
				m_unUpdateDataRanges.insert(it, std::make_pair(dataBegin, dataEnd));
			}
		}

		void _NotifyProgress()
		{
			DWORD dwTime = GetTickCount();
			DWORD dwInterval = dwTime - m_dwLastCallbackTime;
			if (dwInterval < MIN_PROGRESS_NOTIFY_TIME)
			{
				return;
			}
			//每秒字节数
			m_dwCurrentDownloadSpeed = (DWORD)((m_TotalDownloadInfo.ullDownloadedSize - m_ullLastProgressSize) * 1000 / dwInterval);
			m_dwLastCallbackTime = dwTime;
			m_ullLastProgressSize = m_TotalDownloadInfo.ullDownloadedSize;
			MsgDispatcher::DispatchMsg(0, [this]{ m_cbProgress(this, false); });
		}
	};


	//-----------------------------------------------------------demo--------------------------------------------------------------------//
	class TEST
	{
	public:
		static void TestBigFileDownload(LPCWSTR dir)
		{
			std::wstring url = L"http://download.microsoft.com/download/7/c/f/7cf151c3-b735-4e35-a1bb-9a48224f4a95/vs2015.3.ent_chs.iso"; //7G
			std::wstring md5 = L"31DB7D0C16802B6CA0BC08EEFA9A4595";
			wchar_t file_name[MAX_PATH] = { 0 };
			PathCombineW(file_name, dir, mjz::Helper::GetFileNameFromUrl(url, mjz::Helper::NAME_ONLY).c_str());
			auto downloader = mjz::DataDownloader::Create(url, file_name, md5, DEFAULT_DOWNLOAD_REQ_NUM,
				[](mjz::DataDownloader* d, bool end){
				if (end) {
					std::cout << "progress:" << "1000/1000" << std::endl;
					if (!d->IsSuccess())
					{
						std::wcout << d->GetLastErrorString() << std::endl;

						//看看要不要延迟10秒再重试。
						if (d->GetDataDownloaderErrorType() == mjz::DataDownloaderHttpRequestFailed)
						{
							d->DispatchMsg(10 * 1000, [d]{ d->Retry(); });
							return;
						}
					}
					else {
						std::cout << "download success." << std::endl;
					}
					PostQuitMessage(0);
				}
				else {
					std::cout << "progress:" << d->GetProgress(1000) << "/1000, speed:" << d->GetDownloadSpeed() << std::endl;
				}
			});
			MsgDispatcher::RunMessageLoop();
		}

		static void TestSmallFileDownload(LPCWSTR dir)
		{
			std::wstring url = L"http://dldir3.qq.com/minigamefile/Green_QQGame_5.24.57294.0_41.7z"; //50M
			std::wstring md5 = L"1eb3d7b82f32859f1d3b6ecf63c0f6e1";

			wchar_t file_name[MAX_PATH] = { 0 };
			PathCombineW(file_name, dir, mjz::Helper::GetFileNameFromUrl(url, mjz::Helper::NAME_ONLY).c_str());
			auto downloader = mjz::DataDownloader::Create(url, file_name, md5, DEFAULT_DOWNLOAD_REQ_NUM,
				[](mjz::DataDownloader* d, bool end){
				if (end) {
					std::cout << "progress:" << "100/100" << std::endl;
					if (!d->IsSuccess())
					{
						std::wcout << d->GetLastErrorString() << std::endl;
					}
					else {
						std::cout << "download success." << std::endl;
					}
					PostQuitMessage(0);
				}
				else {
					std::cout << "progress:" << d->GetProgress(100) << "/100, speed:" << d->GetDownloadSpeed() << std::endl;
				}
			});
			MsgDispatcher::RunMessageLoop();
		}

		static void TestFileDownloadInMemory()
		{
			std::wstring url = L"http://dldir3.qq.com/minigamefile/Green_QQGame_5.24.57294.0_41.7z"; //50M
			std::wstring md5 = L"1eb3d7b82f32859f1d3b6ecf63c0f6e1";
			auto downloader = mjz::DataDownloader::Create(url, L"", md5, DEFAULT_DOWNLOAD_REQ_NUM,
				[](mjz::DataDownloader* d, bool end){
				if (end) {
					std::cout << "progress:" << "100/100" << std::endl;
					if (!d->IsSuccess())
					{
						std::wcout << d->GetLastErrorString() << std::endl;
					}
					else {
						std::cout << "download success." << std::endl;
					}
					PostQuitMessage(0);
				}
				else {
					std::cout << "progress:" << d->GetProgress(100) << "/100, speed:" << d->GetDownloadSpeed() << std::endl;
				}
			});
			MsgDispatcher::RunMessageLoop();
		}

		static void TestHtmlRequest(const std::wstring& url)
		{
			//std::wstring url = L"https://minigame.qq.com";
			auto http_req = mjz::HttpRequest::Create(url);
			http_req->SetValidSSLCert(TRUE);
			http_req->Send(mjz::VERB_GET, [](mjz::HttpRequest* pReq, bool end) {
				if (end)
				{
					if (pReq->IsSuccess())
					{
						std::wcout << pReq->GetResponseHeader() << std::endl;
						std::cout << pReq->GetRawResponseBody() << std::endl;
					}
					else
					{
						std::wcout << pReq->GetLastErrorString() << std::endl;
						if (pReq->IsSecureFailure())
						{
							if (pReq->SetValidSSLCert(FALSE))
							{
								pReq->Send();
								return;
							}
						}
					}
					PostQuitMessage(0);
				}
			});
			MsgDispatcher::RunMessageLoop();
		}
	};
}
