diff rlgwebd.js @ 171:671bed5039aa

RLGWebD: fix simultaneous watcher bugs. WebSockets should now only receive the intended data, no matter how many of them there are or what they are doing. They should...
author John "Elwin" Edwards
date Sat, 10 Jan 2015 18:54:55 -0500
parents 50e4c9feeac2
children dc12ba30d559
line wrap: on
line diff
--- a/rlgwebd.js	Fri Jan 09 13:06:41 2015 -0500
+++ b/rlgwebd.js	Sat Jan 10 18:54:55 2015 -0500
@@ -125,6 +125,8 @@
   this.framebuf = new Buffer(1024);
   this.frameoff = 0;
   this.playerconn = wsReq.accept(null, wsReq.origin);
+  /* Array for watcher connections. */
+  this.watchers = [];
   /* END setup */
   function ttyrec_chunk(datastr) {
     ss.lasttime = new Date();
@@ -138,9 +140,13 @@
     ss.record.write(chunk);
     ss.framepush(buf);
     /* Send to the player. */
-    var msg = {"t": "d", "d": buf.toString("hex")};
-    ss.playerconn.sendUTF(JSON.stringify(msg));
-    /* For the benefit of watchers. */
+    var msg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
+    ss.playerconn.sendUTF(msg);
+    /* Send to any watchers. */
+    for (var i = 0; i < ss.watchers.length; i++) {
+      if (ss.watchers[i].connected)
+        ss.watchers[i].sendUTF(msg);
+    }
     ss.emit('data', buf);
   }
   this.term.on("data", ttyrec_chunk);
@@ -174,6 +180,12 @@
     var tag = ss.tag();
     fs.unlink(ss.lock);
     ss.record.end();
+    var watchsocks = ss.watchers;
+    ss.watchers = [];
+    for (var i = 0; i < watchsocks.length; i++) {
+      if (watchsocks[i].connected)
+        watchsocks[i].close();
+    }
     if (ss.playerconn.connected) {
       ss.playerconn.sendUTF(JSON.stringify({"t": "q"}));
       ss.playerconn.close();
@@ -207,6 +219,25 @@
   }
   this.playerconn.on('message', messageH);
   this.playerconn.on('close', this.close);
+  /* To attach a watcher. */
+  this.attach = function (wsReq) {
+    var conn = wsReq.accept(null, wsReq.origin);
+    conn.sendUTF(JSON.stringify({
+            "t": "w", "w": this.w, "h": this.h, "p": this.pname, 
+            "g": this.game.uname
+    }));
+    conn.sendUTF(JSON.stringify({"t": "d",
+        "d": this.framebuf.toString("hex", 0, this.frameoff)}));
+    conn.on('close', function () {
+      /* 'this' is the connection when triggered */
+      var n = ss.watchers.indexOf(this);
+      if (n >= 0) {
+        ss.watchers.splice(n, 1);
+        tslog("A WebSocket watcher has left game %s", ss.tag());
+      }
+    });
+    this.watchers.push(conn);
+  };
 }
 TermSession.prototype = new events.EventEmitter();
 
@@ -316,32 +347,6 @@
 }
 DglSession.prototype = new events.EventEmitter();
 
-// Also known as WebSocketAndTermSessionClosureGlueFactory
-function wsWatcher(conn, session) {
-  var ss = this; // is this even needed?
-  var dataH = function(buf) {
-    conn.sendUTF(JSON.stringify({"t": "d", "d": buf.toString("hex")}));
-  };
-  var exitH = function() {
-    if (conn.connected)
-      conn.close();
-  }
-  session.on('data', dataH);
-  session.on('exit', exitH);
-  conn.on('close', function(code, desc) {
-    session.removeListener('data', dataH);
-    session.removeListener('exit', exitH);
-    if (session.tag() in sessions)
-      tslog("A WebSocket watcher has left game %s", session.tag());
-  });
-  conn.sendUTF(JSON.stringify({
-            "t": "w", "w": session.w, "h": session.h, 
-            "p": session.pname, "g": session.game.uname
-  }));
-  conn.sendUTF(JSON.stringify({"t": "d",
-        "d": session.framebuf.toString("hex", 0, session.frameoff)}));
-}
-
 function wsStartGame(wsReq) {
   var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);
   if (!playmatch[1] || !(playmatch[1] in games)) {
@@ -1009,8 +1014,7 @@
       return;
     }
     var tsession = sessions[watchmatch[1]];
-    var conn = wsRequest.accept(null, wsRequest.origin);
-    new wsWatcher(conn, tsession);
+    tsession.attach(wsRequest);
     tslog("Game %s is being watched via WebSockets", tsession.tag());
   }
   else if (playmatch !== null) {