• WinHttp编程初步心得


    代码都在下面了,目前这个版本实现的功能还非常的少,能实现的有:

    1. GET方法。给定一个URL(可以带中文),返回http的响应数据,包括文本和二进制数据
    2. 支持代理服务器,需要用户预先设定好proxy server ip, port, username, password.

    不支持的有:
    1. POST
    2. 查询http响应的header。
    3. 将返回的文本数据重新编码,变成UTF-16。这需要首先从header取出http server响应的数据是什么编码的,然后就可以使用windows的MultiByteToWideChar来进行转换。

    这里总结几点开发过程中的注意点:
    1. MSDN上的资料不是非常的全,所以有些参考了google得来的内容。
    2. WinHttpOpen的时候,如果用AUTO PROXY,也就是让winhttp从注册表读取proxy设置的话(这个注册表的设置是proxycfg.exe生成的,和IE中的那个proxy设置是两码事)。
    3. proxy的用户名和密码需要用WinHttpSetOption来进行设置, timeout设定用WinHttpSetTimeouts
    4. 使用WinHttpCrackUrl可以将一个给定的url(必须以http://, https://打头)分解,设置到URL_COMPONENTS这个structure中。在这里其实就可以做url encoding(也就是将url不支持的字符转换成%xxx这样的东西),但是这样做的话需要手动给URL_COMPONENETS中的member分 配内存,比较麻烦,所以没做。
    5. 使用WinHttpCreateUrl可以根据URL_COMPONENETS重新生成一个string的URL,但是这里没用到。
    6. 使用WinHttpConnect的时候,一定要注意要给出hostname,不是url,所以要手动分解,只取出http server的那个部分,比如http://www.google.com/sfsdfdsf?id=xxx& user=xxxx,hostname就是www.google.com
    7. 然后使用WinHttpOpenRequest的时候,就可以给出去掉了http://之后的后面所有内容了,也就是www.google.com /sfsdfdsf?id=xxx&user=xxxx这样的东西。注意最后的flag设 置:WINHTTP_FLAG_ESCAPE_PERCENT | WINHTTP_FLAG_REFRESH,这两个很有用,WINHTTP_FLAG_ESCAPE_PERCENT就是将url中不合法的内容转 成%xxx,WINHTTP_FLAG_REFRESH就是跳过proxy的cache,每次都请求全新的数据。还有需要注意的 是,WinHttpOpenRequest中,要求给出我们能接受的mimetype,这里可以参考代码,如果要文本,就给text/*,如果要二进制数 据,就可以像这里给出application/*, image/*, audio/*......
    8. 最后WinHttpSendRequest的时候,也可以设定additional header和optional数据。这里的optional数据一般是用于POST的,也就是将需要post到server的数据填充在这个地方。 WinHttpSendRequest将header都发送出去之后,紧接着就会发送这个optional数据。
    9. WinHttpQueryDataAvailable可以check是否还有数据需要接收。WinHttpReadData用来读取数据,注意读出来的数 据就是字节流,像前面所说,如果是文本,那么要根据response header,首先知道是什么编码和字符集,然后再用MultiByteToWideChar来转换。也要注意WinHttpReadData的最后一个 参数,给出了实际读取到的字节数,也可以根据这个信息来判断是否已经接收完了。此外特别需要注意的是,如果用的是一个固定的buffer循环接收,就像这 里一样,那么不要忘记接收完成的时候,要在尾部加上\0,分配buffer的时候也要留出\0的位置。


        /*
        * Schedule Download Http utilities, based on winhttp
        * Written by Eric Zhang <nicolas.m.zhang@gmail.com>
        
    */

        #include 
    "..\include\http.h"
        #include 
    "..\include\logger.h"
        #include 
    "..\include\utils.h"
        #include 
    "..\include\defs.h"
        #include 
    "..\include\macros.h"
        #include 
    <strsafe.h>

        
    // http server's hostname length
        #define HTTP_HOST_NAME_MAX      255
        
    #define HTTP_TEXT_MIMETYPE      1
        
    #define HTTP_BIN_MIMETYPE      2

        
    // mimetypes for WinHttpOpenRequest
        LPCWSTR text_mimetype[] = { L"text/*", L"\0" };
        LPCWSTR bin_mimetype[] 
    = { L"application/*", L"audio/*", L"image/*", L"video/*", L"\0" };

        
    /*
        * This function used internally. It checks the proxy settings and apply
        * to the WinHttp. Return winhttp session handle if succeeded, otherwise
        * return NULL.
        
    */
        
    static HINTERNET open_winhttp()
        {
           HINTERNET session 
    = NULL;
           TCHAR proxy_enabled[REGISTRY_VALUE_SIZE];
           TCHAR proxy_server[REGISTRY_VALUE_SIZE];
           TCHAR proxy_port[REGISTRY_VALUE_SIZE];
           TCHAR proxy_username[REGISTRY_VALUE_SIZE];
           TCHAR proxy_password[REGISTRY_VALUE_SIZE];
           DWORD errcode;
           size_t proxy_username_len 
    = 0;
           size_t proxy_password_len 
    = 0;

           
    // Check whether the proxy has enabled
           SecureZeroMemory(proxy_enabled, sizeof(proxy_enabled));
           utils_get_registry_value(SD_REGISTRY_KEY, HTTP_PROXY_ENABLED_ENTRY, proxy_enabled, 
    sizeof(proxy_enabled));
           
    if (utils_is_string_empty(proxy_enabled) || _tcscmp(proxy_enabled, TEXT("Y")) != 0) {
              
    // proxy isn't enabled, direct connect
              session = WinHttpOpen(HTTP_AGENT_NAME, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
              
    if (session == NULL) {
                 GET_ERROR_CODE(errcode);
                 M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_NO_PROXY_OPEN_FAILED, utils_format_error_string(errcode));
              }
           } 
    else {
              
    // proxy enabled, read server/port/user/pass next
              SecureZeroMemory(proxy_server, sizeof(proxy_server));
              SecureZeroMemory(proxy_port, 
    sizeof(proxy_port));
              SecureZeroMemory(proxy_username, 
    sizeof(proxy_username));
              SecureZeroMemory(proxy_password, 
    sizeof(proxy_password));
              utils_get_registry_value(SD_REGISTRY_KEY, HTTP_PROXY_SERVER_ENTRY, proxy_server, 
    sizeof(proxy_server));
              utils_get_registry_value(SD_REGISTRY_KEY, HTTP_PROXY_PORT_ENTRY, proxy_port, 
    sizeof(proxy_port));
              utils_get_registry_value(SD_REGISTRY_KEY, HTTP_PROXY_USER_ENTRY, proxy_username, 
    sizeof(proxy_username));
              utils_get_registry_value(SD_REGISTRY_KEY, HTTP_PROXY_PASS_ENTRY, proxy_password, 
    sizeof(proxy_password));
              M_GTIF_WITH_LOG(
    !utils_is_string_empty(proxy_server), failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_PROXY_SERVER_ABSENT);
              M_GTIF_WITH_LOG(
    !utils_is_string_empty(proxy_port), failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_PROXY_PORT_ABSENT);
              M_GTIF_WITH_LOG(utils_is_string_int(proxy_port), failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_PROXY_PORT_ILLEGAL);
              
    // add port into server string
              M_GTIF_WITH_LOG(SUCCEEDED(StringCchPrintf(proxy_server, _countof(proxy_server), TEXT("%s:%s"), proxy_server, proxy_port)), failed,
                 LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_STRING_OPERATION_FAILED);

              
    // open
              session = WinHttpOpen(HTTP_AGENT_NAME, WINHTTP_ACCESS_TYPE_NAMED_PROXY, proxy_server, WINHTTP_NO_PROXY_BYPASS, 0);
              
    if (session == NULL) {
                 GET_ERROR_CODE(errcode);
                 M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_OPEN_WITH_PROXY_FAILED, utils_format_error_string(errcode));
              }

              
    // set proxy username & password
              if (!utils_is_string_empty(proxy_username) && !utils_is_string_empty(proxy_password)) {
                 M_GTIF_WITH_LOG(SUCCEEDED(StringCchLength(proxy_username, _countof(proxy_username), 
    &proxy_username_len)), failed,
                    LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_STRING_OPERATION_FAILED);
                 M_GTIF_WITH_LOG(SUCCEEDED(StringCchLength(proxy_password, _countof(proxy_password), 
    &proxy_password_len)), failed,
                    LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_STRING_OPERATION_FAILED);
                 
    if (!WinHttpSetOption(session, WINHTTP_OPTION_PROXY_USERNAME, proxy_username, proxy_username_len)) {
                    GET_ERROR_CODE(errcode);
                    M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_SET_PROXY_USER_FAILED, utils_format_error_string(errcode));
                 }
                 
    if (!WinHttpSetOption(session, WINHTTP_OPTION_PROXY_PASSWORD, proxy_password, proxy_password_len)) {
                    GET_ERROR_CODE(errcode);
                    M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_SET_PROXY_PASS_FAILED, utils_format_error_string(errcode));
                 }
              }
           }

           
    // set timeouts
           if (!WinHttpSetTimeouts(session, HTTP_RESOLVE_TO, HTTP_CONNECT_TO, HTTP_SEND_TO, HTTP_RECV_TO)) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_SET_TIMEOUTS_FAILED, utils_format_error_string(errcode));
           }

           
    return session;
        failed:
           
    if (session != NULL) WinHttpCloseHandle(session);
           
    return NULL;
        }

        
    static BOOL http_get(PCTSTR url, HINTERNET *sess, HINTERNET *conn, HINTERNET *req, DWORD mimetype)
        {
           HINTERNET session 
    = NULL, connect = NULL, request = NULL;
           BOOL result 
    = FALSE;
           DWORD errcode;
           URL_COMPONENTS url_comp;
           size_t url_len_input;
           TCHAR http_host_name[HTTP_HOST_NAME_MAX];
           
    int hostname_index;

           M_RETURN_VAL_IF_FAIL(url 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(sess 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(conn 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(req 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(logger_is_enabled(), FALSE);

           session 
    = open_winhttp();
           M_RETURN_VAL_IF_FAIL(session 
    != NULL, FALSE);  // log has been done in open_winhttp function

           
    // crack url input
           
    // Initialize the URL_COMPONENTS structure.
            SecureZeroMemory(&url_comp, sizeof(url_comp));
            url_comp.dwStructSize 
    = sizeof(url_comp);

            
    // Set required component lengths to non-zero
            
    // so that they are cracked.
            url_comp.dwSchemeLength    = -1;
            url_comp.dwHostNameLength  
    = -1;
            url_comp.dwUrlPathLength   
    = -1;
            url_comp.dwExtraInfoLength 
    = -1;

           M_GTIF_WITH_LOG(SUCCEEDED(StringCchLength(url, STRSAFE_MAX_CCH, 
    &url_len_input)), failed, LOG_LEVEL_ERROR,
              __SDFILE__, __LINE__, HTTP_STRING_OPERATION_FAILED);
           result 
    = WinHttpCrackUrl(url, url_len_input, 0&url_comp);
           
    if (!result) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_CRACK_URL_FAILED, utils_format_error_string(errcode));
           }

           
    /* No need to create url because No winhttp functions accept a whole escaped url
           // get new url length, allocate it
           WinHttpCreateUrl(&url_comp, 0, NULL, &url_len_cracked);
           M_GTIF_WITH_LOG(url_len_cracked > 0, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_CREATE_URL_GET_LEN_FAILED);
           // the length WinHttpCreateUrl generated is wide character numbers
           url_cracked = (TCHAR *)HeapAlloc(sdc_heap, HEAP_ZERO_MEMORY, url_len_cracked * sizeof(WCHAR));
           M_GTIF_WITH_LOG(url_cracked != NULL, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_HEAP_ALLOC_FAILED);
           // call create url again, this time get result url
           result = WinHttpCreateUrl(&url_comp, 0, url_cracked, &url_len_cracked);
           if (!result) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_CREATE_URL_FAILED, utils_format_error_string(errcode));
           } 
    */

           
    // url OK, connect. Notice here WinHttpConnect needs Host/Server name, not the whole URL
           
    // Request file and parameters should be set in WinHttpOpenRequest function
           
    // get hostname
           hostname_index = utils_find_char(url_comp.lpszHostName, (TCHAR)'/');
           
    if (hostname_index < 0) {
              
    // not found or error occurs, use the original hostname
              connect = WinHttpConnect(session, url_comp.lpszHostName, INTERNET_DEFAULT_HTTP_PORT, 0);
           } 
    else {
              
    // found "/"
              memcpy_s(http_host_name, sizeof(http_host_name), url_comp.lpszHostName, hostname_index * sizeof(TCHAR));
              http_host_name[hostname_index] 
    = (TCHAR)'\0';
              connect 
    = WinHttpConnect(session, http_host_name, INTERNET_DEFAULT_HTTP_PORT, 0);
           }
           
    if (connect == NULL) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_CONNECT_FAILED, utils_format_error_string(errcode));
           }

           
    if (mimetype == HTTP_TEXT_MIMETYPE) {
              request 
    = WinHttpOpenRequest(connect, TEXT("GET"), url_comp.lpszUrlPath, NULL, WINHTTP_NO_REFERER,
                          text_mimetype, WINHTTP_FLAG_ESCAPE_PERCENT 
    | WINHTTP_FLAG_REFRESH);
           } 
    else {
              request 
    = WinHttpOpenRequest(connect, TEXT("GET"), url_comp.lpszUrlPath, NULL, WINHTTP_NO_REFERER,
                          bin_mimetype, WINHTTP_FLAG_ESCAPE_PERCENT 
    | WINHTTP_FLAG_REFRESH);
           }
           
    if (request == NULL) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_OPEN_REQUEST_FAILED, utils_format_error_string(errcode));
           }

            
    // Send the request
            result = WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 000 );
           
    if (!result) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_SEND_REQUEST_FAILED, utils_format_error_string(errcode));
           }

           
    // Start receiving
           result = WinHttpReceiveResponse(request, NULL);
           
    if (!result) {
              GET_ERROR_CODE(errcode);
              M_GTIF_WITH_LOG(FALSE, failed, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_RECV_RESPONSE_FAILED, utils_format_error_string(errcode));
           }

           
    // OK, return
           *sess = session;
           
    *conn = connect;
           
    *req = request;
           
    return TRUE;

        failed:
            
    // Close any open handles
            if (request) WinHttpCloseHandle(request);
            
    if (connect) WinHttpCloseHandle(connect);
            
    if (session) WinHttpCloseHandle(session);
           
    return FALSE;
        }

        BOOL http_get_text(PCTSTR url, HINTERNET 
    *sess, HINTERNET *conn, HINTERNET *req)
        {
           
    return http_get(url, sess, conn, req, HTTP_TEXT_MIMETYPE);
        }

        BOOL http_get_bin(PCTSTR url, HINTERNET 
    *sess, HINTERNET *conn, HINTERNET *req)
        {
           
    return http_get(url, sess, conn, req, HTTP_BIN_MIMETYPE);
        }

        BOOL http_check_data_len(HINTERNET request, DWORD 
    *len)
        {
           DWORD data_size 
    = 0;
           BOOL result 
    = FALSE;
           DWORD errcode;

           M_RETURN_VAL_IF_FAIL(request 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(len 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(logger_is_enabled(), FALSE);

           
    // Check the data length
           result = WinHttpQueryDataAvailable(request, &data_size);
           
    if (!result) {
              GET_ERROR_CODE(errcode);
              M_RVIF_WITH_LOG(FALSE, FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_QUERY_DATA_FAILED, utils_format_error_string(errcode));
           }

           M_RVIF_WITH_LOG(data_size 
    > 0, FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_DATA_LENGTH_ILLEGAL, data_size);

           
    *len = data_size;
           
    return TRUE;
        }

        BOOL http_recv(HINTERNET request, LPVOID buffer, DWORD buffer_len, DWORD 
    *bytes_read)
        {
           BOOL result 
    = FALSE;
           DWORD errcode;

           M_RETURN_VAL_IF_FAIL(request 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(buffer 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(buffer_len 
    > 0, FALSE);
           M_RETURN_VAL_IF_FAIL(bytes_read 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(logger_is_enabled(), FALSE);

           
    // Read the Data.
            SecureZeroMemory(buffer, buffer_len);
            result 
    = WinHttpReadData(request, buffer, buffer_len, bytes_read);
           
    if (!result) {
              GET_ERROR_CODE(errcode);
              M_RVIF_WITH_LOG(FALSE, FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__, HTTP_READ_DATA_FAILED, utils_format_error_string(errcode));
           }

           
    return TRUE;
        }

        
    void http_close(HINTERNET *session, HINTERNET *connect, HINTERNET *request)
        {
           
    if (*request) WinHttpCloseHandle(*request);
           
    if (*connect) WinHttpCloseHandle(*connect);
           
    if (*session) WinHttpCloseHandle(*session);

           
    *session = NULL;
           
    *connect = NULL;
           
    *request = NULL;

           
    return;
        }

        BOOL http_check_internet(PCTSTR url)
        {
           HINTERNET session 
    = NULL, connect = NULL, request = NULL;
           BOOL result 
    = FALSE;
           DWORD data_len 
    = 0;

           M_RETURN_VAL_IF_FAIL(url 
    != NULL, FALSE);
           M_RETURN_VAL_IF_FAIL(logger_is_enabled(), FALSE);

           result 
    = http_get_text(url, &session, &connect, &request);
           M_RETURN_VAL_IF_FAIL(result, FALSE); 
    // logger has been done in http_send

           result 
    = http_check_data_len(request, &data_len);
           M_RETURN_VAL_IF_FAIL(result, FALSE);

           
    // internet is OK, no need to receive actual data
           http_close(&session, &connect, &request);

           
    return TRUE;
        }
  • 相关阅读:
    Vue cli 安装慢的原因和解决记录
    spring-boot
    illustrator 2018 cc
    illustrator 2018 cc
    illustrator 2018 cc
    illustrator 2018 cc- 画板技巧
    Tomcat 用户密码配置
    CentOS7上解决tomcat不能被外部浏览访问
    Ubuntu
    SPI、I2C、UART(转)
  • 原文地址:https://www.cnblogs.com/super119/p/2011350.html
Copyright © 2020-2023  润新知