tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

connection.js (7990B)


      1 var expect = require('chai').expect;
      2 var util = require('./util');
      3 
      4 var Connection = require('../lib/protocol/connection').Connection;
      5 
      6 var settings = {
      7  SETTINGS_MAX_CONCURRENT_STREAMS: 100,
      8  SETTINGS_INITIAL_WINDOW_SIZE: 100000
      9 };
     10 
     11 var MAX_PRIORITY = Math.pow(2, 31) - 1;
     12 var MAX_RANDOM_PRIORITY = 10;
     13 
     14 function randomPriority() {
     15  return Math.floor(Math.random() * (MAX_RANDOM_PRIORITY + 1));
     16 }
     17 
     18 function expectPriorityOrder(priorities) {
     19  priorities.forEach(function(bucket, priority) {
     20    bucket.forEach(function(stream) {
     21      expect(stream._priority).to.be.equal(priority);
     22    });
     23  });
     24 }
     25 
     26 describe('connection.js', function() {
     27  describe('Connection class', function() {
     28    describe('method ._insert(stream)', function() {
     29      it('should insert the stream in _streamPriorities in a place determined by stream._priority', function() {
     30        var streams = [];
     31        var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }});
     32        var streamCount = 10;
     33 
     34        for (var i = 0; i < streamCount; i++) {
     35          var stream = { _priority: randomPriority() };
     36          connection._insert(stream, stream._priority);
     37          expect(connection._streamPriorities[stream._priority]).to.include(stream);
     38        }
     39 
     40        expectPriorityOrder(connection._streamPriorities);
     41      });
     42    });
     43    describe('method ._reprioritize(stream)', function() {
     44      it('should eject and then insert the stream in _streamPriorities in a place determined by stream._priority', function() {
     45        var streams = [];
     46        var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }});
     47        var streamCount = 10;
     48        var oldPriority, newPriority, stream;
     49 
     50        for (var i = 0; i < streamCount; i++) {
     51          oldPriority = randomPriority();
     52          while ((newPriority = randomPriority()) === oldPriority);
     53          stream = { _priority: oldPriority };
     54          connection._insert(stream, oldPriority);
     55          connection._reprioritize(stream, newPriority);
     56          stream._priority = newPriority;
     57 
     58          expect(connection._streamPriorities[newPriority]).to.include(stream);
     59          expect(connection._streamPriorities[oldPriority] || []).to.not.include(stream);
     60        }
     61 
     62        expectPriorityOrder(streams);
     63      });
     64    });
     65    describe('invalid operation', function() {
     66      describe('unsolicited ping answer', function() {
     67        it('should be ignored', function() {
     68          var connection = new Connection(util.log, 1, settings);
     69 
     70          connection._receivePing({
     71            stream: 0,
     72            type: 'PING',
     73            flags: {
     74              'PONG': true
     75            },
     76            data: Buffer.alloc(8)
     77          });
     78        });
     79      });
     80    });
     81  });
     82  describe('test scenario', function() {
     83    var c, s;
     84    beforeEach(function() {
     85      c = new Connection(util.log.child({ role: 'client' }), 1, settings);
     86      s = new Connection(util.log.child({ role: 'client' }), 2, settings);
     87      c.pipe(s).pipe(c);
     88    });
     89 
     90    describe('connection setup', function() {
     91      it('should work as expected', function(done) {
     92        setTimeout(function() {
     93          // If there are no exception until this, then we're done
     94          done();
     95        }, 10);
     96      });
     97    });
     98    describe('sending/receiving a request', function() {
     99      it('should work as expected', function(done) {
    100        // Request and response data
    101        var request_headers = {
    102          ':method': 'GET',
    103          ':path': '/'
    104        };
    105        var request_data = Buffer.alloc(0);
    106        var response_headers = {
    107          ':status': '200'
    108        };
    109        var response_data = Buffer.from('12345678', 'hex');
    110 
    111        // Setting up server
    112        s.on('stream', function(server_stream) {
    113          server_stream.on('headers', function(headers) {
    114            expect(headers).to.deep.equal(request_headers);
    115            server_stream.headers(response_headers);
    116            server_stream.end(response_data);
    117          });
    118        });
    119 
    120        // Sending request
    121        var client_stream = c.createStream();
    122        client_stream.headers(request_headers);
    123        client_stream.end(request_data);
    124 
    125        // Waiting for answer
    126        done = util.callNTimes(2, done);
    127        client_stream.on('headers', function(headers) {
    128          expect(headers).to.deep.equal(response_headers);
    129          done();
    130        });
    131        client_stream.on('data', function(data) {
    132          expect(data).to.deep.equal(response_data);
    133          done();
    134        });
    135      });
    136    });
    137    describe('server push', function() {
    138      it('should work as expected', function(done) {
    139        var request_headers = { ':method': 'get', ':path': '/' };
    140        var response_headers = { ':status': '200' };
    141        var push_request_headers = { ':method': 'get', ':path': '/x' };
    142        var push_response_headers = { ':status': '200' };
    143        var response_content = Buffer.alloc(10);
    144        var push_content = Buffer.alloc(10);
    145 
    146        done = util.callNTimes(5, done);
    147 
    148        s.on('stream', function(response) {
    149          response.headers(response_headers);
    150 
    151          var pushed = response.promise(push_request_headers);
    152          pushed.headers(push_response_headers);
    153          pushed.end(push_content);
    154 
    155          response.end(response_content);
    156        });
    157 
    158        var request = c.createStream();
    159        request.headers(request_headers);
    160        request.end();
    161        request.on('headers', function(headers) {
    162          expect(headers).to.deep.equal(response_headers);
    163          done();
    164        });
    165        request.on('data', function(data) {
    166          expect(data).to.deep.equal(response_content);
    167          done();
    168        });
    169        request.on('promise', function(pushed, headers) {
    170          expect(headers).to.deep.equal(push_request_headers);
    171          pushed.on('headers', function(headers) {
    172            expect(headers).to.deep.equal(response_headers);
    173            done();
    174          });
    175          pushed.on('data', function(data) {
    176            expect(data).to.deep.equal(push_content);
    177            done();
    178          });
    179          pushed.on('end', done);
    180        });
    181      });
    182    });
    183    describe('ping from client', function() {
    184      it('should work as expected', function(done) {
    185        c.ping(function() {
    186          done();
    187        });
    188      });
    189    });
    190    describe('ping from server', function() {
    191      it('should work as expected', function(done) {
    192        s.ping(function() {
    193          done();
    194        });
    195      });
    196    });
    197    describe('creating two streams and then using them in reverse order', function() {
    198      it('should not result in non-monotonous local ID ordering', function() {
    199        var s1 = c.createStream();
    200        var s2 = c.createStream();
    201        s2.headers({ ':method': 'get', ':path': '/' });
    202        s1.headers({ ':method': 'get', ':path': '/' });
    203      });
    204    });
    205    describe('creating two promises and then using them in reverse order', function() {
    206      it('should not result in non-monotonous local ID ordering', function(done) {
    207        s.on('stream', function(response) {
    208          response.headers({ ':status': '200' });
    209 
    210          var p1 = s.createStream();
    211          var p2 = s.createStream();
    212          response.promise(p2, { ':method': 'get', ':path': '/p2' });
    213          response.promise(p1, { ':method': 'get', ':path': '/p1' });
    214          p2.headers({ ':status': '200' });
    215          p1.headers({ ':status': '200' });
    216        });
    217 
    218        var request = c.createStream();
    219        request.headers({ ':method': 'get', ':path': '/' });
    220 
    221        done = util.callNTimes(2, done);
    222        request.on('promise', function() {
    223          done();
    224        });
    225      });
    226    });
    227    describe('closing the connection on one end', function() {
    228      it('should result in closed streams on both ends', function(done) {
    229        done = util.callNTimes(2, done);
    230        c.on('end', done);
    231        s.on('end', done);
    232 
    233        c.close();
    234      });
    235    });
    236  });
    237 });