Channels ▼
RSS

Web Development

Sessioning with XMLHttpRequest

Source Code Accompanies This Article. Download It Now.


January, 2006: Sessioning with XMLHttpRequest

Dionysios is a senior web engineer at the Network Management Center of the NTUA (http://www.noc.ntua.gr/) and a freelance consultant. His research interests concern the area of hypermedia with emphasis on architecture, multichannel access, and the Semantic Web. He can be contacted at synodinos@gmail.com.


At some point, most web developers have faced the problem of clients needing to periodically connect to the server with both the client and the server being aware of whether users are connected, then updating the user interface accordingly. In the traditional interface model for web applications, this requires many frames—some hidden—that refresh using metatags or JavaScript, faceless applets that open sockets to the server, or proprietary plug-ins. These solutions are far from elegant and their side effects are enough to make you think about changing the specifications to avoid tackling the problem.

For instance, a challenge I recently faced involved developing a web application that would serve as a session-management mechanism between a Wi-Fi server and users connected to the wireless network. The browser was the only software I could use on the client side, and because the user group was diverse, it was not realistic to require a specific browser. Users would log on via a web form, and a popup window would tell them how long they'd been connected and so on. If this window was terminated, the connection was lost. The most important thing was that this window would have to periodically connect to the server using HTTP to inform of the user's presence and refresh with new information. The reason for that was that because users could theoretically stay online forever, the system needed to know exactly which users were online so that resources would be allocated in an optimized way. Also, this would facilitate a billing method according to how long users stayed online, not on how many kilobytes were downloaded (as is usually done).

In this article, I present a solution based on the XMLHttpRequest object that provides you with a way to use HTML and JavaScript to connect the presentation layer directly to XML data for interim updates without reloading the HTML page. In this way, web clients can retrieve and submit XML data directly, all in the background. To convert retrieved XML data into HTML content, you can rely on the client-side Document Object Model (DOM) to read the XML document node tree and compose HTML elements.

Granted, similar functionality is covered in a proposed W3C Standard, Document Object Model (DOM) Level 3 Load and Save Specification (http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/). This specification defines a platform- and language-neutral interface that lets programs and scripts dynamically load the content of an XML document into a DOM document and serialize a DOM document into an XML document. It also allows filtering of content at load time and at serialization time. Specifically, the LSInput interface represents an input source for data. This interface lets an application encapsulate information about an input source in a single object—a public identifier, system identifier, byte stream, base URI, and/or a character stream—thereby facilitating client-server communication using XML.

Microsoft first implemented this kind of object as an ActiveX object in Internet Explorer 5 for Windows. It is included in XMLHTTP, a set of APIs that lets you send/retrieve data to/from remote web servers using its underlying HTTP protocols and methods. It can be thought of as an XML-based response/request protocol. The server processes the request, and in turn, returns either a text/XML data stream or a binary stream of characters. The XMLHTTP Application Programming Interface is exposed through Microsoft's XML library (MSXML), a library for ASP, Visual Basic, and C++ developers. A client computer can use the XMLHTTP object to send an arbitrary HTTP request, receive the response, and have the Microsoft XML DOM parse that response. This object is integrated with Microsoft XML Core Services (MSXML) to support sending the request body directly from, and parsing the response directly into, the MSXML DOM objects. When combined with the support for Extensible Stylesheet Language (XSL), the XMLHTTP component provides an easy way to send structured queries to HTTP servers and efficiently displays the results with a variety of presentations.

It is also featured in Mozilla's XML Extras module, which contains several features that let you treat XML as data, not as just another document format. The module is structured as a drop-in component and exposes its XML-as-data features both to JavaScript and C++/XPCOM users. The XML Extras module is built by default on all platforms.

Table 1 lists differences in the support of XMLHttpRequest between Mozilla and Microsoft software. The differences in Table 1 force you to use a branching technique for cross-platform compatibility. According to this technique, certain functions need to branch their code, with browser-specific code in each branch. As is obvious in Table 1, the XMLHttpRequest creation is a sure candidate for branching.

XMLHttpResponse Foundations

To illustrate, Listing One creates a minimal HTML skeleton page that imports my main JavaScript file and presents users with a decent interface. Listing One produces a result similar to Figure 1, which presents the sessioning information to users, and begins (onLoad event) the execution of the doLoad() function that is responsible for the XML-based communication with the server.

The information presented to users is primarily generated in the form of XML whenever Listing Two (SESSION_DATA.php) is executed at the server. It is essential that the data returned from the server be sent with a Content-Type set to text/xml. Content that is sent as text/plain or text/html is not accepted by the instance of the request object.

The execution of Listing Two generates a server-side response similar to Figure 2. The most important thing to notice here is that the HTTP response code is "200," which means that the request has succeeded and that there is a content-type header indicating the response is XML.

CheckSession.js

For the JavaScript code, you need to define one variable to represent the time period between checking for connectivity (polling interval) and the time that has passed since the last check; see Listing Three(a). The definition of doLoad() in Listing Three(b) is straightforward. The first time it is loaded, it checks immediately for connectivity, then sets repeated checks. Also, it engages a clock, Counter() (Listing Three[c]), which visually informs users of how long until the next session check. The CheckSession() function in Listing Three(d) calls on loadXMLDoc() and catches any exceptions that might be raised (for example, "server XML response not valid").

Listing Four(a) includes branched object creation, event handler assignment, and submission of a GET request. A single function argument is a string containing the desired URL. The function processReqChange(), Listing Four(b), is simply an onreadystatechange event handler function that allows processing of the response content only if all conditions are met.

Finally, after the client request has been processed and the information has been received, you need to extract from the XML response the elements you need and update the user interface accordingly. This is the responsibility of printSuccess() and printFailure() in Listing Five.

Because the mechanism I describe here defines a simple response/request protocol, it could be optimized to fit certain characteristics. For example, if the connection is lost, the polling can become more frequent, or the script could poll a variety of servers using a round robin or other technique. Also, the information presented to users can become more detailed and the model could be adopted in a different context and acquire more features.

Conclusion

As CSS support continues to grow in contemporary browsers, more and more sophisticated user interfaces will become available. The only piece missing from the puzzle has been a cross-platform means of maintaining a connection with the server so that transactions can occur in the background. XMLHttpRequest fills that void in a way that will have a profound impact on the web-user-interface-design paradigm for years to come.

DDJ



Listing One

<html>
<head>
<title>Check session</title>
<script src="CheckSession.js" language="javascript" 
                                             type="text/javascript"></script>
   <style>
   .label {display:inline; font-weight:bold}
   .msg {display:inline; font-weight:normal}
</style>
</head>
<body onLoad="doLoad();">
<div class="label">Your session is: </div>
<div id="session" class="msg">initializing</div><br>
<div class="label">You are located at the: </div>
<div id="location" class="msg">initializing</div><br>
<div class="label">Signal strength: </div>
<div id="signal_strength" class="msg">initializing</div><br>
<div class="label">Signal quality:</div>
<div id="signal_quality" class="msg">initializing</div><br>
<div class="label">Bytes in: </div>
<div id="bytes_in" class="msg">initializing</div><br>
<div class="label">Bytes out: </div>
<div id="bytes_out" class="msg">initializing</div><br>
<div class="label">Refreshing in: </div>
<div id="refreshing" class="msg">
   <form name="counter"  class="msg">
      <input type="text" name="seconds" value="0" size="3">
   </form>
</div>
</body>
Back to article


Listing Two
<?php
header('Content-Type: text/xml');

// Do the necessary calculation to produce $location, $signal_strength, 
// $signal_quality, $bytes_in and $bytes_out
?>
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
<CheckSessionParameters>
    <location><?php echo $location></location>
    <signal_strength><?php echo $signal_strength></signal_strength>
    <signal_quality><?php echo $signal_quality></signal_quality>
    <bytes_in><?php echo $bytes_in></bytes_in>
    <bytes_out><?php echo $bytes_out></bytes_out>
</CheckSessionParameters>
Back to article


Listing Three
(a)
var period = 300; 
var elapsed = 0; 
(b)
function doLoad() {
   CheckSession();
   setInterval( "CheckSession()", period*1000 );
   setInterval( "Counter()", 1000 );
}
(c)
function Counter() {
   if (period-elapsed) {
      elapsed = elapsed + 1 ;
      document.counter.seconds.value = (period-elapsed);
   } else {
      elapsed = 0;
   }
}
(d)
function CheckSession() {
    try {
        loadXMLDoc("SESSION_DATA.php");
    } catch(e) {
       alert("Error: " + e);
    }
}
Back to article


Listing Four
(a)
function loadXMLDoc(url) {
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
        req.onreadystatechange = processReqChange;
   req.open("GET", url, true);
   req.send(null);
    } else if (window.ActiveXObject) {
        isIE = true;
            req = new ActiveXObject("Microsoft.XMLHTTP");
        if (req) {
        req.onreadystatechange = processReqChange;
        req.open("GET", url, true);
        req.send();
     }
    }
}
(b)
function processReqChange() {
    // only if req shows "loaded"
    if (req.readyState == 4) {
        // only if "OK"
   if (req.status == 200) {
            printSuccess();
        } else {
            printFailure();
        }
  }
}
Back to article


Listing Five
function printSuccess() {
    res = req.responseXML;
    document.getElementById("session").lastChild.nodeValue = 'Active';
    document.getElementById("location").lastChild.nodeValue =
        res.getElementsByTagName('location')[0].firstChild.nodeValue;
    document.getElementById("signal_strength").lastChild.nodeValue =
        res.getElementsByTagName('signal_strength')[0].firstChild.nodeValue;
    document.getElementById("signal_quality").lastChild.nodeValue =
        res.getElementsByTagName('signal_quality')[0].firstChild.nodeValue;
    document.getElementById("bytes_in").lastChild.nodeValue =
        res.getElementsByTagName('bytes_in')[0].firstChild.nodeValue;
    document.getElementById("bytes_out").lastChild.nodeValue =
        res.getElementsByTagName('bytes_out')[0].firstChild.nodeValue;
    elapsed = 0;
}
function printFailure() {
    res = req.responseXML;
document.getElementById("session").lastChild.nodeValue =   
'Inactive';
document.getElementById("location").lastChild.nodeValue = 
"Unknown";  
document.getElementById("signal_strength").lastChild.nodeValue = 
"No signal";
document.getElementById("signal_quality").lastChild.nodeValue = 
"No signal";
    elapsed = 0;
}
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.