概述
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=%dn", 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=%dn", 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=%dn",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 -> %sn", 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=%dn", 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, "--%srn", boundary); int fb_len = strlen(first_boundary);
sprintf_s(delimiter, "rn--%srn", boundary); int del_len = strlen(delimiter);
sprintf_s(end_boundary, "rn--%s--rn", 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-streamrnrn";
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 + "rn";
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 errn");
}
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] = '