Windows平台利用wininet做的HTTP的GET和POST,支持HTTPS

来源:互联网 发布:java开源社区有哪些 编辑:程序博客网 时间:2024/04/30 09:20

                          By Fanxiushu  2014 转载或引用请注明原作者   。

C++开发中,实现HTTP的代码总是比其他开发语言麻烦,现提供 Windows平台下,利用wininet实现的HTTP,支持HTTPS。

希望对还在坚持使用C++开发HTTP通讯的朋友有一小点帮助.


///头文件

///// By Fanxiushu 2014-04-27
#pragma once
///////////////////////////////////////////////////////////GET
struct http_get_param
{
    const char*        url;
    const char*        option_hdr;
    int                trans_timeout;
    //////////////

    const char*        file_path;

};

int http_downfile(http_get_param * hgp);

///////////////////////////////////////////////////////// POST
struct http_post_part
{
    /////
    const char*   name;
    const char*   filename;

    char*         data;
    int           data_len;
};

struct http_post_param
{
    const char*         url;
    const char*         option_hdr;
    int                 trans_timeout;
    ////
    http_post_part*     parts;
    int                 parts_count;

    char*               res_buf;
    int                 res_len;
};

int http_postdata(http_post_param* hpp);

///实现文件, 实现文件的底部是调用实例。

/// By Fanxiushu 2014-04-27

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

struct __http_data_t
{
    HANDLE hEvt;
    HINTERNET hUrl;
};

static void CALLBACK InternetStatusCallback(
    _In_  HINTERNET hInternet,
    _In_  DWORD_PTR dwContext,
    _In_  DWORD dwInternetStatus,
    _In_  LPVOID lpvStatusInformation,
    _In_  DWORD dwStatusInformationLength
    )
{
    __http_data_t* data = (__http_data_t*)dwContext;
    ////
//        printf("**dwInternetStatus=%d\n", dwInternetStatus);
    if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE){
        INTERNET_ASYNC_RESULT* res = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
        data->hUrl = (HINTERNET)res->dwResult;
        SetEvent(data->hEvt);
    }
    else if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED){
        INTERNET_ASYNC_RESULT* res = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
        data->hUrl = (HINTERNET)res->dwResult;
    }
}

int http_downfile( http_get_param * hgp )
{
    if (!hgp || !hgp->url || !hgp->file_path) return -1;

    ///
    DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE;

    char dns[512] = "";
    char uri_path[8192] = "";
    URL_COMPONENTS uc;
    memset(&uc, 0, sizeof(uc));
    uc.dwStructSize = sizeof(uc);
    uc.lpszHostName = dns;
    uc.dwHostNameLength = 512;
    uc.dwUrlPathLength = 8192;
    uc.lpszUrlPath = uri_path;
    InternetCrackUrl(hgp->url, 0, 0, &uc);

    if (uc.nScheme == INTERNET_SCHEME_HTTPS)
        flags |= (SECURITY_IGNORE_ERROR_MASK |
        SECURITY_INTERNET_MASK | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
        INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
        INTERNET_FLAG_RELOAD);

    /////
    HINTERNET hSession = NULL;
    hSession = InternetOpen("HTTP.GetData-UserAgent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
    if (!hSession){
        log_printf("http_downfile: InternetOpen Error <%d>\n", GetLastError());
        return -1;
    }
    InternetSetStatusCallback(hSession, InternetStatusCallback);
    /////
    HANDLE hEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
    __http_data_t data; data.hEvt = hEvt; data.hUrl = NULL;

    HINTERNET hConnect = InternetConnect(hSession, dns, uc.nPort, "", "", INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&data );
    if (!hConnect && GetLastError() == ERROR_IO_PENDING){
        DWORD ret = ::WaitForSingleObject(hEvt, hgp->trans_timeout * 1000);
        hConnect = data.hUrl;
    }
    if (!hConnect){
        log_printf("http_downfile: Connect [%s:%d] Err<%d>\n", dns, uc.nPort, GetLastError());
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        return -1;
    }

    HINTERNET hRequest = HttpOpenRequest(hConnect, "GET", uri_path, HTTP_VERSION, NULL, NULL, flags, (DWORD_PTR)&data);
    if (!hRequest && GetLastError() == ERROR_IO_PENDING ){
        DWORD ret = ::WaitForSingleObject(hEvt, hgp->trans_timeout * 1000);
        hRequest = data.hUrl;
    }
    if (!hRequest){
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        log_printf("http_downfile: Open URL [%s] err=%d.\n", hgp->url, GetLastError());
        return -1;
    }

    //////
    if (uc.nScheme == INTERNET_SCHEME_HTTPS){// 忽略证书错误
        BOOL f;
        DWORD flags = 0; DWORD len = sizeof(flags);
        f = InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, &len);
        flags |= (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_WRONG_USAGE);
        f = InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
        if (!f){
            log_printf("Warning: Set HTTPS Flags Error err=%d\n", GetLastError());
        }
    }
    //////////
    BOOL ff = HttpSendRequest(hRequest, hgp->option_hdr, hgp->option_hdr?strlen(hgp->option_hdr):0, NULL, 0 );
    if (!ff && GetLastError() == ERROR_IO_PENDING){
        if (::WaitForSingleObject(hEvt, hgp->trans_timeout * 1000) == WAIT_OBJECT_0) ff = TRUE;
    }
    if (!ff){
        log_printf("http_downfile:  HttpSendRequest Err=%d\n",GetLastError() );
        InternetCloseHandle(hRequest);
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        return -1;
    }

    /////////////
    FILE* fp = fopen(hgp->file_path, "wb");
    if (!fp){
        log_printf("http_downfile: Can not Create Local File [%s]\n", hgp->file_path );
        InternetCloseHandle(hRequest); InternetCloseHandle(hConnect);  InternetCloseHandle(hSession);
        CloseHandle(hEvt); return -1;
    }

    DWORD total_len = 0; DWORD _sz_len = sizeof(DWORD);
    HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &total_len, &_sz_len, NULL);

    int ret = 0; DWORD cur_len = 0;
    ///
    while (true){
        char buf[16 * 1024];
        DWORD bytes = 0;
        data.hUrl = 0;
        INTERNET_BUFFERS ib; memset(&ib, 0, sizeof(ib));
        ib.dwStructSize = sizeof(INTERNET_BUFFERS);
        ib.dwBufferLength = sizeof(buf)-1;
        ib.lpvBuffer = buf;

        BOOL ff = InternetReadFileEx(hRequest, &ib, IRF_ASYNC, (DWORD_PTR)&data);
        if (!ff){
            if (GetLastError() == ERROR_IO_PENDING){
                if (::WaitForSingleObject(data.hEvt, hgp->trans_timeout * 1000) != WAIT_OBJECT_0){
                    ret = -1; break;
                }
                ////
            }
            else{
                ret = -1; break;
            }
        }
        bytes = ib.dwBufferLength;
        if (bytes == 0)break;
        ////
        if (fwrite(buf, 1, bytes, fp) != bytes){
            log_printf("http_downfile: Local Disk Error Can not Write Data.\n");
            ret = -1;
            break;
        }
        cur_len += bytes;  ////
        ///////progress
        if (total_len > 0){

        }
        /////
        //    buf[bytes]=0; printf( "%s",buf );
    }

    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hSession);
    CloseHandle(hEvt);

    ////
    ::fclose(fp);
    if (ret < 0){
        remove(hgp->file_path);
    }
    ////
    return ret;
}

int http_postdata( http_post_param* hpp )
{
    const char* url = hpp->url;
    const char* option_hdr = hpp->option_hdr;
    hpp->res_buf = NULL;
    hpp->res_len = 0;
    /////
    DWORD flags = INTERNET_FLAG_KEEP_CONNECTION| INTERNET_FLAG_NO_CACHE_WRITE;
    int i;

    char dns[512] = "";
    char uri_path[8192] = "";
    URL_COMPONENTS uc;
    memset(&uc, 0, sizeof(uc));
    uc.dwStructSize = sizeof(uc);
    uc.lpszHostName = dns;
    uc.dwHostNameLength = 512;
    uc.dwUrlPathLength = 8192;
    uc.lpszUrlPath = uri_path;
    InternetCrackUrl(url, 0, 0, &uc);
//    printf("%s -> %d -> %s\n", dns, uc.nPort, uri_path);

    if (uc.nScheme == INTERNET_SCHEME_HTTPS)
        flags |= (SECURITY_IGNORE_ERROR_MASK |
                 SECURITY_INTERNET_MASK | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
                 INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
                 INTERNET_FLAG_RELOAD);
    //////////////////////////

    HINTERNET hSession = NULL;
    HINTERNET hConnect = NULL;
    HINTERNET hRequest = NULL;
    hSession = InternetOpen("HTTP.PostData-UserAgent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
    if (!hSession){
        log_printf("http_postdata: InternetOpen Error <%d>\n", GetLastError());
        return -1;
    }
    InternetSetStatusCallback(hSession, InternetStatusCallback);
    /////
    HANDLE hEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
    __http_data_t hd; hd.hEvt = hEvt; hd.hUrl = NULL;
    hConnect = InternetConnect(hSession, dns, uc.nPort, "", "", INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&hd);
    if (!hConnect && GetLastError() == ERROR_IO_PENDING){
        DWORD ret = ::WaitForSingleObject(hEvt, hpp->trans_timeout * 1000);
        hConnect = hd.hUrl;
    }
    if (!hConnect){
        log_printf("http_postdata: Connect [%s:%d] Err<%d>\n", dns, uc.nPort, GetLastError());
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        return -1;
    }

    hRequest = HttpOpenRequest(hConnect, "POST", uri_path, HTTP_VERSION, NULL, NULL, flags,  (DWORD_PTR)&hd );
    if (!hRequest && GetLastError() == ERROR_IO_PENDING){
        DWORD ret = ::WaitForSingleObject(hEvt, hpp->trans_timeout * 1000);
        hRequest = hd.hUrl;
    }
    if (!hRequest){
        log_printf("http_postdata: HttpOpenRequest [%s:%d] Err<%d>\n", dns, uc.nPort, GetLastError());
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        return -1;
    }
    //////
    if (uc.nScheme == INTERNET_SCHEME_HTTPS){// 忽略证书错误
        BOOL f;
        DWORD flags = 0; DWORD len = sizeof(flags);
        f = InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, &len);
        flags |= (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_WRONG_USAGE );
        f = InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
        if (!f){
            log_printf("Warning: Set HTTPS Flags Error err=%d\n", GetLastError() );
        }
    }
    //////////

    LPSTR boundary = "$$$-----------------------------xiuxiu2014.03-13%%$$$$----()***kk===";
    ///
    LPSTR accept = "Accept: text/html,application/xhtml+xml,application/xml,text/plain;q=0.9,*/*;q=0.8";
    LPSTR accept_lan = "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3";
//    LPSTR accept_encoding = "Accept-Encoding: gzip, deflate";
    char content_type[256];
    sprintf(content_type, "Content-Type: multipart/form-data; boundary=%s", boundary);

    HttpAddRequestHeaders(hRequest, content_type, -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
    HttpAddRequestHeaders(hRequest, accept, -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
    HttpAddRequestHeaders(hRequest, accept_lan, -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
    //
//    HttpAddRequestHeaders(hRequest, accept_encoding, -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); //不接受编码

    //添加附加头
    if (option_hdr){
        HttpAddRequestHeaders(hRequest, option_hdr, -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
    }
    //////
    char first_boundary[256];
    char end_boundary[256];
    char delimiter[256];
    sprintf_s(first_boundary, "--%s\r\n", boundary);     int fb_len = strlen(first_boundary);
    sprintf_s(delimiter, "\r\n--%s\r\n", boundary);      int del_len = strlen(delimiter);
    sprintf_s(end_boundary, "\r\n--%s--\r\n", boundary); int eb_len = strlen(end_boundary);

    struct arr_data{
        const char* data;
        int data_len;
        string str;
        arr_data(const char* d, int l) :data(d), data_len(l){}
        arr_data(const string& o) { str = o;  data = str.c_str(); data_len = str.length(); }
        arr_data(const arr_data& o){ data = o.data; data_len = o.data_len; str = o.str; if (!str.empty()){ data = str.c_str(); data_len = str.length(); } }
    };
    list<arr_data> data_array;
    unsigned int id_idx = 0;

    ///
    char* content_subtype = "Content-Type: application/octet-stream\r\n\r\n";
    int subtype_len = strlen(content_subtype);

    data_array.push_back(arr_data(first_boundary, fb_len )); // first boundary

    DWORD upLen = fb_len + eb_len;

    for (i = 0; i < hpp->parts_count && hpp->parts ; ++i ){
        http_post_part* ps = &(hpp->parts[i]);
        char name[1024]; char filename[1024];
        if (ps->name)sprintf(name, "; name=\"%s\"", ps->name);
        else { sprintf(name, "; name=\"NAME%d\"", id_idx++); }
        if (ps->filename)sprintf(filename, "; filename=\"%s\"", ps->filename);
        else strcpy(filename, "");
        string ctx = string("Content-Disposition: form-data") + name + filename + "\r\n";
        ////
        if ( i != 0  ){ //不是第一个段,多个段之间的分割

            data_array.push_back( arr_data(delimiter, del_len) );  upLen += del_len;
        }
        
        data_array.push_back( arr_data(ctx) ); upLen += ctx.length(); //content_dispos
        data_array.push_back(arr_data(content_subtype, subtype_len)); upLen += subtype_len; // content_subtype
        if (ps->data && ps->data_len > 0){
            data_array.push_back(arr_data(ps->data, ps->data_len)); upLen += ps->data_len;  // DATA
        }
        //////
    }

    data_array.push_back(arr_data(end_boundary, eb_len)); // end boundary

    INTERNET_BUFFERS bufIn;
    memset(&bufIn, 0, sizeof(bufIn));
    bufIn.dwStructSize = sizeof(bufIn);
    bufIn.dwBufferTotal = upLen;
    BOOL ret = HttpSendRequestEx(hRequest, &bufIn, NULL, 0, (DWORD_PTR)&hd);
    if (!ret && GetLastError() == ERROR_IO_PENDING){
        if (::WaitForSingleObject(hEvt, hpp->trans_timeout * 1000) == WAIT_OBJECT_0) ret = TRUE;
    }
    if (!ret){
        log_printf("http_postdata: HttpSendRequestEx [%s:%d] Err<%d>\n", dns, uc.nPort, GetLastError());
        InternetCloseHandle(hRequest);
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hSession);
        CloseHandle(hEvt);
        return -1;
    }

    //////
    list<arr_data>::iterator yy;
    for (yy = data_array.begin(); yy != data_array.end(); ++yy ){
        DWORD bytes = 0;
        arr_data* d = &(*yy);
        ret = InternetWriteFile(hRequest, d->data,d->data_len, &bytes);
        if (!ret && GetLastError() == ERROR_IO_PENDING){
            if (::WaitForSingleObject(hEvt, hpp->trans_timeout * 1000) == WAIT_OBJECT_0) ret = TRUE;
        }
        if (!ret){
            log_printf("http_postdata: InternetWriteFile [%s:%d] Err<%d>\n", dns, uc.nPort, GetLastError());
            InternetCloseHandle(hRequest);
            InternetCloseHandle(hConnect);
            InternetCloseHandle(hSession);
            CloseHandle(hEvt);
            return -1;
        }
    }

    ///
    ret = HttpEndRequest(hRequest, 0, 0, (DWORD_PTR)&hd);
    if (!ret && GetLastError() == ERROR_IO_PENDING){
        if (::WaitForSingleObject(hEvt, hpp->trans_timeout * 1000) == WAIT_OBJECT_0) ret = TRUE;
    }
    if (!ret){
        log_printf("http_postdata: HttpEndRequest err\n");
    }
    //// read response
    DWORD size = 4096;
    char* pbuf = (char*)malloc(size);

    DWORD total_len = 0; DWORD _sz_len = sizeof(DWORD);

    HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &total_len, &_sz_len, NULL);

    ret = 0; DWORD cur_len = 0;
    if (!pbuf){
        ret = -1; goto L;
    }
    ///
    while (true){
        char buf[8 * 1024];
        DWORD bytes = 0;
        hd.hUrl = 0;
        INTERNET_BUFFERS ib; memset(&ib, 0, sizeof(ib));
        ib.dwStructSize = sizeof(INTERNET_BUFFERS);
        ib.dwBufferLength = sizeof(buf)-1;
        ib.lpvBuffer = buf;

        BOOL ff = InternetReadFileEx(hRequest, &ib, IRF_ASYNC, (DWORD_PTR)&hd);
        if (!ff){
            if (GetLastError() == ERROR_IO_PENDING){
                if (::WaitForSingleObject(hd.hEvt, hpp->trans_timeout * 1000) != WAIT_OBJECT_0){
                    ret = -1;
                    goto L;
                }
                ////
            }
            else{
                ret = -1;
                goto L;
            }
        }
        bytes = ib.dwBufferLength;
        if (bytes == 0)break;
        ////
        if (bytes + cur_len >= size-1 ){
            size = cur_len + bytes + 1024 * 8 + 10;
            char* vpp = (char*)realloc(pbuf, size);
            if (!vpp){
                ret = -1;
                goto L;
            }
            pbuf = vpp;
        }

        memcpy(pbuf + cur_len, buf, bytes);
        
        cur_len += bytes;  ////

        ///////progress, 进度
        if (total_len > 0){
            ///
        }
        /////
        //    buf[bytes]=0; printf( "%s",buf );
    }
    
    //////
    pbuf[cur_len] = '\0';
    hpp->res_buf = pbuf;
    pbuf = NULL;
    hpp->res_len = cur_len;

L:

    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hSession);
    CloseHandle(hEvt);
    if (pbuf)free(pbuf);
    ////
    return ret;
}

#if 1

int main(int argc, char** argv)
{
#if 1    // POST
    http_post_param hp; memset(&hp, 0, sizeof(hp));
    hp.trans_timeout = 20;

    hp.url = "https://passport.csdn.net/account/login?ref=toolbar"; // "https://www.google.com.hk/"; "http://192.168.0.120/tmp_dir";

    http_post_part sc[5]; memset(&sc, 0, sizeof(sc));
    sc[0].name = "Client-UniqueID";  sc[0].data = "ID00001"; sc[0].data_len = 8; //sc.filename = "filename.txt";

    sc[1].name = "DATA";
    sc[1].data = "DATA001"; sc[1].data_len = 7;

    sc[2].name = "ScreenSize";
    sc[2].data = "1366,768"; sc[2].data_len = strlen(sc[2].data);

    /////
    hp.parts = sc;
    hp.parts_count = 3; ///
    ///////////////////////////////////


    http_postdata(&hp); printf("DATA_LEN=%d\n%s\n", hp.res_len, hp.res_buf);
//    getchar();
    
    if (hp.res_buf)free(hp.res_buf);

#endif

#if 1    /// GET
    http_get_param hgp; memset(&hgp, 0, sizeof(hgp));

    hgp.url = "https://www.google.com.hk/";
    hgp.trans_timeout = 15;
    hgp.file_path = "test.htm";

    http_downfile(&hgp);

#endif


    return 0;
}

#endif


0 0