PageRenderTime 100ms CodeModel.GetById 3ms app.highlight 87ms RepoModel.GetById 1ms app.codeStats 1ms

/test/server.js

https://github.com/ifraixedes/engine.io
JavaScript | 1764 lines | 1496 code | 212 blank | 56 comment | 68 complexity | 15e07647601af90ce9afc7fb370b5a84 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1
   2/**
   3 * Tests dependencies.
   4 */
   5
   6var http = require('http');
   7var eio = require('..');
   8var eioc = require('engine.io-client');
   9var listen = require('./common').listen;
  10var expect = require('expect.js');
  11var request = require('superagent');
  12var WebSocket = require('ws');
  13
  14/**
  15 * Tests.
  16 */
  17
  18describe('server', function () {
  19
  20  describe('verification', function () {
  21    it('should disallow non-existent transports', function (done) {
  22      var engine = listen(function (port) {
  23        request.get('http://localhost:%d/engine.io/default/'.s(port))
  24          .query({ transport: 'tobi' }) // no tobi transport - outrageous
  25          .end(function (res) {
  26            expect(res.status).to.be(400);
  27            expect(res.body.code).to.be(0);
  28            expect(res.body.message).to.be('Transport unknown');
  29            expect(res.header['access-control-allow-origin']).to.be('*');
  30            done();
  31          });
  32      });
  33    });
  34
  35    it('should disallow `constructor` as transports', function (done) {
  36      // make sure we check for actual properties - not those present on every {}
  37      var engine = listen(function (port) {
  38        request.get('http://localhost:%d/engine.io/default/'.s(port))
  39          .set('Origin', 'http://engine.io')
  40          .query({ transport: 'constructor' })
  41          .end(function (res) {
  42            expect(res.status).to.be(400);
  43            expect(res.body.code).to.be(0);
  44            expect(res.body.message).to.be('Transport unknown');
  45            expect(res.header['access-control-allow-credentials']).to.be('true');
  46            expect(res.header['access-control-allow-origin']).to.be('http://engine.io');
  47            done();
  48          });
  49      });
  50    });
  51
  52    it('should disallow non-existent sids', function (done) {
  53      var engine = listen(function (port) {
  54        request.get('http://localhost:%d/engine.io/default/'.s(port))
  55          .set('Origin', 'http://engine.io')
  56          .query({ transport: 'polling', sid: 'test' })
  57          .end(function (res) {
  58            expect(res.status).to.be(400);
  59            expect(res.body.code).to.be(1);
  60            expect(res.body.message).to.be('Session ID unknown');
  61            expect(res.header['access-control-allow-credentials']).to.be('true');
  62            expect(res.header['access-control-allow-origin']).to.be('http://engine.io');
  63            done();
  64          });
  65      });
  66    });
  67  });
  68
  69  describe('handshake', function () {
  70    it('should send the io cookie', function (done) {
  71      var engine = listen(function (port) {
  72        request.get('http://localhost:%d/engine.io/default/'.s(port))
  73          .query({ transport: 'polling', b64: 1 })
  74          .end(function (res) {
  75            // hack-obtain sid
  76            var sid = res.text.match(/"sid":"([^"]+)"/)[1];
  77            expect(res.headers['set-cookie'][0]).to.be('io=' + sid);
  78            done();
  79          });
  80      });
  81    });
  82
  83    it('should send the io cookie custom name', function (done) {
  84      var engine = listen({ cookie: 'woot' }, function (port) {
  85        request.get('http://localhost:%d/engine.io/default/'.s(port))
  86          .query({ transport: 'polling', b64: 1 })
  87          .end(function (res) {
  88            var sid = res.text.match(/"sid":"([^"]+)"/)[1];
  89            expect(res.headers['set-cookie'][0]).to.be('woot=' + sid);
  90            done();
  91          });
  92      });
  93    });
  94
  95    it('should not send the io cookie', function (done) {
  96      var engine = listen({ cookie: false }, function (port) {
  97        request.get('http://localhost:%d/engine.io/default/'.s(port))
  98          .query({ transport: 'polling' })
  99          .end(function (res) {
 100            expect(res.headers['set-cookie']).to.be(undefined);
 101            done();
 102          });
 103      });
 104    });
 105
 106    it('should register a new client', function (done) {
 107      var engine = listen({ allowUpgrades: false }, function (port) {
 108        expect(Object.keys(engine.clients)).to.have.length(0);
 109        expect(engine.clientsCount).to.be(0);
 110
 111        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 112        socket.on('open', function () {
 113          expect(Object.keys(engine.clients)).to.have.length(1);
 114          expect(engine.clientsCount).to.be(1);
 115          done();
 116        });
 117      });
 118    });
 119
 120    it('should exchange handshake data', function (done) {
 121      var engine = listen({ allowUpgrades: false }, function (port) {
 122        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 123        socket.on('handshake', function (obj) {
 124          expect(obj.sid).to.be.a('string');
 125          expect(obj.pingTimeout).to.be.a('number');
 126          expect(obj.upgrades).to.be.an('array');
 127          done();
 128        });
 129      });
 130    });
 131
 132    it('should allow custom ping timeouts', function (done) {
 133      var engine = listen({ allowUpgrades: false, pingTimeout: 123 }, function (port) {
 134        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 135        socket.on('handshake', function (obj) {
 136          expect(obj.pingTimeout).to.be(123);
 137          done();
 138        });
 139      });
 140    });
 141
 142    it('should trigger a connection event with a Socket', function (done) {
 143      var engine = listen({ allowUpgrades: false }, function (port) {
 144        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 145        engine.on('connection', function (socket) {
 146          expect(socket).to.be.an(eio.Socket);
 147          done();
 148        });
 149      });
 150    });
 151
 152    it('should open with polling by default', function (done) {
 153      var engine = listen({ allowUpgrades: false }, function (port) {
 154        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 155        engine.on('connection', function (socket) {
 156          expect(socket.transport.name).to.be('polling');
 157          done();
 158        });
 159      });
 160    });
 161
 162    it('should be able to open with ws directly', function (done) {
 163      var engine = listen({ transports: ['websocket'] }, function (port) {
 164        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 165        engine.on('connection', function (socket) {
 166          expect(socket.transport.name).to.be('websocket');
 167          done();
 168        });
 169      });
 170    });
 171
 172    it('should not suggest any upgrades for websocket', function (done) {
 173      var engine = listen({ transports: ['websocket'] }, function (port) {
 174        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 175        socket.on('handshake', function (obj) {
 176          expect(obj.upgrades).to.have.length(0);
 177          done();
 178        });
 179      });
 180    });
 181
 182    it('should not suggest upgrades when none are availble', function (done) {
 183      var engine = listen({ transports: ['polling'] }, function (port) {
 184        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { });
 185        socket.on('handshake', function (obj) {
 186          expect(obj.upgrades).to.have.length(0);
 187          done();
 188        });
 189      });
 190    });
 191
 192    it('should only suggest available upgrades', function (done) {
 193      var engine = listen({ transports: ['polling'] }, function (port) {
 194        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { });
 195        socket.on('handshake', function (obj) {
 196          expect(obj.upgrades).to.have.length(0);
 197          done();
 198        });
 199      });
 200    });
 201
 202    it('should suggest all upgrades when no transports are disabled', function (done) {
 203      var engine = listen({}, function (port) {
 204        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { });
 205        socket.on('handshake', function (obj) {
 206          expect(obj.upgrades).to.have.length(1);
 207          expect(obj.upgrades).to.have.contain('websocket');
 208          done();
 209        });
 210      });
 211    });
 212
 213    it('default to polling when proxy doesn\'t support websocket', function (done) {
 214      var engine = listen({ allowUpgrades: false }, function (port) {
 215
 216        engine.on('connection', function (socket) {
 217          socket.on('message', function (msg) {
 218            if ('echo' == msg) socket.send(msg);
 219          });
 220        });
 221
 222        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 223        socket.on('open', function () {
 224          request.get('http://localhost:%d/engine.io/'.s(port))
 225          .set({ connection: 'close' })
 226          .query({ transport: 'websocket', sid: socket.id })
 227          .end(function (err, res) {
 228            expect(err).to.be(null);
 229            expect(res.status).to.be(400);
 230            expect(res.body.code).to.be(3);
 231            socket.send('echo');
 232            socket.on('message', function (msg) {
 233              expect(msg).to.be('echo');
 234              done();
 235            });
 236          });
 237        });
 238      });
 239    });
 240
 241    it('should allow arbitrary data through query string', function (done) {
 242      var engine = listen({ allowUpgrades: false }, function (port) {
 243        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { query: { a: 'b' } });
 244        engine.on('connection', function (conn) {
 245          expect(conn.request._query).to.have.keys('transport', 'a');
 246          expect(conn.request._query.a).to.be('b');
 247          done();
 248        });
 249      });
 250    });
 251
 252    it('should allow data through query string in uri', function (done) {
 253      var engine = listen({ allowUpgrades: false }, function (port) {
 254        var socket = new eioc.Socket('ws://localhost:%d?a=b&c=d'.s(port));
 255        engine.on('connection', function (conn) {
 256          expect(conn.request._query.EIO).to.be.a('string');
 257          expect(conn.request._query.a).to.be('b');
 258          expect(conn.request._query.c).to.be('d');
 259          done();
 260        });
 261      });
 262    });
 263
 264
 265
 266    it('should disallow bad requests', function (done) {
 267      var engine = listen(function (port) {
 268        request.get('http://localhost:%d/engine.io/default/'.s(port))
 269          .set('Origin', 'http://engine.io')
 270          .query({ transport: 'websocket' })
 271          .end(function (res) {
 272            expect(res.status).to.be(400);
 273            expect(res.body.code).to.be(3);
 274            expect(res.body.message).to.be('Bad request');
 275            expect(res.header['access-control-allow-credentials']).to.be('true');
 276            expect(res.header['access-control-allow-origin']).to.be('http://engine.io');
 277            done();
 278          });
 279      });
 280    });
 281  });
 282
 283  describe('close', function () {
 284    it('should be able to access non-empty writeBuffer at closing (server)', function(done) {
 285      var opts = {allowUpgrades: false};
 286      var engine = listen(opts, function (port) {
 287        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 288        engine.on('connection', function (conn) {
 289          conn.on('close', function (reason) {
 290            expect(conn.writeBuffer.length).to.be(1);
 291            setTimeout(function () {
 292              expect(conn.writeBuffer.length).to.be(0); // writeBuffer has been cleared
 293            }, 10);
 294            done();
 295          });
 296          conn.writeBuffer.push({ type: 'message', data: 'foo'});
 297          conn.onError('');
 298        });
 299      });
 300    });
 301
 302    it('should be able to access non-empty writeBuffer at closing (client)', function(done) {
 303      var opts = {allowUpgrades: false};
 304      var engine = listen(opts, function (port) {
 305        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 306        socket.on('open', function() {
 307          socket.on('close', function (reason) {
 308            expect(socket.writeBuffer.length).to.be(1);
 309            expect(socket.callbackBuffer.length).to.be(1);
 310            setTimeout(function() {
 311              expect(socket.writeBuffer.length).to.be(0);
 312              expect(socket.callbackBuffer.length).to.be(0);
 313            }, 10);
 314            done();
 315          });
 316          socket.writeBuffer.push({ type: 'message', data: 'foo'});
 317          socket.callbackBuffer.push(function() {});
 318          socket.onError('');
 319        });
 320      });
 321    });
 322
 323    it('should trigger on server if the client does not pong', function (done) {
 324      var opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 };
 325      var engine = listen(opts, function (port) {
 326        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 327        socket.sendPacket = function (){};
 328        engine.on('connection', function (conn) {
 329          conn.on('close', function (reason) {
 330            expect(reason).to.be('ping timeout');
 331            done();
 332          });
 333        });
 334      });
 335    });
 336
 337    it('should trigger on server even when there is no outstanding polling request (GH-198)', function (done) {
 338      var opts = { allowUpgrades: false, pingInterval: 500, pingTimeout: 500 };
 339      var engine = listen(opts, function (port) {
 340        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 341        engine.on('connection', function (conn) {
 342          conn.on('close', function (reason) {
 343            expect(reason).to.be('ping timeout');
 344            done();
 345          });
 346          // client abruptly disconnects, no polling request on this tick since we've just connected
 347          socket.sendPacket = socket.onPacket = function (){};
 348          socket.close();
 349          // then server app tries to close the socket, since client disappeared
 350          conn.close();
 351        });
 352      });
 353    });
 354
 355    it('should trigger on client if server does not meet ping timeout', function (done) {
 356      var opts = { allowUpgrades: false, pingInterval: 50, pingTimeout: 30 };
 357      var engine = listen(opts, function (port) {
 358        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 359        socket.on('open', function () {
 360          // override onPacket to simulate an inactive server after handshake
 361          socket.onPacket = function(){};
 362          socket.on('close', function (reason, err) {
 363            expect(reason).to.be('ping timeout');
 364            done();
 365          });
 366        });
 367      });
 368    });
 369
 370    it('should trigger on both ends upon ping timeout', function (done) {
 371      var opts = { allowUpgrades: false, pingTimeout: 10, pingInterval: 10 };
 372      var engine = listen(opts, function (port) {
 373        var socket = new eioc.Socket('ws://localhost:%d'.s(port))
 374          , total = 2;
 375
 376        function onClose (reason, err) {
 377          expect(reason).to.be('ping timeout');
 378          --total || done();
 379        }
 380
 381        engine.on('connection', function (conn) {
 382          conn.on('close', onClose);
 383        });
 384
 385        socket.on('open', function () {
 386          // override onPacket to simulate an inactive server after handshake
 387          socket.onPacket = socket.sendPacket = function(){};
 388          socket.on('close', onClose);
 389        });
 390      });
 391    });
 392
 393    it('should trigger when server closes a client', function (done) {
 394      var engine = listen({ allowUpgrades: false }, function (port) {
 395        var socket = new eioc.Socket('ws://localhost:%d'.s(port))
 396          , total = 2;
 397
 398        engine.on('connection', function (conn) {
 399          conn.on('close', function (reason) {
 400            expect(reason).to.be('forced close');
 401            --total || done();
 402          });
 403          setTimeout(function () {
 404            conn.close();
 405          }, 10);
 406        });
 407
 408        socket.on('open', function () {
 409          socket.on('close', function (reason) {
 410            expect(reason).to.be('transport close');
 411            --total || done();
 412          });
 413        });
 414      });
 415    });
 416
 417    it('should trigger when server closes a client (ws)', function (done) {
 418      var opts = { allowUpgrades: false, transports: ['websocket'] };
 419      var engine = listen(opts, function (port) {
 420        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] })
 421          , total = 2;
 422
 423        engine.on('connection', function (conn) {
 424          conn.on('close', function (reason) {
 425            expect(reason).to.be('forced close');
 426            --total || done();
 427          });
 428          setTimeout(function () {
 429            conn.close();
 430          }, 10);
 431        });
 432
 433        socket.on('open', function () {
 434          socket.on('close', function (reason) {
 435            expect(reason).to.be('transport close');
 436            --total || done();
 437          });
 438        });
 439      });
 440    });
 441
 442    it('should trigger when client closes', function (done) {
 443      var engine = listen({ allowUpgrades: false }, function (port) {
 444        var socket = new eioc.Socket('ws://localhost:%d'.s(port))
 445          , total = 2;
 446
 447        engine.on('connection', function (conn) {
 448          conn.on('close', function (reason) {
 449            expect(reason).to.be('transport close');
 450            --total || done();
 451          });
 452        });
 453
 454        socket.on('open', function () {
 455          socket.on('close', function (reason) {
 456            expect(reason).to.be('forced close');
 457            --total || done();
 458          });
 459
 460          setTimeout(function () {
 461            socket.close();
 462          }, 10);
 463        });
 464      });
 465    });
 466
 467    it('should trigger when client closes (ws)', function (done) {
 468      var opts = { allowUpgrades: false, transports: ['websocket'] };
 469      var engine = listen(opts, function (port) {
 470        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] })
 471          , total = 2;
 472
 473        engine.on('connection', function (conn) {
 474          conn.on('close', function (reason) {
 475            expect(reason).to.be('transport close');
 476            --total || done();
 477          });
 478        });
 479
 480        socket.on('open', function () {
 481          socket.on('close', function (reason) {
 482            expect(reason).to.be('forced close');
 483            --total || done();
 484          });
 485
 486          setTimeout(function () {
 487            socket.close();
 488          }, 10);
 489        });
 490      });
 491    });
 492
 493    it('should trigger when calling socket.close() in payload', function (done) {
 494      var engine = listen({ allowUpgrades: false }, function (port) {
 495        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 496
 497        engine.on('connection', function (conn) {
 498          conn.send(null, function () {socket.close();});
 499          conn.send('this should not be handled');
 500
 501          conn.on('close', function (reason) {
 502            expect(reason).to.be('transport close');
 503            done();
 504          });
 505        });
 506
 507        socket.on('open', function () {
 508          socket.on('message', function (msg) {
 509            expect(msg).to.not.be('this should not be handled');
 510          });
 511
 512          socket.on('close', function (reason) {
 513            expect(reason).to.be('forced close');
 514          });
 515        });
 516      });
 517    });
 518
 519    it('should abort upgrade if socket is closed (GH-35)', function (done) {
 520      var engine = listen({ allowUpgrades: true }, function (port) {
 521        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 522        socket.on('open', function () {
 523          socket.close();
 524          // we wait until complete to see if we get an uncaught EPIPE
 525          setTimeout(function(){
 526            done();
 527          }, 100);
 528        });
 529      });
 530    });
 531
 532    it('should trigger if a poll request is ongoing and the underlying ' +
 533       'socket closes, as in a browser tab close', function ($done) {
 534      var engine = listen({ allowUpgrades: false }, function (port) {
 535        // hack to access the sockets created by node-xmlhttprequest
 536        // see: https://github.com/driverdan/node-XMLHttpRequest/issues/44
 537        var request = require('http').request;
 538        var sockets = [];
 539        http.request = function(opts){
 540          var req = request.apply(null, arguments);
 541          req.on('socket', function(socket){
 542            sockets.push(socket);
 543          });
 544          return req;
 545        };
 546
 547        function done(){
 548          http.request = request;
 549          $done();
 550        }
 551
 552        var socket = new eioc.Socket('ws://localhost:%d'.s(port))
 553          , serverSocket;
 554
 555        engine.on('connection', function(s){
 556          serverSocket = s;
 557        });
 558
 559        socket.transport.on('poll', function(){
 560          // we set a timer to wait for the request to actually reach
 561          setTimeout(function(){
 562            // at this time server's `connection` should have been fired
 563            expect(serverSocket).to.be.an('object');
 564
 565            // OPENED readyState is expected - we qre actually polling
 566            expect(socket.transport.pollXhr.xhr.readyState).to.be(1);
 567
 568            // 2 requests sent to the server over an unique port means
 569            // we should have been assigned 2 sockets
 570            expect(sockets.length).to.be(2);
 571
 572            // expect the socket to be open at this point
 573            expect(serverSocket.readyState).to.be('open');
 574
 575            // kill the underlying connection
 576            sockets[1].end();
 577            serverSocket.on('close', function(reason, err){
 578              expect(reason).to.be('transport error');
 579              expect(err.message).to.be('poll connection closed prematurely');
 580              done();
 581            });
 582          }, 50);
 583        });
 584      });
 585    });
 586
 587    it('should not trigger with connection: close header', function($done){
 588      var engine = listen({ allowUpgrades: false }, function(port){
 589        // intercept requests to add connection: close
 590        var request = http.request;
 591        http.request = function(){
 592          var opts = arguments[0];
 593          opts.headers = opts.headers || {};
 594          opts.headers.Connection = 'close';
 595          return request.apply(this, arguments);
 596        };
 597
 598        function done(){
 599          http.request = request;
 600          $done();
 601        }
 602
 603        engine.on('connection', function(socket){
 604          socket.on('message', function(msg){
 605            expect(msg).to.equal('test');
 606            socket.send('woot');
 607          });
 608        });
 609
 610        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 611        socket.on('open', function(){
 612          socket.send('test');
 613        });
 614        socket.on('message', function(msg){
 615          expect(msg).to.be('woot');
 616          done();
 617        });
 618      });
 619    });
 620
 621    it('should not trigger early with connection `ping timeout`' +
 622       'after post handshake timeout', function (done) {
 623      // first timeout should trigger after `pingInterval + pingTimeout`,
 624      // not just `pingTimeout`.
 625      var opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 };
 626      var engine = listen(opts, function (port) {
 627        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 628        var clientCloseReason = null;
 629
 630        socket.on('handshake', function() {
 631          socket.onPacket = function(){};
 632        });
 633        socket.on('open', function () {
 634          socket.on('close', function (reason) {
 635            clientCloseReason = reason;
 636          });
 637        });
 638
 639        setTimeout(function() {
 640          expect(clientCloseReason).to.be(null);
 641          done();
 642        }, 200);
 643      });
 644    });
 645
 646    it('should not trigger early with connection `ping timeout` ' +
 647       'after post ping timeout', function (done) {
 648      // ping timeout should trigger after `pingInterval + pingTimeout`,
 649      // not just `pingTimeout`.
 650      var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 };
 651      var engine = listen(opts, function (port) {
 652        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 653        var clientCloseReason = null;
 654
 655        engine.on('connection', function(conn){
 656          conn.on('heartbeat', function() {
 657            conn.onPacket = function(){};
 658          });
 659        });
 660
 661        socket.on('open', function () {
 662          socket.on('close', function (reason) {
 663            clientCloseReason = reason;
 664          });
 665        });
 666
 667        setTimeout(function() {
 668          expect(clientCloseReason).to.be(null);
 669          done();
 670        }, 100);
 671      });
 672    });
 673
 674    it('should trigger early with connection `transport close` ' +
 675       'after missing pong', function (done) {
 676      // ping timeout should trigger after `pingInterval + pingTimeout`,
 677      // not just `pingTimeout`.
 678      var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 };
 679      var engine = listen(opts, function (port) {
 680        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 681        var clientCloseReason = null;
 682
 683        socket.on('open', function () {
 684          socket.on('close', function (reason) {
 685            clientCloseReason = reason;
 686          });
 687        });
 688
 689        engine.on('connection', function(conn){
 690          conn.on('heartbeat', function() {
 691            setTimeout(function() {
 692              conn.close();
 693            }, 20);
 694            setTimeout(function() {
 695              expect(clientCloseReason).to.be('transport close');
 696              done();
 697            }, 100);
 698          });
 699        });
 700      });
 701    });
 702
 703    it('should trigger with connection `ping timeout` ' +
 704       'after `pingInterval + pingTimeout`', function (done) {
 705      var opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 };
 706      var engine = listen(opts, function (port) {
 707        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 708        var clientCloseReason = null;
 709
 710        socket.on('open', function () {
 711          socket.on('close', function (reason) {
 712            clientCloseReason = reason;
 713          });
 714        });
 715
 716        engine.on('connection', function(conn){
 717          conn.once('heartbeat', function() {
 718            setTimeout(function() {
 719              socket.onPacket = function(){};
 720              expect(clientCloseReason).to.be(null);
 721            }, 150);
 722            setTimeout(function() {
 723              expect(clientCloseReason).to.be(null);
 724            }, 350);
 725            setTimeout(function() {
 726              expect(clientCloseReason).to.be('ping timeout');
 727              done();
 728            }, 500);
 729          });
 730        });
 731      });
 732    });
 733
 734    // tests https://github.com/LearnBoost/engine.io-client/issues/207
 735    // websocket test, transport error
 736    it('should trigger transport close before open for ws', function(done){
 737      var opts = { transports: ['websocket'] };
 738      var engine = listen(opts, function (port) {
 739        var socket = new eioc.Socket('ws://invalidserver:%d'.s(port));
 740        socket.on('open', function(){
 741          done(new Error('Test invalidation'));
 742        });
 743        socket.on('close', function(reason){
 744          expect(reason).to.be('transport error');
 745          done();
 746        });
 747      });
 748    });
 749
 750    // tests https://github.com/LearnBoost/engine.io-client/issues/207
 751    // polling test, transport error
 752    it('should trigger transport close before open for xhr', function(done){
 753      var opts = { transports: ['polling'] };
 754      var engine = listen(opts, function (port) {
 755        var socket = new eioc.Socket('http://invalidserver:%d'.s(port));
 756        socket.on('open', function(){
 757          done(new Error('Test invalidation'));
 758        });
 759        socket.on('close', function(reason){
 760          expect(reason).to.be('transport error');
 761          done();
 762        });
 763      });
 764    });
 765
 766    // tests https://github.com/LearnBoost/engine.io-client/issues/207
 767    // websocket test, force close
 768    it('should trigger force close before open for ws', function(done){
 769      var opts = { transports: ['websocket'] };
 770      var engine = listen(opts, function (port) {
 771        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 772        socket.on('open', function(){
 773          done(new Error('Test invalidation'));
 774        });
 775        socket.on('close', function(reason){
 776          expect(reason).to.be('forced close');
 777          done();
 778        });
 779        socket.close();
 780      });
 781    });
 782
 783    // tests https://github.com/LearnBoost/engine.io-client/issues/207
 784    // polling test, force close
 785    it('should trigger force close before open for xhr', function(done){
 786      var opts = { transports: ['polling'] };
 787      var engine = listen(opts, function (port) {
 788        var socket = new eioc.Socket('http://localhost:%d'.s(port));
 789        socket.on('open', function(){
 790          done(new Error('Test invalidation'));
 791        });
 792        socket.on('close', function(reason){
 793          expect(reason).to.be('forced close');
 794          done();
 795        });
 796        socket.close();
 797      });
 798    });
 799
 800  });
 801
 802  describe('messages', function () {
 803    this.timeout(5000);
 804
 805    it('should arrive from server to client', function (done) {
 806      var engine = listen({ allowUpgrades: false }, function (port) {
 807        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 808        engine.on('connection', function (conn) {
 809          conn.send('a');
 810        });
 811        socket.on('open', function () {
 812          socket.on('message', function (msg) {
 813            expect(msg).to.be('a');
 814            done();
 815          });
 816        });
 817      });
 818    });
 819
 820    it('should arrive from server to client (multiple)', function (done) {
 821      var engine = listen({ allowUpgrades: false }, function (port) {
 822        var socket = new eioc.Socket('ws://localhost:%d'.s(port))
 823          , expected = ['a', 'b', 'c']
 824          , i = 0;
 825
 826        engine.on('connection', function (conn) {
 827          conn.send('a');
 828          // we use set timeouts to ensure the messages are delivered as part
 829          // of different.
 830          setTimeout(function () {
 831            conn.send('b');
 832
 833            setTimeout(function () {
 834              // here we make sure we buffer both the close packet and
 835              // a regular packet
 836              conn.send('c');
 837              conn.close();
 838            }, 50);
 839          }, 50);
 840
 841          conn.on('close', function () {
 842            // since close fires right after the buffer is drained
 843            setTimeout(function () {
 844              expect(i).to.be(3);
 845              done();
 846            }, 50);
 847          });
 848        });
 849        socket.on('open', function () {
 850          socket.on('message', function (msg) {
 851            expect(msg).to.be(expected[i++]);
 852          });
 853        });
 854      });
 855    });
 856
 857    it('should not be receiving data when getting a message longer than maxHttpBufferSize when polling', function(done) {
 858      var opts = { allowUpgrades: false, transports: ['polling'], maxHttpBufferSize: 5 };
 859      var engine = listen(opts, function (port) {
 860        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 861        engine.on('connection', function (conn) {
 862          conn.on('message', function(msg) {
 863            console.log(msg);
 864          });
 865        });
 866        socket.on('open', function () {
 867          socket.send('aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha');
 868        });
 869      });
 870      setTimeout(done, 1000);
 871    });
 872
 873    it('should receive data when getting a message shorter than maxHttpBufferSize when polling', function(done) {
 874      var opts = { allowUpgrades: false, transports: ['polling'], maxHttpBufferSize: 5 };
 875      var engine = listen(opts, function (port) {
 876        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
 877        engine.on('connection', function (conn) {
 878          conn.on('message', function(msg) {
 879            expect(msg).to.be('a');
 880            done();
 881          });
 882        });
 883        socket.on('open', function () {
 884          socket.send('a');
 885        });
 886      });
 887    });
 888
 889
 890    it('should arrive from server to client (ws)', function (done) {
 891      var opts = { allowUpgrades: false, transports: ['websocket'] };
 892      var engine = listen(opts, function (port) {
 893        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 894        engine.on('connection', function (conn) {
 895          conn.send('a');
 896        });
 897        socket.on('open', function () {
 898          socket.on('message', function (msg) {
 899            expect(msg).to.be('a');
 900            done();
 901          });
 902        });
 903      });
 904    });
 905
 906    it('should arrive from server to client (ws)', function (done) {
 907      var opts = { allowUpgrades: false, transports: ['websocket'] };
 908      var engine = listen(opts, function (port) {
 909        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] })
 910          , expected = ['a', 'b', 'c']
 911          , i = 0;
 912
 913        engine.on('connection', function (conn) {
 914          conn.send('a');
 915          setTimeout(function () {
 916            conn.send('b');
 917            setTimeout(function () {
 918              conn.send('c');
 919              conn.close();
 920            }, 50);
 921          }, 50);
 922          conn.on('close', function () {
 923            setTimeout(function () {
 924              expect(i).to.be(3);
 925              done();
 926            }, 50);
 927          });
 928        });
 929
 930        socket.on('open', function () {
 931          socket.on('message', function (msg) {
 932            expect(msg).to.be(expected[i++]);
 933          });
 934        });
 935      });
 936    });
 937
 938    it('should arrive when binary data is sent as Int8Array (ws)', function (done) {
 939      var binaryData = new Int8Array(5);
 940      for (var i = 0; i < binaryData.length; i++) {
 941        binaryData[i] = i;
 942      }
 943
 944      var opts = { allowUpgrades: false, transports: ['websocket'] };
 945      var engine = listen(opts, function(port) {
 946        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 947
 948        engine.on('connection', function (conn) {
 949          conn.send(binaryData);
 950        });
 951
 952        socket.on('open', function () {
 953          socket.on('message', function(msg) {
 954            for (var i = 0; i < binaryData.length; i++) {
 955              var num = msg.readInt8(i);
 956              expect(num).to.be(i);
 957            }
 958            done();
 959          });
 960        });
 961      });
 962    });
 963
 964    it('should arrive when binary data is sent as Int32Array (ws)', function (done) {
 965      var binaryData = new Int32Array(5);
 966      for (var i = 0; i < binaryData.length; i++) {
 967        binaryData[i] = (i + 100) * 9823;
 968      }
 969
 970      var opts = { allowUpgrades: false, transports: ['websocket'] };
 971      var engine = listen(opts, function(port) {
 972        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 973
 974        engine.on('connection', function (conn) {
 975          conn.send(binaryData);
 976        });
 977
 978        socket.on('open', function () {
 979          socket.on('message', function(msg) {
 980            for (var i = 0, ii = 0; i < binaryData.length; i += 4, ii++) {
 981              var num = msg.readInt32LE(i);
 982              expect(num).to.be((ii + 100) * 9823);
 983            }
 984            done();
 985          });
 986        });
 987      });
 988    });
 989
 990    it('should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)', function (done) {
 991      var binaryData = new Int32Array(5);
 992      for (var i = 0; i < binaryData.length; i++) {
 993        binaryData[i] = (i + 100) * 9823;
 994      }
 995
 996      var opts = { allowUpgrades: false, transports: ['websocket'] };
 997      var engine = listen(opts, function(port) {
 998        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
 999
1000        engine.on('connection', function (conn) {
1001          conn.send(binaryData.buffer);
1002        });
1003
1004        socket.on('open', function () {
1005          socket.on('message', function(msg) {
1006            for (var i = 0, ii = 0; i < binaryData.length; i += 4, ii++) {
1007              var num = msg.readInt32LE(i);
1008              expect(num).to.be((ii + 100) * 9823);
1009            }
1010            done();
1011          });
1012        });
1013      });
1014    });
1015
1016    it('should arrive when binary data is sent as Buffer (ws)', function (done) {
1017      var binaryData = Buffer(5);
1018      for (var i = 0; i < binaryData.length; i++) {
1019        binaryData.writeInt8(i, i);
1020      }
1021
1022      var opts = { allowUpgrades: false, transports: ['websocket'] };
1023      var engine = listen(opts, function(port) {
1024        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1025
1026        engine.on('connection', function (conn) {
1027          conn.send(binaryData);
1028        });
1029
1030        socket.on('open', function () {
1031          socket.on('message', function(msg) {
1032            for (var i = 0; i < binaryData.length; i++) {
1033              var num = msg.readInt8(i);
1034              expect(num).to.be(i);
1035            }
1036            done();
1037          });
1038        });
1039      });
1040    });
1041
1042    it('should arrive when binary data sent as Buffer (polling)', function (done) {
1043      var binaryData = Buffer(5);
1044      for (var i = 0; i < binaryData.length; i++) {
1045        binaryData.writeInt8(i, i);
1046      }
1047
1048      var opts = { allowUpgrades: false, transports: ['polling'] };
1049      var engine = listen(opts, function(port) {
1050        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1051
1052        engine.on('connection', function (conn) {
1053          conn.send(binaryData);
1054        });
1055
1056        socket.on('open', function() {
1057          socket.on('message', function(msg) {
1058            for (var i = 0; i < binaryData.length; i++) {
1059              var num = msg.readInt8(i);
1060              expect(num).to.be(i);
1061            }
1062
1063            done();
1064          });
1065        });
1066      });
1067    });
1068
1069    it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)', function (done) {
1070      var binaryData = Buffer(5);
1071      for (var i = 0; i < binaryData.length; i++) {
1072        binaryData.writeInt8(i, i);
1073      }
1074
1075      var opts = { allowUpgrades: false, transports: ['websocket'] };
1076      var engine = listen(opts, function(port) {
1077        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1078        socket.binaryType = 'arraybuffer';
1079
1080        engine.on('connection', function (conn) {
1081          conn.send(binaryData);
1082        });
1083
1084        socket.on('open', function() {
1085          socket.on('message', function(msg) {
1086            expect(msg instanceof ArrayBuffer).to.be(true);
1087            var intArray = new Int8Array(msg);
1088            for (var i = 0; i < binaryData.length; i++) {
1089              expect(intArray[i]).to.be(i);
1090            }
1091
1092            done();
1093          });
1094        });
1095      });
1096    });
1097
1098    it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)', function (done) {
1099      var binaryData = Buffer(5);
1100      for (var i = 0; i < binaryData.length; i++) {
1101        binaryData.writeInt8(i, i);
1102      }
1103
1104      var opts = { allowUpgrades: false, transports: ['polling'] };
1105      var engine = listen(opts, function(port) {
1106        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1107        socket.binaryType = 'arraybuffer';
1108
1109        engine.on('connection', function (conn) {
1110          conn.send(binaryData);
1111        });
1112
1113        socket.on('open', function() {
1114          socket.on('message', function(msg) {
1115            expect(msg instanceof ArrayBuffer).to.be(true);
1116            var intArray = new Int8Array(msg);
1117            for (var i = 0; i < binaryData.length; i++) {
1118              expect(intArray[i]).to.be(i);
1119            }
1120
1121            done();
1122          });
1123        });
1124      });
1125    });
1126
1127
1128    it('should trigger a flush/drain event', function(done){
1129      var engine = listen({ allowUpgrades: false }, function(port){
1130        engine.on('connection', function(socket){
1131          var totalEvents = 4;
1132
1133          engine.on('flush', function(sock, buf){
1134            expect(sock).to.be(socket);
1135            expect(buf).to.be.an('array');
1136            --totalEvents || done();
1137          });
1138          socket.on('flush', function(buf){
1139            expect(buf).to.be.an('array');
1140            --totalEvents || done();
1141          });
1142
1143          engine.on('drain', function(sock){
1144            expect(sock).to.be(socket);
1145            expect(socket.writeBuffer.length).to.be(0);
1146            --totalEvents || done();
1147          });
1148          socket.on('drain', function(){
1149            expect(socket.writeBuffer.length).to.be(0);
1150            --totalEvents || done();
1151          });
1152
1153          socket.send('aaaa');
1154        });
1155
1156        new eioc.Socket('ws://localhost:%d'.s(port));
1157      });
1158    });
1159
1160    it('should interleave with pongs if many messages buffered ' +
1161       'after connection open', function (done) {
1162      this.slow(4000);
1163      this.timeout(8000);
1164
1165      var opts = {
1166        transports: ['websocket'],
1167        pingInterval: 200,
1168        pingTimeout: 100
1169      };
1170
1171      var engine = listen(opts, function (port) {
1172        var messageCount = 100;
1173        var messagePayload = new Array(256 * 256).join('a');
1174        var connection = null;
1175        engine.on('connection', function (conn) {
1176          connection = conn;
1177        });
1178        var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1179        socket.on('open', function () {
1180          for (var i=0;i<messageCount;i++) {
1181//            connection.send('message: ' + i);   // works
1182            connection.send(messagePayload + '|message: ' + i);   // does not work
1183          }
1184          var receivedCount = 0;
1185          socket.on('message', function (msg) {
1186            receivedCount += 1;
1187            if (receivedCount === messageCount) {
1188              done();
1189            }
1190          });
1191        });
1192      });
1193    });
1194
1195    it('should support chinese', function(done){
1196      var engine = listen({ allowUpgrades: false }, function (port) {
1197        var socket = new eioc.Socket('ws://localhost:%d'.s(port));
1198        var shi = '石室詩士施氏,嗜獅,誓食十獅。';
1199        var shi2 = '氏時時適市視獅。';
1200        engine.on('connection', function (conn) {
1201          conn.send('.');
1202          conn.send(shi);
1203          conn.send(shi2);
1204          conn.once('message', function(msg0){
1205            expect(msg0).to.be('.');
1206            conn.once('message', function(msg){
1207              expect(msg).to.be(shi);
1208              conn.once('message', function(msg2){
1209                expect(msg2).to.be(shi2);
1210                done();
1211              });
1212            });
1213          });
1214        });
1215        socket.on('open', function(){
1216          socket.once('message', function(msg0){
1217            expect(msg0).to.be('.');
1218            socket.once('message', function(msg){
1219              expect(msg).to.be(shi);
1220              socket.once('message', function(msg2){
1221                expect(msg2).to.be(shi2);
1222                socket.send('.');
1223                socket.send(shi);
1224                socket.send(shi2);
1225              });
1226            });
1227          });
1228        });
1229      });
1230    });
1231  });
1232
1233  describe('send', function() {
1234    describe('writeBuffer', function() {
1235      it('should not empty until `drain` event (polling)', function (done) {
1236        var engine = listen({ allowUpgrades: false }, function (port) {
1237          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1238          var totalEvents = 2;
1239          socket.on('open', function() {
1240            socket.send('a');
1241            socket.send('b');
1242            // writeBuffer should be nonempty, with 'a' still in it
1243            expect(socket.writeBuffer.length).to.eql(2);
1244          });
1245          socket.transport.on('drain', function() {
1246            expect(socket.writeBuffer.length).to.eql(--totalEvents);
1247            totalEvents || done();
1248          });
1249        });
1250      });
1251
1252      it('should not empty until `drain` event (websocket)', function (done) {
1253        var engine = listen({ allowUpgrades: false }, function (port) {
1254          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1255          var totalEvents = 2;
1256          socket.on('open', function() {
1257            socket.send('a');
1258            socket.send('b');
1259            // writeBuffer should be nonempty, with 'a' still in it
1260            expect(socket.writeBuffer.length).to.eql(2);
1261          });
1262          socket.transport.on('drain', function() {
1263            expect(socket.writeBuffer.length).to.eql(--totalEvents);
1264            totalEvents || done();
1265          });
1266        });
1267      });
1268    });
1269
1270    describe('callback', function() {
1271      it('should execute in order when message sent (client) (polling)', function (done) {
1272        var engine = listen({ allowUpgrades: false }, function (port) {
1273          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1274          var i = 0;
1275          var j = 0;
1276
1277          engine.on('connection', function(conn) {
1278            conn.on('message', function(msg) {
1279              conn.send(msg);
1280            });
1281          });
1282
1283          socket.on('open', function () {
1284            socket.on('message', function(msg) {
1285              // send another packet until we've sent 3 total
1286              if (++i < 3) {
1287                expect(i).to.eql(j);
1288                sendFn();
1289              } else {
1290                done();
1291              }
1292            });
1293
1294            function sendFn() {
1295              socket.send(j, (function(value) {
1296                j++;
1297              })(j));
1298            }
1299
1300            sendFn();
1301          });
1302        });
1303      });
1304
1305      it('should execute in order when message sent (client) (websocket)', function (done) {
1306        var engine = listen({ allowUpgrades: false }, function (port) {
1307          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1308          var i = 0;
1309          var j = 0;
1310
1311          engine.on('connection', function(conn) {
1312            conn.on('message', function(msg) {
1313              conn.send(msg);
1314            });
1315          });
1316
1317          socket.on('open', function () {
1318            socket.on('message', function(msg) {
1319              // send another packet until we've sent 3 total
1320              if (++i < 3) {
1321                expect(i).to.eql(j);
1322                sendFn();
1323              } else {
1324                done();
1325              }
1326            });
1327
1328            function sendFn() {
1329              socket.send(j, (function(value) {
1330                j++;
1331              })(j));
1332            }
1333
1334            sendFn();
1335          });
1336        });
1337      });
1338
1339      it('should execute in order with payloads (client) (polling)', function (done) {
1340        var engine = listen({ allowUpgrades: false }, function (port) {
1341          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1342          var i = 0;
1343          var lastCbFired = 0;
1344
1345          engine.on('connection', function(conn) {
1346            conn.on('message', function(msg) {
1347              conn.send(msg);
1348            });
1349          });
1350
1351          socket.on('open', function () {
1352            socket.on('message', function(msg) {
1353              expect(msg).to.eql(i + 1);
1354              i++;
1355            });
1356
1357            function cb(value) {
1358              expect(value).to.eql(lastCbFired + 1);
1359              lastCbFired = value;
1360              if (value == 3) {
1361                done();
1362              }
1363            }
1364
1365            // 2 and 3 will be in the same payload
1366            socket.once('flush', function() {
1367              socket.send(2, function() { cb(2); });
1368              socket.send(3, function() { cb(3); });
1369            });
1370
1371            socket.send(1, function() { cb(1); });
1372          });
1373        });
1374      });
1375
1376      it('should execute in order with payloads (client) (websocket)', function (done) {
1377        var engine = listen({ allowUpgrades: false }, function (port) {
1378          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1379          var i = 0;
1380          var lastCbFired = 0;
1381
1382          engine.on('connection', function(conn) {
1383            conn.on('message', function(msg) {
1384              conn.send(msg);
1385            });
1386          });
1387
1388          socket.on('open', function () {
1389            socket.on('message', function(msg) {
1390              expect(msg).to.eql(i + 1);
1391              i++;
1392            });
1393
1394            function cb(value) {
1395              expect(value).to.eql(lastCbFired + 1);
1396              lastCbFired = value;
1397              if (value == 3) {
1398                done();
1399              }
1400            }
1401
1402            // 2 and 3 will be in the same payload
1403            socket.once('flush', function() {
1404              socket.send(2, function() { cb(2); });
1405              socket.send(3, function() { cb(3); });
1406            });
1407
1408            socket.send(1, function() { cb(1); });
1409          });
1410        });
1411      });
1412
1413      it('should execute when message sent (polling)', function (done) {
1414        var engine = listen({ allowUpgrades: false }, function (port) {
1415          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] });
1416          var i = 0;
1417          var j = 0;
1418
1419          engine.on('connection', function (conn) {
1420            conn.send('a', function (transport) {
1421              i++;
1422            });
1423          });
1424          socket.on('open', function () {
1425            socket.on('message', function (msg) {
1426              j++;
1427            });
1428          });
1429
1430          setTimeout(function() {
1431            expect(i).to.be(j);
1432            done();
1433          }, 10);
1434        });
1435      });
1436
1437      it('should execute when message sent (websocket)', function (done) {
1438        var engine = listen({ allowUpgrades: false }, function (port) {
1439          var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] });
1440          var i = 0;
1441          var j = 0;
1442
1443          engine.on('connection', function (conn) {
1444            conn.send('a', function (transport) {
1445              i++;
1446            });
1447          });
1448
1449          socket.on('open', function () {
1450            socket.on('message', function (msg) {
1451              j++;
1452            });
1453          });
1454
1455          setTimeout(function () {
1456            expect(i).to.be(j);
1457            done();
1458          }, 10);
1459        });
1460      });
1461
1462      it('should execute once for each send', function (done) {
1463        var engine = listen(function (port) {
1464          var socket = new eioc.Socket('ws://localhost:%d'.s(port));
1465          var a = 0;
1466          var b = 0;
1467          var c = 0;
1468          var all = 0;
1469
1470          engine.on('connection', function (conn) {
1471            conn.send('a');
1472            conn.send('b');
1473            conn.send(

Large files files are truncated, but you can click here to view the full file