Archive for the ‘Microsoft’ Category

Making HTTP Requests in C++ with WinInet and CFNetwork

First, a disclaimer: I love cURL, but there are times when you simply do not want/need to use third-party libraries. This post has nothing to do with cURL. It has everything to do with making HTTP requests with the native API’s for doing HTTP requests on Windows and OS X.

If you are here, odds are that you’ve just spent the last few hours in a futile attempt to understand either the WinInet or CFNetwork documentation. In fairness, the documentation from Microsoft and Apple is decent enough … But let’s face it, you want a quick and dirty example and there are none.

The following snippets are complete working examples. They are simple HTTP GET requests from start to finish. They DO NOT take into consideration proxy servers or HTTP auth. They DO NOT show you how to do an HTTP post. Once you get the basics though, it’s really not that far of a leap to get the rest.

If anyone would like to dive into those subject in more detail, we certainly can. Let me know, and we can talk.

Note that there’s not a lot of error handling in the code below. Just take a look at the API docs for information about what these functions return, and how to respond to errors.

Windows via WinInet:

#include <Windows.h>
#include <WinInet.h>
#include <iostream>
#include <string>

int main(int argc, char *argv[])
{
  HINTERNET hInternet = InternetOpenW(L"MyUserAgent", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

  if( hInternet==NULL )
  {
    std::cout << "InternetOpenW failed with error code " << GetLastError() << std::endl;
  }
  else
  {
    HINTERNET hConnect = InternetConnectW(hInternet, L"www.your_server.com", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);

    if( hConnect==NULL )
    {
      std::cout << "InternetConnectW failed with error code " << GetLastError() << std::endl;
    }
    else
    {
      const wchar_t* parrAcceptTypes[] = { L"text/*", NULL };
      HINTERNET hRequest = HttpOpenRequestW(hConnect, L"GET", L"", NULL, NULL, parrAcceptTypes, 0, 0);

      if( hRequest==NULL )
      {
        std::cout << "HttpOpenRequestW failed with error code " << GetLastError() << std::endl;
      }
      else
      {
        BOOL bRequestSent = HttpSendRequestW(hRequest, NULL, 0, NULL, 0);

        if( !bRequestSent )
        {
          std::cout << "HttpSendRequestW failed with error code " << GetLastError() << std::endl;
        }
        else
        {
          std::string strResponse;
          const int nBuffSize = 1024;
          char buff[nBuffSize];

          BOOL bKeepReading = true;
          DWORD dwBytesRead = -1;

          while(bKeepReading && dwBytesRead!=0)
          {
            bKeepReading = InternetReadFile( hRequest, buff, nBuffSize, &dwBytesRead );
            strResponse.append(buff, dwBytesRead);
          }

          std::cout << strResponse << std::endl;
        }

        InternetCloseHandle(hRequest);
      }

      InternetCloseHandle(hConnect);
    }

    InternetCloseHandle(hInternet);
  }

  return 0;
}

OS X via CFNetwork*

#include <iostream>
#include <fstream>
#include <CoreFoundation/CoreFoundation.h>
#include <CFNetwork/CFNetwork.h>
#include <CFNetwork/CFHTTPStream.h>

int main(int argc, char *argv[])
{
  CFURLRef cfUrl = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://www.foo.com/stuff"), NULL);
  CFHTTPMessageRef cfHttpReq = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), cfUrl, kCFHTTPVersion1_1);

  CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, cfHttpReq);
  CFReadStreamOpen(readStream);

  CFMutableDataRef cfResp = CFDataCreateMutable(kCFAllocatorDefault, 0);

  CFIndex numBytesRead;

  do
  {
    const int nBuffSize = 1024;
    UInt8 buff[nBuffSize];
    numBytesRead = CFReadStreamRead(readStream, buff, nBuffSize);

    if( numBytesRead > 0 )
    {
      CFDataAppendBytes(cfResp, buff, numBytesRead);
    }
    else if( numBytesRead < 0 )
    {
      CFStreamError error = CFReadStreamGetError(readStream);
      std::cout << error.error << std::endl;
    }
  } while( numBytesRead > 0 );

  CFReadStreamClose(readStream);

  // to write to file, uncomment code below.
  //std::ofstream oFile;
  //oFile.open("/Users/cbarnes/Desktop/file.out", std::ios::out|std::ios::binary);
  //oFile.write( (const char*)CFDataGetBytePtr(cfResp), CFDataGetLength(cfResp));

  CFRelease(cfUrl);
  CFRelease(cfHttpReq);
  CFRelease(readStream);
  CFRelease(cfResp);

  return 0;
}

* To keep things simple, this code does not use run loops, polling, or callbacks. It just blocks until the request is complete. If you’d like to see an example of such, let me know.