Read response as string wininet - c++

This function creates a post request to an endpoint and receives a JSON response, the goal is to save the json data as a string.
I checked the Microsoft docs but I can't figure it out
void POST(std::string data, std::string endpoint )
{
static TCHAR hdrs[] = _T("Content-Type: application/json");
static LPCSTR accept[2]={"*/*", NULL};
// for clarity, error-checking has been removed
HINTERNET hSession = InternetOpen(DEFAULT_USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hSession, MY_HOST, ALT_HTTP_PORT , NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", endpoint.c_str(), NULL, NULL, accept, 0, 1);
HttpSendRequest(hRequest, hdrs, strlen(hdrs), (PVOID)data.c_str() , data.length());
// close any valid internet-handles
}

Related

Sending file to HTTP server (C++ Wininet)

I have code for uploading small .txt files on my HTTP-server:
#include <wininet.h>
#define BUF_SIZE 4096
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList * list = new TStringList();
AnsiString Path = "";
if(OpenDialog1->Execute()) {
Path = "filename=\""+OpenDialog1->FileName+"\"";
list->LoadFromFile(OpenDialog1->FileName);
}
char data[BUF_SIZE] = "";
static char hdrs[] = "Content-Type: multipart/form-data; boundary=---------------------------7d82751e2bc0858";
strcat(data,"-----------------------------7d82751e2bc0858");
strcat(data,"\n");
strcat(data,"Content-Disposition: form-data; name=\"files[]\"; ");
strcat(data,Path.c_str());
strcat(data,"\n");
strcat(data,"Content-Type: application/octet-stream");
strcat(data,"\n\n");
strcat(data,list->Text.c_str());
strcat(data,"\n");
strcat(data,"-----------------------------7d82751e2bc0858");
strcat(data,"\n");
strcat(data,"Content-Disposition: form-data; name=\"user\"");
strcat(data,"\r\n\r\n");
strcat(data,"username");
strcat(data,"\r\n");
strcat(data,"-----------------------------7d82751e2bc0858");
strcat(data,"\n");
HINTERNET hSession = InternetOpen("MyAgent",INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hSession, "localhost",INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", "new_upltest.php", NULL, NULL, NULL, 0, 1);
HttpSendRequest(hRequest, hdrs, strlen(hdrs), data, strlen(data));
delete list;
}
It's working fine, but now I'm needing to upload big files (over 70-100 mb), that can be images (.jpg, .png, .bmp) and other document types (.pdf, .docx, etc).
Is it possible to solve my task with this code? I will be grateful for each advices...
P.S. My IDE is C++ Builder 6, but I think it doesn't matter.
This is working example for one file uploading:
void http_upload_file(PCHAR szServer, PCHAR szScript, PCHAR szParam, PCHAR szValue, PCHAR szFile)
{
PCHAR szHeaders = "Content-Type: multipart/form-data; boundary=----qwerty";
PCHAR szData = "------qwerty\r\n"
"Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n"
"------qwerty\r\n"
"Content-Disposition: form-data; name=\"files[]\"; filename=\"%s\"\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Transfer-Encoding: binary\r\n\r\n";
PCHAR szDataEnd = "\r\n------qwerty--\r\n";
char szHeader[512];
HINTERNET hSession, hConnect, hRequest;
DWORD dwFileSize, dwBytesRead, dwContentLength,dwBytesWritten;
hSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hSession)
{
hConnect = InternetConnect(hSession, szServer, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP,0, 0);
if (hConnect)
{
hRequest = HttpOpenRequest(hConnect, "POST", szScript, NULL, NULL, 0, 0, 0);
if (hRequest)
{
HANDLE hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
dwFileSize = GetFileSize(hFile, NULL);
wsprintf(szHeader, szData, szParam, szValue, szFile);
dwContentLength = lstrlen(szHeader) + dwFileSize + lstrlen(szDataEnd);
LPBYTE pBuf = (LPBYTE)malloc(dwContentLength);
CopyMemory(&pBuf[0], szHeader, lstrlen(szHeader));
ReadFile(hFile, &pBuf[lstrlen(szHeader)], dwFileSize, &dwBytesRead, NULL);
CopyMemory(&pBuf[lstrlen(szHeader) + dwFileSize], szDataEnd, lstrlen(szDataEnd));
HttpSendRequest(hRequest, szHeaders, lstrlen(szHeaders), pBuf, dwContentLength);
CloseHandle(hFile);
free(pBuf);
}
}
InternetCloseHandle(hRequest);
}
InternetCloseHandle(hConnect);
}
InternetCloseHandle(hSession);
}

POST Request in WinHttp c++

I'm trying to make a POST request in c++ with the WinHTTP api Click to the Microsoft Guide, the problem is that the example that is available in the microsoft webpage is a "GET" request so I came up with this code searching on the internet:
First we call the code:
HttpsWebRequestPost("example.com", "/api.php?action=UserLogin", "loginUsername=" + USERNAME + "&loginPassword=" + PASSWORD + "&url=/index.php?page=Portal");
Then:
#include <Windows.h>
#include <WinHttp.h>
#include <stdio.h>
#include <iostream> //getchar
#include <fstream>
#pragma comment(lib, "winhttp.lib")
using namespace std;
std::wstring get_utf16(const std::string &str, int codepage)
{
if (str.empty()) return std::wstring();
int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
return res;
}
string HttpsWebRequestPost(string domain, string url, string dat)
{
//Extra
LPSTR data = const_cast<char *>(dat.c_str());;
DWORD data_len = strlen(data);
wstring sdomain = get_utf16(domain, CP_UTF8);
wstring surl = get_utf16(url, CP_UTF8);
string response;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession, sdomain.c_str(),
INTERNET_DEFAULT_HTTP_PORT, 0);
// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, L"POST", surl.c_str(),
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
0);
// Send a request.
if (hRequest)
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
(LPVOID)data, data_len,
data_len, 0);
// End the request.
if (bResults)
bResults = WinHttpReceiveResponse(hRequest, NULL);
// Keep checking for data until there is nothing left.
if (bResults)
{
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
printf("Error %u in WinHttpQueryDataAvailable.\n",
GetLastError());
// Allocate space for the buffer.
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
printf("Out of memory\n");
dwSize = 0;
}
else
{
// Read the data.
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
dwSize, &dwDownloaded))
printf("Error %u in WinHttpReadData.\n", GetLastError());
else
//printf("%s", pszOutBuffer);
response = response + string(pszOutBuffer);
// Free the memory allocated to the buffer.
delete[] pszOutBuffer;
}
} while (dwSize > 0);
}
// Report any errors.
if (!bResults)
printf("Error %d has occurred.\n", GetLastError());
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
return response;
}
But using WireShark I only get:
Hypertext Transfer Protocol
POST ***************** HTTP/1.1\r\n
Connection: Keep-Alive\r\n
User-Agent: WinHTTP Example/1.0\r\n
Content-Length: **\r\n
Host: ******\r\n
\r\n
Anyone can help meto fix this or know an easier method?
Thanks
In order for PHP (or any other post-processing language) to recognise POST data, add this:
LPCWSTR additionalHeaders = L"Content-Type: application/x-www-form-urlencoded\r\n";
DWORD headersLength = -1;
bResults = WinHttpSendRequest( hRequest,
additionalHeaders,
headersLength ,
(LPVOID)data,
data_len,
data_len,
0);
The rest of a code is functional, should work:
The following code works for me when I tried to post text in key(info) and value(this is some info) pair. The status code 200 returned at the end.
int main()
{
DWORD dwSize = 0;
LPVOID lpOutBuffer = NULL;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
std::wstring url{ L"http://app.pomdit.com:3010/uploadfile" };
URL_COMPONENTS components{};
components.dwStructSize = sizeof(components);
components.dwHostNameLength = (DWORD)-1;
components.dwUrlPathLength = (DWORD)-1;
if (!WinHttpCrackUrl(url.c_str(), static_cast<DWORD>(url.length()), 0, &components)) {
wprintf((L"WinHttpCrackUrl(): " + ErrorMessage(GetLastError())).c_str());
}
std::wstring hostName(components.lpszHostName ? std::wstring{ components.lpszHostName, components.dwHostNameLength } : L"localhost");
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession, hostName.c_str(),
components.nPort, 0);
// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, L"POST", components.lpszUrlPath,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
0);
const WCHAR* ContentType =
L"Content-Type: multipart/form-data;boundary = 19024605111143684786787635207";
const char* MultipartRequestBody =
"--19024605111143684786787635207\r\n"
"Content-Disposition: form-data; name=\"info\"\r\n"
"\r\n"
"this is some info\r\n"
"--19024605111143684786787635207\r\n";
bResults = WinHttpSendRequest(hRequest, ContentType, wcslen(ContentType),
(LPVOID)MultipartRequestBody,
strlen(MultipartRequestBody),
strlen(MultipartRequestBody),
NULL);
// End the request.
if (bResults)
bResults = WinHttpReceiveResponse(hRequest, NULL);
// First, use WinHttpQueryHeaders to obtain the size of the buffer.
if (bResults)
{
DWORD status{}, len = sizeof(status);
bResults = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &len, NULL);
if (bResults)
{
printf("Status code = %d.\n", status);
}
}
else
{
wprintf(L"WinHttpReceiveResponse(): %s\n", ErrorMessage(GetLastError()).c_str());
}
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}

Send JSON data via HttpOpenRequest

I'm not programming at C++, but i'm asking for someone who does, so i'm sorry if my question is simple or stupid.
I need a simple example of using HttpOpenRequest/HttpSendRequest objects in order to send JSON data to some web service/site.
Thank you
Here is a very bare bones example to send a JSON string to http://hostname/path/scriptname. You will have to add proper error checking, status code checking, etc as needed:
HINTERNET hInternet = InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hInternet, _T("hostname"), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
LPTSTR rgpszAcceptTypes[] = {_T("application/json"), NULL};
HINTERNET hRequest = HttpOpenRequest(hConnect, _T("POST"), _T("/path/scriptname"), NULL, NULL, rgpszAcceptTypes, 0, 0);
HttpAddRequestHeaders(hRequest, _T("Content-Type: application/json\r\n"), -1, HTTP_ADDREQ_FLAG_ADD);
char *JsonData = "..."; // your actual JSON data here
HttpSendRequest(hRequest, NULL, 0, JsonData, strlen(JsonData))
DWORD StatusCode = 0;
DWORD StatusCodeLen = sizeof(StatusCode);
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &StatusCode, &StatusCodeLen, NULL);
if (StatusCode == 200)
{
// use InternetQueryDataAvailable() and InternetReadFile()
// to read any response data as needed...
}
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);

POST request to a friendly URL fails

I'm trying to post an HTTP request to an HTML form that is accessible through the friendly URL address.
But when I post it, library returns error code "bad URL provided".
HINTERNET hRequest = HttpOpenRequest(hConn, L"POST", L"newform",
NULL, L"http://www.example.com/add/newform/",
rgpszAcceptTypes, NULL, NULL);
DWORD error_code = GetLastError();
bool hResult = HttpSendRequestW(hRequest,NULL,NULL,NULL,NULL);
error_code = GetLastError(); //12005 - bad URL provided
What am I doing wrong, why does my request fails?
The server name (www.site.com in your above example) must be specified in the call to InternetConnect, as must the protocol (INTERNET_SERVICE_HTTP).
In the call to HttpOpenRequest you only give the name of the "object", which in this case is "/add/newform/".
For example (added for more clarity):
HINTERNET hConn = InternetConnect(hInternet, L"www.site.com", INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
HINTERNET hRequest = HttpOpenRequest(hConn, L"POST", L"/add/newform", NULL, NULL,
rgpszAcceptTypes, NULL, NULL);

WCHAR to String, how do i do it?

String* Adder::downloadUrl(String* url)
{
DWORD dwSize = 0;
LPVOID lpOutBuffer = NULL;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen( L"A WinHTTP Example Program/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect( hSession, L"www.google.com",
INTERNET_DEFAULT_HTTP_PORT, 0);
// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
0);
// Send a request.
if (hRequest)
bResults = WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
// End the request.
if (bResults)
bResults = WinHttpReceiveResponse( hRequest, NULL);
// First, use WinHttpQueryHeaders to obtain the size of the buffer.
if (bResults)
{
WinHttpQueryHeaders( hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX, NULL,
&dwSize, WINHTTP_NO_HEADER_INDEX);
// Allocate memory for the buffer.
if( GetLastError( ) == ERROR_INSUFFICIENT_BUFFER )
{
lpOutBuffer = new WCHAR[dwSize/sizeof(WCHAR)];
// Now, use WinHttpQueryHeaders to retrieve the header.
bResults = WinHttpQueryHeaders( hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
lpOutBuffer, &dwSize,
WINHTTP_NO_HEADER_INDEX);
}
}
// Print the header contents.
if (bResults)
printf("Header contents: \n%S",lpOutBuffer);
// Free the allocated memory.
delete [] lpOutBuffer;
// Report any errors.
if (!bResults)
printf("Error %d has occurred.\n",GetLastError());
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
String* retOne;
return retOne;
}
I want to get response as string i am using dll in C#, dont know vc++ at all, please suggest a way.
String* retOne //how to get response;
return retOne;
UPDATE
// Convert a wide Unicode string to an UTF8 string
std::string utf8_encode(const std::wstring &wstr)
{
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo( size_needed, 0 );
WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
String* retOne = utf8_encode(lpOutBuffer);
Gives error: 'utf8_encode' : cannot convert parameter 1 from 'LPVOID' to 'const std::wstring
Please don't post comments suggesting use of .net libraries.
Looks like you need the WideCharToMultiByte function
Whats hard in this process is to understand the function WideCharToMultiByte.
Basically what you need to do in order to get the whole string (and avoid garbage as a result of not having a null terminating string) is first use:
int size_needed = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)lpOutBuffer, -1, NULL, 0, NULL, NULL);
char *stringMe = new char[size_needed + 1];
While size_needed will be the required buffer size for lpMultiByteStr in the WideCharToMultiByte function.
Afterwards you just need to use that function again, since now you have the complete size:
WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)lpOutBuffer, -1, stringMe, size_needed, NULL, NULL);
stringMe[size_needed + 1] = NULL;
Now you can also convert it to String:
std::string serverOutput(stringMe);
Try this:
String* retOne = utf8_encode(std::wstring(lpOutBuffer));
or
String* retOne = utf8_encode(std::wstring((WCHAR*)lpOutBuffer));