Hack Forums
[C] Asynchronous WinInet client wrapper - Printable Version

+- Hack Forums (https://hackforums.net)
+-- Forum: Tech (https://hackforums.net/forumdisplay.php?fid=444)
+--- Forum: Coding (https://hackforums.net/forumdisplay.php?fid=151)
+---- Forum: Software Development (https://hackforums.net/forumdisplay.php?fid=118)
+---- Thread: [C] Asynchronous WinInet client wrapper (/showthread.php?tid=4865279)



Asynchronous WinInet client wrapper - Deliriumā„¢ - Jun 14, 2015

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
#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:
Code
#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:


Code
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:


Code
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.

Code
#include "stdafx.h"

Enjoy šŸ™‚


* - Only3 - Jun 14, 2015

will help for ppl that need async request
maybe http://veridium.net/programming-tutorials/http-client-async-wininet


* - D3M0T3P - Jun 15, 2015

don't use "using namespace std;" in your header
thanks ;-) for sharing


* - Deliriumā„¢ - Jun 15, 2015

(Jun 14, 2015 09:09 PM)Only3 Wrote:  will help for ppl that need async request
maybe http://veridium.net/programming-tutorials/http-client-async-wininet

This is what my code provides. Async HTTP requests.

(Jun 15, 2015 01:35 AM)D3M0T3P Wrote:  don't use "using namespace std;" in your header
thanks ;-) for sharing

Why? It saves me time from writing string blabla instead of std::string blabla.


* - Benoit įµ‡įµƒĖ”Ė”Ė¢ - Jun 16, 2015

(Jun 15, 2015 03:12 AM)Deliriumā„¢ Wrote:  This is what my code provides. Async HTTP requests.

Why? It saves me time from writing string blabla instead of std::string blabla.

It is considered a bad coding practice , beats the whole purpose of the namespace.


* - FSFHHDFY - Jun 16, 2015

Don't even lecture this kid on bad practice, this code is an abomination of cluster fucks.


* - Deliriumā„¢ - Jun 16, 2015

(Jun 16, 2015 01:01 PM)KeDDy Wrote:  It is considered a bad coding practice , beats the whole purpose of the namespace.

I'm using it mainly because it's a habit I had since I was learning C++. I know it's bad practice because it can cause conflicts and prevent a reader from understanding where an identifier originates. The only reason I asked you why is because, any advice is merely of no use if you don't explain it, right? šŸ˜

(Jun 16, 2015 01:09 PM)FSFHHDFY Wrote:  Don't even lecture this kid on bad practice, this code is an abomination of cluster fucks.

Are you probably mad? Because people getting mad of the internet are the kids.


* - 0xDEAD10CC - Jun 16, 2015

Header files should NEVER have 'using namespace std;' ever... Do you know how header files are included? You're just opening up a can of worms for any other programmer wanting to use your code when other conflicts may possibly arise. Having that in a header file is the worst thing you can do.


* - Benoit įµ‡įµƒĖ”Ė”Ė¢ - Jun 17, 2015

(Jun 16, 2015 04:06 PM)Deliriumā„¢ Wrote:  ....

Yeah of course, thanks I might use it in the future.


* - Deliriumā„¢ - Jun 17, 2015

(Jun 16, 2015 08:55 PM)0xDEAD10CC Wrote:  Header files should NEVER have 'using namespace std;' ever... Do you know how header files are included? You're just opening up a can of worms for any other programmer wanting to use your code when other conflicts may possibly arise. Having that in a header file is the worst thing you can do.

As I mentioned before, I know why it's bad practice but it's a bad habit I have left since the days I've been learning C++. I sometimes forget it :P

I will edit the source later to fix this.


* - 0xDEAD10CC - Jun 17, 2015

(Jun 17, 2015 08:56 AM)Deliriumā„¢ Wrote:  As I mentioned before, I know why it's bad practice but it's a bad habit I have left since the days I've been learning C++. I sometimes forget it :P

I will edit the source later to fix this.

I'm not talking about 'using namespace std;' in general. Whether you like doing that or not, it should never ever be used in a header file. Source files ONLY, if you must, but never header files. I don't think you understand what I was getting at. People above only mentioned that 'using namespace std;' is bad practice... I was talking about it being worse that it's in a header file.


* - Deliriumā„¢ - Jun 17, 2015

(Jun 17, 2015 11:01 AM)0xDEAD10CC Wrote:  I'm not talking about 'using namespace std;' in general. Whether you like doing that or not, it should never ever be used in a header file. Source files ONLY, if you must, but never header files. I don't think you understand what I was getting at. People above only mentioned that 'using namespace std;' is bad practice... I was talking about it being worse that it's in a header file.

Indeed, I missed that point. Thank you for notifying me, something new I learned today I guess :)