.h
#pragma once
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H
#include <windows.h>
#include <WinInet.h>
#include <string>
using namespace std;
#define IE_AGENT _T("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)")
// 操作成功
#define SUCCESS 0
// 操作失败
#define FAILURE 1
// 操作超时
#define OUTTIME 2
class HttpClient
{
public:
HttpClient();
~HttpClient();
int HttpGet(LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse, std::vector<std::string> & vePostHeaders, const char * pSavePath = NULL);
int HttpPost(LPCTSTR strUrl, LPCTSTR strPostData, std::vector<std::string> & vePostHeaders, string &strResponse, const char * pSavePath = NULL);
int HttpPostEX(LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse, const char * pInFilePath = NULL, const char * pOutFilePath = NULL);
int HttpPostEX(std::string strUrl, std::map<std::string, std::string> mapPostData, std::map<std::string, std::string> mapInFileName, string &strResponse, const char * pOutFilePath = NULL);
private:
int ExecuteRequest(LPCTSTR strMethod, LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse);
void Clear();
};
#endif // HTTPCLIENT_H
.cpp
#include "stdafx.h"
#include "HttpClient.h"
#include <Shlwapi.h>
#include <algorithm>
#include <cctype>
#include <functional>
#include "ParseUrl.h"
#define BUFFER_SIZE 1024
#define NORMAL_CONNECT INTERNET_FLAG_KEEP_CONNECTION
#define SECURE_CONNECT NORMAL_CONNECT | INTERNET_FLAG_SECURE
#define NORMAL_REQUEST INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE
#define SECURE_REQUEST NORMAL_REQUEST | INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID
HttpClient::HttpClient()
{
}
HttpClient::~HttpClient()
{
Clear();
}
void HttpClient::Clear()
{
}
int HttpClient::ExecuteRequest(LPCTSTR strMethod, LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse)
{
return SUCCESS;
}
int HttpClient::HttpGet(LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse, std::vector<std::string> & vePostHeaders, const char * pSavePath)
{
PARSEDURL pu;
pu.cbSize = sizeof(pu);
HRESULT hr = ParseURL(strUrl, &pu);
if (SUCCEEDED(hr))
{
if (pu.nScheme != URL_SCHEME_HTTP && pu.nScheme != URL_SCHEME_HTTPS)
{
return FAILURE;
}
}
Uri url = Uri::Parse(strUrl);
// 建立会话
HINTERNET hInternet;
hInternet = InternetOpen(IE_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL)
return FAILURE;
// 建立连接
HINTERNET hConnect;
hConnect = InternetConnect(hInternet, url.Host.c_str(), atoi((CStringHelper::ws2s(url.Port)).c_str()),
L"", L"", INTERNET_SERVICE_HTTP, INTERNET_FLAG_PASSIVE, 0);
if (hInternet == NULL)
{
InternetCloseHandle(hInternet);
return FAILURE;
}
std::wstring wstrObject = url.Path + url.QueryString;
HINTERNET httpFile;
httpFile = HttpOpenRequest(hConnect, L"GET", wstrObject.c_str(), HTTP_VERSION, NULL, 0, INTERNET_FLAG_NO_UI | INTERNET_FLAG_DONT_CACHE, 1);
if (httpFile == NULL)
{
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return FAILURE;
}
for (UINT i = 0; i < vePostHeaders.size(); i++)
{
std::string strHeader = vePostHeaders[i];
HttpAddRequestHeadersA(httpFile, strHeader.c_str(), strHeader.length(), HTTP_ADDREQ_FLAG_ADD);
}
#if 0
std::wstring wstrAddHeaders = L"Accept: *,*/*";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Accept-Language: zh-cn";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Content-Type: application/x-www-form-urlencoded";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Accept-Encoding: gzip, deflate";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
#endif
HttpSendRequest(httpFile, NULL, NULL, 0, 0);
FILE *fp = NULL;
if (pSavePath)
{
fopen_s(&fp, pSavePath, "wb");
}
char buf[BUFFER_SIZE + 1] = { 0 };
DWORD buf_len = BUFFER_SIZE;
DWORD buf_read = BUFFER_SIZE;
DWORD buf_count = 0;
std::string strRawResponse;
while (1)
{
InternetReadFile(httpFile, buf, buf_len, &buf_read);
if (buf_read == 0) break;
buf[buf_read] = '\0';
if (pSavePath && fp)
{
fwrite(buf, 1, buf_read, fp);
}
else
{
strRawResponse += buf;
}
buf_count += buf_read;
memset(buf, 0, BUFFER_SIZE + 1);
}
if (fp)
{
fclose(fp);
}
strResponse = strRawResponse;
InternetCloseHandle(httpFile);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return SUCCESS;
}
int HttpClient::HttpPost(LPCTSTR strUrl, LPCTSTR strPostData, std::vector<std::string> & vePostHeaders, string &strResponse, const char * pSavePath)
{
PARSEDURL pu;
pu.cbSize = sizeof(pu);
HRESULT hr = ParseURL(strUrl, &pu);
if (SUCCEEDED(hr))
{
if (pu.nScheme != URL_SCHEME_HTTP && pu.nScheme != URL_SCHEME_HTTPS)
{
return FAILURE;
}
}
Uri url = Uri::Parse(strUrl);
// 建立会话
HINTERNET hInternet;
hInternet = InternetOpen(IE_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL)
return FAILURE;
// 建立连接
HINTERNET hConnect;
hConnect = InternetConnect(hInternet, url.Host.c_str(), atoi((CStringHelper::ws2s(url.Port)).c_str()),
L"", L"", INTERNET_SERVICE_HTTP, INTERNET_FLAG_PASSIVE, 0);
if (hInternet == NULL)
{
InternetCloseHandle(hInternet);
return FAILURE;
}
std::wstring wstrObject = url.Path + url.QueryString;
HINTERNET httpFile;
httpFile = HttpOpenRequest(hConnect, L"POST", wstrObject.c_str(), HTTP_VERSION, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (httpFile == NULL)
{
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return FAILURE;
}
for (UINT i=0; i<vePostHeaders.size(); i++)
{
std::string strHeader = vePostHeaders[i];
HttpAddRequestHeadersA(httpFile, strHeader.c_str(), strHeader.length(), HTTP_ADDREQ_FLAG_ADD);
}
#if 0
std::wstring wstrAddHeaders = L"Accept: *,*/*";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Accept-Language: zh-cn";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Content-Type: application/x-www-form-urlencoded";
// wstrAddHeaders = L"Content-Type: application/json;charset=UTF-8";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
wstrAddHeaders = L"Accept-Encoding: gzip, deflate";
HttpAddRequestHeaders(httpFile, wstrAddHeaders.c_str(), wstrAddHeaders.length(), HTTP_ADDREQ_FLAG_ADD);
#endif
std::wstring wstrPostData = strPostData;
std::string strPostData2 = CStringHelper::ws2s(wstrPostData);
DWORD dwSize = strPostData2.length();
HttpSendRequestA(httpFile, NULL, NULL, (LPVOID)strPostData2.c_str(), dwSize);
FILE *fp = NULL;
if (pSavePath)
{
fopen_s(&fp, pSavePath, "wb");
}
char buf[BUFFER_SIZE + 1] = { 0 };
DWORD buf_len = BUFFER_SIZE;
DWORD buf_read = BUFFER_SIZE;
DWORD buf_count = 0;
std::string strRawResponse;
while (1)
{
InternetReadFile(httpFile, buf, buf_len, &buf_read);
if (buf_read == 0) break;
buf[buf_read] = '\0';
if (pSavePath && fp)
{
fwrite(buf, 1, buf_read, fp);
}
else
{
strRawResponse += buf;
}
buf_count += buf_read;
memset(buf, 0, BUFFER_SIZE + 1);
}
if (fp)
{
fclose(fp);
}
strResponse = strRawResponse;
InternetCloseHandle(httpFile);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return SUCCESS;
}
BOOL UseHttpSendRequestDataToWeb(HINTERNET hRequest, std::map<std::string, std::string> mapPostData, std::map<std::string, std::string> mapInFileName, BOOL bUseHeader)
{
if (!hRequest)
return FALSE;
char szFormat[1024] = { 0 };
ULONGLONG ullPostSize = 0;
std::string strKey = "";
std::string strValue = "";
std::string strFilePath = "";
std::string strFileName = "";
std::string strFormat = "";
std::map<std::string, std::string> mapPostFile;
std::vector<std::string> vecPostData;
std::map<std::string, std::string>::iterator it;
for (it = mapInFileName.begin(); it != mapInFileName.end(); it++)
{
strKey = it->first;
strValue = it->second;
FILE *fp = NULL;
fopen_s(&fp, strValue.c_str(), "rb");
if (NULL == fp)
{
continue;
}
fseek(fp, 0L, SEEK_END);
ullPostSize += ftell(fp);
fclose(fp);
strFileName = CStringHelper::GetFileName(strValue.c_str());
sprintf_s(szFormat, "-----------------------------7dd16d1320516\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n", strKey.c_str(), strFileName.c_str());
strFormat = szFormat;
ullPostSize += strFormat.length();
mapPostFile.insert(make_pair(strValue, strFormat));
}
std::map<std::string, std::string>::iterator it2;
for (it2 = mapPostData.begin(); it2 != mapPostData.end(); it2++)
{
std::string strKey = it2->first;
std::string strValue = it2->second;
sprintf_s(szFormat, "-----------------------------7dd16d1320516\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n\"%s\"\r\n", strKey.c_str(), strValue.c_str());
strFormat = szFormat;
ullPostSize += strFormat.length();
vecPostData.push_back(strFormat);
}
//此处是header和body的前置数据段,一般向WEB发送数据是不需要的(即:bUseHeader=FALSE)
//因为模拟了通过web页面向服务器post数据,所以需要添加boundary(分隔符, 用于描述不同的数据名称、标识、data==)
//模拟时,\r\n是需要的,个数是通过Fiddler2软件截获正常网页发送时为防止错才固定的
//至于数字7dd16d1320516本来是随机值,但对于业务来说,此处仅仅是分割作用,无所谓了
char szHeader[] = "Accept: *,*/*\r\nConnection: Keep-Alive\r\nContent-Type: application/octet-stream\r\nContent-Type: multipart/form-data; boundary=---------------------------7dd16d1320516\r\n";
char* pchEnd = "\r\n-----------------------------7dd16d1320516--\r\n";
int nEndLen = strlen(pchEnd);
//发送上传请求
INTERNET_BUFFERSA bufferIn = { 0 };
bufferIn.dwStructSize = sizeof(INTERNET_BUFFERSA); //必须设置,否则出错
bufferIn.lpcszHeader = szHeader;
bufferIn.dwHeadersLength = strlen(szHeader);
bufferIn.dwBufferTotal = ullPostSize + nEndLen;
if (!HttpSendRequestExA(hRequest, &bufferIn, NULL, 0, 0))
return FALSE;
const int N_SizeEveryTime = 1024;
BYTE bufData[N_SizeEveryTime] = { 0 };
DWORD dwBytesRead = 0;
DWORD dwBytesOut = 0;
DWORD dwBytesOutAll = 0;
BOOL bRet = TRUE;
for (UINT i = 0; i < vecPostData.size(); i++)
{
std::string strPostData = vecPostData[i];
bRet = InternetWriteFile(hRequest, strPostData.c_str(), strPostData.length(), &dwBytesOut);
if (!bRet)
return FALSE;
dwBytesOutAll += dwBytesOut;
}
std::map<std::string, std::string>::iterator it3;
for (it3 = mapPostFile.begin(); it3 != mapPostFile.end(); it3++)
{
strKey = it3->first;
strValue = it3->second;
bRet = InternetWriteFile(hRequest, strValue.c_str(), strValue.length(), &dwBytesOut);
if (!bRet)
return FALSE;
dwBytesOutAll += dwBytesOut;
FILE *fp = NULL;
fopen_s(&fp, strKey.c_str(), "rb");
if (NULL == fp)
{
return FALSE;
}
//读取并发送数据
while (TRUE)
{
dwBytesRead = fread(bufData, 1, N_SizeEveryTime, fp);
if (dwBytesRead <= 0)
break;
//write数据
bRet = InternetWriteFile(hRequest, bufData, dwBytesRead, &dwBytesOut);
if (!bRet)
return FALSE;
dwBytesOutAll += dwBytesOut;
}
fclose(fp);
}
//如果filename不为空,则发送表尾结束符
if (bUseHeader)
{
bRet = InternetWriteFile(hRequest, pchEnd, nEndLen, &dwBytesOut);
if (!bRet)
return FALSE;
dwBytesOutAll += dwBytesOut;
}
//结束上传请求
if (!HttpEndRequest(hRequest, NULL, 0, 0))
{
DWORD dwErr = GetLastError();
return FALSE;
}
return dwBytesOutAll == bufferIn.dwBufferTotal;
}
//int HttpClient::HttpPostEX(LPCTSTR strUrl, LPCTSTR strPostData, string &strResponse, const char * pInFilePath, const char * pOutFilePath)
int HttpClient::HttpPostEX(std::string strUrl, std::map<std::string, std::string> mapPostData, std::map<std::string, std::string> mapInFileName, string &strResponse, const char * pOutFilePath)
{
const int N_BufferSizeRead_Net = 1024;
BOOL bRet = FALSE;
DWORD dwErr = 0;
HINTERNET hOpen = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
int nRet = FAILURE;
do
{
PARSEDURLA pu;
pu.cbSize = sizeof(pu);
HRESULT hr = ParseURLA(strUrl.c_str(), &pu);
if (SUCCEEDED(hr))
{
if (pu.nScheme != URL_SCHEME_HTTP && pu.nScheme != URL_SCHEME_HTTPS)
{
break;;
}
}
Uri url = Uri::Parse(CStringHelper::s2ws(strUrl));
std::wstring wstrObject = url.Path + url.QueryString;
//Initializes an application's use of the WinINet functions.
hOpen = InternetOpen(IE_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!hOpen)
goto EndFlag;
//Opens an File Transfer Protocol (FTP), Gopher, or HTTP session for a given site
hConnect = InternetConnect(hOpen, url.Host.c_str(), atoi((CStringHelper::ws2s(url.Port)).c_str()), NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
if (!hConnect)
goto EndFlag;
//Creates an HTTP request handle.
TCHAR* szAccept[] = { _T("*/*"), NULL };
hRequest = HttpOpenRequest(hConnect, _T("POST"), wstrObject.c_str(), HTTP_VERSION/*NULL*/, NULL, /*NULL*/(LPCTSTR*)szAccept, INTERNET_FLAG_NO_CACHE_WRITE /*| INTERNET_FLAG_RELOAD /*| INTERNET_FLAG_KEEP_CONNECTION*/, 0);
if (!hRequest)
goto EndFlag;
if (UseHttpSendRequestDataToWeb(hRequest, mapPostData, mapInFileName, true))
{
//Retrieves header information associated with an HTTP request
int nState = 0;
DWORD n = sizeof(nState);
::HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &nState, &n, 0);
// if (nState != 200)
// goto EndFlag;
char chBuf[N_BufferSizeRead_Net] = { 0 };
DWORD dwBytesRead = 0;
FILE *fp = NULL;
if (pOutFilePath)
{
fopen_s(&fp, pOutFilePath, "wb");
}
do
{
//读取上传完成后的返回结果
memset(chBuf, 0, N_BufferSizeRead_Net);
if (InternetReadFile(hRequest, chBuf, N_BufferSizeRead_Net - 1, &dwBytesRead) && dwBytesRead > 0)
{
/// <读取保存返回的数据>
if (fp && nState == 200)
{
fwrite(chBuf, 1, dwBytesRead, fp);
}
else
{
strResponse += chBuf;
}
}
else
{
dwErr = GetLastError();
break;
}
} while (/*dwContentLen > 0 &&*/ dwBytesRead > 0);
if (fp)
{
fclose(fp);
}
nRet = SUCCESS;
}
EndFlag:
if (hRequest)
{
bRet = InternetCloseHandle(hRequest);
hRequest = NULL;
}
if (hConnect)
{
bRet = InternetCloseHandle(hConnect);
hConnect = NULL;
}
if (hOpen)
{
bRet = InternetCloseHandle(hOpen);
hOpen = NULL;
}
break;
} while (0);
return nRet;
}