导航:[首页]->[network]->[Winhttp练笔]
    #define USE_WINHTTP

    #include <cassert>
    #include <string>
    #include <sstream>
    #include <tchar.h>
    #ifdef USE_WINHTTP
    #include <windows.h>
    #include <winhttp.h>
    #pragma comment(lib, "winhttp.lib")
    #else
    #include <wininet.h>
    #pragma comment(lib, "wininet.lib")
    #endif

    static void FormatWinHttpMessage(DWORD errCode,std::wstring* str){
        HMODULE hModule = ::GetModuleHandleW(L"winhttp.dll");
        if(hModule){
            LPTSTR pBuffer = NULL;
            DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
            if(!!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_IGNORE_INSERTS |
                FORMAT_MESSAGE_FROM_HMODULE,
                hModule,
                errCode,
                systemLocale,
                (LPTSTR)&pBuffer,
                0,
                NULL))
            {
                if(str)
                    *str = pBuffer;
                LocalFree(pBuffer);
            }
        }
    }

    class WinHttpConnection;
    class WinHttpRequest;
    class WinHttpSession;
    class WinHttpHandle
    {
    public:
        explicit WinHttpHandle() :
            m_handle_(0)
        {}

        virtual ~WinHttpHandle()
        {
            this->Close();
        }

        bool Attach(HINTERNET handle)
        {
            assert(NULL == m_handle_);
            m_handle_ = handle;
            return NULL != m_handle_;
        }

        HINTERNET Detach()
        {
            HANDLE handle = m_handle_;
            m_handle_ = NULL;
            return handle;
        }

        void Close()
        {
            if (NULL != m_handle_)
            {
                if(!::WinHttpCloseHandle(m_handle_))
                {
                    assert(0);
                    ;
                }
                m_handle_ = NULL;
            }
        }

        DWORD SetOption(DWORD option,
                          const void* value,
                          DWORD length)
        {
            assert(NULL != m_handle_);
            if (!!::WinHttpSetOption(m_handle_,
                                    option,
                                    const_cast<void*>(value),
                                    length))
            {
                return 0;
            }
            return ::GetLastError();
        }

        HRESULT QueryOption(DWORD option,
                            void* value,
                            DWORD& length) const
        {
            // WINHTTP_OPTION_URL 必须转换成wchar_t
            // 注意返回值 ERROR_NOT_ENOUGH_MEMORY
            assert(NULL != m_handle_);
            if (!!::WinHttpQueryOption(m_handle_,
                                      option,
                                      value,
                                      &length))
            {
                return 0;
            }
            return ::GetLastError();
        }

        bool        Empty() const{
            return NULL == m_handle_;
        }
    protected:
        HINTERNET m_handle_;
    };

    class WinHttpSession : public WinHttpHandle
    {
        friend class WinHttpConnection;
    public:
        DWORD Init()
        {
            return InitEx(0,
                WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                WINHTTP_NO_PROXY_NAME,
                WINHTTP_NO_PROXY_BYPASS,
                0);
        }


        DWORD InitEx(const wchar_t* usr_agent,
            DWORD access_type,
            const wchar_t* proxy_name,
            const wchar_t* proxy_pwd,
            DWORD flag)
        {
            if (Attach(::WinHttpOpen(usr_agent, // no agent string
                                      access_type,
                                      proxy_name,
                                      proxy_pwd,
                                      flag)))
            {
                return 0;
            }
            return ::GetLastError();
        }
    };

    class WinHttpConnection : public WinHttpHandle
    {
        friend class WinHttpRequest;
    public:
        DWORD Init(const wchar_t* server_name,
                           INTERNET_PORT port,
                           WinHttpSession& session)
        {
            if (Attach(::WinHttpConnect(session.m_handle_,
                                         server_name,
                                         port,
                                         0))) // reserved
            {
                return 0;
            }
            return ::GetLastError();
        }
    };


    class WinHttpRequest : public WinHttpHandle
    {
    public:
        DWORD Init(const wchar_t* verb,
            const wchar_t* path,
            WinHttpConnection& connection)
        {
            return InitEx(verb,
                path,
                0,
                WINHTTP_NO_REFERER,
                WINHTTP_DEFAULT_ACCEPT_TYPES,
                0,
                connection);
        }


        DWORD InitEx(const wchar_t* verb,
            const wchar_t* object_name,
            const wchar_t* version,
            const wchar_t* referrer,
            const wchar_t** accept_types,
            DWORD flag,
            WinHttpConnection& connection)
        {
            if (Attach(::WinHttpOpenRequest(connection.m_handle_,
                                         verb,
                                         object_name,
                                         version,
                                         referrer,
                                         accept_types,
                                         flag))) 
            {
                return 0;
            }
            return ::GetLastError();
        }

        DWORD SendRequest(){
            assert(m_handle_);
            if(!!::WinHttpSendRequest(m_handle_,
                WINHTTP_NO_ADDITIONAL_HEADERS,//WinHttpAddRequestHeaders
                0, // headers length
                WINHTTP_NO_REQUEST_DATA,
                0, // request data length
                0, // total length
                0))
            {
                return 0;
            }
            return ::GetLastError();
        }

        DWORD  ReceiveResponse(){
            assert(m_handle_);
            if(!!::WinHttpReceiveResponse(m_handle_,0))
                return 0;
            else
                return ::GetLastError();
        }

        // 注意:
        // WINHTTP_QUERY_STATUS_TEXT 返回unicode字符,需要做如下转换才能用
        //  wchar_t* ptr_ = reinterpret_cast<wchar_t*>(buf);
        // 注意返回值 ERROR_NOT_ENOUGH_MEMORY
        DWORD QueryHeaders(DWORD flag,
            char* buf,
            DWORD* buflen){
            assert(m_handle_);
            assert(buf);
            assert(buflen);
            if(!!::WinHttpQueryHeaders(m_handle_,
                flag,
                WINHTTP_HEADER_NAME_BY_INDEX,
                reinterpret_cast<LPVOID>(buf),
                buflen,
                WINHTTP_NO_HEADER_INDEX))
            {
                return 0;
            }
            return ::GetLastError();
        }
        // 查询一个DWORD数据
        DWORD QueryDWordHeaders(DWORD flag,
            DWORD* value){
            assert(value);
            DWORD len_ = sizeof(*value);
            return QueryHeaders(flag | WINHTTP_QUERY_FLAG_NUMBER,
                reinterpret_cast<char*>(value),
                &len_);
        }
        // 获得http code
        DWORD QueryHttpCode(DWORD* value){
            return QueryDWordHeaders(WINHTTP_QUERY_STATUS_CODE,value);
        }
        // 获得content 长度
        DWORD QueryContentLength(DWORD* value){
            return QueryDWordHeaders(WINHTTP_QUERY_CONTENT_LENGTH,value);
        }

        //WinHttpAddRequestHeaders

        DWORD QueryDataAvailable(DWORD* value){
            assert(m_handle_);
            assert(value);
            if(!!::WinHttpQueryDataAvailable(m_handle_,value)){
                return 0;
            }
            return ::GetLastError();
        }

        DWORD ReadData(char* buf,
            DWORD buflen,
            DWORD* readed){
            assert(m_handle_);
            assert(buf);
            assert(buflen);
            if(!!::WinHttpReadData(m_handle_,
                buf,
                buflen,
                readed)){
                return 0;
            }
            return ::GetLastError();
        }

        DWORD AddRequestHeaders(const wchar_t* headers,
            DWORD length,
            DWORD modifiers){
            assert(m_handle_);
            assert(headers);
            if(!!::WinHttpAddRequestHeaders(m_handle_,
                headers,
                length,
                modifiers)){
                return 0;
            }
            return ::GetLastError();
        }
        DWORD       AddRangeHeader(DWORD offset){
            std::wostringstream strbuf_;
            strbuf_ << L"Range:bytes=" << offset << L"-";
            std::wstring header_ = strbuf_.str();
            return this->AddRequestHeaders(header_.c_str(),
                -1,
                WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);
        }

        static DWORD CrackUrl(URL_COMPONENTS* url_comp,const wchar_t* url){
            assert(url_comp);
            assert(url);

            ZeroMemory(url_comp, sizeof(*url_comp));
            url_comp->dwStructSize = sizeof(*url_comp);
            url_comp->dwSchemeLength    = (DWORD)-1;
            url_comp->dwHostNameLength  = (DWORD)-1;
            url_comp->dwUrlPathLength   = (DWORD)-1;
            url_comp->dwExtraInfoLength = (DWORD)-1;

            if (!!::WinHttpCrackUrl(url,(DWORD)wcslen(url), 0, url_comp)){
                return 0;
            }
            return ::GetLastError();
        }
    };

    class Downloader {
    private:
        enum{DldBuflen = 4096};
    public:
        DWORD InitFromUrl(const wchar_t* url){
            URL_COMPONENTS url_comp_;
            DWORD ret_ = 0;
            ret_ = WinHttpRequest::CrackUrl(&url_comp_,url);
            if(!!ret_)
                return ret_;
            std::wstring host_(url_comp_.lpszHostName);
            std::wstring path_(url_comp_.lpszUrlPath);
            host_ = host_.substr(0,host_.size() - path_.size());

            ret_ = Init(host_.c_str(),url_comp_.nPort);
            if(!!ret_){
                return ret_;
            }
            m_path_ = path_;
            return 0;
        }

        DWORD Init(const wchar_t* host,
            INTERNET_PORT port){
            assert(m_session_.Empty());
            assert(m_connection_.Empty());
            DWORD ret_ = m_session_.Init();
            if(!!ret_){
                return ret_;
            }
            ret_ = m_connection_.Init(host,port,m_session_);
            if(!!ret_){
                m_session_.Close();
                return ret_;
            }
            return 0;
        }

        void        Close(){
            m_session_.Close();
            m_connection_.Close();
        }

        DWORD       DownloadEx(const wchar_t* path,DWORD offset){
            assert(!m_session_.Empty());
            assert(!m_connection_.Empty());
            assert(path || !m_path_.empty());
            if(!path)
                path = m_path_.c_str();

            WinHttpRequest request_;
            DWORD ret_ = request_.Init(L"get",path,m_connection_);
            if(!!ret_)
                return ret_;

            if(offset > 0)
            {
                ret_ = request_.AddRangeHeader(offset);
                if(!!ret_)
                    return ret_;
            }

            ret_ = request_.SendRequest();
            if(!!ret_)
                return ret_;
            ret_ = request_.ReceiveResponse();
            if(!!ret_)
                return ret_;

            // 判断http code
            DWORD http_code_;
            ret_ = request_.QueryHttpCode(&http_code_);
            if(!!ret_)
                return ret_;
            if(HTTP_STATUS_OK != http_code_ &&
                HTTP_STATUS_PARTIAL_CONTENT != http_code_)
                return 0;

            return DowndloadData(request_);
        }
        DWORD       Download(const wchar_t* path){
            return DownloadEx(path,0);
        }

    private:
        DWORD       DowndloadData(WinHttpRequest& request){

            char buf_[DldBuflen];
            DWORD available_,ret_,readed_,total_;
            total_ = 0;
            do{
                ret_ = request.QueryDataAvailable(&available_);
                if(!!ret_)
                    return false;
                if(!available_)
                    break;
                ret_ = request.ReadData(buf_,sizeof(buf_),&readed_);
                if(!!ret_)
                    return false;
                total_ += readed_;
            }while(true);
            return 0;
        }
    private:
        WinHttpSession      m_session_;
        WinHttpConnection   m_connection_;
        std::wstring        m_path_;
    };


    #endif


    #include "myhttp.h"
    #include <iostream>
    #include <cstdio>

    int main(){
        /*
        WinHttpSession session_;
        assert(!session_.Init());

        //http://ttplayer.qianqian.com/download/ttpsetup_593-44059078.exe
        //http://msdn.microsoft.com/en-us/library/windows/desktop/aa384070(v=vs.85).aspx
        WinHttpConnection connection_;
        assert(!connection_.Init(L"msdn.microsoft.com",80,session_));

        WinHttpRequest request_;
        assert(!request_.Init(
            L"get",
            L"/en-us/library/windows/desktop/aa384070(v=vs.85).aspx",
            connection_));
        assert(!request_.SendRequest());
        assert(!request_.ReceiveResponse());
        DWORD code_;
        assert(!request_.QueryHttpCode(&code_));
        assert(!request_.QueryContentLength(&code_));

        char buf[1024];
        DWORD buflen = 1024;
        DWORD ret_ = request_.QueryHeaders(WINHTTP_QUERY_HOST,buf,&buflen);
        wchar_t* ptr_ = reinterpret_cast<wchar_t*>(buf);

        buflen = 1024;
        ret_ = request_.QueryOption(WINHTTP_OPTION_URL,buf,buflen);

        std::wstring errstr_;
        FormatWinHttpMessage(ret_,&errstr_);
        */
        // 6223320

        //http://169.254.161.130/hfs.exe


        Downloader download_;
        download_.InitFromUrl(L"http://169.254.161.130/hfs.exe");
        //assert(!download_.Init(L"169.254.161.130",80));
        assert(!download_.Download(NULL));
        return 0;
    }