CWinHttp.h
cpp
#pragma once
#include <Windows.h>
#include <WinHttp.h>
#include <string>
#include <vector>
#include <tchar.h>
#define WIN_HTTP_USER_AGENT LR"(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0)"
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
typedef struct _WIN_HTTP_URL_COMPONENTS WIN_HTTP_URL_COMPONENTS, *LPWIN_HTTP_URL_COMPONENTS;
class CWinHttpResponse
{
public:
CWinHttpResponse() :code(0){}
DWORD code; //响应状态码
std::string result; //响应结果
};
class CWinHttp
{
public:
CWinHttp();
~CWinHttp();
//
// @brief: 发送Get请求
// @param: strUrl URL链接
// @param: strHeader 请求头
// @ret: CWinHttpResponse 响应结果
CWinHttpResponse Get(
const _tstring& strUrl,
const _tstring& strHeader = _T("Accept: */*\r\nContent-Type:application/json;charset=UTF-8")
);
//
// @brief: 发送Post请求
// @param: strUrl URL链接
// @param: strParam 请求参数
// @param: strHeader 请求头
// @ret: CWinHttpResponse 响应结果
CWinHttpResponse Post(
const _tstring& strUrl,
const _tstring& strParam,
const _tstring& strHeader = _T("Accept: */*\r\nContent-Type:application/json;charset=UTF-8")
);
private:
//
// @brief: 执行请求
// @param: strUrl URL链接
// @param: strMethod 请求方法
// @param: strParam 请求参数
// @param: strHeader 请求头
// @ret: CWinHttpResponse 响应结果
CWinHttpResponse _DoSendRequest(
const _tstring& strUrl,
const _tstring& strMethod,
const std::string& strParam,
const _tstring& strHeader
);
//
// @brief: 分解链接
// @param: strUrl URL链接
// @param: lpUci 分解输出缓冲
// @ret: bool 分解成功与否
bool _CrackUrl(
const _tstring& strUrl,
LPWIN_HTTP_URL_COMPONENTS lpUci
);
// 获取状态码
//
// @param: hRequest 请求句柄
// @ret: DWORD 状态码
DWORD _GetStatusCodes(
HINTERNET hRequest
);
// 查询资源大小
//
// @param: hRequest 请求句柄
// @ret: DWORD 状态码
ULONGLONG _QueryContentLength(
HINTERNET hRequest
);
// 发送请求
// @param: hRequest 请求句柄
// @param: lpszHeaders 请求头
// @param: dwHeadersLength 请求头长度
// @param: lpData 请求数据
// @param: dwSize 请求数据长度
// @ret: bool 请求成功与否
bool _SendRequest(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpData,
DWORD dwSize
);
// 读取响应数据
// @param: hRequest 请求句柄
// @param: strResponse 响应结果
// @ret: bool 请求成功与否
bool _ReadResponseData(
HINTERNET hRequest,
std::string& strResponse
);
//
// @brief: 宽字节字符串转多字节字符串
// @param: CodePage 代码页
// @param: str 字符串
// @ret: std::string 转换后的多字节字符串
static std::string _WStrToMultiStr(
UINT CodePage,
const std::wstring& str
);
//
// @brief: 多字节字符串转宽字节字符串
// @param: CodePage 代码页
// @param: str 字符串
// @ret: std::wstring 转换后的宽字节字符串
static std::wstring _MultiStrToWStr(
UINT CodePage,
const std::string& str
);
//
// @brief: 字符串转UTF-8编码字符串
// @param: str 字符串
// @ret: std::string 转换后的UTF-8编码字符串
static std::string _TStrToU8Str(const _tstring& str);
//
// @brief: 字符串转宽字节字符串
// @param: str 字符串
// @ret: std::wstring 转换后的宽字节字符串
static std::wstring _TStrToWStr(const _tstring& str);
};
CWinHttp.cpp
cpp
#include "CWinHttp.h"
#include <strsafe.h>
#include <schannel.h>
#pragma comment(lib, "winhttp.lib")
#define HTTP_READ_BLOCK_SIZE (1024 * 8) // 读取块大小
#define INTERNET_MAX_HOST_NAME_LENGTH 256
#define INTERNET_MAX_USER_NAME_LENGTH 128
#define INTERNET_MAX_PASSWORD_LENGTH 128
#define INTERNET_MAX_PORT_NUMBER_LENGTH 5 // INTERNET_PORT is unsigned short
#define INTERNET_MAX_PORT_NUMBER_VALUE 65535 // maximum unsigned short value
#define INTERNET_MAX_PATH_LENGTH 2048
#define INTERNET_MAX_SCHEME_LENGTH 32 // longest protocol name length
#define INTERNET_MAX_URL_LENGTH (INTERNET_MAX_SCHEME_LENGTH \
+ sizeof("://") \
+ INTERNET_MAX_PATH_LENGTH)
typedef struct _WIN_HTTP_URL_COMPONENTS
{
WCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH];
WCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH];
WCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
WCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
WCHAR szUrlPath[INTERNET_MAX_URL_LENGTH];
WCHAR szExtraInfo[MAX_PATH];
URL_COMPONENTS uc = { 0 };
_WIN_HTTP_URL_COMPONENTS()
{
ZeroMemory(this, sizeof(*this));
this->uc.dwStructSize = sizeof(this->uc);
this->uc.lpszUrlPath = this->szUrlPath;
this->uc.dwUrlPathLength = _countof(this->szUrlPath);
this->uc.lpszScheme = this->szScheme;
this->uc.dwSchemeLength = _countof(this->szScheme);
this->uc.lpszHostName = this->szHostName;
this->uc.dwHostNameLength = _countof(this->szHostName);
this->uc.lpszUserName = this->szUserName;
this->uc.dwUserNameLength = _countof(this->szUserName);
this->uc.lpszPassword = this->szPassword;
this->uc.dwPasswordLength = _countof(this->szPassword);
this->uc.lpszExtraInfo = this->szExtraInfo;
this->uc.dwExtraInfoLength = _countof(this->szExtraInfo);
}
}WIN_HTTP_URL_COMPONENTS;
CWinHttp::CWinHttp()
{
}
CWinHttp::~CWinHttp()
{
}
CWinHttpResponse CWinHttp::Get(
const _tstring& strUrl,
const _tstring& strHeader/* = _T("Content-Type:application/json;charset=UTF-8")*/
)
{
return _DoSendRequest(strUrl, _T("GET"), std::string(), strHeader);
}
CWinHttpResponse CWinHttp::Post(
const _tstring& strUrl,
const _tstring& strParam,
const _tstring& strHeader/* = _T("Content-Type:application/json;charset=UTF-8")*/
)
{
return _DoSendRequest(strUrl, _T("POST"), _TStrToU8Str(strParam), strHeader);
}
CWinHttpResponse CWinHttp::_DoSendRequest(
const _tstring& strUrl,
const _tstring& strMethod/* = _T("GET")*/,
const std::string& strParam,
const _tstring& strHeader
)
{
CWinHttpResponse responseResult;
WIN_HTTP_URL_COMPONENTS urlParts;
ULONGLONG ullContentLength = 0;
HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
bool bSuccess = false;
// 分解URL
if (!_CrackUrl(strUrl, &urlParts))
{
return responseResult;
}
do
{
// 初始化应用程序对 WinINet 函数的使用
hSession = ::WinHttpOpen(
WIN_HTTP_USER_AGENT, //指向字符串变量的指针,该变量包含调用 WinHTTP 函数的应用程序或实体的名称
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, //所需的访问类型
WINHTTP_NO_PROXY_NAME, //代理访问时要使用的代理服务器的名称
WINHTTP_NO_PROXY_BYPASS, //代理访问时要使用的代理服务器的密码
WINHTTP_FLAG_SECURE_DEFAULTS // 指示影响此函数行为的各种选项的标志
);
if (NULL == hSession)
{
break;
}
// 打开给定站点的 HTTP 会话
hConnect = ::WinHttpConnect(
hSession,//由先前调用 WinHttpOpen 返回的有效 HINTERNETWinHTTP 会话句柄
urlParts.szHostName, //HTTP 服务器的主机名
urlParts.uc.nPort, //建立连接的服务器上的 TCP/IP 端口
0 //保留参数, 必须为0
);
if (NULL == hConnect)
{
break;
}
// 创建 HTTPS / HTTP 请求句柄
DWORD dwFlags = (INTERNET_SCHEME_HTTPS == urlParts.uc.nScheme) ? WINHTTP_FLAG_SECURE : 0;
hRequest = ::WinHttpOpenRequest(
hConnect, //WinHttpConnect 返回的 HTTP 会话的 HINTERNET 连接句柄
_TStrToWStr(strMethod).c_str(), //请求的 HTTP 谓词
urlParts.szUrlPath, //指定 HTTP 谓词的目标资源名称
NULL, //HTTP 版本的字符串的指针
WINHTTP_NO_REFERER,//指定从中获取 请求 pwszObjectName 中的 URL 的文档的 URL
WINHTTP_DEFAULT_ACCEPT_TYPES, //指定客户端接受的媒体类型
dwFlags //Internet 标志值
);
if (NULL == hRequest)
{
break;
}
// 设置安全标志
DWORD dwSecurityFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
if (!::WinHttpSetOption(
hRequest,
WINHTTP_OPTION_SECURITY_FLAGS,
&dwSecurityFlags,
sizeof(dwSecurityFlags)
))
{
break;
}
// 设置客户端证书上下文
if (!::WinHttpSetOption(
hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0
))
{
break;
}
std::wstring wstrHeader = _TStrToWStr(strHeader);
// 添加请求头
::WinHttpAddRequestHeaders(
hRequest,
wstrHeader.data(),
(DWORD)wstrHeader.size(),
WINHTTP_ADDREQ_FLAG_ADD
);
// 发送请求
if (!_SendRequest(
hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
(LPVOID)strParam.data(),
(DWORD)strParam.size()
))
{
break;
}
// 等待接收 WinHttpSendRequest 发起的 HTTP 请求的响应
if (!::WinHttpReceiveResponse(hRequest, NULL))
{
break;
}
// 获取状态码
responseResult.code = _GetStatusCodes(hRequest);
if (responseResult.code < 200 || responseResult.code >= 300)
{
//TODO
}
// 获取内容大小
ullContentLength = _QueryContentLength(hRequest);
// 读取响应内容
if (!_ReadResponseData(hRequest, responseResult.result))
{
break;
}
bSuccess = true;
} while (false);
// 释放资源
if (hRequest)
{
::WinHttpCloseHandle(hRequest);
}
if (hConnect)
{
::WinHttpCloseHandle(hConnect);
}
if (hSession)
{
::WinHttpCloseHandle(hSession);
}
return responseResult;
}
bool CWinHttp::_CrackUrl(
const _tstring& strUrl,
LPWIN_HTTP_URL_COMPONENTS lpUci
)
{
// 将 URL 分解到其组件部件中
if (!::WinHttpCrackUrl(_TStrToWStr(strUrl).c_str(), 0, 0, &lpUci->uc))
{
return false;
}
if (0 < ::wcslen(lpUci->uc.lpszExtraInfo))
{
::wcscat_s(lpUci->uc.lpszUrlPath, _countof(lpUci->szUrlPath), lpUci->uc.lpszExtraInfo);
}
return true;
}
DWORD CWinHttp::_GetStatusCodes(
HINTERNET hRequest
)
{
DWORD dwRespCode = 0;
DWORD dwBufferLength = sizeof(dwRespCode);
// 查询请求状态码
if (!::WinHttpQueryHeaders(
hRequest, //WinHttpOpenRequest 返回的 HINTERNET 请求句柄
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, //指定"查询信息标志"页上列出的属性标志和修饰符标志的组合
WINHTTP_HEADER_NAME_BY_INDEX, //标头名称
&dwRespCode, //接收信息的缓冲区
&dwBufferLength, //数据缓冲区的长度
NULL //从零开始的标头索引的指针,用于枚举具有相同名称的多个标头
))
{
return dwRespCode;
}
return dwRespCode;
}
ULONGLONG CWinHttp::_QueryContentLength(
HINTERNET hRequest
)
{
ULONGLONG ullContentLength = 0;
DWORD dwBufferLength = sizeof(ullContentLength);
// 查询资源大小
if (!::WinHttpQueryHeaders(
hRequest,
WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER64,
WINHTTP_HEADER_NAME_BY_INDEX, //标头名称
&ullContentLength, //接收信息的缓冲区
&dwBufferLength, //数据缓冲区的长度
NULL //从零开始的标头索引的指针,用于枚举具有相同名称的多个标头
))
{
return 0;
}
return ullContentLength;
}
bool CWinHttp::_SendRequest(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpData,
DWORD dwSize
)
{
// 将指定的请求发送到 HTTP 服务器
if (::WinHttpSendRequest(
hRequest, //WinHttpOpenRequest 返回的 HINTERNET 句柄
lpszHeaders,//要追加到请求的其他标头
dwHeadersLength,//附加标头的长度(以字符为单位)
lpData,//请求标头之后发送的任何可选数据
dwSize, //可选数据的长度(以字节为单位)
dwSize, //发送的总数据的长度
NULL
))
{
return true;
}
// 安全 HTTP 服务器需要客户端证书
if (ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED != ::GetLastError())
{
return false;
}
/*SecPkgContext_IssuerListInfoEx* pIssuerList = NULL;
DWORD dwBufferSize = sizeof(SecPkgContext_IssuerListInfoEx*);
// 获取INTERNET_OPTION_SECURITY_FLAGS标志
if (::WinHttpQueryOption(
hRequest, //要查询信息的 HINTERNET 句柄
WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST, //查询的 Internet 选项
&pIssuerList, //接收选项设置的缓冲区
&dwBufferSize //接收选项设置的缓冲区的长度
))
{
// 使用 pIssuerList 进行证书存储筛选
::GlobalFree(pIssuerList);
return false;
}*/
// 没有客户端证书
if (!::WinHttpSetOption(
hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0
))
{
return false;
}
// 再次发送请求
if (::WinHttpSendRequest(
hRequest, //WinHttpOpenRequest 返回的 HINTERNET 句柄
lpszHeaders,//要追加到请求的其他标头
dwHeadersLength,//附加标头的长度(以字符为单位)
lpData,//请求标头之后发送的任何可选数据
dwSize, //可选数据的长度(以字节为单位)
dwSize, //发送的总数据的长度
NULL
))
{
return true;
}
return false;
}
bool CWinHttp::_ReadResponseData(
HINTERNET hRequest,
std::string& strResponse
)
{
std::string strBuffer(HTTP_READ_BLOCK_SIZE, 0);
DWORD dwRead = 0;
DWORD dwAvailable = 0;
bool bSuccess = false;
while (true)
{
// 检查可用数据
dwAvailable = 0;
if (!::WinHttpQueryDataAvailable(hRequest, &dwAvailable))
{
break;
}
// 没有更多可用数据
if (!dwAvailable)
{
bSuccess = true;
break;
}
// 读取数据
strBuffer.resize(dwAvailable);
if (!::WinHttpReadData(hRequest, &strBuffer[0], dwAvailable, &dwRead))
{
break;
}
// 传输已完成
if (0 == dwRead)
{
bSuccess = true;
break;
}
// 追加响应数据
strBuffer.resize(dwRead);
strResponse += strBuffer;
};
return bSuccess;
}
std::string CWinHttp::_WStrToMultiStr(UINT CodePage, const std::wstring& str)
{
//计算缓冲区所需的字节长度
int cbMultiByte = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, 0);
std::string strResult(cbMultiByte, 0);
//成功则返回写入到指示的缓冲区的字节数
size_t nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size(), NULL, NULL);
//调整内容长度
strResult.resize(nConverted);
return strResult;
}
std::wstring CWinHttp::_MultiStrToWStr(UINT CodePage, const std::string& str)
{
//计算缓冲区所需的字符长度
int cchWideChar = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, NULL);
std::wstring strResult(cchWideChar, 0);
//成功则返回写入到指示的缓冲区的字符数
size_t nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size());
//调整内容长度
strResult.resize(nConverted);
return strResult;
}
std::string CWinHttp::_TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODE
return _WStrToMultiStr(CP_UTF8, str);
#else
return _WStrToMultiStr(CP_UTF8, _MultiStrToWStr(CP_ACP, str));
#endif
}
std::wstring CWinHttp::_TStrToWStr(const _tstring& str)
{
#ifdef _UNICODE
return str;
#else
return _MultiStrToWStr(CP_ACP, str);
#endif
}
main.cpp
cpp
#include <locale>
#include <iostream>
#include "CWinHttp.h"
#define GET_URL1 R"(https://gitee.com/flame_cyclone/fcassistant-binary/releases/download/1.0.1.15/LuaExample.lua)"
#define GET_URL2 R"(https://gitee.com/flame_cyclone/fcassistant-binary/releases/download/1.0.1.15/LuaExample.lua2)"
#define POST_URL R"(https://gitee.com/flame_cyclone/fcassistant-binary/releases/download/1.0.1.15/LuaExample.lua)"
int main()
{
setlocale(LC_ALL, "");
_tstring strParam = _T(R"({"name":"fc"})");
std::string strResponse;
CWinHttp winHttp;
CWinHttpResponse winHttpResponse;
winHttpResponse = winHttp.Get(_T(GET_URL1));
winHttpResponse = winHttp.Get(_T(GET_URL2));
winHttpResponse = winHttp.Post(_T(GET_URL2), strParam);
winHttpResponse = winHttp.Post(_T(POST_URL), strParam);
strParam = _T(R"({"name":"fc2"})");
winHttpResponse = winHttp.Post(_T(POST_URL), strParam);
return 0;
}