• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[C++] Asynchronous WinInet client

Delirium

OTLand veteran
Staff member
Global Moderator
Joined
May 28, 2007
Messages
3,365
Solutions
1
Reaction score
289
Location
Athens, Greece
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:
C++:
#include "AsyncInet.h"

AsyncInet::AsyncInet()
{
    this->hConnectEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    this->hRequestOpenEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    this->hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    this->hInetInstance = NULL;
    this->hInetConnect = NULL;
    this->hInetRequest = NULL;
}

AsyncInet::~AsyncInet()
{
    if (this->hConnectEvent)
        CloseHandle(this->hConnectEvent);

    if (this->hRequestOpenEvent)
        CloseHandle(this->hRequestOpenEvent);

    if (this->hRequestCompleteEvent)
        CloseHandle(this->hRequestCompleteEvent);

    CloseConnection();
}

BOOL AsyncInet::Connect(
    string szAddr,
    unsigned short port,
    string szAgent,
    DWORD dwTimeout)
{
    CloseConnection();

    ResetEvent(this->hConnectEvent);
    ResetEvent(this->hRequestOpenEvent);
    ResetEvent(this->hRequestCompleteEvent);

    if (!(this->hInetInstance =
        InternetOpenA(szAgent.c_str(),
        INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC)))
    {
        return FALSE;
    }

    if (InternetSetStatusCallbackA(this->hInetInstance, (INTERNET_STATUS_CALLBACK)&CallbackFunction))
    {
        return FALSE;
    }

    this->context.dwContext = AsyncInet::CONTEXT_CONNECT;
    this->context.obj = this;

    this->hInetConnect = InternetConnectA(this->hInetInstance, szAddr.c_str(),
        port, NULL, NULL,
        INTERNET_SERVICE_HTTP, INTERNET_FLAG_KEEP_CONNECTION |
        INTERNET_FLAG_NO_CACHE_WRITE, reinterpret_cast<DWORD>(&this->context));

    if (this->hInetConnect == NULL)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            if (WaitForSingleObject(this->hConnectEvent, dwTimeout))
                return FALSE;
        }
        return FALSE;
    }

    if (this->hInetConnect == NULL)
    {
        return FALSE;
    }

    return TRUE;
}

void AsyncInet::CloseConnection()
{
    if (this->hInetInstance)
        InternetCloseHandle(this->hInetInstance);

    if (this->hInetConnect)
        InternetCloseHandle(this->hInetConnect);

    if (this->hInetRequest)
        InternetCloseHandle(this->hInetRequest);

    this->hInetInstance = NULL;
    this->hInetConnect = NULL;
    this->hInetRequest = NULL;

}

void WINAPI AsyncInet::CallbackFunction(
    HINTERNET hInternet,
    DWORD dwContext,
    DWORD dwInetStatus,
    LPVOID lpStatusInfo,
    DWORD dwStatusInfoLength)
{

    InetContext *ctx = reinterpret_cast<InetContext*>(dwContext);

    switch (ctx->dwContext)
    {
    case AsyncInet::CONTEXT_CONNECT:
        if (dwInetStatus == INTERNET_STATUS_HANDLE_CREATED)
        {
            INTERNET_ASYNC_RESULT *inetResult = reinterpret_cast<INTERNET_ASYNC_RESULT*>(lpStatusInfo);
            ctx->obj->hInetConnect = reinterpret_cast<HINTERNET>(inetResult->dwResult);
            SetEvent(ctx->obj->hConnectEvent);
        }
        break;
    case AsyncInet::CONTEXT_REQUESTHANDLE:
        switch (dwInetStatus)
        {
        case INTERNET_STATUS_HANDLE_CREATED:
            {
                INTERNET_ASYNC_RESULT *inetResult1 = reinterpret_cast<INTERNET_ASYNC_RESULT*>(lpStatusInfo);
                ctx->obj->hInetRequest = reinterpret_cast<HINTERNET>(inetResult1->dwResult);
                SetEvent(ctx->obj->hRequestOpenEvent);
            }
            break;
        case INTERNET_STATUS_REQUEST_COMPLETE:
            SetEvent(ctx->obj->hRequestCompleteEvent);
            break;
        }
        break;
    }
}

BOOL AsyncInet::SendRequest(string szURL, RequestType requestType, string szRequestData, string szReferrer, DWORD dwTimeout)
{

    this->context.dwContext = AsyncInet::CONTEXT_REQUESTHANDLE;
    this->context.obj = this;

    string szVerb;
    if (requestType == RequestType::GET)
        szVerb = "GET";
    else
        szVerb = "POST";

    this->hInetRequest = HttpOpenRequestA(this->hInetConnect, szVerb.c_str(), szURL.c_str(),
        NULL, szReferrer.c_str(), NULL,
        INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION
        | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_FORMS_SUBMIT
        | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
        | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP, reinterpret_cast<DWORD>(&this->context));

    if (this->hInetRequest == NULL)
    {
        cerr << GetLastError() << endl;
        if (GetLastError() == ERROR_IO_PENDING)
        {
            if (WaitForSingleObject(this->hRequestOpenEvent, dwTimeout))
                return FALSE;
        }
    }

    if (this->hInetRequest == NULL)
        return FALSE;

    BOOL requestFlag;

    if (requestType == RequestType::GET)
    {
        requestFlag = HttpSendRequestA(this->hInetRequest, NULL, 0, const_cast<char*>(szRequestData.c_str()), szRequestData.size());
    }
    else {
        string szHeaders = "Content-Type: application/x-www-form-urlencoded";
        requestFlag = HttpSendRequestA(this->hInetRequest, szHeaders.c_str(), szHeaders.size(), const_cast<char*>(szRequestData.c_str()), szRequestData.size());
    }

    if (!requestFlag)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            if (WaitForSingleObject(this->hRequestCompleteEvent, dwTimeout) == WAIT_TIMEOUT)
            {
                CloseConnection();
                return FALSE;
            }
        }
    }
    return TRUE;
}

unsigned long AsyncInet::ReadData(PBYTE lpBuffer, DWORD dwSize, DWORD dwTimeout)
{
    INTERNET_BUFFERS inetBuffers;
    memset(&inetBuffers, 0, sizeof(inetBuffers));

    inetBuffers.dwStructSize = sizeof(inetBuffers);
    inetBuffers.lpvBuffer = lpBuffer;
    inetBuffers.dwBufferLength = dwSize - 1;

    this->context.dwContext = AsyncInet::CONTEXT_REQUESTHANDLE;
    this->context.obj = this;

    if (!InternetReadFileEx(this->hInetRequest, &inetBuffers, 0,
        reinterpret_cast<DWORD>(&this->context)))
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            if (WaitForSingleObject(this->hRequestCompleteEvent, dwTimeout) == WAIT_TIMEOUT)
            {
                return FALSE;
            }
        }
    }

    return inetBuffers.dwBufferLength;
}

AsyncInet.h:
C++:
#pragma once

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <Windows.h>
#include <WinInet.h>

#include "AsyncInet.h"

#pragma comment (lib, "Wininet.lib")

using namespace std;

class AsyncInet;

typedef struct _InetContext
{
    AsyncInet *obj;
    DWORD dwContext;
} InetContext;

typedef enum _RequestType {
    GET,
    POST
} RequestType;

class AsyncInet
{
public:
    AsyncInet();
    ~AsyncInet();

    enum {
        CONTEXT_CONNECT,
        CONTEXT_REQUESTHANDLE
    };

    BOOL Connect(
        string szAddr,
        unsigned short port = INTERNET_DEFAULT_HTTP_PORT,
        string szAgent = "AsyncInet",
        DWORD dwTimeout = 20 * 1000);

    BOOL SendRequest(
        string szURL,
        RequestType requestType,
        string szRequestData,
        string szReferrer,
        DWORD dwTimeout = 20 * 1000);

    unsigned long ReadData(
        PBYTE lpBuffer,
        DWORD dwSize,
        DWORD dwTimeout = 20 * 1000);

    void CloseConnection();

    static void WINAPI CallbackFunction(
        HINTERNET hInternet,
        DWORD dwContext,
        DWORD dwInetStatus,
        LPVOID lpStatusInfo,
        DWORD dwStatusInfoLength);

protected:
    InetContext context;
    HANDLE hConnectEvent, hRequestOpenEvent, hRequestCompleteEvent;
    HINTERNET hInetInstance, hInetConnect, hInetRequest;

};


  • Usage example for a GET request:
C++:
AsyncInet inet;
if (inet.Connect("example.com"))
{
    if (inet.SendRequest("test.php?id=5", RequestType::GET, "", ""))
    {
        string szResponse;
        char szBuf[1024];
        int iLength;

        while ((iLength = inet.ReadData(reinterpret_cast<PBYTE>(szBuf), 1024)) > 0)
        {

            szBuf[iLength] = 0;               
            szResponse += szBuf;
        }

        cout << szResponse.c_str() << endl;
    }
}


  • Usage example for a POST request:
C++:
AsyncInet inet;
if (inet.Connect("example.com"))
{
    if (inet.SendRequest("test.php", RequestType::POST, "somedata=somevalue&anotherone=anothervalue", ""))
    {
        string szResponse;
        char szBuf[1024];
        int iLength;

        while ((iLength = inet.ReadData(reinterpret_cast<PBYTE>(szBuf), 1024)) > 0)
        {

            szBuf[iLength] = 0;               
            szResponse += szBuf;
        }

        cout << szResponse.c_str() << endl;
    }
}


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.

C++:
#include "stdafx.h"

Enjoy.
 
i like how your code enforces the use of
C++:
using namespace std;

edit: nvm that was the case in ur git repo
edit2: where can i set request headers
 
Back
Top