I need to send WININET request to rest server and get json response. Code works only while fiddler is working. I've tried everything what I've found but every time I get 0 into bytesRead. There aren't any errors and HttpSendRequestand and InternetReadFile both return true but buffer is still empty.
Server works fine and answers in a right way. When fiddler is running I get my 76 bytes, which I need. I tried to figure out using that blog Help! Running Fiddler Fixes My App???, no luck.
HINTERNET session = nullptr;
HINTERNET request = nullptr;
HINTERNET connect = nullptr;
BOOST_SCOPE_EXIT((&session)(&request)(&connect)) {
if (request != nullptr)
::InternetCloseHandle(request);
if (session != nullptr)
::InternetCloseHandle(session);
if (connect != nullptr)
::InternetCloseHandle(connect);
} BOOST_SCOPE_EXIT_END;
try
{
//std::wstring agent(L"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
std::wstring agent{ L"Mozilla/5.0 (compatible)" };
session = ::InternetOpen(agent.c_str(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!session)
return;
InternetSetStatusCallback(
session,
(INTERNET_STATUS_CALLBACK)IStatusCallback);
connect = InternetConnect(
session
, serverName.c_str()
, INTERNET_DEFAULT_HTTP_PORT
, NULL
, NULL
, INTERNET_SERVICE_HTTP
, 0
, 1);
if (!connect)
return;
//const wchar_t* parrAcceptTypes[] = { L"text/*", NULL };
const wchar_t* parrAcceptTypes[] = { L"application/json", L"text/*", NULL };
request = HttpOpenRequest(
connect
, L"GET"
, virtualFolder.c_str()
, L"HTTP/1.1"
, NULL
, parrAcceptTypes
, /*INTERNET_FLAG_KEEP_CONNECTION*/INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE
, 1);
std::string data;
if (request)
{
BOOL isRequestComplete = HttpSendRequest(request, NULL, 0, NULL, 0);
if (isRequestComplete)
{
const int dataSize = 1024;
BYTE buff[dataSize];
DWORD bytesRead = (DWORD)-1;
BOOL bKeepReading = true;
while (bKeepReading && bytesRead != 0)
{
bKeepReading = InternetReadFile(request, buff, sizeof(buff) - 1, &bytesRead);
data.append((char*)buff, bytesRead);
}
}
else
{
DWORD dwErr = GetLastError();
InternetErrorDlg(parent_, request, dwErr, 0, NULL);
LERR_ << "Request was failed. Error code: " << dwErr;
}
}
Related
I'm trying to send message to telegram chat from bot using winapi and c++.
Here is my code
char szData[1024];
// initialize WinInet
HINTERNET hInternet = ::InternetOpen(TEXT("WinInet Test"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL)
{
// open HTTP session
HINTERNET hConnect = ::InternetConnect(hInternet, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect != NULL)
{
wstring request = L"/bot<bot_id>/sendMessage";
// open request
HINTERNET hRequest = ::HttpOpenRequest(hConnect, L"GET", (LPCWSTR)request.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest != NULL)
{
// send request
const wchar_t* params = L"?chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequest(hRequest, NULL, 0, (LPVOID)params, wcslen(params));
if (isSend)
{
for (;;)
{
// reading data
DWORD dwByteRead;
BOOL isRead = ::InternetReadFile(hRequest, szData, sizeof(szData) - 1, &dwByteRead);
// break cycle if error or end
if (isRead == FALSE || dwByteRead == 0)
break;
// saving result
szData[dwByteRead] = 0;
}
}
// close request
::InternetCloseHandle(hRequest);
}
// close session
::InternetCloseHandle(hConnect);
}
// close WinInet
::InternetCloseHandle(hInternet);
}
wstring answer = CharPToWstring(szData);
return answer;
But I've got {"ok":false,"error_code":400,"description":"Bad Request: message text is empty"} response. <chat_id> is id consisted of digits(12345678).
If I run this request in postman or in browser - then everything is ok.
I also tried to run this request using WinHttp* methods and result is the same.
What should I change in my request parameters to make it work?
There are a number of issues with this code:
You don't need to typecast the return value of wstring::c_str() to LPCWSTR (aka const wchar_t*), as it is already that type.
You can't send body data in a GET request. The Telegram Bot API expects body data to be sent in a POST request instead.
You are telling HttpSendRequest() to send body data from a wchar_t* UTF-16 string, but that is not the correct encoding that the server is expecting. You need to use a char* UTF-8 string instead.
You are not sending a Content-Type request header to tell the server what the format of the body data is. The API supports several different formats. In this case, since you are sending the data in application/x-www-form-urlencoded format, you need to add a Content-Type: application/x-www-form-urlencoded header to the request.
With all of that said, try this instead:
// initialize WinInet
HINTERNET hInternet = ::InternetOpenW(L"WinInet Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL) ... // error handling
// open HTTP session
HINTERNET hConnect = ::InternetConnectW(hInternet, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect == NULL) ... // error handling
// open request
wstring wsResource = L"/bot<bot_id>/sendMessage";
HINTERNET hRequest = ::HttpOpenRequestW(hConnect, L"POST", wsResource.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest == NULL) ... // error handling
// send request
string sBody = u8"chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequestW(hRequest, L"Content-Type: application/x-www-form-urlencoded", -1L, sBody.c_str(), sBody.size());
if (!isSend) ... // error handling
string sReply;
char szData[1024];
DWORD dwByteRead;
while (::InternetReadFile(hRequest, szData, sizeof(szData), &dwByteRead) && dwByteRead != 0)
{
// saving result
sReply.append(szData, dwByteRead);
}
...
// use sReply as needed ...
I've wrote program that does simple HTTP POST.
When using HTTP protocol all goes well, but when changing to HTTPS i get this error:
ERROR_INTERNET_CANNOT_CONNECT
Request("GET","www.google.pl",80,false,"/","",""); //works
Request("GET","www.google.pl",443,true,"/","",""); //ERROR_INTERNET_CANNOT_CONNECT
What's funny is that i've also tried it on XP and there, insteed of ERROR_INTERNET_CANNOT_CONNECT i have (Internet Explorer 7, which can open https pages correctly)
Here is the Request() body:
string Request(const char* Method, const char* Host,int Port,bool Secured, const char* url, const char* header, const char* data)
{
char buffer[1024];
string ret;
HINTERNET internet = InternetOpenA("mikmas", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(internet == NULL)
throw OpenInternetException();
HINTERNET connect = InternetConnectA(internet, Host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if(connect == NULL)
{
InternetCloseHandle(internet);
throw CantConnectException();
}
int flag=INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_PRAGMA_NOCACHE;
if (Secured)
flag|=INTERNET_FLAG_SECURE;
HINTERNET request = HttpOpenRequest(connect, Method, url, "HTTP/1.1", NULL, NULL, flag, NULL);
if(request == NULL)
{
InternetCloseHandle(request);
InternetCloseHandle(connect);
throw CantOpenRequestException();
}
int datalen = 0;
if(data != NULL) datalen = strlen(data);
int headerlen = 0;
if(header != NULL) headerlen = strlen(header);
if (!HttpSendRequest(request, header, headerlen, (void*)data, datalen))
throw CantSendRequestException();
DWORD read;
while(InternetReadFile(request, buffer, 1023, &read) && read)
{
buffer[read] = 0;
ret+=buffer;
}
InternetCloseHandle(request);
InternetCloseHandle(connect);
InternetCloseHandle(internet);
return ret;
}
I'm really confused when using WinINET. A so simple action - stupid, simplest POST over https and it's almost impossible in cpp?
I'm using WinINet class in MFC to upload files to webserver.
When I run this program, httpSendRequestEx returns 12005 error which is invalid url.
If I put http:// in front of the url, httpSendRequestEx returns 12007 error which is "The server name could not be resolved".
I spent whole day to fix this problem, but I have no idea how to fix it.
Please help me
Here is my code.
CString m_strBoundary = ("abcdefg");
LPCTSTR lpszFilePath = "C:\\MFCprj\\WinINet_Example2\\WinINet_Example2\\Full.jpg";
LPCTSTR lpszFileName = "Full.jpg";;
HANDLE hFile = CreateFile("C:\\MFCprj\\WinINet_Example2\\WinINet_Example2\\Full.jpg", GENERIC_READ, 0, 0, 0, 0, 0);
HINTERNET hInternet = ::InternetOpen("WININETEXAMPLE", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(!hInternet)
{
AfxMessageBox("InternetOpen has an error");
}
HINTERNET hConnection = ::InternetConnect(hInternet, "192.168.88.4:8090/mrbs/controller?do=XML_MULTIPART_TEST_ACTION", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
if(!hConnection)
{
AfxMessageBox("InternetConnect has an error");
}
HINTERNET hRequest = ::HttpOpenRequest(hConnection, "POST", "/jsp/inc/head.jsp", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
if(!hRequest)
{
AfxMessageBox("HttpOpenRequest has an error");
}
//HTTP header
_bstr_t strHeader;
strHeader = "Content-Type: multipart/form-data; boundary=";
strHeader = strHeader + m_strBoundary;
::HttpAddRequestHeaders(hRequest, (LPCTSTR)strHeader, -1, 0);
//Body header
_bstr_t strFilePartHead("--");
strFilePartHead = strFilePartHead + m_strBoundary;
strFilePartHead = strFilePartHead + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"Full.jpg\"";
strFilePartHead = strFilePartHead + "\"\r\nContent-Type: application/vnd.ms-excel\r\n\r\n";
_bstr_t strFilePartTail("\r\n");
strFilePartTail = strFilePartTail + "--";
strFilePartTail = strFilePartTail + m_strBoundary;
strFilePartTail = strFilePartTail + "--\r\n";
INTERNET_BUFFERS BufferIn;
BufferIn.dwStructSize = sizeof(INTERNET_BUFFERS);
BufferIn.Next = NULL;
BufferIn.lpcszHeader = (LPCTSTR)strHeader;
BufferIn.dwBufferLength = 0;
BufferIn.dwBufferTotal = GetFileSize(lpszFilePath) + strlen( (LPCSTR)strFilePartHead ) + strlen( (LPCSTR)strFilePartTail );
BufferIn.dwHeadersLength = lstrlen( (LPCTSTR)strHeader );
BufferIn.dwHeadersTotal = lstrlen( (LPCTSTR)strHeader );
BufferIn.lpvBuffer = NULL;
BufferIn.dwOffsetHigh = 0;
BufferIn.dwOffsetLow = 0;
if(!::HttpSendRequestEx(hRequest, &BufferIn, NULL, 0, NULL))
{
DWORD dwError = GetLastError();
::InternetCloseHandle(hRequest);
::InternetCloseHandle(hConnection);
::InternetCloseHandle(hInternet);
}
InternetConnect accepts only the server name/address, and the port.
hConnection = ::InternetConnect(hInternet, "192.168.88.4", 8090, ...);
That's because InternetConnect only makes a connection to a server (a port on a machine somewhere). The URL forms part of the request to the server, so the additional string ("/mrbs/controller?do=XML_MULTIPART_TEST_ACTION") you were passing to InternetConnect actually needs to be given to HttpOpenRequest.
hRequest = ::HttpOpenRequest(hConnection, "POST", "/mrbs/controller?do=XML_MULTIPART_TEST_ACTION", ...);
I'm not sure where the "/jsp/inc/head.jsp" that you were originally passing to HttpOpenRequest fits in, but if that really is part of the URL too then you would do this:
hRequest = ::HttpOpenRequest(hConnection, "POST", "/mrbs/controller?do=XML_MULTIPART_TEST_ACTION/jsp/inc/head.jsp", ...);
I have some API URL that returns JSON data like this:
{"data":[{"id":"1","param":"value1"},{"id":"2","param":"value2"}, ... {"id":"N","param":"valueN"}]}
Every id should be unique.
When i'm requesting this API URL through browser (Firefox or Chrome) it returns valid data, i.e. every id is unique and non of the parts of data is duplicated.
But when i'm requesting this API URL through my own function based on WinInet it duplicates some parts of data. It still is valid JSON, but some of id's are duplicated and some of them not. Just like this:
{"data":[{"id":"1","param":"value1"},{"id":"1","param":"value1"},{"id":"2","param":"value2"},{"id":"3","param":"value3"},{"id":"3","param":"value3"} ... {"id":"N","param":"valueN"}]}
I'm using GET method. Here is my WinInet function:
using namespace std;
enum EMethod {M_UNKNOWN, M_POST, M_GET, M_DELETE};
enum EProtocol {P_UNKNOWN, P_HTTP, P_HTTPS};
// |example:|
/*
string responce = SendInetRequest(
P_HTTPS, M_GET, "www.site.com", "page.php", "param1=val1¶m2=val2");
*/
string SendInetRequest(const EProtocol _eProtocol,
const EMethod _eMethod,
const string& _server,
const string& _page,
const string& _params = "")
{
char szData[1024];
string recvStr;
// initialize WinInet
HINTERNET hInternet = ::InternetOpenA("WinInet Test",
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL)
{
INTERNET_PORT port = NULL;
DWORD flags = NULL;
switch (_eProtocol)
{
case P_HTTP:
port = INTERNET_DEFAULT_HTTP_PORT;
flags = /*INTERNET_FLAG_KEEP_CONNECTION*/
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE;
break;
case P_HTTPS:
port = INTERNET_DEFAULT_HTTPS_PORT;
flags = INTERNET_FLAG_SECURE |
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE;
break;
default:
return NULL;
}
// open HTTP session
HINTERNET hConnect = ::InternetConnectA(hInternet,
_server.c_str(), port, NULL,NULL, INTERNET_SERVICE_HTTP, 0, 1);
if (hConnect != NULL)
{
string request;
string method;
string headers;
string data;
switch (_eMethod)
{
case M_GET:
method = "GET";
request = _page +
(_params.empty() ? "" : ("?" + _params));
break;
case M_POST:
method = "POST";
request = _page;
headers = "Content-Type: application/x-www-form-urlencoded";
data = _params.empty() ? "" : _params;
break;
case M_DELETE:
method = "DELETE";
request = _page +
(_params.empty() ? "" : ("?" + _params));
break;
default:
return NULL;
}
// open request
HINTERNET hRequest = ::HttpOpenRequestA(hConnect, method.c_str(),
(LPCSTR)request.c_str() ,NULL, NULL, 0, flags, 1);
if (hRequest != NULL)
{
// send request
BOOL isSend = ::HttpSendRequestA(hRequest, headers.c_str(),
headers.length(), (LPVOID)data.c_str(), data.length());
if (isSend)
{
for(;;)
{
// reading data
DWORD dwByteRead;
BOOL isRead = ::InternetReadFile(hRequest,
szData, sizeof(szData) - 1, &dwByteRead);
// break cycle if error or end
if (isRead == FALSE || dwByteRead == 0)
break;
// saving result
recvStr.append(szData, dwByteRead);
szData[dwByteRead] = 0;
}
}
// close request
::InternetCloseHandle(hRequest);
}
// close session
::InternetCloseHandle(hConnect);
}
// close WinInet
::InternetCloseHandle(hInternet);
}
return recvStr;
}
So what is wrong with my function? Why does it duplicate some parts of responce JSON data unlike normal browsers?
You need to add the null terminator byte to szData before you append it to recvStr, so that you only append the data that was just received.
// saving result
szData[dwByteRead] = 0;
recvStr.append(szData, dwByteRead);
Hey all i want to login onto my works webpage with wininet, this is my current code:
int main()
{
HINTERNET hInet = InternetOpenA("UserAgent/1.0", INTERNET_OPEN_TYPE_PRECONFIG,0, 0, 0 );
if(!hInet)
{
printf("hInet Failed!\n");
return -1;
}
HINTERNET hConnection = InternetConnectA( hInet,"app.tamigo.com",INTERNET_DEFAULT_HTTPS_PORT,"","", INTERNET_SERVICE_HTTP,0,0);
if (!hConnection)
{
InternetCloseHandle(hInet);
printf("InternetConnectA failed!\n");
return -1;
}
HINTERNET hRequest = HttpOpenRequestA( hConnection, "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",NULL,"https://app.tamigo.com/Home/Pages/Login.aspx", NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 );
if (!hRequest)
{
printf("BuildRequestHeader failed %d!\n",GetLastError());
InternetCloseHandle(hConnection);
InternetCloseHandle(hInet);
return -1;
}
HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = 10;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
while (!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
printf("%s", pInfoBuffer);
free(pInfoBuffer);
cin.get();
return 1;
}
if this code is right, i have to login with my username and pass,i got a cookie using "Firefox plugin Tamper Data". How can i set this cookie with wininet?
Thanks alot for reading and for your time
If the cookie already exists from a previous WinInet request, then WinInet will send it automatically. However, if the cookie does not exist in WinInet's cookie cache (if instance, if you got the cookie from another source), then you will have to use HttpAddRequestHeaders() to provide your own Cookie: request header before calling HttpSendRequest().