Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed connection close handling

  • Loading branch information...
commit 6b92c52c2d314ac14e6157aacd56d548f1cecf94 1 parent d2ab6fb
Micheil Smith authored March 25, 2011
57  lib/ws/connection.js
@@ -39,7 +39,7 @@ function Connection(manager, options, req, socket, upgradeHead) {
39 39
   if (connection._options.debug) {
40 40
     debug = function() {
41 41
       util.error('\033[90mWS: ' +
42  
-        Array.prototype.join.call(arguments, ', ') +
  42
+        Array.prototype.join.call(arguments, ' ') +
43 43
         '\033[39m'
44 44
       );
45 45
       process.stdout.flush();
@@ -58,10 +58,14 @@ function Connection(manager, options, req, socket, upgradeHead) {
58 58
     }
59 59
   });
60 60
 
  61
+  // Close timeout, for browsers that don't send the close packet.
  62
+  connection._closeTimer = undefined;
  63
+
61 64
   // Set the initial connecting state.
62 65
   connection.state(1);
63 66
   // Setup the connection manager's state change listeners:
64 67
   connection.on('stateChange', function(state, laststate) {
  68
+    if (connection._options.debug) debug(connection.id, 'stateChange: ', laststate, '->', state);
65 69
     if (state === 4) {
66 70
       manager.attach(connection);
67 71
       // Handle first frame breakages.
@@ -96,10 +100,16 @@ function Connection(manager, options, req, socket, upgradeHead) {
96 100
       debug(connection.id, 'recv: ' + message);
97 101
       connection.emit('message', message);
98 102
     });
99  
-    
  103
+
100 104
     parser.on("close", function() {
101  
-        debug(connection.id, "requested close");
102  
-        connection.close();
  105
+      debug(connection.id, "requested close");
  106
+
  107
+      // Timer to catch clients that don't send close packets.
  108
+      // I'm looking at you safari and chrome.
  109
+      if (connection._closeTimer) {
  110
+        clearTimeout(connection._closeTimer);
  111
+      }
  112
+      connection.state(5);
103 113
     });
104 114
 
105 115
     socket.on('data', function(data) {
@@ -169,6 +179,7 @@ util.inherits(Connection, _events.EventEmitter);
169 179
   Various utility style functions:
170 180
 -----------------------------------------------*/
171 181
 function write(connection, data) {
  182
+  debug(connection.id, 'write: ', (new Buffer(data)).inspect());
172 183
   if (connection._socket.writable) {
173 184
     return connection._socket.write(data, 'binary');
174 185
   }
@@ -267,8 +278,6 @@ Connection.prototype.inspect = function() {
267 278
 
268 279
 Connection.prototype.write = function(data) {
269 280
   if (this._state === 4) {
270  
-    debug(this.id, 'write: ' + data);
271  
-
272 281
     return write(this, '\u0000' + data + '\uffff');
273 282
   } else {
274 283
     debug(this.id, '\033[31mCould not send.');
@@ -287,14 +296,18 @@ Connection.prototype.broadcast = function(data) {
287 296
 };
288 297
 
289 298
 Connection.prototype.close = function() {
290  
-  if (this._state == 4 && this._socket.writable) {
291  
-    write(this, '\xff\x00');
  299
+  var connection = this;
  300
+
  301
+  if (connection._state == 4 && connection._socket.writable) {
  302
+    write(connection, '\xff\x00');
292 303
   }
293 304
 
294  
-  this.state(5);
  305
+  // Add a two second timeout for closing connections.
  306
+  connection._closeTimer = setTimeout(function(){
  307
+    connection.state(5);
  308
+  }, 15 * 1000);
295 309
 };
296 310
 
297  
-
298 311
 Connection.prototype.reject = function(reason) {
299 312
   debug(this.id, 'rejected. Reason: ' + reason);
300 313
   this.state(5);
@@ -398,12 +411,16 @@ function Parser() {
398 411
 
399 412
   this.frameData = [];
400 413
   this.order = 0;
  414
+  this.closing = false;
401 415
 }
402 416
 
403 417
 util.inherits(Parser, events.EventEmitter);
404 418
 
405 419
 Parser.prototype.write = function(data) {
406 420
   var pkt, msg;
  421
+
  422
+  debug('parse.write', (new Buffer(data)).inspect())
  423
+
407 424
   for (var i = 0, len = data.length; i < len; i++) {
408 425
     if (this.order == 0) {
409 426
       if (data[i] & 0x80 == 0x80) {
@@ -411,22 +428,28 @@ Parser.prototype.write = function(data) {
411 428
       } else {
412 429
         this.order = -1;
413 430
       }
414  
-    } else if (this.order == -1) {
  431
+    }
  432
+
  433
+    if (this.order == -1) {
415 434
       if (data[i] === 0xFF) {
416 435
         pkt = new Buffer(this.frameData);
417 436
         this.order = 0;
418 437
         this.frameData = [];
419 438
 
420 439
         this.emit('message', pkt.toString('utf8', 0, pkt.length));
421  
-      } else {
  440
+      } else if (data[i] !== 0x00) {
422 441
         this.frameData.push(data[i]);
423 442
       }
424 443
     } else if (this.order == 1) {
425  
-      debug('High Order packet handling is not yet implemented.');
426  
-      this.order = 0;
427  
-      
428  
-      if(data[i] === 0x00){
429  
-        this.emit("close");
  444
+      if (this.closing && data[i] === 0x00) {
  445
+        this.emit('close');
  446
+        this.closing = false;
  447
+      } else if (data[i] === 0xFF && this.frameData.length == 0) {
  448
+        this.closing = true;
  449
+      } else {
  450
+        debug('High Order packet handling is not yet implemented.');
  451
+        this.order = 0;
  452
+        this.closing = false;
430 453
       }
431 454
     }
432 455
   }
4  samples/handshake-packets
@@ -45,7 +45,8 @@ GET / HTTP/1.1
45 45
 Connection: Upgrade
46 46
 Upgrade: WebSocket
47 47
 Sec-WebSocket-Key1: 3e6b263  4 17 80
48  
-Origin: http://example.com
  48
+Origin: http://localhost:8000
  49
+Host: http://localhost:8000
49 50
 Sec-WebSocket-Key2: 17  9 G`ZD9   2 2b 7X 3 /r90
50 51
 
51 52
 WjN}|M(6
@@ -54,6 +55,7 @@ WjN}|M(6
54 55
 
55 56
 
56 57
 
  58
+
57 59
 GET / HTTP/1.1
58 60
 Connection: Upgrade
59 61
 Host: example.com
18  test/manual/chat-server.js
@@ -11,7 +11,7 @@ var httpServer = http.createServer(function(req, res){
11 11
       res.end("");
12 12
     } else {
13 13
       res.writeHead(200, {'Content-Type': 'text/html', 'Connection': 'close'});
14  
-      fs.createReadStream( path.normalize(path.join(__dirname, "chat.html")), {
  14
+      fs.createReadStream( path.normalize(path.join(__dirname, "client.html")), {
15 15
         'flags': 'r',
16 16
         'encoding': 'binary',
17 17
         'mode': 0666,
@@ -39,18 +39,28 @@ server.addListener("listening", function(){
39 39
 
40 40
 // Handle WebSocket Requests
41 41
 server.addListener("connection", function(conn){
42  
-
  42
+  console.log('[*] open');
43 43
   conn.send("** Connected as: user_"+conn.id);
44 44
   conn.send("** Type `/nick USERNAME` to change your username");
45 45
 
46 46
   conn.broadcast("** "+conn.id+" connected");
47 47
 
48 48
   conn.addListener("message", function(message){
49  
-    server.broadcast(conn.id+": "+message);
  49
+    if (message == 'close') {
  50
+      console.log('[-] close requested')
  51
+      conn.close();
  52
+    } else {
  53
+      console.log('[+] ', (new Buffer(message)).inspect());
  54
+      server.broadcast(conn.id+": "+message);
  55
+    }
50 56
   });
  57
+  
  58
+  conn.addListener("close", function(){
  59
+    console.log('[*] close');
  60
+  })
51 61
 });
52 62
 
53  
-server.addListener("close", function(conn){
  63
+server.addListener("disconnect", function(conn){
54 64
   server.broadcast("<"+conn.id+"> disconnected");
55 65
 });
56 66
 
1  test/manual/client.html
@@ -85,6 +85,7 @@
85 85
     
86 86
     conn.onclose = function() {
87 87
       log("closed");
  88
+			conn = false;
88 89
     };
89 90
 
90 91
     conn.onopen = function() {

0 notes on commit 6b92c52

Please sign in to comment.
Something went wrong with that request. Please try again.