Documents/uHTTP Lib Ajax

From Nutwiki
Jump to: navigation, search

MicroHTTP Library: Using Ajax

With Asynchronous JavaScript, data can be exchanged between the webserver and the webbrowser in the background. On this page I will explain, how to implement this technique with the MicroHTTP library.

HTML Document

The following HTML document is kept intentionally simple. It's body contains a div element named time and references an external Javascript.

<html>
  <head>
    <title>AJAX Sample</title>
  </head>
  <body>
    <div id="time"></div>
    <script type="text/javascript" src="script.js"></script>
  </body>
</html>

Javascript

The active part on the client's side is the following Javascript:

var req = GetRequestObject();
var wdog = 1;

window.onload = main();

function GetRequestObject()
{
  if(window.XMLHttpRequest) {
    return new XMLHttpRequest();
  }
  else if (window.ActiveXObject) {
    try {
      return new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        return new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) {
      }
    }
  } 
  alert('Ajax not available on this browser');
}

function RequestData()
{
  if (req.readyState == 0) {
    wdog++;
    req.open('GET', 'clock.cgi', true);
    req.onreadystatechange = DisplayTime;
    req.send(null);
  } else {
    req.onreadystatechange = function () { };
    req.abort();
    setTimeout("RequestData();", 100);
  }
}

function Watchdog() {
  if (wdog != 0) {
    wdog = 0;
  } else {
    req.onreadystatechange = function () { };
    req.abort();
    setTimeout("RequestData();", 100);
  }
  setTimeout("Watchdog();", 5000);
}

function DisplayTime()
{
  if(req.readyState == 4) {
    var response = eval('(' + req.responseText + ')');
    document.getElementById('time').innerHTML = response.time;
    RequestData();
  }
}

function main() {
  RequestData();
  Watchdog();
}

When loaded into the brwoser, the script is running in the background and periodically requests clock.cgi from the webserver. A watchdog function is used to automatically recover from lost connections.

Initially, Ajax used XML as its data exchange format. However, XML doesn't fit well in small embedded systems, so we will use JSON instead.

req.open('GET', 'clock.cgi', true);

sends a HTTP request the server to execute clock.cgi, which is expected to return the current time in JSON format

{ "time": "HH:MM:SS" }

which is then evaluated and displayed by

var response = eval('(' + req.responseText + ')');
document.getElementById('time').innerHTML = response.time;

As a result, the browser continuously displays the current time.

CGI Function

There is nothing special in the required CGI function:

static int CgiClock(HTTPD_SESSION *hs)
{
    time_t now;
    const struct _tm *ltm;

    NutSleep(1000);

    HttpSendHeaderTop(hs, 200);
    HttpSendHeaderBottom(hs, "text", "html", -1);

    time(&now);
    ltm = localtime(&now);
    s_printf(hs->s_stream, "{\"time\": \"%02d:%02d:%02d\"}\r\n", ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
    s_flush(hs->s_stream);

    return 0;
}

Just one problem may appear on most browser: Due to internal caching, the browser may request the CGI just once and satisfy further request with his cached data. To avoid this, we can insert an additional HTTP header to disable caching. Unfortunately, there is no unique way to achieve this. The following should work for all major webbrowsers.

HttpSendHeaderTop(hs, 200);
s_puts("Cache-Control: no-cache, must-revalidate\r\n", hs->s_stream);
s_puts("Content-Type: text/html; charset=iso-8859-1\r\n", hs->s_stream);
s_puts("Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n", hs->s_stream);
HttpSendHeaderBottom(hs, "text", "html", -1);

For the sake of completeness, here is the code to start the Ajax webserver.

StreamInit();
MediaTypeInitDefaults();
HttpRegisterCgiFunction("clock.cgi", CgiClock);
HttpRegisterMediaType("cgi", NULL, NULL, HttpCgiFunctionHandler);
StreamClientAccept(HttpdClientHandler, NULL);

Actually there is nothing new in here.

That's all for now

The MicroHTTP library offers several other features like redirecting, error handling etc. By the time I will try to add more to this documentation. Since then, you may refer to the uHttp samples and the API documentation that comes with the Nut/OS distribution.