amqp-node
Version:
An AMQP 0-9-1 (e.g., RabbitMQ) library and client.
215 lines (184 loc) • 5.58 kB
JavaScript
;
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
]);
describe('Explicit parsing', function () {
it('Parse heartbeat', function () {
var input = inputs();
var frames = new Frames(input);
input.write(HB);
assert(frames.recvFrame() === HEARTBEAT);
assert(!frames.recvFrame());
});
it('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) {
it(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)));
describe('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
});
}
it('Parse trace of methods',
testPartitioning(function (bufs) {
return bufs;
}));
it('Parse concat\'d methods',
testPartitioning(function (bufs) {
return [Buffer.concat(bufs)];
}));
it('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));
describe('Content framing', function () {
it('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());
});