frame.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. 'use strict';
  2. var assert = require('assert');
  3. var succeed = require('./util').succeed;
  4. var fail = require('./util').fail;
  5. var connection = require('../lib/connection');
  6. var Frames = connection.Connection;
  7. var HEARTBEAT = require('../lib/frame').HEARTBEAT;
  8. var Stream = require('stream');
  9. var PassThrough = Stream.PassThrough;
  10. var defs = require('../lib/defs');
  11. // We'll need to supply a stream which we manipulate ourselves
  12. function inputs() {
  13. // don't coalesce buffers, since that could mess up properties
  14. // (e.g., encoded frame size)
  15. return new PassThrough({objectMode: true});
  16. }
  17. var HB = Buffer.from([defs.constants.FRAME_HEARTBEAT,
  18. 0, 0, // channel 0
  19. 0, 0, 0, 0, // zero size
  20. defs.constants.FRAME_END]);
  21. suite("Explicit parsing", function() {
  22. test('Parse heartbeat', function() {
  23. var input = inputs();
  24. var frames = new Frames(input);
  25. input.write(HB);
  26. assert(frames.recvFrame() === HEARTBEAT);
  27. assert(!frames.recvFrame());
  28. });
  29. test('Parse partitioned', function() {
  30. var input = inputs();
  31. var frames = new Frames(input);
  32. input.write(HB.subarray(0, 3));
  33. assert(!frames.recvFrame());
  34. input.write(HB.subarray(3));
  35. assert(frames.recvFrame() === HEARTBEAT);
  36. assert(!frames.recvFrame());
  37. });
  38. function testBogusFrame(name, bytes) {
  39. test(name, function(done) {
  40. var input = inputs();
  41. var frames = new Frames(input);
  42. frames.frameMax = 5; //for the max frame test
  43. input.write(Buffer.from(bytes));
  44. frames.step(function(err, frame) {
  45. if (err != null) done();
  46. else done(new Error('Was a bogus frame!'));
  47. });
  48. });
  49. }
  50. testBogusFrame('Wrong sized frame',
  51. [defs.constants.FRAME_BODY,
  52. 0,0, 0,0,0,0, // zero length
  53. 65, // but a byte!
  54. defs.constants.FRAME_END]);
  55. testBogusFrame('Unknown method frame',
  56. [defs.constants.FRAME_METHOD,
  57. 0,0, 0,0,0,4,
  58. 0,0,0,0, // garbage ID
  59. defs.constants.FRAME_END]);
  60. testBogusFrame('> max frame',
  61. [defs.constants.FRAME_BODY,
  62. 0,0, 0,0,0,6, // too big!
  63. 65,66,67,68,69,70,
  64. defs.constants.FRAME_END]);
  65. });
  66. // Now for a bit more fun.
  67. var amqp = require('./data');
  68. var claire = require('claire');
  69. var choice = claire.choice;
  70. var forAll = claire.forAll;
  71. var repeat = claire.repeat;
  72. var label = claire.label;
  73. var sequence = claire.sequence;
  74. var transform = claire.transform;
  75. var sized = claire.sized;
  76. var assertEqualModuloDefaults =
  77. require('./codec').assertEqualModuloDefaults;
  78. var Trace = label('frame trace',
  79. repeat(choice.apply(choice, amqp.methods)));
  80. suite("Parsing", function() {
  81. function testPartitioning(partition) {
  82. return forAll(Trace).satisfy(function(t) {
  83. var bufs = [];
  84. var input = inputs();
  85. var frames = new Frames(input);
  86. var i = 0, ex;
  87. frames.accept = function(f) {
  88. // A minor hack to make sure we get the assertion exception;
  89. // otherwise, it's just a test that we reached the line
  90. // incrementing `i` for each frame.
  91. try {
  92. assertEqualModuloDefaults(t[i], f.fields);
  93. }
  94. catch (e) {
  95. ex = e;
  96. }
  97. i++;
  98. };
  99. t.forEach(function(f) {
  100. f.channel = 0;
  101. bufs.push(defs.encodeMethod(f.id, 0, f.fields));
  102. });
  103. partition(bufs).forEach(function (chunk) { input.write(chunk); });
  104. frames.acceptLoop();
  105. if (ex) throw ex;
  106. return i === t.length;
  107. }).asTest({times: 20})
  108. };
  109. test("Parse trace of methods",
  110. testPartitioning(function(bufs) { return bufs; }));
  111. test("Parse concat'd methods",
  112. testPartitioning(function(bufs) {
  113. return [Buffer.concat(bufs)];
  114. }));
  115. test("Parse partitioned methods",
  116. testPartitioning(function(bufs) {
  117. var full = Buffer.concat(bufs);
  118. var onethird = Math.floor(full.length / 3);
  119. var twothirds = 2 * onethird;
  120. return [
  121. full.subarray(0, onethird),
  122. full.subarray(onethird, twothirds),
  123. full.subarray(twothirds)
  124. ];
  125. }));
  126. });
  127. var FRAME_MAX_MAX = 4096 * 4;
  128. var FRAME_MAX_MIN = 4096;
  129. var FrameMax = amqp.rangeInt('frame max',
  130. FRAME_MAX_MIN,
  131. FRAME_MAX_MAX);
  132. var Body = sized(function(_n) {
  133. return Math.floor(Math.random() * FRAME_MAX_MAX);
  134. }, repeat(amqp.Octet));
  135. var Content = transform(function(args) {
  136. return {
  137. method: args[0].fields,
  138. header: args[1].fields,
  139. body: Buffer.from(args[2])
  140. }
  141. }, sequence(amqp.methods['BasicDeliver'],
  142. amqp.properties['BasicProperties'], Body));
  143. suite("Content framing", function() {
  144. test("Adhere to frame max",
  145. forAll(Content, FrameMax).satisfy(function(content, max) {
  146. var input = inputs();
  147. var frames = new Frames(input);
  148. frames.frameMax = max;
  149. frames.sendMessage(
  150. 0,
  151. defs.BasicDeliver, content.method,
  152. defs.BasicProperties, content.header,
  153. content.body);
  154. var f, i = 0, largest = 0;
  155. while (f = input.read()) {
  156. i++;
  157. if (f.length > largest) largest = f.length;
  158. if (f.length > max) {
  159. return false;
  160. }
  161. }
  162. // The ratio of frames to 'contents' should always be >= 2
  163. // (one properties frame and at least one content frame); > 2
  164. // indicates fragmentation. The largest is always, of course <= frame max
  165. //console.log('Frames: %d; frames per message: %d; largest frame %d', i, i / t.length, largest);
  166. return true;
  167. }).asTest());
  168. });