Simple protocol for Ajax Push

From MemberWiki

Jump to: navigation, search

Problem Statement

Ajax Push/Comet provides notification from the server to the browser by making use of HTTP connections that remain blocked in a client read state for extended periods, but these connections cannot be used for additional HTTP traffic due to typical browser HTTP pipelining strategies. Since most browsers limit themselves to two connections per domain, the browser connection limit can quickly be exhausted by multiple Ajax Push/Comet applications, particularly when several applications are opened in several browser windows. The following protocol describes a mechanism for sharing a single blocking HTTP connection among all clients from that domain in a single browser.

Protocol Details

A GET or POST (POST should be used) to the URL

 http://<host>:<port>/openajax-push

blocks and returns an "&" separated (form encoded) list of clientIDs when content associated with those clientIDs is available. Individual clients within the browser then retrieve their content in a toolkit-dependent manner.

For example, notification that content for the clientIDs "1", "3", "5", and "7" is available is sent over the network as follows.

C: POST /openajax-push HTTP/1.1
C: Accept: */*
C: Cookie: BROWSERID=D8921099ED29BA291A4E1D02C9118CBA
C: Content-Type: application/x-www-form-urlencoded; charset=UTF-8
C: Content-Length: 0
C: Connection: keep-alive
C: Host: localhost:8080
C: 

... time passes ...

S: HTTP/1.1 200 OK
S: Content-Type: application/x-www-form-urlencoded;charset=UTF-8
S: Content-Length: 7
S: Date: Thu, 19 Apr 2007 16:09:53 GMT
S: Server: Apache-Coyote/1.1
S: 
S: 1&3&5&7

Within the browser, callbacks associated with clientIDs "1", "3", "5", and "7" will then be invoked, so that the individual clients can retrieve their messages by their own non-blocking mechanism. Individual Ajax toolkits are responsible for their own reliability and ordering properties. The simple protocol described in this document provides only notification that updated content may be available. Note that these callbacks must be propagated from one window to another, so an inter-window communication mechanism is required, such as in-browser cookie polling.

BROWSERID may be specified in the searchpart (or "query string") but should be specified in a cookie. A cookie is preferred, as there must be a single, unique, BROWSERID per browser per host due to the prevalent browser two-connection limit.

The application is responsible for obtaining a clientID from the PushServer so that subsequent content associated with the application (page updates or changed data) can be retrieved. (This simplifies the protocol by effectively allowing subscription to notifications to occur on the server.)

The application includes the following procedure when rendering pages:

 1. Check for BROWSERID
 2. If an BROWSERID is not found, obtain one from the PushServer 
    and set the BROWSERID cookie
 3. Obtain PushServer instance using BROWSERID
 4. Obtain clientID for the page (or page fragment)
 5. Encode clientID in page

A PushServer can be disposed of when no notification requests have been received within a defined time interval.

The Ajax page initializes in the browser as follows:

 1. Check if a notification connection is open
 2. If a connection is not open, wait for notifications on
    http://host/openajax-push
 2. When notifications are received, clear notification status for
    each clientID and ensure callback associated with a given
    clientID is invoked

(There are a variety of ways to implement the in-browser cookie polling mechanism; it's not clear if it should be described in this document.)

The Ajax page interacts with the server in response to a callback as follows:

 1. Ajax application uses clientID to obtain associated content
    from the server in a toolkit-dependent manner

A possible server-side API supporting this functionality is as follows:

org.openajax.push.PushServer {
    public static String getBrowserID();
    public static PushServer getPushServer(String browserID);
    public String openClient();
    public String closeClient(String clientID);
    public void notifyClient(String clientID);
      /* can throw ClientNotBound exception for invalid clientID */
}

A possible client-side API supporting this functionality is as follows:

OpenAjax.registerPushClient = function (
        clientID,  // String
        callback ) // callback function for notification

Future Directions

As specified, the protocol is not efficient as the delivery of a single message requires two HTTP requests, one to deliver the notification that a message is available, and another to deliver the message itself. The protocol will be enhanced so that messages destined for the requesting window can be delivered directly as notifications. For instance, if the push notification is sent as a cookie value (reasonable, since the number of clients receiving updates at any one time should be small) a message in any desired format could be delivered at the same time as the body of the response.

In essence, this is a workaround for browser limitations. For instance, if the browser connection limit was two connections per frame rather than two connections per browser, the JavaScript implementation could be vastly simplified, bringing the necessity to share an HTTP connection only between Ajax clients inside a common frame. Within a single frame, Ajax clients can communicate directly through JavaScript APIs, such as the existing OpenAjax hub.

However, there are still efficiency benefits to be obtained from making use of a single push channel, even among multiple windows. Here, an efficient cross-window communication mechanism could greatly help the JavaScript implementation, such as

http://www.whatwg.org/specs/web-apps/current-work/#crossDocumentMessages