You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.7 KiB
199 lines
5.7 KiB
'use strict'; |
|
|
|
var assert = require('assert'); |
|
var succeed = require('./util').succeed; |
|
var fail = require('./util').fail; |
|
|
|
var connection = require('../lib/connection'); |
|
var Frames = connection.Connection; |
|
var HEARTBEAT = require('../lib/frame').HEARTBEAT; |
|
var Stream = require('stream'); |
|
var PassThrough = Stream.PassThrough || |
|
require('readable-stream/passthrough'); |
|
|
|
var defs = require('../lib/defs'); |
|
|
|
// We'll need to supply a stream which we manipulate ourselves |
|
|
|
function inputs() { |
|
// don't coalesce buffers, since that could mess up properties |
|
// (e.g., encoded frame size) |
|
return new PassThrough({objectMode: true}); |
|
} |
|
|
|
var HB = new Buffer([defs.constants.FRAME_HEARTBEAT, |
|
0, 0, // channel 0 |
|
0, 0, 0, 0, // zero size |
|
defs.constants.FRAME_END]); |
|
|
|
suite("Explicit parsing", function() { |
|
|
|
test('Parse heartbeat', function() { |
|
var input = inputs(); |
|
var frames = new Frames(input); |
|
input.write(HB); |
|
assert(frames.recvFrame() === HEARTBEAT); |
|
assert(!frames.recvFrame()); |
|
}); |
|
|
|
test('Parse partitioned', function() { |
|
var input = inputs(); |
|
var frames = new Frames(input); |
|
input.write(HB.slice(0, 3)); |
|
assert(!frames.recvFrame()); |
|
input.write(HB.slice(3)); |
|
assert(frames.recvFrame() === HEARTBEAT); |
|
assert(!frames.recvFrame()); |
|
}); |
|
|
|
function testBogusFrame(name, bytes) { |
|
test(name, function(done) { |
|
var input = inputs(); |
|
var frames = new Frames(input); |
|
frames.frameMax = 5; //for the max frame test |
|
input.write(new Buffer(bytes)); |
|
frames.step(function(err, frame) { |
|
if (err != null) done(); |
|
else done(new Error('Was a bogus frame!')); |
|
}); |
|
}); |
|
} |
|
|
|
testBogusFrame('Wrong sized frame', |
|
[defs.constants.FRAME_BODY, |
|
0,0, 0,0,0,0, // zero length |
|
65, // but a byte! |
|
defs.constants.FRAME_END]); |
|
|
|
testBogusFrame('Unknown method frame', |
|
[defs.constants.FRAME_METHOD, |
|
0,0, 0,0,0,4, |
|
0,0,0,0, // garbage ID |
|
defs.constants.FRAME_END]); |
|
|
|
testBogusFrame('> max frame', |
|
[defs.constants.FRAME_BODY, |
|
0,0, 0,0,0,6, // too big! |
|
65,66,67,68,69,70, |
|
defs.constants.FRAME_END]); |
|
|
|
}); |
|
|
|
// Now for a bit more fun. |
|
|
|
var amqp = require('./data'); |
|
var claire = require('claire'); |
|
var choice = claire.choice; |
|
var forAll = claire.forAll; |
|
var repeat = claire.repeat; |
|
var label = claire.label; |
|
var sequence = claire.sequence; |
|
var transform = claire.transform; |
|
var sized = claire.sized; |
|
|
|
var assertEqualModuloDefaults = |
|
require('./codec').assertEqualModuloDefaults; |
|
|
|
var Trace = label('frame trace', |
|
repeat(choice.apply(choice, amqp.methods))); |
|
|
|
suite("Parsing", function() { |
|
|
|
function testPartitioning(partition) { |
|
return forAll(Trace).satisfy(function(t) { |
|
var bufs = []; |
|
var input = inputs(); |
|
var frames = new Frames(input); |
|
var i = 0, ex; |
|
frames.accept = function(f) { |
|
// A minor hack to make sure we get the assertion exception; |
|
// otherwise, it's just a test that we reached the line |
|
// incrementing `i` for each frame. |
|
try { |
|
assertEqualModuloDefaults(t[i], f.fields); |
|
} |
|
catch (e) { |
|
ex = e; |
|
} |
|
i++; |
|
}; |
|
|
|
t.forEach(function(f) { |
|
f.channel = 0; |
|
bufs.push(defs.encodeMethod(f.id, 0, f.fields)); |
|
}); |
|
|
|
partition(bufs).forEach(input.write.bind(input)); |
|
frames.acceptLoop(); |
|
if (ex) throw ex; |
|
return i === t.length; |
|
}).asTest({times: 20}) |
|
}; |
|
|
|
test("Parse trace of methods", |
|
testPartitioning(function(bufs) { return bufs; })); |
|
|
|
test("Parse concat'd methods", |
|
testPartitioning(function(bufs) { |
|
return [Buffer.concat(bufs)]; |
|
})); |
|
|
|
test("Parse partitioned methods", |
|
testPartitioning(function(bufs) { |
|
var full = Buffer.concat(bufs); |
|
var onethird = Math.floor(full.length / 3); |
|
var twothirds = 2 * onethird; |
|
return [ |
|
full.slice(0, onethird), |
|
full.slice(onethird, twothirds), |
|
full.slice(twothirds) |
|
]; |
|
})); |
|
}); |
|
|
|
var FRAME_MAX_MAX = 4096 * 4; |
|
var FRAME_MAX_MIN = 4096; |
|
|
|
var FrameMax = amqp.rangeInt('frame max', |
|
FRAME_MAX_MIN, |
|
FRAME_MAX_MAX); |
|
|
|
var Body = sized(function(_n) { |
|
return Math.floor(Math.random() * FRAME_MAX_MAX); |
|
}, repeat(amqp.Octet)); |
|
|
|
var Content = transform(function(args) { |
|
return { |
|
method: args[0].fields, |
|
header: args[1].fields, |
|
body: new Buffer(args[2]) |
|
} |
|
}, sequence(amqp.methods['BasicDeliver'], |
|
amqp.properties['BasicProperties'], Body)); |
|
|
|
suite("Content framing", function() { |
|
test("Adhere to frame max", |
|
forAll(Content, FrameMax).satisfy(function(content, max) { |
|
var input = inputs(); |
|
var frames = new Frames(input); |
|
frames.frameMax = max; |
|
frames.sendMessage( |
|
0, |
|
defs.BasicDeliver, content.method, |
|
defs.BasicProperties, content.header, |
|
content.body); |
|
var f, i = 0, largest = 0; |
|
while (f = input.read()) { |
|
i++; |
|
if (f.length > largest) largest = f.length; |
|
if (f.length > max) { |
|
return false; |
|
} |
|
} |
|
// The ratio of frames to 'contents' should always be >= 2 |
|
// (one properties frame and at least one content frame); > 2 |
|
// indicates fragmentation. The largest is always, of course <= frame max |
|
//console.log('Frames: %d; frames per message: %d; largest frame %d', i, i / t.length, largest); |
|
return true; |
|
}).asTest()); |
|
});
|
|
|