framer.js (12531B)
1 var expect = require('chai').expect; 2 var util = require('./util'); 3 4 var framer = require('../lib/protocol/framer'); 5 var Serializer = framer.Serializer; 6 var Deserializer = framer.Deserializer; 7 8 var frame_types = { 9 DATA: ['data'], 10 HEADERS: ['priority_information', 'data'], 11 PRIORITY: ['priority_information'], 12 RST_STREAM: ['error'], 13 SETTINGS: ['settings'], 14 PUSH_PROMISE: ['promised_stream', 'data'], 15 PING: ['data'], 16 GOAWAY: ['last_stream', 'error'], 17 WINDOW_UPDATE: ['window_size'], 18 CONTINUATION: ['data'], 19 ALTSVC: ['protocolID', 'host', 'port', 'origin', 'maxAge'] 20 }; 21 22 var test_frames = [{ 23 frame: { 24 type: 'DATA', 25 flags: { END_STREAM: false, RESERVED2: false, RESERVED4: false, 26 PADDED: false }, 27 stream: 10, 28 29 data: Buffer.from('12345678', 'hex') 30 }, 31 // length + type + flags + stream + content 32 buffer: Buffer.from('000004' + '00' + '00' + '0000000A' + '12345678', 'hex') 33 34 }, { 35 frame: { 36 type: 'HEADERS', 37 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 38 PADDED: false, RESERVED5: false, PRIORITY: false }, 39 stream: 15, 40 41 data: Buffer.from('12345678', 'hex') 42 }, 43 buffer: Buffer.from('000004' + '01' + '00' + '0000000F' + '12345678', 'hex') 44 45 }, { 46 frame: { 47 type: 'HEADERS', 48 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 49 PADDED: false, RESERVED5: false, PRIORITY: true }, 50 stream: 15, 51 priorityDependency: 10, 52 priorityWeight: 5, 53 exclusiveDependency: false, 54 55 data: Buffer.from('12345678', 'hex') 56 }, 57 buffer: Buffer.from('000009' + '01' + '20' + '0000000F' + '0000000A' + '05' + '12345678', 'hex') 58 59 60 }, { 61 frame: { 62 type: 'HEADERS', 63 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 64 PADDED: false, RESERVED5: false, PRIORITY: true }, 65 stream: 15, 66 priorityDependency: 10, 67 priorityWeight: 5, 68 exclusiveDependency: true, 69 70 data: Buffer.from('12345678', 'hex') 71 }, 72 buffer: Buffer.from('000009' + '01' + '20' + '0000000F' + '8000000A' + '05' + '12345678', 'hex') 73 74 }, { 75 frame: { 76 type: 'PRIORITY', 77 flags: { }, 78 stream: 10, 79 80 priorityDependency: 9, 81 priorityWeight: 5, 82 exclusiveDependency: false 83 }, 84 buffer: Buffer.from('000005' + '02' + '00' + '0000000A' + '00000009' + '05', 'hex') 85 86 }, { 87 frame: { 88 type: 'PRIORITY', 89 flags: { }, 90 stream: 10, 91 92 priorityDependency: 9, 93 priorityWeight: 5, 94 exclusiveDependency: true 95 }, 96 buffer: Buffer.from('000005' + '02' + '00' + '0000000A' + '80000009' + '05', 'hex') 97 98 }, { 99 frame: { 100 type: 'RST_STREAM', 101 flags: { }, 102 stream: 10, 103 104 error: 'INTERNAL_ERROR' 105 }, 106 buffer: Buffer.from('000004' + '03' + '00' + '0000000A' + '00000002', 'hex') 107 108 }, { 109 frame: { 110 type: 'SETTINGS', 111 flags: { ACK: false }, 112 stream: 10, 113 114 settings: { 115 SETTINGS_HEADER_TABLE_SIZE: 0x12345678, 116 SETTINGS_ENABLE_PUSH: true, 117 SETTINGS_MAX_CONCURRENT_STREAMS: 0x01234567, 118 SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF, 119 SETTINGS_MAX_FRAME_SIZE: 0x00010000 120 } 121 }, 122 buffer: Buffer.from('00001E' + '04' + '00' + '0000000A' + '0001' + '12345678' + 123 '0002' + '00000001' + 124 '0003' + '01234567' + 125 '0004' + '89ABCDEF' + 126 '0005' + '00010000', 'hex') 127 128 }, { 129 frame: { 130 type: 'PUSH_PROMISE', 131 flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false, 132 PADDED: false }, 133 stream: 15, 134 135 promised_stream: 3, 136 data: Buffer.from('12345678', 'hex') 137 }, 138 buffer: Buffer.from('000008' + '05' + '00' + '0000000F' + '00000003' + '12345678', 'hex') 139 140 }, { 141 frame: { 142 type: 'PING', 143 flags: { ACK: false }, 144 stream: 15, 145 146 data: Buffer.from('1234567887654321', 'hex') 147 }, 148 buffer: Buffer.from('000008' + '06' + '00' + '0000000F' + '1234567887654321', 'hex') 149 150 }, { 151 frame: { 152 type: 'GOAWAY', 153 flags: { }, 154 stream: 10, 155 156 last_stream: 0x12345678, 157 error: 'PROTOCOL_ERROR' 158 }, 159 buffer: Buffer.from('000008' + '07' + '00' + '0000000A' + '12345678' + '00000001', 'hex') 160 161 }, { 162 frame: { 163 type: 'WINDOW_UPDATE', 164 flags: { }, 165 stream: 10, 166 167 window_size: 0x12345678 168 }, 169 buffer: Buffer.from('000004' + '08' + '00' + '0000000A' + '12345678', 'hex') 170 }, { 171 frame: { 172 type: 'CONTINUATION', 173 flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true }, 174 stream: 10, 175 176 data: Buffer.from('12345678', 'hex') 177 }, 178 // length + type + flags + stream + content 179 buffer: Buffer.from('000004' + '09' + '04' + '0000000A' + '12345678', 'hex') 180 }, { 181 frame: { 182 type: 'ALTSVC', 183 flags: { }, 184 stream: 0, 185 186 maxAge: 31536000, 187 port: 4443, 188 protocolID: "h2", 189 host: "altsvc.example.com", 190 origin: "" 191 }, 192 buffer: Buffer.from(Buffer.from('00002B' + '0A' + '00' + '00000000' + '0000', 'hex') + Buffer.from('h2="altsvc.example.com:4443"; ma=31536000', 'ascii')) 193 }, { 194 frame: { 195 type: 'ALTSVC', 196 flags: { }, 197 stream: 0, 198 199 maxAge: 31536000, 200 port: 4443, 201 protocolID: "h2", 202 host: "altsvc.example.com", 203 origin: "https://onlyme.example.com" 204 }, 205 buffer: Buffer.from(Buffer.from('000045' + '0A' + '00' + '00000000' + '001A', 'hex') + Buffer.from('https://onlyme.example.comh2="altsvc.example.com:4443"; ma=31536000', 'ascii')) 206 207 }, { 208 frame: { 209 type: 'BLOCKED', 210 flags: { }, 211 stream: 10 212 }, 213 buffer: Buffer.from('000000' + '0B' + '00' + '0000000A', 'hex') 214 }]; 215 216 var deserializer_test_frames = test_frames.slice(0); 217 var padded_test_frames = [{ 218 frame: { 219 type: 'DATA', 220 flags: { END_STREAM: false, RESERVED2: false, RESERVED4: false, 221 PADDED: true }, 222 stream: 10, 223 data: Buffer.from('12345678', 'hex') 224 }, 225 // length + type + flags + stream + pad length + content + padding 226 buffer: Buffer.from('00000B' + '00' + '08' + '0000000A' + '06' + '12345678' + '000000000000', 'hex') 227 228 }, { 229 frame: { 230 type: 'HEADERS', 231 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 232 PADDED: true, RESERVED5: false, PRIORITY: false }, 233 stream: 15, 234 235 data: Buffer.from('12345678', 'hex') 236 }, 237 // length + type + flags + stream + pad length + data + padding 238 buffer: Buffer.from('00000B' + '01' + '08' + '0000000F' + '06' + '12345678' + '000000000000', 'hex') 239 240 }, { 241 frame: { 242 type: 'HEADERS', 243 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 244 PADDED: true, RESERVED5: false, PRIORITY: true }, 245 stream: 15, 246 priorityDependency: 10, 247 priorityWeight: 5, 248 exclusiveDependency: false, 249 250 data: Buffer.from('12345678', 'hex') 251 }, 252 // length + type + flags + stream + pad length + priority dependency + priority weight + data + padding 253 buffer: Buffer.from('000010' + '01' + '28' + '0000000F' + '06' + '0000000A' + '05' + '12345678' + '000000000000', 'hex') 254 255 }, { 256 frame: { 257 type: 'HEADERS', 258 flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false, 259 PADDED: true, RESERVED5: false, PRIORITY: true }, 260 stream: 15, 261 priorityDependency: 10, 262 priorityWeight: 5, 263 exclusiveDependency: true, 264 265 data: Buffer.from('12345678', 'hex') 266 }, 267 // length + type + flags + stream + pad length + priority dependency + priority weight + data + padding 268 buffer: Buffer.from('000010' + '01' + '28' + '0000000F' + '06' + '8000000A' + '05' + '12345678' + '000000000000', 'hex') 269 270 }, { 271 frame: { 272 type: 'PUSH_PROMISE', 273 flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false, 274 PADDED: true }, 275 stream: 15, 276 277 promised_stream: 3, 278 data: Buffer.from('12345678', 'hex') 279 }, 280 // length + type + flags + stream + pad length + promised stream + data + padding 281 buffer: Buffer.from('00000F' + '05' + '08' + '0000000F' + '06' + '00000003' + '12345678' + '000000000000', 'hex') 282 283 }]; 284 for (var idx = 0; idx < padded_test_frames.length; idx++) { 285 deserializer_test_frames.push(padded_test_frames[idx]); 286 } 287 288 289 describe('framer.js', function() { 290 describe('Serializer', function() { 291 describe('static method .commonHeader({ type, flags, stream }, buffer_array)', function() { 292 it('should add the appropriate 9 byte header buffer in front of the others', function() { 293 for (var i = 0; i < test_frames.length; i++) { 294 var test = test_frames[i]; 295 var buffers = [test.buffer.slice(9)]; 296 var header_buffer = test.buffer.slice(0,9); 297 Serializer.commonHeader(test.frame, buffers); 298 expect(buffers[0]).to.deep.equal(header_buffer); 299 } 300 }); 301 }); 302 303 Object.keys(frame_types).forEach(function(type) { 304 var tests = test_frames.filter(function(test) { return test.frame.type === type; }); 305 var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'; 306 describe('static method .' + type + '(' + frame_shape + ', buffer_array)', function() { 307 it('should push buffers to the array that make up a ' + type + ' type payload', function() { 308 for (var i = 0; i < tests.length; i++) { 309 var test = tests[i]; 310 var buffers = []; 311 Serializer[type](test.frame, buffers); 312 expect(util.concat(buffers)).to.deep.equal(test.buffer.slice(9)); 313 } 314 }); 315 }); 316 }); 317 318 describe('transform stream', function() { 319 it('should transform frame objects to appropriate buffers', function() { 320 var stream = new Serializer(util.log); 321 322 for (var i = 0; i < test_frames.length; i++) { 323 var test = test_frames[i]; 324 stream.write(test.frame); 325 var chunk, buffer = Buffer.alloc(0); 326 while (chunk = stream.read()) { 327 buffer = util.concat([buffer, chunk]); 328 } 329 expect(buffer).to.be.deep.equal(test.buffer); 330 } 331 }); 332 }); 333 }); 334 335 describe('Deserializer', function() { 336 describe('static method .commonHeader(header_buffer, frame)', function() { 337 it('should augment the frame object with these properties: { type, flags, stream })', function() { 338 for (var i = 0; i < deserializer_test_frames.length; i++) { 339 var test = deserializer_test_frames[i], frame = {}; 340 Deserializer.commonHeader(test.buffer.slice(0,9), frame); 341 expect(frame).to.deep.equal({ 342 type: test.frame.type, 343 flags: test.frame.flags, 344 stream: test.frame.stream 345 }); 346 } 347 }); 348 }); 349 350 Object.keys(frame_types).forEach(function(type) { 351 var tests = deserializer_test_frames.filter(function(test) { return test.frame.type === type; }); 352 var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'; 353 describe('static method .' + type + '(payload_buffer, frame)', function() { 354 it('should augment the frame object with these properties: ' + frame_shape, function() { 355 for (var i = 0; i < tests.length; i++) { 356 var test = tests[i]; 357 var frame = { 358 type: test.frame.type, 359 flags: test.frame.flags, 360 stream: test.frame.stream 361 }; 362 Deserializer[type](test.buffer.slice(9), frame); 363 expect(frame).to.deep.equal(test.frame); 364 } 365 }); 366 }); 367 }); 368 369 describe('transform stream', function() { 370 it('should transform buffers to appropriate frame object', function() { 371 var stream = new Deserializer(util.log); 372 373 var shuffled = util.shuffleBuffers(deserializer_test_frames.map(function(test) { return test.buffer; })); 374 shuffled.forEach(stream.write.bind(stream)); 375 376 for (var j = 0; j < deserializer_test_frames.length; j++) { 377 expect(stream.read()).to.be.deep.equal(deserializer_test_frames[j].frame); 378 } 379 }); 380 }); 381 }); 382 383 describe('bunyan formatter', function() { 384 describe('`frame`', function() { 385 var format = framer.serializers.frame; 386 it('should assign a unique ID to each frame', function() { 387 var frame1 = { type: 'DATA', data: Buffer.alloc(10) }; 388 var frame2 = { type: 'PRIORITY', priority: 1 }; 389 expect(format(frame1).id).to.be.equal(format(frame1)); 390 expect(format(frame2).id).to.be.equal(format(frame2)); 391 expect(format(frame1)).to.not.be.equal(format(frame2)); 392 }); 393 }); 394 }); 395 });