Archive for the ‘HTML’ 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.

XMLHttpRequest (Part 2)

Just a continuation of the last post.

This XMLHttpRequest implementation straightens things out a bit. And, yes, I am well aware that there are better ways to do null checking. But if I did everything for you, you’d never have to learn anything for yourself…

function isObject(obj)
{
  var bIsObject = false;
  
  if( obj )
  {
    bIsObject = ( typeof obj=='object' );
  }
  
  return bIsObject;
}

function getObjectProperty( obj, strPropName )
{
  var prop = null;
  
  if( isObject(obj) && strPropName )
  {
    if( obj.hasOwnProperty(strPropName) )
    {
      prop = obj[strPropName];
    }
  }
  
  return prop;
}

function doHttpRequest( strUrl, objParams )
{
  var xmlhttp = null;

  try
  {
    // for all modern browsers
    xmlhttp = new XMLHttpRequest();
  }
  catch(e1)
  {
    try
    {
      // for IE5 and IE6
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    catch(e2)
    {
      alert("XMLHttpRequest object could not be created...");
    }
  }
  
  if( !xmlhttp )
  {
    alert("XMLHttpRequest object could not be created...");
  }
  else
  {
    var objRequestData = getObjectProperty(objParams, 'objRequestData');
    var bDoPost = getObjectProperty(objParams, 'bDoPost');
    var funcCallback = getObjectProperty(objParams, 'funcCallback');
    var objCallbackParams = getObjectProperty(objParams, 'objCallbackParams');
    
    if( !strUrl )
    {
      alert('URL cannot be blank...');
    }
    else
    {
      var strRequestData = '';
      if( objRequestData )
      {
        for( var strKey in objRequestData )
        {
          var strValue = objRequestData[strKey];

          if( strRequestData.length>0 )
          {
            strRequest += "&";
          }

          strRequestData += encodeURIComponent(strKey) + "=" + encodeURIComponent(strValue);
        }
      }
      
      xmlhttp.onreadystatechange = function()
      {
        if(xmlhttp.readyState == 4)
        {
          if( xmlhttp.status!=200 )
          {
            alert("Error in HTTP Request...\n\nStatus: "+xmlhttp.status+"\n"+xmlhttp.statusText);
          }
          else
          {
            if(!objCallbackParams)
            {
              objCallbackParams = { 'xmlhttp':xmlhttp };
            }
            else
            {
              objCallbackParams.xmlhttp = xmlhttp;
            }
            
            if( funcCallback )
            {
              funcCallback(objCallbackParams);
            }
            else
            {
              alert('http request done...');
            }
          }
        }
      }
      
      if( bDoPost )
      {
        xmlhttp.open("POST", strUrl, true);
        xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp.send(strRequestData);
      }
      else
      {
        xmlhttp.open("GET", strUrl+"?"+strRequestData, true);
        xmlhttp.send(null);
      }
    }
  }
}

XMLHttpRequest

I hate the term “ajax” and I mostly refuse to use it… So I will refrain here.

The following javascript provides the basic/core functionality for all modern web applications: the asynchronous HTTP request.

function doHttpRequest( strUrl, objRequestData, bDoPost, callback )
{
  var xmlhttp = new XMLHttpRequest();
  
  if(!xmlhttp)
  {
    alert('Your browser is too old... Sorry.');
  }
  else
  {
    var strRequestData = "";
    for( var strKey in objRequestData )
    {
      var strValue = objRequestData[strKey];
      
      if( strRequestData.length>0 )
      {
        strRequest += "&";
      }
      
      strRequestData += encodeURIComponent(strKey) + "=" + encodeURIComponent(strValue);
    }
    
    xmlhttp.onreadystatechange = function()
    {
      if(xmlhttp.readyState == 4)
      {
        if( xmlhttp.status!=200 )
        {
          alert("Error in HTTP Request...\n\nStatus: "+xmlhttp.status+"\n"+xmlhttp.statusText);
        }
        else
        {
          var cbParams = { 'xmlhttp':xmlhttp };
          callback(cbParams);
        }
      }
    }
    
    if( bDoPost )
    {
      xmlhttp.open("POST", strUrl, true);
      xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      xmlhttp.send(strRequestData);
    }
    else
    {
      xmlhttp.open("GET", strUrl+"?"+strRequestData, true);
      xmlhttp.send(null);
    }
  }
}

Anyway, that’s all mostly for my benefit. I usually end up having to write that stupid function from scratch once or twice a month…

You also may ask “why would you bother with the nuts-and-bolts like this when you could use some existing javascript library/framework?”. Well, there are people who use frameworks … and then there are the people who write the frameworks. There’s a lot more job security in the latter :)

wordpress theme updates

Try as I may to switch to another CMS, I keep falling back to WordPress. My last attempt was Drupal, which I quickly grew tired of. WordPress doesn’t do everything I would like it to do, but the things it does are amazing.

I’ve been working a bit on my theme to add some extra functionality. Here’s what we’ve gotten so far:

  • Three column format: My sidebar were getting a bit too long, so I split it in two.
  • Variable width layout: Because there are three columns, I needed a bit of extra horizontal space. I was getting tired of the fixed width/centered layout, so I implemented the variable width middle column to hold the main content.
  • HTML5: I’m using quite a few HTML5 specific tags in my theme, but everything should also be ok in older browsers too. It was a bit of a beast getting rid of the XHTML-ness that WordPress puts in automatically, but I’m getting there.
  • CSS 3: Some CSS 3 eye candy here and there (rounded corners, gradients, etc).

Now that the most current version of each of the major browsers support HTML5 and CSS 3, I’m going to be relying on them pretty heavily here.

And for those who refuse to upgrade … well, sorry :)

Comments? Smart remarks?

html5

I’m making at attempt to HTML5-ize the blog here.

It hasn’t been too difficult of a task thus far, but I’ve got a few little issues to work out. Check out the validation result:

Validation Report for http://cryptofreek.org

It looks like all my problem are related to bad attribute values in WordPress generated code. Anybody know how to fix that?

[UPDATE]
Looks like I’ve fixed most of those problems :)

But, there are multiple other issues with the “rel” attribute in other tags throughout the site. Perhaps I can find a way to just strip out all of those attributes until a better solution is found…