Marcin Lulek avatar Marcin Lulek committed ab28262

stubs of websocket support + example client implementation based on dojo 1.8 AMD style

Comments (0)

Files changed (6)

gevent_cometd/app_views.py

     import simplejson as json
 
 log = logging.getLogger(__name__)
+logging.basicConfig(level=logging.DEBUG)
 
 def garbage_control():
     """garbage control loop"""
     while True:
         gevent.sleep(1)
-        #lock both users and channels
+        # lock both users and channels
         with users_lock:
             with channels_lock:
-            #garbage control connections
+            # garbage control connections
                 try:
                     gc_conns = user.Connection.gc_pass()
                     for user_name, conns in gc_conns.items():
-                        #now remove conn ids from channels
+                        # now remove conn ids from channels
                         for channel_inst in gevent_cometd.channels.values():
                             for conn_id in conns:
-                                if (user_name in channel_inst.connections and 
+                                if (user_name in channel_inst.connections and
                                     conn_id in channel_inst.connections[user_name]):
                                     channel_inst.connections[user_name].remove(conn_id)
-                            #remove empty user key from channel conns
-                            #first remove the channel name from user object
+                            # remove empty user key from channel conns
+                            # first remove the channel name from user object
                             user_inst = user.User.by_name(user_name)
                             if user_inst and channel_inst.name in user_inst.channels:
                                 user_inst.channels.remove(channel_inst.name)
-                            #remove user key from channel connections
+                            # remove user key from channel connections
                             if user_name in channel_inst.connections and not channel_inst.connections[user_name]:
                                 channel_inst.connections.pop(user_name, None)
                     # now make pass over users and remove user objects that weren't active for at least 1 day
                 except KeyboardInterrupt, e:
                     raise
 
-#start our gc loop
+# start our gc loop
 gevent.spawn(garbage_control)
 
 
                 # everything is ok so lets add new connection to channel and connection list
                 user_inst, connection = user.User.add_connection(user_name, conn_id, user_status)
                 for channel_name in subscribe_to_channels:
-                    #user gets assigned to a channel
+                    # user gets assigned to a channel
                     channel.Channel.get_channel(channel_name).add_connection(user_inst, connection)
         res = Response(json.dumps({'conn_id':connection.id, 'status':user_inst.status}), request=request)
+        log.info('connecting %s with uuid %s' % (user_name, connection.id))
     return res
 
 def disconnect(request, *args):
     if res:
         return res
     conn_id = request_data['conn_id'] if request_data else request.GET.get('conn_id')
+    log.info('recycling %s' % conn_id)
     connection = user.Connection.by_id(conn_id)
     if connection:
         connection.mark_for_gc()
         # lets lock it just in case
         with users_lock:
             with channels_lock:
-                #find the right user
+                # find the right user
                 user_inst = user.User.by_name(connection.user)
                 if user_inst:
                     for channel_name in subscribe_to_channels:
 
 def siege(request, *args):
     raise Exception('disabled')
-    #siege test
+    # siege test
     import uuid
     import random
     with users_lock:
         req_channels = gevent_cometd.channels.keys()
     else:
         req_channels = request_data['channels']
-    #return requested channel info
+    # return requested channel info
     for channel_inst in [chan for chan in gevent_cometd.channels.values() if chan.name in req_channels]:
         json_data["channels"][channel_inst.name] = {}
         json_data["channels"][channel_inst.name]['total_users'] = len(channel_inst.connections)
     user_inst = user.User.by_name(connection.user)
     user_inst.last_active = datetime.datetime.utcnow()
     connection.last_active = datetime.datetime.utcnow()
+
+    websocket = request.environ.get("wsgi.websocket")
+    if websocket is None:
+        print 'NOT WEBSOCKET'
+    else:
+        print 'WEBSOCKET'
+
     if request.method.upper() == 'OPTIONS':
-        #preflight
+        # preflight
         res = Response(' ' * 2048, request=request,
                        content_type='application/json')
         util.add_cors_headers(res)

gevent_cometd/start.py

 import logging
 import optparse
 
+from geventwebsocket.handler import WebSocketHandler
 from gevent_cometd import config
 from webob import Request, exc
 
 
 
 class WSGIRouter(object):
-        
+
     def __call__(self, environ, start_response):
         """ try to find the right request"""
         req = Request(environ)
 
-        #pop the / from path
+        # pop the / from path
         query_parts = req.path[1:].split('/')
         action = query_parts[0]
         if not action:
             action = 'index'
 
-        #try to find the action
+        # try to find the action
         try:
             method = getattr(app_views, action)
         except AttributeError:
             # The exception object itself is a WSGI application/response:
             res = e
 
-        #return proper response
-        return res(environ, start_response)       
+        # return proper response
+        return res(environ, start_response)
 
 def cli_start():
     parser = optparse.OptionParser()
     config['secret'] = options.secret
     config['admin_secret'] = options.admin_secret
     config['allow_posting_from'] = [ip.strip() for ip in options.allow_posting_from.split(',')]
-    print 'Serving on 0.0.0.0:%s' % config['port'] 
+    print 'Serving on 0.0.0.0:%s' % config['port']
     app = WSGIRouter()
-    pywsgi.WSGIServer(('', config['port']), app, log=None).serve_forever()
+    pywsgi.WSGIServer(('', config['port']), app, log=None, 
+                      handler_class=WebSocketHandler).serve_forever()

gevent_cometd/static/client.js

-
-var WebSocket = window.WebSocket;
-
-function Socket(/*dojo.__XhrArgs*/ argsOrUrl){
-    // summary:
-    //      Provides a simple socket connection using WebSocket, or alternate
-    //      communication mechanisms in legacy browsers for comet-style communication. This is based
-    //      on the WebSocket API and returns an object that implements the WebSocket interface:
-    //      http://dev.w3.org/html5/websockets/#websocket
-    //  description:
-    //      Provides socket connections. This can be used with virtually any Comet protocol.
-    //  argsOrUrl:
-    //      This uses the same arguments as the other I/O functions in Dojo, or a
-    //      URL to connect to. The URL should be a relative URL in order to properly
-    //      work with WebSockets (it can still be host relative, like //other-site.org/endpoint)
-    // returns:
-    //      An object that implements the WebSocket API
-    // example:
-    //      | dojo.require("dojox.socket");
-    //      | var socket = dojox.socket({"//comet-server/comet");
-    //      | // we could also add auto-reconnect support
-    //      | // now we can connect to standard HTML5 WebSocket-style events
-    //      | dojo.connect(socket, "onmessage", function(event){
-    //      |    var message = event.data;
-    //      |    // do something with the message
-    //      | });
-    //      | // send something
-    //      | socket.send("hi there");
-    //      | whenDone(function(){
-    //      |   socket.close();
-    //      | });
-    //      You can also use the Reconnect module:
-    //      | dojo.require("dojox.socket");
-    //      | dojo.require("dojox.socket.Reconnect");
-    //      | var socket = dojox.socket({url:"/comet"});
-    //      | // add auto-reconnect support
-    //      | socket = dojox.socket.Reconnect(socket);
-    if(typeof argsOrUrl == "string"){
-        argsOrUrl = {url: argsOrUrl};
-    }
-    return WebSocket ? dojox.socket.WebSocket(argsOrUrl, true) : dojox.socket.LongPoll(argsOrUrl);
-};
-dojox.socket = Socket;
-
-Socket.WebSocket = function(args, fallback){
-    // summary:
-    //      A wrapper for WebSocket, than handles standard args and relative URLs
-    var ws = new WebSocket(new dojo._Url(document.baseURI.replace(/^http/i,'ws'), args.url));
-    ws.on = function(type, listener){
-        ws.addEventListener(type, listener, true);
-    };
-    var opened;
-    dojo.connect(ws, "onopen", function(event){
-        opened = true;
-    });
-    dojo.connect(ws, "onclose", function(event){
-        if(opened){
-            return;
-        }
-        if(fallback){
-            Socket.replace(ws, dojox.socket.LongPoll(args), true);
-        }
-    });
-    return ws;
-};
-Socket.replace = function(socket, newSocket, listenForOpen){
-    // make the original socket a proxy for the new socket
-    socket.send = dojo.hitch(newSocket, "send");
-    socket.close = dojo.hitch(newSocket, "close");
-    if(listenForOpen){
-        proxyEvent("open");
-    }
-    // redirect the events as well
-    dojo.forEach(["message", "close", "error"], proxyEvent);
-    function proxyEvent(type){
-        (newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){
-            var newEvent = document.createEvent("MessageEvent");
-            newEvent.initMessageEvent(event.type, false, false, event.data, event.origin, event.lastEventId, event.source);
-            socket.dispatchEvent(newEvent);
-        }, true);
-    }
-};
-Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
-    // summary:
-    //      Provides a simple long-poll based comet-style socket/connection to a server and returns an
-    //      object implementing the WebSocket interface:
-    //      http://dev.w3.org/html5/websockets/#websocket
-    //  args:
-    //      This uses the same arguments as the other I/O functions in Dojo, with this addition:
-    //  args.interval:
-    //      Indicates the amount of time (in milliseconds) after a response was received
-    //      before another request is made. By default, a request is made immediately
-    //      after getting a response. The interval can be increased to reduce load on the
-    //      server or to do simple time-based polling where the server always responds
-    //      immediately.
-    //  args.transport:
-    //      Provide an alternate transport like dojo.io.script.get
-    // returns:
-    //      An object that implements the WebSocket API
-    // example:
-    //      | dojo.require("dojox.socket.LongPoll");
-    //      | var socket = dojox.socket.LongPoll({url:"/comet"});
-    //      or:
-    //      | dojo.require("dojox.socket.LongPoll");
-    //      | dojox.socket.LongPoll.add();
-    //      | var socket = dojox.socket({url:"/comet"});
-
-var cancelled = false,
-        first = true,
-        timeoutId,
-        connections = [];
-    
-    // create the socket object
-    var socket = {
-        send: function(data){
-            // summary:
-            //      Send some data using XHR or provided transport
-            var sendArgs = dojo.delegate(args);
-            sendArgs.rawBody = data;
-            clearTimeout(timeoutId);
-            var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
-                socket.transport(sendArgs);
-            connections.push(deferred);
-            deferred.then(function(response){
-                // got a response
-                socket.readyState = 1;
-                // remove the current connection
-                connections.splice(dojo.indexOf(connections, deferred), 1);
-                // reconnect to listen for the next message if there are no active connections,
-                // we queue it up in case one of the onmessage handlers has a message to send
-                if(!connections.length){
-                    timeoutId = setTimeout(connect, args.interval);
-                }
-                if(response){
-                    // now send the message along to listeners
-                    fire("message", {data: response}, deferred);
-                }
-            }, function(error){
-                connections.splice(dojo.indexOf(connections, deferred), 1);
-                // an error occurred, fire the appropriate event listeners
-                if(!cancelled){
-                    fire("error", {error:error}, deferred);
-                    if(!connections.length){
-                        socket.readyState = 3;
-                        fire("close", {wasClean:false}, deferred);
-                    }
-                }
-            });
-            return deferred;
-        },
-        close: function(){
-            // summary:
-            //      Close the connection
-            socket.readyState = 2;
-            cancelled = true;
-            for(var i = 0; i < connections.length; i++){
-                connections[i].cancel();
-            }
-            socket.readyState = 3;
-            fire("close", {wasClean:true});
-        },
-        transport: args.transport || dojo.xhrPost,
-        args: args,
-        url: args.url,
-        readyState: 0,
-        CONNECTING: 0,
-        OPEN: 1,
-        CLOSING: 2,
-        CLOSED: 3,
-        dispatchEvent: function(event){
-            fire(event.type, event);
-        },
-        on: function(type, callback){
-            return dojo.connect(this, "on" + type, callback);
-        },
-        firstRequest: function(args){
-            // summary:
-            //      This allows for special handling for the first request. This is useful for
-            //      providing information to disambiguate between the first request and
-            //      subsequent long-poll requests so the server can properly setup a
-            //      connection on the first connection or reject a request for an expired
-            //      connection if the request is not expecting to be the first for a connection.
-            //      This method can be overriden. The default behavior is to include a Pragma
-            //      header with a value of "start-long-poll"
-            var headers = (args.headers || (args.headers = {}));
-            headers.Pragma = "start-long-poll";
-            try{
-                return this.transport(args);
-            }finally{
-                // cleanup the header so it is not used on subsequent requests
-                delete headers.Pragma;
-            }
-        }
-    };
-    function connect(){
-        if(socket.readyState == 0){
-            // we fire the open event now because we really don't know when the "socket"
-            // is truly open, and this gives us a to do a send() and get it included in the
-            // HTTP request
-            fire("open",{});
-        }
-        // make the long-poll connection, to wait for response from the server
-        if(!connections.length){
-            socket.send();
-        }
-    }
-    function fire(type, object, deferred){
-        console.log('fire event');
-        console.log(dojo.isIE);
-        if(socket["on" + type]){
-            if(dojo.isIE && dojo.isIE < 9){
-                console.log('ie 8');
-                var event = {};
-            }
-            else{
-                console.log('non ie 8');
-                var event = document.createEvent("HTMLEvents");
-                event.initEvent(type, false, false);
-            }
-            dojo.mixin(event, object);
-            event.ioArgs = deferred && deferred.ioArgs;
-            socket["on" + type](event);
-        }
-    }
-    // provide an alias for Dojo's connect method
-    socket.connect = socket.on;
-    // do the initial connection
-    setTimeout(connect);
-    return socket;
-};
-
-dojo.require('dojox.socket.Reconnect');
-dojo.require('dojox.io.xhrPlugins');
-dojo.require('dojo.io.script');
-
-// webapp_url, server_url are globals set in html - for demo purposes
-
-// this is our actual code everything above is just hacked dojo socket to work with ie < 8
-
-
-var conn_id = null;
-var socket = null;
-var messages_node = null;
-//callbacks
-
-var jsonp_call = function(args, message) {
-    // The parameters to pass to xhrGet, the url, how to handle it, and the
-    // callbacks.
-    var jsonpArgs = {
-        url : args.url,
-        callbackParamName : "callback",
-        content : {}
-    };
-    var deferred = dojo.io.script.get(jsonpArgs);
-    return deferred;
-};
-
-var channel_message_callback = function(entry) {
-    console.log('received message !');
-    var li = dojo.create('li', null, messages_node);
-    dojo.create('strong', {
-        innerHTML : 'channel:' + entry.channel + ' '
-    }, li);
-    dojo.create('strong', {
-        innerHTML : 'user: ' + entry.user + ' '
-    }, li);
-    dojo.create('em', {
-        innerHTML : entry.message
-    }, li);
-}
-
-var presence_callback = function(entry) {
-    console.log('received presence message !');
-    var li = dojo.create('li', null, messages_node);
-    dojo.create('em', {
-        innerHTML : 'user:' + entry.user + ' joined channel: ' + entry.channel
-    }, li);
-}
-
-//lets connect this together
-
-init = function() {
-    dojox.io.xhrPlugins.addCrossSiteXhr("");
-    messages_node = dojo.query('.messages')[0];
-    var req = {
-        'user' : Math.random(),
-        'channels' : [ 'pub_chan', 'pub_chan2' ]
-    }
-
-    var post_conf = {
-        url : webapp_url+"/connect",
-        handleAs : 'json',
-        postData : dojo.toJson(req, true),
-        load : function(data) {
-            console.log('attempting to create socket');
-            conn_id = data.conn_id;
-            var socket_conf = {
-                url : server_url+"/listen?conn_id=" + data.conn_id,
-                handleAs : 'json',
-                headers : {
-                    "Content-Type" : "application/json"
-                }
-            }
-            if (dojo.isOpera || (dojo.isIE && dojo.isIE < 9)) {
-                socket_conf.transport = jsonp_call;
-            }
-            socket = dojox.socket.LongPoll(socket_conf);
-            socket = dojox.socket.Reconnect(socket, {
-                reconnectTime : 1000
-            });
-            console.log('created socket with reconnect capability');
-            socket.on("message", function(event) {
-                dojo.forEach(event.data, function(entry) {
-                    // do something with the data from the server
-                    if (entry.type == 'message') {
-                        channel_message_callback(entry)
-                    } else if (entry.type == 'join') {
-                        presence_callback(entry);
-                    }
-                });
-            });
-            socket.reconnect_timout = null;
-            socket.on("error", function(event) {
-                if(socket.reconnect_timout){
-                    clearTimeout(socket.reconnect_timout);
-                }
-                socket.reconnect_timout = setTimeout(function(){
-                    console.log('reconnected');
-                    var post_conf = {
-                            url : webapp_url+"/connect",
-                            handleAs : 'json',
-                            postData : dojo.toJson(req, true),
-                            headers : {
-                                "Content-Type" : "application/json"
-                            },
-                            load : function(data) {
-                                socket.args.url = server_url+"/listen?conn_id=" + data.conn_id;
-                            }
-                        };
-                        var deferred = dojo.rawXhrPost(post_conf);
-                }, 500); // try to collapse multiple reconnects into 1
-            });
-            console.log('added events to socket');
-            var p_conf = {
-                    url : webapp_url+"/subscribe",
-                    handleAs : 'json',
-                    postData : dojo.toJson({conn_id:data.conn_id,channels:['test_channel']}, true),
-                    headers : {
-                        "Content-Type" : "application/json"
-                    },
-                    load : function(data) {
-                        console.log('subscribed to additional test channel after initial connection');
-                        return true;
-                    }
-                }
-                var deferred = dojo.rawXhrPost(p_conf);
-            
-        }
-    };
-    console.log('connecting to get UID of connection');
-    var deferred = dojo.rawXhrPost(post_conf);
-
-}
-dojo.addOnLoad(init);
-
-dojo.addOnUnload(function() {
-    var req = {
-        'conn_id' : conn_id,
-    }
-    var post_conf = {
-        url : server_url+"/disconnect",
-        handleAs : 'json',
-        postData : dojo.toJson(req, true),
-        headers : {
-            "Content-Type" : "application/json"
-        },
-        load : function(data) {
-            return true;
-        }
-    }
-    var deferred = dojo.rawXhrPost(post_conf);
-});

gevent_cometd/static/client_1.6.js

+
+var WebSocket = window.WebSocket;
+
+function Socket(/*dojo.__XhrArgs*/ argsOrUrl){
+    // summary:
+    //      Provides a simple socket connection using WebSocket, or alternate
+    //      communication mechanisms in legacy browsers for comet-style communication. This is based
+    //      on the WebSocket API and returns an object that implements the WebSocket interface:
+    //      http://dev.w3.org/html5/websockets/#websocket
+    //  description:
+    //      Provides socket connections. This can be used with virtually any Comet protocol.
+    //  argsOrUrl:
+    //      This uses the same arguments as the other I/O functions in Dojo, or a
+    //      URL to connect to. The URL should be a relative URL in order to properly
+    //      work with WebSockets (it can still be host relative, like //other-site.org/endpoint)
+    // returns:
+    //      An object that implements the WebSocket API
+    // example:
+    //      | dojo.require("dojox.socket");
+    //      | var socket = dojox.socket({"//comet-server/comet");
+    //      | // we could also add auto-reconnect support
+    //      | // now we can connect to standard HTML5 WebSocket-style events
+    //      | dojo.connect(socket, "onmessage", function(event){
+    //      |    var message = event.data;
+    //      |    // do something with the message
+    //      | });
+    //      | // send something
+    //      | socket.send("hi there");
+    //      | whenDone(function(){
+    //      |   socket.close();
+    //      | });
+    //      You can also use the Reconnect module:
+    //      | dojo.require("dojox.socket");
+    //      | dojo.require("dojox.socket.Reconnect");
+    //      | var socket = dojox.socket({url:"/comet"});
+    //      | // add auto-reconnect support
+    //      | socket = dojox.socket.Reconnect(socket);
+    if(typeof argsOrUrl == "string"){
+        argsOrUrl = {url: argsOrUrl};
+    }
+    return WebSocket ? dojox.socket.WebSocket(argsOrUrl, true) : dojox.socket.LongPoll(argsOrUrl);
+};
+dojox.socket = Socket;
+
+Socket.WebSocket = function(args, fallback){
+    // summary:
+    //      A wrapper for WebSocket, than handles standard args and relative URLs
+    var ws = new WebSocket(new dojo._Url(document.baseURI.replace(/^http/i,'ws'), args.url));
+    ws.on = function(type, listener){
+        ws.addEventListener(type, listener, true);
+    };
+    var opened;
+    dojo.connect(ws, "onopen", function(event){
+        opened = true;
+    });
+    dojo.connect(ws, "onclose", function(event){
+        if(opened){
+            return;
+        }
+        if(fallback){
+            Socket.replace(ws, dojox.socket.LongPoll(args), true);
+        }
+    });
+    return ws;
+};
+Socket.replace = function(socket, newSocket, listenForOpen){
+    // make the original socket a proxy for the new socket
+    socket.send = dojo.hitch(newSocket, "send");
+    socket.close = dojo.hitch(newSocket, "close");
+    if(listenForOpen){
+        proxyEvent("open");
+    }
+    // redirect the events as well
+    dojo.forEach(["message", "close", "error"], proxyEvent);
+    function proxyEvent(type){
+        (newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){
+            var newEvent = document.createEvent("MessageEvent");
+            newEvent.initMessageEvent(event.type, false, false, event.data, event.origin, event.lastEventId, event.source);
+            socket.dispatchEvent(newEvent);
+        }, true);
+    }
+};
+Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
+    // summary:
+    //      Provides a simple long-poll based comet-style socket/connection to a server and returns an
+    //      object implementing the WebSocket interface:
+    //      http://dev.w3.org/html5/websockets/#websocket
+    //  args:
+    //      This uses the same arguments as the other I/O functions in Dojo, with this addition:
+    //  args.interval:
+    //      Indicates the amount of time (in milliseconds) after a response was received
+    //      before another request is made. By default, a request is made immediately
+    //      after getting a response. The interval can be increased to reduce load on the
+    //      server or to do simple time-based polling where the server always responds
+    //      immediately.
+    //  args.transport:
+    //      Provide an alternate transport like dojo.io.script.get
+    // returns:
+    //      An object that implements the WebSocket API
+    // example:
+    //      | dojo.require("dojox.socket.LongPoll");
+    //      | var socket = dojox.socket.LongPoll({url:"/comet"});
+    //      or:
+    //      | dojo.require("dojox.socket.LongPoll");
+    //      | dojox.socket.LongPoll.add();
+    //      | var socket = dojox.socket({url:"/comet"});
+
+var cancelled = false,
+        first = true,
+        timeoutId,
+        connections = [];
+    
+    // create the socket object
+    var socket = {
+        send: function(data){
+            // summary:
+            //      Send some data using XHR or provided transport
+            var sendArgs = dojo.delegate(args);
+            sendArgs.rawBody = data;
+            clearTimeout(timeoutId);
+            var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
+                socket.transport(sendArgs);
+            connections.push(deferred);
+            deferred.then(function(response){
+                // got a response
+                socket.readyState = 1;
+                // remove the current connection
+                connections.splice(dojo.indexOf(connections, deferred), 1);
+                // reconnect to listen for the next message if there are no active connections,
+                // we queue it up in case one of the onmessage handlers has a message to send
+                if(!connections.length){
+                    timeoutId = setTimeout(connect, args.interval);
+                }
+                if(response){
+                    // now send the message along to listeners
+                    fire("message", {data: response}, deferred);
+                }
+            }, function(error){
+                connections.splice(dojo.indexOf(connections, deferred), 1);
+                // an error occurred, fire the appropriate event listeners
+                if(!cancelled){
+                    fire("error", {error:error}, deferred);
+                    if(!connections.length){
+                        socket.readyState = 3;
+                        fire("close", {wasClean:false}, deferred);
+                    }
+                }
+            });
+            return deferred;
+        },
+        close: function(){
+            // summary:
+            //      Close the connection
+            socket.readyState = 2;
+            cancelled = true;
+            for(var i = 0; i < connections.length; i++){
+                connections[i].cancel();
+            }
+            socket.readyState = 3;
+            fire("close", {wasClean:true});
+        },
+        transport: args.transport || dojo.xhrPost,
+        args: args,
+        url: args.url,
+        readyState: 0,
+        CONNECTING: 0,
+        OPEN: 1,
+        CLOSING: 2,
+        CLOSED: 3,
+        dispatchEvent: function(event){
+            fire(event.type, event);
+        },
+        on: function(type, callback){
+            return dojo.connect(this, "on" + type, callback);
+        },
+        firstRequest: function(args){
+            // summary:
+            //      This allows for special handling for the first request. This is useful for
+            //      providing information to disambiguate between the first request and
+            //      subsequent long-poll requests so the server can properly setup a
+            //      connection on the first connection or reject a request for an expired
+            //      connection if the request is not expecting to be the first for a connection.
+            //      This method can be overriden. The default behavior is to include a Pragma
+            //      header with a value of "start-long-poll"
+            var headers = (args.headers || (args.headers = {}));
+            headers.Pragma = "start-long-poll";
+            try{
+                return this.transport(args);
+            }finally{
+                // cleanup the header so it is not used on subsequent requests
+                delete headers.Pragma;
+            }
+        }
+    };
+    function connect(){
+        if(socket.readyState == 0){
+            // we fire the open event now because we really don't know when the "socket"
+            // is truly open, and this gives us a to do a send() and get it included in the
+            // HTTP request
+            fire("open",{});
+        }
+        // make the long-poll connection, to wait for response from the server
+        if(!connections.length){
+            socket.send();
+        }
+    }
+    function fire(type, object, deferred){
+        console.log('fire event');
+        console.log(dojo.isIE);
+        if(socket["on" + type]){
+            if(dojo.isIE && dojo.isIE < 9){
+                console.log('ie 8');
+                var event = {};
+            }
+            else{
+                console.log('non ie 8');
+                var event = document.createEvent("HTMLEvents");
+                event.initEvent(type, false, false);
+            }
+            dojo.mixin(event, object);
+            event.ioArgs = deferred && deferred.ioArgs;
+            socket["on" + type](event);
+        }
+    }
+    // provide an alias for Dojo's connect method
+    socket.connect = socket.on;
+    // do the initial connection
+    setTimeout(connect);
+    return socket;
+};
+
+dojo.require('dojox.socket.Reconnect');
+dojo.require('dojox.io.xhrPlugins');
+dojo.require('dojo.io.script');
+
+// webapp_url, server_url are globals set in html - for demo purposes
+
+// this is our actual code everything above is just hacked dojo socket to work with ie < 8
+
+
+var conn_id = null;
+var socket = null;
+var messages_node = null;
+//callbacks
+
+var jsonp_call = function(args, message) {
+    // The parameters to pass to xhrGet, the url, how to handle it, and the
+    // callbacks.
+    var jsonpArgs = {
+        url : args.url,
+        callbackParamName : "callback",
+        content : {}
+    };
+    var deferred = dojo.io.script.get(jsonpArgs);
+    return deferred;
+};
+
+var channel_message_callback = function(entry) {
+    console.log('received message !');
+    var li = dojo.create('li', null, messages_node);
+    dojo.create('strong', {
+        innerHTML : 'channel:' + entry.channel + ' '
+    }, li);
+    dojo.create('strong', {
+        innerHTML : 'user: ' + entry.user + ' '
+    }, li);
+    dojo.create('em', {
+        innerHTML : entry.message
+    }, li);
+}
+
+var presence_callback = function(entry) {
+    console.log('received presence message !');
+    var li = dojo.create('li', null, messages_node);
+    dojo.create('em', {
+        innerHTML : 'user:' + entry.user + ' joined channel: ' + entry.channel
+    }, li);
+}
+
+//lets connect this together
+
+init = function() {
+    dojox.io.xhrPlugins.addCrossSiteXhr("");
+    messages_node = dojo.query('.messages')[0];
+    var req = {
+        'user' : Math.random(),
+        'channels' : [ 'pub_chan', 'pub_chan2' ]
+    }
+
+    var post_conf = {
+        url : webapp_url+"/connect",
+        handleAs : 'json',
+        postData : dojo.toJson(req, true),
+        load : function(data) {
+            console.log('attempting to create socket');
+            conn_id = data.conn_id;
+            var socket_conf = {
+                url : server_url+"/listen?conn_id=" + data.conn_id,
+                handleAs : 'json',
+                headers : {
+                    "Content-Type" : "application/json"
+                }
+            }
+            if (dojo.isOpera || (dojo.isIE && dojo.isIE < 9)) {
+                socket_conf.transport = jsonp_call;
+            }
+            socket = dojox.socket.LongPoll(socket_conf);
+            socket = dojox.socket.Reconnect(socket, {
+                reconnectTime : 1000
+            });
+            console.log('created socket with reconnect capability');
+            socket.on("message", function(event) {
+                dojo.forEach(event.data, function(entry) {
+                    // do something with the data from the server
+                    if (entry.type == 'message') {
+                        channel_message_callback(entry)
+                    } else if (entry.type == 'join') {
+                        presence_callback(entry);
+                    }
+                });
+            });
+            socket.reconnect_timout = null;
+            socket.on("error", function(event) {
+                if(socket.reconnect_timout){
+                    clearTimeout(socket.reconnect_timout);
+                }
+                socket.reconnect_timout = setTimeout(function(){
+                    console.log('reconnected');
+                    var post_conf = {
+                            url : webapp_url+"/connect",
+                            handleAs : 'json',
+                            postData : dojo.toJson(req, true),
+                            headers : {
+                                "Content-Type" : "application/json"
+                            },
+                            load : function(data) {
+                                socket.args.url = server_url+"/listen?conn_id=" + data.conn_id;
+                            }
+                        };
+                        var deferred = dojo.rawXhrPost(post_conf);
+                }, 500); // try to collapse multiple reconnects into 1
+            });
+            console.log('added events to socket');
+            var p_conf = {
+                    url : webapp_url+"/subscribe",
+                    handleAs : 'json',
+                    postData : dojo.toJson({conn_id:data.conn_id,channels:['test_channel']}, true),
+                    headers : {
+                        "Content-Type" : "application/json"
+                    },
+                    load : function(data) {
+                        console.log('subscribed to additional test channel after initial connection');
+                        return true;
+                    }
+                }
+                var deferred = dojo.rawXhrPost(p_conf);
+            
+        }
+    };
+    console.log('connecting to get UID of connection');
+    var deferred = dojo.rawXhrPost(post_conf);
+
+}
+dojo.addOnLoad(init);
+
+dojo.addOnUnload(function() {
+    var req = {
+        'conn_id' : conn_id,
+    }
+    var post_conf = {
+        url : server_url+"/disconnect",
+        handleAs : 'json',
+        postData : dojo.toJson(req, true),
+        headers : {
+            "Content-Type" : "application/json"
+        },
+        load : function(data) {
+            return true;
+        }
+    }
+    var deferred = dojo.rawXhrPost(post_conf);
+});

gevent_cometd/static/main.js

+define([ 'demo/client', 'dojo/dom-form', 'dojo/request', 'dojo/on',
+        'dojo/query', 'dojo/topic', 'dojo/dom-construct' ], function(
+DemoClient, DomForm, Request, On, Query, Topic, DomConstruct) {
+
+    var start = function(webapp_url, server_url, connection_request) {
+
+        var messages_node = Query('.messages')[0];
+
+        Query('#msg_form').on('submit', function(evt) {
+            evt.preventDefault();
+            Request.post(this.action, {
+                data : DomForm.toJson(this),
+                handleAs : "text"
+            });
+            Query('textarea[name=message]')[0].value = '';
+            console.log('sent message');
+        });
+
+        var on_connect_callback = function(state) {
+            var payload = JSON.stringify({
+                conn_id : state.conn_id,
+                channels : [ 'test_channel' ]
+            });
+
+            Request.post(webapp_url + "/subscribe", {
+                handleAs : 'json',
+                data : payload,
+                headers : {
+                    "Content-Type" : "application/json"
+                }
+            }).then(
+            function(data) {
+                console.log('subscribed to additional test channel '
+                + 'after initial connection');
+                console.log(data);
+                return true;
+            });
+        }
+
+        var channel_message_callback = function(entry) {
+            console.log('received message !');
+            var li = DomConstruct.create('li', null, messages_node);
+            DomConstruct.create('strong', {
+                innerHTML : 'channel:' + entry.channel + ' '
+            }, li);
+            DomConstruct.create('strong', {
+                innerHTML : 'user: ' + entry.user + ' '
+            }, li);
+            DomConstruct.create('em', {
+                innerHTML : entry.message
+            }, li);
+        }
+
+        var presence_callback = function(entry) {
+            console.log('received presence message !');
+            var li = DomConstruct.create('li', null, messages_node);
+            DomConstruct.create('em', {
+                innerHTML : 'user:' + entry.user + ' joined channel: '
+                + entry.channel
+            }, li);
+        }
+
+        DemoClient.connect(webapp_url, server_url, connection_request,
+        on_connect_callback);
+        Topic.subscribe("gevent_cometd/message", channel_message_callback)
+        Topic.subscribe("gevent_cometd/join", presence_callback)
+
+    }
+
+    return {
+        start : start
+    };
+});

gevent_cometd/templates/demo.mak

 <head>
 <meta charset="UTF-8">
 <title>Chat demo</title>
-<script
-	src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"
-	type="text/javascript"></script>
-<script src="/static/client.js" type="text/javascript"></script>
+<script type="text/javascript">
+var dojoConfig = {
+baseUrl: "/static/",
+packages: [
+    { name: "dojo", location: "//ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/" },
+    { name: "dijit", location: "//ajax.googleapis.com/ajax/libs/dojo/1.8.1/dijit/" },
+    { name: "dojox", location: "//ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojox/" },
+    { name: "demo", location: "./"}
+]
+};
+</script>
+<script src="//ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/dojo.js" data-dojo-config="async: true"></script>
 </head>
 <style type="text/css">
 body{
 </style>
 <body>
 	<ul class="messages"></ul>
-
-<script type="text/javascript">	
-var webapp_url = '${config['webapp_url']}';
-var server_url = '${config['cometd.server']['server']}';
-</script>	
 	
 <form action="/message" id="msg_form" method="post">
 <p>
 </form>
 
 <script type="text/javascript">
-    dojo.addOnLoad(function() {
-        var form = dojo.byId("msg_form");
-        dojo.connect(form, "onsubmit", function(event) {
-            dojo.stopEvent(event);
-            var req = dojo.formToObject(form);
-            var xhrArgs = {
-                url : form.action,
-                postData: dojo.toJson(req, true),
-                handleAs : "text",
-                load : function(data) {
-                },
-                error : function(error) {
-                    console.log(error);
-                }
-            }
-            dojo.query('textarea[name=message]')[0].value = '';
-            var deferred = dojo.xhrPost(xhrArgs);
-            console.log('sent message');
-        });
+require(["demo"],function(Demo){
+    Demo.start('${config['webapp_url']}',
+    '${config['cometd.server']['server']}',
+    {'user' : Math.random(),
+        'channels' : [ 'pub_chan', 'pub_chan2' ]
     });
+});
 </script>
-	
+
 </body>
 </html>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.