Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Asynchronous HTTP Comet architectures

An introduction to asynchronous, non-blocking HTTP programming

  • Print
  • Feedback

Page 6 of 7

The Comet pattern

Comet has popularized the asynchronous processing and non-blocking streaming of response data. Comet defines a Web architecture that enables the server to push events or data to the client without waiting for the client to request it. In essence, the Comet pattern is based on creating and maintaining long-lived HTTP connections.

Long-lived HTTP connections are required because the HTTP protocol is based on a request-response communication pattern. This means that data is only delivered to the client when specifically requested. An HTTP call is always initiated by the client. There is no direct way for the server to notify the client about events or to push data.

Polling is a simple solution to the need for server-initiated event notification. With polling, the client regularly asks the server if a new event has occurred. Based on the server response, the client is able to decide if further server requests are required. The event latency of this approach depends on the polling period. Increasing the polling rate reduces event latency. The downside of this is that frequent polling wastes system resources and scales poorly. Most polling calls return empty, with no new events having occurred.

The long poll is one of the two Comet strategies to reduce the frequency of HTTP calls. With a long poll, the HTTP call and the HTTP connection stay open until an event on the server side has occurred or a timeout is reached. Upon getting the response message on the client side, a new HTTP call will be opened immediately. Based on this approach the server is able to send events or to push data at any time. The downside is that open HTTP connections consume system resources such as socket buffers. Asynchronous message handling on both client and server side will help avoid outstanding threads.

The second Comet strategy is response body streaming. Like the long poll, the client in a streamed response implementation opens a long-running HTTP call. Instead of closing the HTTP connection after the response, the server keeps the response body stream open after sending the event or pushing the data. If further events occur on the server side, or data has to push, the open body stream will be used to transfer the data. Besides asynchronous message handling the client side should implement non-blocking input streaming to avoid outstanding threads. On the server side, user-managed output streaming is required to trigger the write operation on the application level.

An example of a Comet application

Listing 13 shows the JavaScript for an example Comet-style application that employs the forever frame, an iframe that receives script blocks and uses progressive rendering. The forever frame is one of several implementations that realize the streamed response strategy. That means the response body stream is closed after sending the response. The open body stream will be used to send the server-side triggered notifications.

The forever frame uses features of HTTP to incrementally deliver data through a hidden HTML iframe element. In Listing 13 only a paragraph ("text area") and a simple link will be printed out in the main browser window. Beside these visible elements, a hidden inline frame is also added to the page. Clicking the link sends a request to the server, which is answered by an (open body) response. Based on the target definition of the link the response will be handled in the context of the iframe. Instead of sending regular HTML text elements the server returns a JavaScript block. Because the browse renders HTML pages incrementally, the script block is executed as it is received. By returning a JavaScript block like parent.printTime("Sun Feb 10 10:05:20 CET 2008") the JavaScript function printTime() will be executed, which sets the paragraph content with the given time.

Listing 13. Forever Frame -- JavaScript example

<html>
  <head>
    <script language="Javascript">
      function printTime(time) {
         document.getElementById('text').innerHTML = time; 
      }
    </script>
  </head> 

  <body>
    <iframe id="iframe" name="iframe" width ="0" height ="0" border="0">
    </iframe>
    
    <a href ="time" target="iframe"/>start timer</a>
    <p id="text">0</p>
  </body>
</html>

Comet on the server side

As already mentioned, the client-side request will be answered by the server with an open-body response message. On the server side a JavaScript block is sent periodically to update the time on the browser page. Similar to Listing 8 a message header object is passed over after the send() method has been called. The returned body channel is then used to write the JavaScript block by a TimerTask.

Listing 14. Forever Frame -- server example

class ForeverFrameHandler implements IHttpRequestHandler {
                
   private final Timer timer = new Timer("timer", true);
        
        
   public void onRequest(HttpRequest req, IHttpResponseContext respCtx) throws IOException {
  
      // handle the time request
      if (req.getRequestURI().endsWith("/time")) {
                
         // write the message header by retrieving the body handle
         final BodyDataSink outChannel = respCtx.send(new HttpResponseHeader(200, "text/html"));

         // timer task definition                  
         TimerTask timerTask = new TimerTask() {
                
            public void run() {
               try {
                  String script = "<script>\r\n" +
                                  "  parent.printTime(\"" + new Date().toString() + "\");\r\n" +
                                  "</script>";
                  outChannel.write(script);
               } catch (IOException ioe) {
                  cancel();
                  try {
                     outChannel.close();
                  } catch (IOException ignore) { }
               }
            }      
         };
         
         
         // start the timer task 
         timer.schedule(timerTask, 0, 1000);
      }
      
      //..
   }
}

IServer server = new HttpServer(8080, new ForeverFrameHandler());
server.run();

Chunking

The message body content in Listing 14 is written using chunked transfer encoding. This feature of the HTTP/1.1 specification makes it possible to send content data without knowing when the end of the message will be reached. Chunked transfer encoding, or chunking, breaks the message content down into a series of blocks of data, which can be transmitted in chunks. Each chunk starts with a leading chunk size field. The zero size chunk indicates the end of the message, as you can see in Listing 15.

Listing 15. Chunked HTTP response

HTTP/1.1 200 OK
Content-Type: text/html; charset=iso-8859-1
Transfer-Encoding: chunked
Server: xSocket-http/2.0-alpha-3

47
<script>
  parent.printTime("Sun Feb 10 10:05:20 CET 2008");
</script>
47
<script>
  parent.printTime("Sun Feb 10 10:05:22 CET 2008");
</script>
47
<script>
  parent.printTime("Sun Feb 10 10:05:23 CET 2008");
</script>

[...]

47
<script>
  parent.printTime("Sun Feb 10 12:25:04 CET 2008");
</script>
47
<script>
  parent.printTime("Sun Feb 10 12:25:05 CET 2008");
</script>
0

  • Print
  • Feedback

Resources