http下载时加Cookie

遇到在http下载时,需要加Cookie的情况.


如果一个url要匹配一个Cookie才能下载时,可以用wireshark观察类似软件可以正常下载时的发送的http请求内容。

然后修正自己的实现,直到我们可以发送正确的http请求内容.


对于多任务多线程的http下载,确实比单线程http下载块多了.

任务做完了, 整理一个Demo记录一下~. 

工程下载点 : srcHttpDownload.rar


测试程序:

/// @file       srcHttpDownload.cpp
/// @brief      记录在http下载操作中,加入Cookie的实现
/// @note       当http请求失败时,看看wireshark的抓包结果,看看服务器返回的信息
///             看看正确的http请求的发送内容,
///             修改自己的实现,一直到发送出和样例软件一样的结果就ok了

#include "stdafx.h"
#include "http_request.h"

/// @note 有些http下载是要加Cookie的,Cookie不对,会被http服务器拒绝

/// 远程url, 需要下载的目标
#define DL_URL \
    L"https://download-installer.cdn.mozilla.net/pub/" \
    L"firefox/releases/31.0/win32/en-GB/Firefox%20Setup%2031.0.exe"

/// 需要加的Cookie
#define DL_COOKIE   L"Cookie: my_cookie"

/// file save to 
#define DL_FILE_SAVE_TO L"d:\\my_dl.dat"

DWORD DownloadUrl(const WCHAR* pcUrl, const WCHAR* pcCookie, const WCHAR* pcFileSaveTo);

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD   dwRc = S_FALSE;

    dwRc = DownloadUrl(DL_URL, DL_COOKIE, DL_FILE_SAVE_TO);
    _tprintf(L"download %s\r\n", (S_OK == dwRc) ? L"ok" : L"failed");

    _tprintf(L"END, press any key to quit\r\n");

    /** runresult
    >> DownloadUrl(https://download-installer.cdn.mozilla.net/pub/firefox/releases/3
    1.0/win32/en-GB/Firefox%20Setup%2031.0.exe, Cookie: my_cookie)
    downloading...
    ...
    downloading..., read back 32768/31948800 bytes
    downloading..., read back 32768/31981568 bytes
    downloading..., read back 32768/32014336 bytes
    downloading..., read back 14888/32029224 bytes
    downloading over
    download ok
    END, press any key to quit
    */

    getwchar();
	return 0;
}

DWORD DownloadUrl(const WCHAR* pcUrl, const WCHAR* pcCookie, const WCHAR* pcFileSaveTo)
{
    DWORD   dwRc = S_FALSE;
    CLsHttpRequest  Dl;

    _ASSERT(NULL != pcUrl);
    _ASSERT(NULL != pcCookie);
    _ASSERT(NULL != pcFileSaveTo);

    _tprintf(L">> DownloadUrl(%s, %s)\r\n", pcUrl, pcCookie);
    dwRc = Dl.DownloadUrl(pcUrl, pcCookie, pcFileSaveTo);

    return dwRc;
}

http请求类实现:

/// @file       http_request.h
/// @brief      http请求的操作

#ifndef __HTTP_REQUEST_H__
#define __HTTP_REQUEST_H__

#include "stdafx.h"

#include <atlconv.h>
#include <tchar.h>
#include <string>

#include <wininet.h>
#pragma comment(lib, "wininet.lib")

#include <UrlMon.h>
#pragma comment(lib, "urlmon.lib")

#ifndef HTTP_READ_FILE_BUFFER_SIZE
    /// 32KB read once form remote
    #define HTTP_READ_FILE_BUFFER_SIZE ((long)1024 * 32)
#endif

class CLsHttpRequest
{
public:
    CLsHttpRequest();
    virtual ~CLsHttpRequest();

    DWORD DownloadUrl(
        const WCHAR* pcUrl, 
        const WCHAR* pcCookie,
        const WCHAR* pcFileSaveTo);

private:
    HINTERNET create_session();
    HINTERNET create_connect();
    HINTERNET open_request();
    DWORD send_request();
    DWORD http_read();
    void FreeResource();

    std::wstring GetSystemUserAgent();
    void GetUrlComponent(    
        const WCHAR* pcUrl, 
        std::wstring& strHost, 
        std::wstring& strObject, 
        INTERNET_SCHEME& nScheme);

    void SetHttpMaxConnectNumberEx(HINTERNET hHandle);
    BOOL add_http_headers(HINTERNET& http_request, const WCHAR * pcHttpRange);
    BOOL add_http_headers_item(HINTERNET& http_request, const WCHAR * pcHttpHeaderItem);

private:
    HINTERNET m_hSession;
    HINTERNET m_hConnect;
    HINTERNET m_http_request;

    std::wstring    m_strUrl;
    std::wstring    m_strCookie; 
    std::wstring    m_strFileSaveTo;

    std::wstring    m_strHost;
    std::wstring    m_strObject;
    INTERNET_SCHEME m_nScheme;
    DWORD           m_dwFlagOpenHttpRequest;
};

#endif // #ifndef __HTTP_REQUEST_H__


/// @file       http_request.cpp
/// @brief      ...

#include "stdafx.h"
#include <vector>
#include "http_request.h"

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) \
    if (NULL != (p)) \
    { \
        delete (p); \
        (p) = NULL; \
    }
#endif

#ifndef SAFE_CLOSE_HANDLE
#define SAFE_CLOSE_HANDLE(h) \
    if (NULL != (h)) \
    { \
        CloseHandle((h)); \
        (h) = NULL; \
    }
#endif

/// @note http 操作形成的句柄, 关闭时要用 InternetCloseHandle
/// 如果用 CloseHandle, 会崩溃
#ifndef SAFE_CLOSE_HTTP_HANDLE
#define SAFE_CLOSE_HTTP_HANDLE(h) \
    if (NULL != (h)) \
    { \
    InternetCloseHandle((h)); \
    (h) = NULL; \
    }
#endif

CLsHttpRequest::CLsHttpRequest()
{
    m_hSession = NULL;
    m_hConnect = NULL;
    m_http_request = NULL;

    m_strUrl = L"";
    m_strCookie = L"";
    m_strFileSaveTo = L"";

    m_strHost = L"";
    m_strObject = L"";
    m_nScheme = INTERNET_SCHEME_UNKNOWN;

    /// 需要指定 INTERNET_FLAG_NO_COOKIES, 才能使用自己的Cookie !!!
    m_dwFlagOpenHttpRequest = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_COOKIES /*| INTERNET_FLAG_NO_CACHE_WRITE*/;
}

CLsHttpRequest::~CLsHttpRequest()
{
    FreeResource();
}

void CLsHttpRequest::FreeResource()
{
    SAFE_CLOSE_HTTP_HANDLE(m_http_request);
    SAFE_CLOSE_HTTP_HANDLE(m_hConnect);
    SAFE_CLOSE_HTTP_HANDLE(m_hSession);
}

DWORD CLsHttpRequest::DownloadUrl(
    const WCHAR* pcUrl, 
    const WCHAR* pcCookie,
    const WCHAR* pcFileSaveTo)
{
    DWORD   dwRc = S_FALSE;

    if ((NULL == pcUrl)
        || (NULL == pcFileSaveTo))
    {
        goto END_DownloadUrl;
    }

    m_strUrl = pcUrl;
    m_strFileSaveTo = pcFileSaveTo;
    m_strCookie = (NULL != pcCookie) ? pcCookie : L"";

    if (NULL == create_session())
        goto END_DownloadUrl;

    if (NULL == create_connect())
        goto END_DownloadUrl;

    if (NULL == open_request())
        goto END_DownloadUrl;

    if (S_OK != send_request())
        goto END_DownloadUrl;

    if (S_OK != http_read())
        goto END_DownloadUrl;

    dwRc = S_OK;

END_DownloadUrl:
    FreeResource();
    return dwRc;
}

std::wstring CLsHttpRequest::GetSystemUserAgent()
{
    USES_CONVERSION;
    DWORD  n = 1024;
    char   t[1024] = {0};

    ObtainUserAgentString(0, t, &n);
    return A2W(t);
}

HINTERNET CLsHttpRequest::create_session()
{
    if (NULL == m_hSession)
    {
        m_hSession = InternetOpen(
            GetSystemUserAgent().c_str(),
            INTERNET_OPEN_TYPE_DIRECT, // INTERNET_OPEN_TYPE_PRECONFIG,
            NULL,
            NULL,
            0);
    }

    return m_hSession;
}

HINTERNET CLsHttpRequest::create_connect()
{
    INTERNET_PORT       nPort = INTERNET_DEFAULT_HTTP_PORT;

    _ASSERT(m_strUrl.size() > 0);
    GetUrlComponent(m_strUrl.c_str(), m_strHost, m_strObject, m_nScheme);
    if (INTERNET_SCHEME_HTTPS == m_nScheme)
    {
        nPort = INTERNET_DEFAULT_HTTPS_PORT;
    }

    _ASSERT(NULL != m_hSession);
    m_hConnect = InternetConnect(
        m_hSession, 
        m_strHost.c_str(), 
        nPort, 
        NULL, 
        NULL, 
        INTERNET_SERVICE_HTTP, 
        0, 
        1);

    if (NULL != m_hConnect)
    {
        /// 试验过了, 在程序开始设置 HINTERNE 为NULL, 那种http连接数, 是没用的
        /// 只有在InternetConnect 所在线程中, 对有效httpConnect设置指定连接数, 才有效
        SetHttpMaxConnectNumberEx(m_hConnect);
    }

    return m_hConnect;
}

void CLsHttpRequest::GetUrlComponent(
    const WCHAR* pcUrl, 
    std::wstring& strHost, 
    std::wstring& strObject, 
    INTERNET_SCHEME& nScheme)
{
    BOOL                bRc = FALSE;
    DWORD               dwBufferSize = 0;
    URL_COMPONENTS      uc;
    std::vector<TCHAR>  vec1;
    std::vector<TCHAR>  vec2;

    _ASSERT(NULL != pcUrl);

    ZeroMemory(&uc, sizeof(uc));
    dwBufferSize = _tcslen(pcUrl) + sizeof(WCHAR);
    vec1.resize(dwBufferSize, (TCHAR)0);
    vec2.resize(dwBufferSize, (TCHAR)0);

    uc.dwStructSize = sizeof(uc);
    uc.lpszHostName = &vec1[0];
    uc.dwHostNameLength = dwBufferSize;
    uc.lpszUrlPath = &vec2[0];
    uc.dwUrlPathLength = dwBufferSize;

    bRc = InternetCrackUrl(pcUrl, _tcslen(pcUrl), 0, &uc);
    _ASSERT(bRc);

    strHost = &vec1[0];
    strObject = &vec2[0];
    nScheme = uc.nScheme;
}

void CLsHttpRequest::SetHttpMaxConnectNumberEx(HINTERNET hHandle)
{
    BOOL    bRc = FALSE;
    DWORD   dwErr = 0;
    DWORD   dwHttpConnectMax = 50/*5 * 5 * 2 + 10*/; ///< (5条任务,一个任务5条线程) * 2 + 10(备用数目)

    DWORD   dwRecvBufLen = 0;

    /// 设置不成功, 设置成功的,取出来还是原来的值.
    bRc = InternetSetOption(hHandle,INTERNET_OPTION_MAX_CONNS_PER_SERVER,&dwHttpConnectMax,sizeof(DWORD));
    dwErr = ::GetLastError();
    //    _ASSERT(bRc); ///< 虽然为FALSE, 但是已经有效果了

    bRc = InternetSetOption(hHandle,INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER,&dwHttpConnectMax,sizeof(DWORD));
    dwErr = ::GetLastError();
    //    _ASSERT(bRc);  ///< 虽然为FALSE, 但是已经有效果了

    /// 这里得到的连接数, 也是旧的, 但是从程序运行效果上看,已经生效了
}

HINTERNET CLsHttpRequest::open_request()
{
    const WCHAR * szAcceptTypes[] = {L"*/*", NULL};

    if (INTERNET_SCHEME_HTTPS == m_nScheme)
    {
        m_dwFlagOpenHttpRequest |= INTERNET_FLAG_SECURE ;
    }

    _ASSERT(NULL != m_hConnect);
    m_http_request = HttpOpenRequest(
        m_hConnect, 
        L"GET",
        m_strObject.c_str(), 
        L"HTTP/1.1", 
        NULL, 
        szAcceptTypes, 
        m_dwFlagOpenHttpRequest, 
        1);

    return m_http_request;
}

BOOL CLsHttpRequest::add_http_headers_item(HINTERNET& http_request, const WCHAR * pcHttpHeaderItem)
{
    BOOL            bRc = FALSE;
    DWORD           dwRc = 0;

    _ASSERT(NULL != pcHttpHeaderItem);

    bRc = ::HttpAddRequestHeaders(
        http_request, 
        pcHttpHeaderItem, 
        -1, 
        HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE);
    dwRc = ::GetLastError();
    _ASSERT(bRc);

    return bRc;
}

BOOL CLsHttpRequest::add_http_headers(HINTERNET& http_request, const WCHAR * pcHttpRange)
{
    std::wstring    strTmp = L"";
    BOOL            bRc = FALSE;
    DWORD           dwErrSn = 0;

    strTmp = L"Accept: */*";
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    strTmp = L"Connection: keep-alive";
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    strTmp = L"Host: ";
    strTmp += m_strHost.c_str();
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    if (NULL != pcHttpRange)
    {
        strTmp = pcHttpRange;
        bRc = add_http_headers_item(http_request, strTmp.c_str());
        _ASSERT(bRc);
    }

    /// 需要保留 user agent,可以增加下载速度
    strTmp = L"User-Agent: ";
    strTmp += GetSystemUserAgent().c_str();
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    strTmp = L"Content-Length: 0";
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    /// 加入自己的Cookie !!!
    strTmp = m_strCookie.c_str();
    bRc = add_http_headers_item(http_request, strTmp.c_str());
    _ASSERT(bRc);

    return TRUE;
}

DWORD CLsHttpRequest::send_request()
{
    BOOL            bRc = FALSE;

    _ASSERT(NULL != m_http_request);
    add_http_headers(m_http_request, L"Range: bytes=0-\r\n");

    bRc = ::HttpSendRequest(
        m_http_request, 
        NULL,
        0,
        NULL, 
        0);

    return bRc ? S_OK : S_FALSE;
}

DWORD CLsHttpRequest::http_read()
{
    BOOL        bReadOkOnce = FALSE;
    BYTE*       pcBufRead = new BYTE[HTTP_READ_FILE_BUFFER_SIZE];
    _ASSERT(NULL != pcBufRead);
    BOOL        bReadResult = FALSE;
    DWORD       dwReadBack = 0;
    LONGLONG    llRdBackTotal = 0;

    _tprintf(L"downloading...\r\n");
    while (true)
    {
        bReadResult = InternetReadFile(
            m_http_request, 
            pcBufRead, 
            HTTP_READ_FILE_BUFFER_SIZE, 
            &dwReadBack);

        if (bReadResult && (dwReadBack > 0))
        {
            bReadOkOnce = TRUE;
            llRdBackTotal += dwReadBack;
            _tprintf(L"downloading..., read back %d/%I64d bytes\r\n", 
                dwReadBack, 
                llRdBackTotal);

            /// @todo
            /// please cat pcBufRead[0] ~ pcBufRead[dwReadBack - 1] to disk file m_strFileSaveTo
        }
        else
        {
            break;
        }
    }

    _tprintf(L"downloading over\r\n");

END_proc_http_request:
    SAFE_DELETE(pcBufRead);
    return bReadOkOnce ? S_OK : S_FALSE;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值