C + + calls HTTP interface to send request with UTF-8 encoding

Time:2021-6-11

Reference to the article:
https://blog.csdn.net/breakso…
https://blog.csdn.net/barry10…
https://blog.csdn.net/elaine_…
https://blog.csdn.net/yuanwow…
https://blog.csdn.net/erdong1…
And Microsoft’s official documents.

After comparing winhttp, WinInet and libcurl, I decided to use winhttp, which seems to be the most specialized for HTTP. Anyway, the requirements are very simple, and probably don’t need to be extended.

Requirement: Send a request with several parameters with short value and one parameter with variable value length, and receive the returned data.

#include <string>
#include <iostream>
#include <windows.h>
#include <winhttp.h> 
#include <atlstr.h>
#pragma comment(lib,"winhttp.lib")
#pragma comment(lib,"user32.lib")
using namespace std;

BOOL HttpSend(wchar_t* pwszType, wchar_t* pwszIp, WORD nServerPort, wchar_t* pwcsSourcePath, char* content, char* pszOutData)
//pwszType: http verb
//pwszIp: ip
//pwcsSourcePath: url path
//content: parameters and text
{
    DWORD  dwSize = 0;
    wchar_t*  lpHeadBuffer = NULL;
    HINTERNET  hSession = NULL;
    HINTERNET  hConnect = NULL;
    HINTERNET  hRequest = NULL;
    BOOL  bResults = FALSE;
    BOOL  bUTF8Code = TRUE;

    //Obtain a session handle
    hSession = WinHttpOpen(L"User Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if(!hSession)
    {
        cout << "Open failed!" << endl;
        return FALSE;
    }

    //Specify an HTTP server with ip and port
    hConnect = WinHttpConnect(hSession, pwszIp, nServerPort, 0);
    if(!hConnect)
    {
        cout << "Connect failed!" << endl;
        return FALSE;
    }

    //Create an HTTP request handle
    hRequest = WinHttpOpenRequest(hConnect, pwszType, pwcsSourcePath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    if(!hRequest)
    {
        cout << "Open request failed!" << endl;
        return FALSE;
    }

    //Add header
    std::wstring wstrHeader[] = { L"Content-type: application/x-www-form-urlencoded\r\n"};
    WinHttpAddRequestHeaders(hRequest, wstrHeader[0].c_str(), wstrHeader[0].length(), WINHTTP_ADDREQ_FLAG_ADD);
    
    //Send a request
    /*USES_CONVERSION;
    std::string strExtInfo = CW2A(content, CP_UTF8);    //get content
    DWORD dwTotal = strExtInfo.length();*/
    DWORD dwTotal = strlen(content);
    //bResults = WinHttpSendRequest(hRequest, wstrHeader[0].c_str(), wstrHeader[0].length(), WINHTTP_NO_REQUEST_DATA, 0, dwTotal, 0);
    bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, dwTotal, 0);
    if(!bResults)
    {
        cout << "SendRequest failed!" << endl;
        return FALSE;
    }

    //Write data to the server
    bResults = WinHttpWriteData(hRequest, content, dwTotal, NULL);
    if(!bResults)
    {
        cout << "WriteData failed!" << endl;
        return FALSE;
    }

    //End the request, receive response from server
    bResults = WinHttpReceiveResponse(hRequest,NULL);

    //Get header info
    if(bResults)
    {
        bResults=WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX);
        //Allocate buffer by header length
        if( GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        //If the function fails and ERROR_INSUFFICIENT_BUFFER is returned, lpdwBufferLength specifies the number of bytes that the application must allocate to receive the string.
        {
            lpHeadBuffer = new wchar_t[dwSize / sizeof(wchar_t)];
            bResults = WinHttpQueryHeaders(hRequest,WINHTTP_QUERY_RAW_HEADERS_CRLF,WINHTTP_HEADER_NAME_BY_INDEX, lpHeadBuffer, &dwSize,WINHTTP_NO_HEADER_INDEX);
        }
        //Get Content-Type from header to specify encoding
        if ( NULL != wcsstr(lpHeadBuffer, L"charset=gbk") )
        {
            bUTF8Code = FALSE;
        }
    }
    else
    {
        cout << "Get header failed!" << endl;
    }
    printf("Header contents: \n%S", lpHeadBuffer);
    delete [] lpHeadBuffer;

    //Get data from server
    LPSTR pszOutBuffer = NULL;
    DWORD dwDownloaded = 0;
    wchar_t *pwText = NULL;
    int nSize = 0;
    if (bResults)
    {
        do
        {
            //Check for available data to get data size in bytes
            dwSize = 0;
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){
                cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
                bResults = FALSE;
                break;
            }           
            if (!dwSize)    
            {
                break;  //data size 0          
            }

            //Allocate buffer by data size
            pszOutBuffer = new char[dwSize + 1];
            if (!pszOutBuffer)
            {
                cout<<"Out of memory."<<endl;
                bResults = FALSE;
                break;
            }
            ZeroMemory(pszOutBuffer, dwSize + 1);

            //Read data from server
            if (!WinHttpReadData(hRequest, pszOutBuffer, dwSize, &dwDownloaded))
            {
                cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
            }
            if (!dwDownloaded)
            {
                delete [] pszOutBuffer;
                bResults = FALSE;
                break;
            }

            if ( FALSE == bUTF8Code )
            {
                //Transcoding to UTF-8
                int len = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0);    //If the function succeeds and cchWideChar is 0, the return value is the required size, in characters, for the buffer indicated by lpWideCharStr
                wchar_t* pwszUnicode = new wchar_t[len];
                memset(pwszUnicode, 0, sizeof(wchar_t) * len);
                MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, pwszUnicode, len);        //map to wide char
                int transLen = WideCharToMultiByte(CP_UTF8, 0, pwszUnicode, len, NULL, 0, NULL, NULL);    //get length
                nSize += transLen;
                WideCharToMultiByte(CP_UTF8, 0, pwszUnicode,len, pszOutData+strlen(pszOutData), transLen, NULL, NULL);    //transcoding
            }
            else
            {
                strcpy_s(pszOutData + nSize, strlen(pszOutBuffer) + 1, pszOutBuffer);    //SizeInBytes should not less than src length(including '
#include <string>
#include <iostream>
#include <windows.h>
#include <winhttp.h> 
#include <atlstr.h>
#pragma comment(lib,"winhttp.lib")
#pragma comment(lib,"user32.lib")
using namespace std;
BOOL HttpSend(wchar_t* pwszType, wchar_t* pwszIp, WORD nServerPort, wchar_t* pwcsSourcePath, char* content, char* pszOutData)
//pwszType: http verb
//pwszIp: ip
//pwcsSourcePath: url path
//content: parameters and text
{
DWORD  dwSize = 0;
wchar_t*  lpHeadBuffer = NULL;
HINTERNET  hSession = NULL;
HINTERNET  hConnect = NULL;
HINTERNET  hRequest = NULL;
BOOL  bResults = FALSE;
BOOL  bUTF8Code = TRUE;
//Obtain a session handle
hSession = WinHttpOpen(L"User Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(!hSession)
{
cout << "Open failed!" << endl;
return FALSE;
}
//Specify an HTTP server with ip and port
hConnect = WinHttpConnect(hSession, pwszIp, nServerPort, 0);
if(!hConnect)
{
cout << "Connect failed!" << endl;
return FALSE;
}
//Create an HTTP request handle
hRequest = WinHttpOpenRequest(hConnect, pwszType, pwcsSourcePath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if(!hRequest)
{
cout << "Open request failed!" << endl;
return FALSE;
}
//Add header
std::wstring wstrHeader[] = { L"Content-type: application/x-www-form-urlencoded\r\n"};
WinHttpAddRequestHeaders(hRequest, wstrHeader[0].c_str(), wstrHeader[0].length(), WINHTTP_ADDREQ_FLAG_ADD);
//Send a request
/*USES_CONVERSION;
std::string strExtInfo = CW2A(content, CP_UTF8);    //get content
DWORD dwTotal = strExtInfo.length();*/
DWORD dwTotal = strlen(content);
//bResults = WinHttpSendRequest(hRequest, wstrHeader[0].c_str(), wstrHeader[0].length(), WINHTTP_NO_REQUEST_DATA, 0, dwTotal, 0);
bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, dwTotal, 0);
if(!bResults)
{
cout << "SendRequest failed!" << endl;
return FALSE;
}
//Write data to the server
bResults = WinHttpWriteData(hRequest, content, dwTotal, NULL);
if(!bResults)
{
cout << "WriteData failed!" << endl;
return FALSE;
}
//End the request, receive response from server
bResults = WinHttpReceiveResponse(hRequest,NULL);
//Get header info
if(bResults)
{
bResults=WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX);
//Allocate buffer by header length
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER)
//If the function fails and ERROR_INSUFFICIENT_BUFFER is returned, lpdwBufferLength specifies the number of bytes that the application must allocate to receive the string.
{
lpHeadBuffer = new wchar_t[dwSize / sizeof(wchar_t)];
bResults = WinHttpQueryHeaders(hRequest,WINHTTP_QUERY_RAW_HEADERS_CRLF,WINHTTP_HEADER_NAME_BY_INDEX, lpHeadBuffer, &dwSize,WINHTTP_NO_HEADER_INDEX);
}
//Get Content-Type from header to specify encoding
if ( NULL != wcsstr(lpHeadBuffer, L"charset=gbk") )
{
bUTF8Code = FALSE;
}
}
else
{
cout << "Get header failed!" << endl;
}
printf("Header contents: \n%S", lpHeadBuffer);
delete [] lpHeadBuffer;
//Get data from server
LPSTR pszOutBuffer = NULL;
DWORD dwDownloaded = 0;
wchar_t *pwText = NULL;
int nSize = 0;
if (bResults)
{
do
{
//Check for available data to get data size in bytes
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){
cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
bResults = FALSE;
break;
}           
if (!dwSize)    
{
break;  //data size 0          
}
//Allocate buffer by data size
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
cout<<"Out of memory."<<endl;
bResults = FALSE;
break;
}
ZeroMemory(pszOutBuffer, dwSize + 1);
//Read data from server
if (!WinHttpReadData(hRequest, pszOutBuffer, dwSize, &dwDownloaded))
{
cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
}
if (!dwDownloaded)
{
delete [] pszOutBuffer;
bResults = FALSE;
break;
}
if ( FALSE == bUTF8Code )
{
//Transcoding to UTF-8
int len = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0);    //If the function succeeds and cchWideChar is 0, the return value is the required size, in characters, for the buffer indicated by lpWideCharStr
wchar_t* pwszUnicode = new wchar_t[len];
memset(pwszUnicode, 0, sizeof(wchar_t) * len);
MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, pwszUnicode, len);        //map to wide char
int transLen = WideCharToMultiByte(CP_UTF8, 0, pwszUnicode, len, NULL, 0, NULL, NULL);    //get length
nSize += transLen;
WideCharToMultiByte(CP_UTF8, 0, pwszUnicode,len, pszOutData+strlen(pszOutData), transLen, NULL, NULL);    //transcoding
}
else
{
strcpy_s(pszOutData + nSize, strlen(pszOutBuffer) + 1, pszOutBuffer);    //SizeInBytes should not less than src length(including '\0')
nSize += strlen(pszOutBuffer) + 1;
}
delete [] pszOutBuffer;
} while (dwSize > 0);
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
return bResults;
}
}
') nSize += strlen(pszOutBuffer) + 1; } delete [] pszOutBuffer; } while (dwSize > 0); if (hRequest) WinHttpCloseHandle(hRequest); if (hConnect) WinHttpCloseHandle(hConnect); if (hSession) WinHttpCloseHandle(hSession); return bResults; } }

In order to meet the requirements of UTF-8 coding, the input and output should be converted between ANSI and UTF-8 formats

char* Ansi2Utf8(string content)
{
    int nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), NULL, 0);
    wchar_t* wszText = new wchar_t[nLen + 1];
    nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), wszText, nLen);
    wszText[nLen] = '
char* Ansi2Utf8(string content)
{
int nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), NULL, 0);
wchar_t* wszText = new wchar_t[nLen + 1];
nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), wszText, nLen);
wszText[nLen] = '\0';
nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, NULL, NULL, NULL, NULL);
char* contextBuf = new char[nLen];
nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, contextBuf, nLen, NULL, NULL);
contextBuf[nLen] = '\0';
return contextBuf;
}
char* Utf82Ansi(char* res)
{
int len = MultiByteToWideChar(CP_UTF8, 0, res, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_UTF8, 0, res, -1, wstr, len);
len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
return str;
}
'; nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, NULL, NULL, NULL, NULL); char* contextBuf = new char[nLen]; nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, contextBuf, nLen, NULL, NULL); contextBuf[nLen] = '
char* Ansi2Utf8(string content)
{
int nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), NULL, 0);
wchar_t* wszText = new wchar_t[nLen + 1];
nLen = MultiByteToWideChar(CP_ACP, NULL, content.c_str(), content.length(), wszText, nLen);
wszText[nLen] = '\0';
nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, NULL, NULL, NULL, NULL);
char* contextBuf = new char[nLen];
nLen = WideCharToMultiByte(CP_UTF8, NULL, wszText, -1, contextBuf, nLen, NULL, NULL);
contextBuf[nLen] = '\0';
return contextBuf;
}
char* Utf82Ansi(char* res)
{
int len = MultiByteToWideChar(CP_UTF8, 0, res, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_UTF8, 0, res, -1, wstr, len);
len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
return str;
}
'; return contextBuf; } char* Utf82Ansi(char* res) { int len = MultiByteToWideChar(CP_UTF8, 0, res, -1, NULL, 0); wchar_t* wstr = new wchar_t[len + 1]; memset(wstr, 0, len + 1); MultiByteToWideChar(CP_UTF8, 0, res, -1, wstr, len); len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); char* str = new char[len + 1]; memset(str, 0, len + 1); WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL); return str; }

Last call

string text = "xxxx";
    string content = "&key=value&..." + text;
    //trans ansi text to UTF-8 //Assume that input encoding = UTF-8
    char* contextBuf = Ansi2Utf8(content);
    
    wchar_t verb[] = L"POST";
    wchar_ T IP [] = L "(IP address)";
    Word port = port number;
    wchar_ T path [] is L "path";
    char res[1000];
    memset(res, 0, 1000);
    BOOL bRet = HttpSend(verb, ip, port, path, contextBuf, res);
    if (!bRet)
    {
        system("pause");
        return -1;
    }

    char* transText = Utf82Ansi(res);

    cout << "Original text: " << text << endl;
    cout << "Translation: " << transText << endl;
    system("pause");

There are countless holes in the middle. It seems that ANSI transcoding to UTF-8 is not direct… Record.