michael@0: var expect = require('chai').expect; michael@0: var util = require('./util'); michael@0: michael@0: var framer = require('../lib/framer'); michael@0: var Serializer = framer.Serializer; michael@0: var Deserializer = framer.Deserializer; michael@0: michael@0: var frame_types = { michael@0: DATA: ['data'], michael@0: HEADERS: ['priority', 'data'], michael@0: PRIORITY: ['priority'], michael@0: RST_STREAM: ['error'], michael@0: SETTINGS: ['settings'], michael@0: PUSH_PROMISE: ['promised_stream', 'data'], michael@0: PING: ['data'], michael@0: GOAWAY: ['last_stream', 'error'], michael@0: WINDOW_UPDATE: ['window_size'], michael@0: CONTINUATION: ['data'] michael@0: }; michael@0: michael@0: var test_frames = [{ michael@0: frame: { michael@0: type: 'DATA', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, RESERVED4: false, michael@0: RESERVED8: false, PAD_LOW: false, PAD_HIGH: false }, michael@0: stream: 10, michael@0: michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: // length + type + flags + stream + content michael@0: buffer: new Buffer('0004' + '00' + '00' + '0000000A' + '12345678', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'HEADERS', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, michael@0: PRIORITY: false, PAD_LOW: false, PAD_HIGH: false }, michael@0: stream: 15, michael@0: michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: buffer: new Buffer('0004' + '01' + '00' + '0000000F' + '12345678', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'HEADERS', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, michael@0: PRIORITY: true, PAD_LOW: false, PAD_HIGH: false }, michael@0: stream: 15, michael@0: michael@0: priority: 3, michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: buffer: new Buffer('0008' + '01' + '08' + '0000000F' + '00000003' + '12345678', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'PRIORITY', michael@0: flags: { }, michael@0: stream: 10, michael@0: michael@0: priority: 3 michael@0: }, michael@0: buffer: new Buffer('0004' + '02' + '00' + '0000000A' + '00000003', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'RST_STREAM', michael@0: flags: { }, michael@0: stream: 10, michael@0: michael@0: error: 'INTERNAL_ERROR' michael@0: }, michael@0: buffer: new Buffer('0004' + '03' + '00' + '0000000A' + '00000002', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'SETTINGS', michael@0: flags: { ACK: false }, michael@0: stream: 10, michael@0: michael@0: settings: { michael@0: SETTINGS_HEADER_TABLE_SIZE: 0x12345678, michael@0: SETTINGS_ENABLE_PUSH: true, michael@0: SETTINGS_MAX_CONCURRENT_STREAMS: 0x01234567, michael@0: SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF michael@0: } michael@0: }, michael@0: buffer: new Buffer('0014' + '04' + '00' + '0000000A' + '01' + '12345678' + michael@0: '02' + '00000001' + michael@0: '03' + '01234567' + michael@0: '04' + '89ABCDEF', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'PUSH_PROMISE', michael@0: flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false }, michael@0: stream: 15, michael@0: michael@0: promised_stream: 3, michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: buffer: new Buffer('0008' + '05' + '00' + '0000000F' + '00000003' + '12345678', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'PING', michael@0: flags: { ACK: false }, michael@0: stream: 15, michael@0: michael@0: data: new Buffer('1234567887654321', 'hex') michael@0: }, michael@0: buffer: new Buffer('0008' + '06' + '00' + '0000000F' + '1234567887654321', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'GOAWAY', michael@0: flags: { }, michael@0: stream: 10, michael@0: michael@0: last_stream: 0x12345678, michael@0: error: 'PROTOCOL_ERROR' michael@0: }, michael@0: buffer: new Buffer('0008' + '07' + '00' + '0000000A' + '12345678' + '00000001', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'WINDOW_UPDATE', michael@0: flags: { }, michael@0: stream: 10, michael@0: michael@0: window_size: 0x12345678 michael@0: }, michael@0: buffer: new Buffer('0004' + '08' + '00' + '0000000A' + '12345678', 'hex') michael@0: }, { michael@0: frame: { michael@0: type: 'CONTINUATION', michael@0: flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true, michael@0: RESERVED8: false, PAD_LOW: false, PAD_HIGH: false }, michael@0: stream: 10, michael@0: michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: // length + type + flags + stream + content michael@0: buffer: new Buffer('0004' + '09' + '04' + '0000000A' + '12345678', 'hex') michael@0: }]; michael@0: michael@0: var deserializer_test_frames = test_frames.slice(0); michael@0: var padded_test_frames = [{ michael@0: frame: { michael@0: type: 'DATA', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, RESERVED4: false, michael@0: RESERVED8: false, PAD_LOW: true, PAD_HIGH: false }, michael@0: stream: 10, michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: // length + type + flags + stream + pad_low control + content + padding michael@0: buffer: new Buffer('000B' + '00' + '10' + '0000000A' + '06' + '12345678' + '000000000000', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'HEADERS', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, michael@0: PRIORITY: false, PAD_LOW: true, PAD_HIGH: false }, michael@0: stream: 15, michael@0: michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: buffer: new Buffer('000B' + '01' + '10' + '0000000F' + '06' + '12345678' + '000000000000', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'HEADERS', michael@0: flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, michael@0: PRIORITY: true, PAD_LOW: true, PAD_HIGH: false }, michael@0: stream: 15, michael@0: michael@0: priority: 3, michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: buffer: new Buffer('000F' + '01' + '18' + '0000000F' + '06' + '00000003' + '12345678' + '000000000000', 'hex') michael@0: michael@0: }, { michael@0: frame: { michael@0: type: 'CONTINUATION', michael@0: flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true, michael@0: RESERVED8: false, PAD_LOW: true, PAD_HIGH: false }, michael@0: stream: 10, michael@0: michael@0: data: new Buffer('12345678', 'hex') michael@0: }, michael@0: // length + type + flags + stream + content michael@0: buffer: new Buffer('000B' + '09' + '14' + '0000000A' + '06' + '12345678' + '000000000000', 'hex') michael@0: }]; michael@0: for (var idx = 0; idx < padded_test_frames.length; idx++) { michael@0: deserializer_test_frames.push(padded_test_frames[idx]); michael@0: } michael@0: michael@0: michael@0: describe('framer.js', function() { michael@0: describe('Serializer', function() { michael@0: describe('static method .commonHeader({ type, flags, stream }, buffer_array)', function() { michael@0: it('should add the appropriate 8 byte header buffer in front of the others', function() { michael@0: for (var i = 0; i < test_frames.length; i++) { michael@0: var test = test_frames[i]; michael@0: var buffers = [test.buffer.slice(8)]; michael@0: var header_buffer = test.buffer.slice(0,8); michael@0: Serializer.commonHeader(test.frame, buffers); michael@0: expect(buffers[0]).to.deep.equal(header_buffer); michael@0: } michael@0: }); michael@0: }); michael@0: michael@0: Object.keys(frame_types).forEach(function(type) { michael@0: var tests = test_frames.filter(function(test) { return test.frame.type === type; }); michael@0: var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'; michael@0: describe('static method .' + type + '(' + frame_shape + ', buffer_array)', function() { michael@0: it('should push buffers to the array that make up a ' + type + ' type payload', function() { michael@0: for (var i = 0; i < tests.length; i++) { michael@0: var test = tests[i]; michael@0: var buffers = []; michael@0: Serializer[type](test.frame, buffers); michael@0: expect(util.concat(buffers)).to.deep.equal(test.buffer.slice(8)); michael@0: } michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: describe('transform stream', function() { michael@0: it('should transform frame objects to appropriate buffers', function() { michael@0: var stream = new Serializer(util.log); michael@0: michael@0: for (var i = 0; i < test_frames.length; i++) { michael@0: var test = test_frames[i]; michael@0: stream.write(test.frame); michael@0: var chunk, buffer = new Buffer(0); michael@0: while (chunk = stream.read()) { michael@0: buffer = util.concat([buffer, chunk]); michael@0: } michael@0: expect(buffer).to.be.deep.equal(test.buffer); michael@0: } michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: describe('Deserializer', function() { michael@0: describe('static method .commonHeader(header_buffer, frame)', function() { michael@0: it('should augment the frame object with these properties: { type, flags, stream })', function() { michael@0: for (var i = 0; i < deserializer_test_frames.length; i++) { michael@0: var test = deserializer_test_frames[i], frame = {}; michael@0: Deserializer.commonHeader(test.buffer.slice(0,8), frame); michael@0: expect(frame).to.deep.equal({ michael@0: type: test.frame.type, michael@0: flags: test.frame.flags, michael@0: stream: test.frame.stream michael@0: }); michael@0: } michael@0: }); michael@0: }); michael@0: michael@0: Object.keys(frame_types).forEach(function(type) { michael@0: var tests = deserializer_test_frames.filter(function(test) { return test.frame.type === type; }); michael@0: var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'; michael@0: describe('static method .' + type + '(payload_buffer, frame)', function() { michael@0: it('should augment the frame object with these properties: ' + frame_shape, function() { michael@0: for (var i = 0; i < tests.length; i++) { michael@0: var test = tests[i]; michael@0: var frame = { michael@0: type: test.frame.type, michael@0: flags: test.frame.flags, michael@0: stream: test.frame.stream michael@0: }; michael@0: Deserializer[type](test.buffer.slice(8), frame); michael@0: expect(frame).to.deep.equal(test.frame); michael@0: } michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: describe('transform stream', function() { michael@0: it('should transform buffers to appropriate frame object', function() { michael@0: var stream = new Deserializer(util.log); michael@0: michael@0: var shuffled = util.shuffleBuffers(deserializer_test_frames.map(function(test) { return test.buffer; })); michael@0: shuffled.forEach(stream.write.bind(stream)); michael@0: michael@0: for (var j = 0; j < deserializer_test_frames.length; j++) { michael@0: expect(stream.read()).to.be.deep.equal(deserializer_test_frames[j].frame); michael@0: } michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: describe('bunyan formatter', function() { michael@0: describe('`frame`', function() { michael@0: var format = framer.serializers.frame; michael@0: it('should assign a unique ID to each frame', function() { michael@0: var frame1 = { type: 'DATA', data: new Buffer(10) }; michael@0: var frame2 = { type: 'PRIORITY', priority: 1 }; michael@0: expect(format(frame1).id).to.be.equal(format(frame1)); michael@0: expect(format(frame2).id).to.be.equal(format(frame2)); michael@0: expect(format(frame1)).to.not.be.equal(format(frame2)); michael@0: }); michael@0: }); michael@0: }); michael@0: });