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 });