Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Take a REST : A Windows C++ Library for Quick Web Interfaces Interaction

5.00/5 (16 votes)
30 May 2019CPOL3 min read 31.1K   540  
A few functions and you can call various Web libraries like Google Drive

REST class is now part of my RGF library.

Introduction

Too many services are available for the web (Google Drive, Facebook APIs, etc.) but you only get a Java SDK, a PHP SDK, a Python SDK and never a Windows SDK. Here is a small library that can take care of any REST interface in a quick manner.

I will use this library in my next article, which will provide a Google/Onedrive/Dropbox drive library for Windows.

The ihandle Wrapper

To facilitate coding, the HINTERNET WinInet handle is wrapped in an ihandle class:

C++
class ihandle
        {
        private:
            HINTERNET hX = 0;
            std::shared_ptr<size_t> ptr = std::make_shared<size_t>();

        public:

            void Close()
                {
                if (!ptr || !ptr.unique())
                    {
                    ptr.reset();
                    return;
                    }
                ptr.reset();
                if (hX != 0)
                    InternetCloseHandle(hX);
                hX = 0;
                }

            ihandle()
                {
                hX = 0;
                }
            ~ihandle()
                {
                Close();
                }
            ihandle(const ihandle& h)
                {
                Dup(h);
                }
            ihandle(ihandle&& h)
                {
                Move(std::forward<ihandle>(h));
                }
            ihandle(HINTERNET hY)
                {
                hX = hY;
                }
            ihandle& operator =(const ihandle& h)
                {
                Dup(h);
                return *this;
                }
            ihandle& operator =(ihandle&& h)
                {
                Move(std::forward<ihandle>(h));
                return *this;
                }

            void Dup(const ihandle& h)
                {
                Close();
                hX = h.hX;
                ptr = h.ptr;
                }
            void Move(ihandle&& h)
                {
                Close();
                hX = h.hX;
                ptr = h.ptr;
                h.ptr.reset();
                h.hX = 0;
                }
            operator HINTERNET() const
                {
                return hX;
                }
        }

This class provides copy/move functions and can be used in place of the standard HINTERNET handle.

The Providers

The library must read from or write data to somewhere. The abstract interface which the library expects to read data is data_provider:

C++
class data_provider
    {
    public:

        virtual size_t s() = 0;
        virtual size_t Read(char* Buff, size_t sz) = 0;
        virtual bool CanOnce() = 0;
        virtual std::tuple<const char*, size_t> Once() = 0;

    };

For convenience, the library provides two implementations of it, memory_data_provider and file_data_provider which provide reading functions from a memory buffer or a file handle respectively.

The abstract interface to write data is data_writer:

C++
class data_writer
    {
    public:

        virtual DWORD Write(const char* Buff, DWORD sz) = 0;
        virtual size_t s() = 0;

    };

In addition, the library provides memory_data_writer and file_data_writer to write to memory or a handle, for convenience.

The Library

Constructor: Pass the agent name.

C++
REST(const wchar_t* ag = 0);

Connect or disconnect:

C++
HRESULT Connect(const wchar_t* host, bool SSL = false, 
unsigned short Port = 0, DWORD flg = 0,const wchar_t* user= 0,const wchar_t* pass = 0)
void Disconnect();

If SSL is true, the default port is 443 and flags are appended INTERNET_FLAG_SECURE.

Make a request:

C++
ihandle Request2(const wchar_t* url, data_provider& dp, bool Once = true, 
    const wchar_t* verb = L"POST", std::initializer_list<wstring> hdrs = {}, 
    std::function<HRESULT(size_t sent, size_t tot, void*)> fx = nullptr, 
    void* lp = 0, 
    const char* extradata1 = 0, DWORD extradatasize1 = 0,
    const char* extradata2 = 0,DWORD extradatasize2 = 0);

The parameters to this function are:

  • The url to connect.
    • If you pass a full url (like https://accounts.google.com/o/oauth2), then the function:
      • If the request is a GET, takes the entire url with InternetOpenURL and returns that handle without doing anything else (all other parameters are ignored).
      • If the request is a verb other than GET, it cracks the url to its parts and proceeds to the steps as if you had passed a relative url.
    • If you pass a relative url, then the function uses it to HttpOpenRequest().
  • The data provider to provide more data to the request. This is used when the request requires a HTTP body, for example, when passing form data or when uploading a file.
  • Once can be true to instruct the function to pass the entire data in dp at once. This is ignored unless the data provider is a memory data provider and extradata1.extradata2 are null.
  • verb is the HTTP verb to send, like GET, POST or PUT.
  • hdrs include extra HTTP headers to be sent to the request (for example, in Google Auth, you can use an Authorization: Bearer header).
  • fx and lp specify a function and a custom value, The function is to be called periodically as long as the upload is progressing.
  • extradata1/extradatasize1 are sent, if not null, before the upload of the data in dp.
  • extradata2/extradatasize2 are sent, if not null, after the upload of the data in dp.

The function returns a non-null ihandle on success.

For convenience, the library provides the RequestWithBuffer and RequestWithFile functions.

Get any headers related to a request:

C++
long Headers(ihandle& hI3,std::map<wstring,wstring>& t);

It returns all the headers in a map and also the HTTP result code (say, 200 or 404) as a return value.

Read the result:

C++
HRESULT Read2(ihandle& hI3, data_writer& dw, 
std::function<HRESULT(unsigned long long, unsigned long long, void*)> 
                      fx = nullptr, void* lp = 0);

The parameters to this function are:

  • The handle to read
  • A provider to write the data
  • fx and lp specify a function and a custom value, The function is to be called periodically as long as the download is progressing.

If the size of the download is unknown, the second parameter of the fx function is -1.

For convenience, the library provides the ReadToFile and ReadToMemory functions.

Google Authentication

The Google authentication flow is demonstrated in text.exe.

  • The app asks for the Client ID and secret. To use it, you must have an application's client ID and secret (created in Google API Console).
  • The app opens a Google permission URL which you click "Allow" in order to get a code.
  • The app asks for the code.
  • The app then uses REST to get the access token:
C++
if (FAILED(Connect(L"accounts.google.com", true)))
    return false;

string u;
// Build the thing to be passed as the request body
u = {...} ;

wchar_t au[1000] = { 0 };
swprintf_s(au, 1000, L"Content-Length: %u", (DWORD)u.length());
auto hi = RequestWithBuffer(L"/o/oauth2/token", L"POST",
     { L"Content-Type: application/x-www-form-urlencoded",au }, 
      u.data(), u.size());

The return is then read:

C++
vector<char> out;
ReadToMemory(hi, out);
out.resize(out.size() + 1); // to make it a ASCIIZ
char* p = (char*)out.data();
j.Parse(p);

and parsed with JSON, to provide the refresh and access token.

History

  • 21-01-2017: Added user+pass to connect and some minor fixes
  • 04-01-2017: Fixed a (serious) bug, duh
  • 01-01-2017: Happy new year (and first release)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)