[C++] Asynchronous WinInet client

Discussion in 'Programming' started by Delirium, Aug 4, 2018.

  1. Delirium

    Delirium OTLand veteran & ex mod Premium User

    Joined:
    May 28, 2007
    Messages:
    3,270
    Likes Received:
    130
    Best Answers:
    0
    Hello.

    I had a WinInet client wrapper which I often used in my projects. I modified it today to support both POST and GET methods along with sending your own data and all that done asynchronously.

    So, here's the code:

    AsyncInet.cpp:
    Code (C++):
    1. #include "AsyncInet.h"
    2.  
    3. AsyncInet::AsyncInet()
    4. {
    5.     this->hConnectEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    6.     this->hRequestOpenEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    7.     this->hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    8.  
    9.     this->hInetInstance = NULL;
    10.     this->hInetConnect = NULL;
    11.     this->hInetRequest = NULL;
    12. }
    13.  
    14. AsyncInet::~AsyncInet()
    15. {
    16.     if (this->hConnectEvent)
    17.         CloseHandle(this->hConnectEvent);
    18.  
    19.     if (this->hRequestOpenEvent)
    20.         CloseHandle(this->hRequestOpenEvent);
    21.  
    22.     if (this->hRequestCompleteEvent)
    23.         CloseHandle(this->hRequestCompleteEvent);
    24.  
    25.     CloseConnection();
    26. }
    27.  
    28. BOOL AsyncInet::Connect(
    29.     string szAddr,
    30.     unsigned short port,
    31.     string szAgent,
    32.     DWORD dwTimeout)
    33. {
    34.     CloseConnection();
    35.  
    36.     ResetEvent(this->hConnectEvent);
    37.     ResetEvent(this->hRequestOpenEvent);
    38.     ResetEvent(this->hRequestCompleteEvent);
    39.  
    40.     if (!(this->hInetInstance =
    41.         InternetOpenA(szAgent.c_str(),
    42.         INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC)))
    43.     {
    44.         return FALSE;
    45.     }
    46.  
    47.     if (InternetSetStatusCallbackA(this->hInetInstance, (INTERNET_STATUS_CALLBACK)&CallbackFunction))
    48.     {
    49.         return FALSE;
    50.     }
    51.  
    52.     this->context.dwContext = AsyncInet::CONTEXT_CONNECT;
    53.     this->context.obj = this;
    54.  
    55.     this->hInetConnect = InternetConnectA(this->hInetInstance, szAddr.c_str(),
    56.         port, NULL, NULL,
    57.         INTERNET_SERVICE_HTTP, INTERNET_FLAG_KEEP_CONNECTION |
    58.         INTERNET_FLAG_NO_CACHE_WRITE, reinterpret_cast<DWORD>(&this->context));
    59.  
    60.     if (this->hInetConnect == NULL)
    61.     {
    62.         if (GetLastError() == ERROR_IO_PENDING)
    63.         {
    64.             if (WaitForSingleObject(this->hConnectEvent, dwTimeout))
    65.                 return FALSE;
    66.         }
    67.         return FALSE;
    68.     }
    69.  
    70.     if (this->hInetConnect == NULL)
    71.     {
    72.         return FALSE;
    73.     }
    74.  
    75.     return TRUE;
    76. }
    77.  
    78. void AsyncInet::CloseConnection()
    79. {
    80.     if (this->hInetInstance)
    81.         InternetCloseHandle(this->hInetInstance);
    82.  
    83.     if (this->hInetConnect)
    84.         InternetCloseHandle(this->hInetConnect);
    85.  
    86.     if (this->hInetRequest)
    87.         InternetCloseHandle(this->hInetRequest);
    88.  
    89.     this->hInetInstance = NULL;
    90.     this->hInetConnect = NULL;
    91.     this->hInetRequest = NULL;
    92.  
    93. }
    94.  
    95. void WINAPI AsyncInet::CallbackFunction(
    96.     HINTERNET hInternet,
    97.     DWORD dwContext,
    98.     DWORD dwInetStatus,
    99.     LPVOID lpStatusInfo,
    100.     DWORD dwStatusInfoLength)
    101. {
    102.  
    103.     InetContext *ctx = reinterpret_cast<InetContext*>(dwContext);
    104.  
    105.     switch (ctx->dwContext)
    106.     {
    107.     case AsyncInet::CONTEXT_CONNECT:
    108.         if (dwInetStatus == INTERNET_STATUS_HANDLE_CREATED)
    109.         {
    110.             INTERNET_ASYNC_RESULT *inetResult = reinterpret_cast<INTERNET_ASYNC_RESULT*>(lpStatusInfo);
    111.             ctx->obj->hInetConnect = reinterpret_cast<HINTERNET>(inetResult->dwResult);
    112.             SetEvent(ctx->obj->hConnectEvent);
    113.         }
    114.         break;
    115.     case AsyncInet::CONTEXT_REQUESTHANDLE:
    116.         switch (dwInetStatus)
    117.         {
    118.         case INTERNET_STATUS_HANDLE_CREATED:
    119.             {
    120.                 INTERNET_ASYNC_RESULT *inetResult1 = reinterpret_cast<INTERNET_ASYNC_RESULT*>(lpStatusInfo);
    121.                 ctx->obj->hInetRequest = reinterpret_cast<HINTERNET>(inetResult1->dwResult);
    122.                 SetEvent(ctx->obj->hRequestOpenEvent);
    123.             }
    124.             break;
    125.         case INTERNET_STATUS_REQUEST_COMPLETE:
    126.             SetEvent(ctx->obj->hRequestCompleteEvent);
    127.             break;
    128.         }
    129.         break;
    130.     }
    131. }
    132.  
    133. BOOL AsyncInet::SendRequest(string szURL, RequestType requestType, string szRequestData, string szReferrer, DWORD dwTimeout)
    134. {
    135.  
    136.     this->context.dwContext = AsyncInet::CONTEXT_REQUESTHANDLE;
    137.     this->context.obj = this;
    138.  
    139.     string szVerb;
    140.     if (requestType == RequestType::GET)
    141.         szVerb = "GET";
    142.     else
    143.         szVerb = "POST";
    144.  
    145.     this->hInetRequest = HttpOpenRequestA(this->hInetConnect, szVerb.c_str(), szURL.c_str(),
    146.         NULL, szReferrer.c_str(), NULL,
    147.         INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION
    148.         | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_FORMS_SUBMIT
    149.         | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
    150.         | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP, reinterpret_cast<DWORD>(&this->context));
    151.  
    152.     if (this->hInetRequest == NULL)
    153.     {
    154.         cerr << GetLastError() << endl;
    155.         if (GetLastError() == ERROR_IO_PENDING)
    156.         {
    157.             if (WaitForSingleObject(this->hRequestOpenEvent, dwTimeout))
    158.                 return FALSE;
    159.         }
    160.     }
    161.  
    162.     if (this->hInetRequest == NULL)
    163.         return FALSE;
    164.  
    165.     BOOL requestFlag;
    166.  
    167.     if (requestType == RequestType::GET)
    168.     {
    169.         requestFlag = HttpSendRequestA(this->hInetRequest, NULL, 0, const_cast<char*>(szRequestData.c_str()), szRequestData.size());
    170.     }
    171.     else {
    172.         string szHeaders = "Content-Type: application/x-www-form-urlencoded";
    173.         requestFlag = HttpSendRequestA(this->hInetRequest, szHeaders.c_str(), szHeaders.size(), const_cast<char*>(szRequestData.c_str()), szRequestData.size());
    174.     }
    175.  
    176.     if (!requestFlag)
    177.     {
    178.         if (GetLastError() == ERROR_IO_PENDING)
    179.         {
    180.             if (WaitForSingleObject(this->hRequestCompleteEvent, dwTimeout) == WAIT_TIMEOUT)
    181.             {
    182.                 CloseConnection();
    183.                 return FALSE;
    184.             }
    185.         }
    186.     }
    187.     return TRUE;
    188. }
    189.  
    190. unsigned long AsyncInet::ReadData(PBYTE lpBuffer, DWORD dwSize, DWORD dwTimeout)
    191. {
    192.     INTERNET_BUFFERS inetBuffers;
    193.     memset(&inetBuffers, 0, sizeof(inetBuffers));
    194.  
    195.     inetBuffers.dwStructSize = sizeof(inetBuffers);
    196.     inetBuffers.lpvBuffer = lpBuffer;
    197.     inetBuffers.dwBufferLength = dwSize - 1;
    198.  
    199.     this->context.dwContext = AsyncInet::CONTEXT_REQUESTHANDLE;
    200.     this->context.obj = this;
    201.  
    202.     if (!InternetReadFileEx(this->hInetRequest, &inetBuffers, 0,
    203.         reinterpret_cast<DWORD>(&this->context)))
    204.     {
    205.         if (GetLastError() == ERROR_IO_PENDING)
    206.         {
    207.             if (WaitForSingleObject(this->hRequestCompleteEvent, dwTimeout) == WAIT_TIMEOUT)
    208.             {
    209.                 return FALSE;
    210.             }
    211.         }
    212.     }
    213.  
    214.     return inetBuffers.dwBufferLength;
    215. }
    AsyncInet.h:
    Code (C++):
    1. #pragma once
    2.  
    3. #include <stdio.h>
    4. #include <tchar.h>
    5. #include <iostream>
    6. #include <Windows.h>
    7. #include <WinInet.h>
    8.  
    9. #include "AsyncInet.h"
    10.  
    11. #pragma comment (lib, "Wininet.lib")
    12.  
    13. using namespace std;
    14.  
    15. class AsyncInet;
    16.  
    17. typedef struct _InetContext
    18. {
    19.     AsyncInet *obj;
    20.     DWORD dwContext;
    21. } InetContext;
    22.  
    23. typedef enum _RequestType {
    24.     GET,
    25.     POST
    26. } RequestType;
    27.  
    28. class AsyncInet
    29. {
    30. public:
    31.     AsyncInet();
    32.     ~AsyncInet();
    33.  
    34.     enum {
    35.         CONTEXT_CONNECT,
    36.         CONTEXT_REQUESTHANDLE
    37.     };
    38.  
    39.     BOOL Connect(
    40.         string szAddr,
    41.         unsigned short port = INTERNET_DEFAULT_HTTP_PORT,
    42.         string szAgent = "AsyncInet",
    43.         DWORD dwTimeout = 20 * 1000);
    44.  
    45.     BOOL SendRequest(
    46.         string szURL,
    47.         RequestType requestType,
    48.         string szRequestData,
    49.         string szReferrer,
    50.         DWORD dwTimeout = 20 * 1000);
    51.  
    52.     unsigned long ReadData(
    53.         PBYTE lpBuffer,
    54.         DWORD dwSize,
    55.         DWORD dwTimeout = 20 * 1000);
    56.  
    57.     void CloseConnection();
    58.  
    59.     static void WINAPI CallbackFunction(
    60.         HINTERNET hInternet,
    61.         DWORD dwContext,
    62.         DWORD dwInetStatus,
    63.         LPVOID lpStatusInfo,
    64.         DWORD dwStatusInfoLength);
    65.  
    66. protected:
    67.     InetContext context;
    68.     HANDLE hConnectEvent, hRequestOpenEvent, hRequestCompleteEvent;
    69.     HINTERNET hInetInstance, hInetConnect, hInetRequest;
    70.  
    71. };


    • Usage example for a GET request:
    Code (C++):
    1. AsyncInet inet;
    2. if (inet.Connect("example.com"))
    3. {
    4.     if (inet.SendRequest("test.php?id=5", RequestType::GET, "", ""))
    5.     {
    6.         string szResponse;
    7.         char szBuf[1024];
    8.         int iLength;
    9.  
    10.         while ((iLength = inet.ReadData(reinterpret_cast<PBYTE>(szBuf), 1024)) > 0)
    11.         {
    12.  
    13.             szBuf[iLength] = 0;              
    14.             szResponse += szBuf;
    15.         }
    16.  
    17.         cout << szResponse.c_str() << endl;
    18.     }
    19. }

    • Usage example for a POST request:
    Code (C++):
    1. AsyncInet inet;
    2. if (inet.Connect("example.com"))
    3. {
    4.     if (inet.SendRequest("test.php", RequestType::POST, "somedata=somevalue&anotherone=anothervalue", ""))
    5.     {
    6.         string szResponse;
    7.         char szBuf[1024];
    8.         int iLength;
    9.  
    10.         while ((iLength = inet.ReadData(reinterpret_cast<PBYTE>(szBuf), 1024)) > 0)
    11.         {
    12.  
    13.             szBuf[iLength] = 0;              
    14.             szResponse += szBuf;
    15.         }
    16.  
    17.         cout << szResponse.c_str() << endl;
    18.     }
    19. }

    I do not remember where I found the original class before my own modifications therefore I cannot credit anyone at the time being. It's been a long time.

    If your project includes a precompiled header (Visual Studio users mainly), add the following line before #include "AsyncInet.h" in AsyncInet.cpp. Also make sure that you've got no double includes or any conflict might arise.

    Code (C++):
    1. #include "stdafx.h"
    Enjoy.
     
    Okke and Vulcan_ like this.
  2. tokenzz

    tokenzz :thinking:

    Joined:
    Feb 2, 2013
    Messages:
    776
    Likes Received:
    343
    Best Answers:
    2
    i like how your code enforces the use of
    Code (C++):
    1. using namespace std;
    edit: nvm that was the case in ur git repo
    edit2: where can i set request headers
     
    Raggaer likes this.

Share This Page

Loading...