amqplib 0.10.5 → 0.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change log for amqplib
2
2
 
3
+ ## Unreleased
4
+ - Remove bitsyntax dependency - See https://github.com/amqp-node/amqplib/pull/785. Thanks @ikenfin
5
+ - Stop checking if frame max is exceeded when parsing frames - See https://github.com/amqp-node/amqplib/pull/784. Thanks @ohroy
6
+
7
+ ## v0.10.6
8
+ - Replace references to the old squaremo/amqp.name repo with ones to amqp-node/amqplib
9
+ - Use a frame_max of 131072 by default for RabbitMQ 4.1.0 compatibility
10
+
3
11
  ## Changes in v0.10.5
4
12
 
5
13
  git log v0.10.4..v0.10.5
package/Makefile CHANGED
@@ -2,7 +2,7 @@ RABBITMQ_SRC_VERSION=v3.12.13
2
2
  JSON=amqp-rabbitmq-0.9.1.json
3
3
  AMQP_JSON=https://raw.githubusercontent.com/rabbitmq/rabbitmq-server/$(RABBITMQ_SRC_VERSION)/deps/rabbitmq_codegen/$(JSON)
4
4
 
5
- NODEJS_VERSIONS='10.21' '11.15' '12.18' '13.14' '14.5' '15.8' '16.3.0' '18.1.0' '20.10.0'
5
+ NODEJS_VERSIONS='10.21' '11.15' '12.18' '13.14' '14.5' '15.8' '16.3.0' '18.1.0' '20.10.0' '22.14.0'
6
6
 
7
7
  MOCHA=./node_modules/.bin/mocha
8
8
  _MOCHA=./node_modules/.bin/_mocha
package/README.md CHANGED
@@ -16,8 +16,8 @@
16
16
  A library for making AMQP 0-9-1 clients for Node.JS, and an AMQP 0-9-1 client for Node.JS v10+.
17
17
 
18
18
  This library does not implement [AMQP
19
- 1.0](https://github.com/squaremo/amqp.node/issues/63) or [AMQP
20
- 0-10](https://github.com/squaremo/amqp.node/issues/94).
19
+ 1.0](https://github.com/amqp-node/amqplib/issues/63) or [AMQP
20
+ 0-10](https://github.com/amqp-node/amqplib/issues/94).
21
21
 
22
22
  Project status:
23
23
 
@@ -112,7 +112,7 @@ println(
112
112
  '/** @preserve This file is generated by the script\n',
113
113
  '* ../bin/generate-defs.js, which is not in general included in a\n',
114
114
  '* distribution, but is available in the source repository e.g. at\n',
115
- '* https://github.com/squaremo/amqp.node/\n',
115
+ '* https://github.com/amqp-node/amqplib/\n',
116
116
  '*/');
117
117
 
118
118
  println("'use strict';"); nl();
@@ -28,8 +28,13 @@ class CallbackModel extends EventEmitter {
28
28
  this.connection._updateSecret(newSecret, reason, cb);
29
29
  }
30
30
 
31
- createChannel (cb) {
31
+ createChannel (options, cb) {
32
+ if (arguments.length === 1) {
33
+ cb = options;
34
+ options = undefined;
35
+ }
32
36
  var ch = new Channel(this.connection);
37
+ ch.setOptions(options);
33
38
  ch.open(function (err, ok) {
34
39
  if (err === null)
35
40
  cb && cb(null, ch);
@@ -39,8 +44,13 @@ class CallbackModel extends EventEmitter {
39
44
  return ch;
40
45
  }
41
46
 
42
- createConfirmChannel (cb) {
47
+ createConfirmChannel (options, cb) {
48
+ if (arguments.length === 1) {
49
+ cb = options;
50
+ options = undefined;
51
+ }
43
52
  var ch = new ConfirmChannel(this.connection);
53
+ ch.setOptions(options);
44
54
  ch.open(function (err) {
45
55
  if (err !== null)
46
56
  return cb && cb(err);
package/lib/channel.js CHANGED
@@ -47,8 +47,12 @@ class Channel extends EventEmitter {
47
47
  this.handleMessage = acceptDeliveryOrReturn;
48
48
  }
49
49
 
50
+ setOptions(options) {
51
+ this.options = options;
52
+ }
53
+
50
54
  allocate () {
51
- this.ch = this.connection.freshChannel(this);
55
+ this.ch = this.connection.freshChannel(this, this.options);
52
56
  return this;
53
57
  }
54
58
 
@@ -30,14 +30,16 @@ class ChannelModel extends EventEmitter {
30
30
  return promisify(this.connection._updateSecret.bind(this.connection))(newSecret, reason);
31
31
  }
32
32
 
33
- async createChannel() {
33
+ async createChannel(options) {
34
34
  const channel = new Channel(this.connection);
35
+ channel.setOptions(options);
35
36
  await channel.open();
36
37
  return channel;
37
38
  }
38
39
 
39
- async createConfirmChannel() {
40
+ async createConfirmChannel(options) {
40
41
  const channel = new ConfirmChannel(this.connection);
42
+ channel.setOptions(options);
41
43
  await channel.open();
42
44
  await channel.rpc(defs.ConfirmSelect, {nowait: false}, defs.ConfirmSelectOk);
43
45
  return channel;
package/lib/connect.js CHANGED
@@ -31,7 +31,7 @@ var CLIENT_PROPERTIES = {
31
31
  "product": "amqplib",
32
32
  "version": require('../package.json').version,
33
33
  "platform": fmt('Node.JS %s', process.version),
34
- "information": "http://squaremo.github.io/amqp.node",
34
+ "information": "https://amqp-node.github.io/amqplib/",
35
35
  "capabilities": {
36
36
  "publisher_confirms": true,
37
37
  "exchange_exchange_bindings": true,
@@ -66,7 +66,7 @@ function openFrames(vhost, query, credentials, extraClientProperties) {
66
66
 
67
67
  // tune-ok
68
68
  'channelMax': intOrDefault(query.channelMax, 0),
69
- 'frameMax': intOrDefault(query.frameMax, 0x1000),
69
+ 'frameMax': intOrDefault(query.frameMax, 131072),
70
70
  'heartbeat': intOrDefault(query.heartbeat, 0),
71
71
 
72
72
  // open
package/lib/connection.js CHANGED
@@ -553,7 +553,7 @@ class Connection extends EventEmitter {
553
553
 
554
554
  recvFrame () {
555
555
  // %%% identifying invariants might help here?
556
- var frame = parseFrame(this.rest, this.frameMax);
556
+ var frame = parseFrame(this.rest);
557
557
 
558
558
  if (!frame) {
559
559
  var incoming = this.stream.read();
package/lib/frame.js CHANGED
@@ -4,12 +4,11 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ const ints = require('buffer-more-ints')
7
8
  var defs = require('./defs');
8
9
  var constants = defs.constants;
9
10
  var decode = defs.decode;
10
11
 
11
- var Bits = require('@acuminous/bitsyntax');
12
-
13
12
  module.exports.PROTOCOL_HEADER = "AMQP" + String.fromCharCode(0, 0, 9, 1);
14
13
 
15
14
  /*
@@ -33,74 +32,137 @@ FRAME_HEADER = constants.FRAME_HEADER,
33
32
  FRAME_BODY = constants.FRAME_BODY,
34
33
  FRAME_END = constants.FRAME_END;
35
34
 
36
- var bodyCons =
37
- Bits.builder(FRAME_BODY,
38
- 'channel:16, size:32, payload:size/binary',
39
- FRAME_END);
35
+ // expected byte sizes for frame parts
36
+ const TYPE_BYTES = 1
37
+ const CHANNEL_BYTES = 2
38
+ const SIZE_BYTES = 4
39
+ const FRAME_HEADER_BYTES = TYPE_BYTES + CHANNEL_BYTES + SIZE_BYTES
40
+ const FRAME_END_BYTES = 1
41
+
42
+ /**
43
+ * @typedef {{
44
+ * type: number,
45
+ * channel: number,
46
+ * size: number,
47
+ * payload: Buffer,
48
+ * rest: Buffer
49
+ * }} FrameStructure
50
+ */
51
+
52
+ /**
53
+ * This is a polyfill which will read a big int 64 bit as a number.
54
+ * @arg { Buffer } buffer
55
+ * @arg { number } offset
56
+ * @returns { number }
57
+ */
58
+ function readInt64BE(buffer, offset) {
59
+ /**
60
+ * We try to use native implementation if available here because
61
+ * buffer-more-ints does not
62
+ */
63
+ if (typeof Buffer.prototype.readBigInt64BE === 'function') {
64
+ return Number(buffer.readBigInt64BE(offset))
65
+ }
66
+
67
+ return ints.readInt64BE(buffer, offset)
68
+ }
40
69
 
41
70
  // %%% TESTME possibly better to cons the first bit and write the
42
71
  // second directly, in the absence of IO lists
43
- module.exports.makeBodyFrame = function(channel, payload) {
44
- return bodyCons({channel: channel, size: payload.length, payload: payload});
72
+ /**
73
+ * Make a frame header
74
+ * @arg { number } channel
75
+ * @arg { Buffer } payload
76
+ */
77
+ module.exports.makeBodyFrame = function (channel, payload) {
78
+ const frameSize = FRAME_HEADER_BYTES + payload.length + FRAME_END_BYTES
79
+
80
+ const frame = Buffer.alloc(frameSize)
81
+
82
+ let offset = 0
83
+
84
+ offset = frame.writeUInt8(FRAME_BODY, offset)
85
+ offset = frame.writeUInt16BE(channel, offset)
86
+ offset = frame.writeInt32BE(payload.length, offset)
87
+
88
+ payload.copy(frame, offset)
89
+ offset += payload.length
90
+
91
+ frame.writeUInt8(FRAME_END, offset)
92
+
93
+ return frame
45
94
  };
46
95
 
47
- var frameHeaderPattern = Bits.matcher('type:8', 'channel:16',
48
- 'size:32', 'rest/binary');
96
+ /**
97
+ * Parse an AMQP frame
98
+ * @arg { Buffer } bin
99
+ * @arg { number } max
100
+ * @returns { FrameStructure | boolean }
101
+ */
102
+ function parseFrame(bin) {
103
+ if (bin.length < FRAME_HEADER_BYTES) {
104
+ return false
105
+ }
49
106
 
50
- function parseFrame(bin, max) {
51
- var fh = frameHeaderPattern(bin);
52
- if (fh) {
53
- var size = fh.size, rest = fh.rest;
54
- if (size > max) {
55
- throw new Error('Frame size exceeds frame max');
56
- }
57
- else if (rest.length > size) {
58
- if (rest[size] !== FRAME_END)
59
- throw new Error('Invalid frame');
60
-
61
- return {
62
- type: fh.type,
63
- channel: fh.channel,
64
- size: size,
65
- payload: rest.slice(0, size),
66
- rest: rest.slice(size + 1)
67
- };
68
- }
107
+ const type = bin.readUInt8(0)
108
+ const channel = bin.readUInt16BE(1)
109
+ const size = bin.readUInt32BE(3)
110
+
111
+ const totalSize = FRAME_HEADER_BYTES + size + FRAME_END_BYTES
112
+
113
+ if (bin.length < totalSize) {
114
+ return false
69
115
  }
70
- return false;
71
- }
72
116
 
73
- module.exports.parseFrame = parseFrame;
117
+ const frameEnd = bin.readUInt8(FRAME_HEADER_BYTES + size)
118
+
119
+ if (frameEnd !== FRAME_END) {
120
+ throw new Error('Invalid frame')
121
+ }
74
122
 
75
- var headerPattern = Bits.matcher('class:16',
76
- '_weight:16',
77
- 'size:64',
78
- 'flagsAndfields/binary');
123
+ return {
124
+ type,
125
+ channel,
126
+ size,
127
+ payload: bin.subarray(FRAME_HEADER_BYTES, FRAME_HEADER_BYTES + size),
128
+ rest: bin.subarray(totalSize)
129
+ }
130
+ }
79
131
 
80
- var methodPattern = Bits.matcher('id:32, args/binary');
132
+ module.exports.parseFrame = parseFrame;
81
133
 
82
134
  var HEARTBEAT = {channel: 0};
83
135
 
84
- module.exports.decodeFrame = function(frame) {
85
- var payload = frame.payload;
136
+ /**
137
+ * Decode AMQP frame into JS object
138
+ * @param { FrameStructure } frame
139
+ * @returns
140
+ */
141
+ module.exports.decodeFrame = (frame) => {
142
+ const payload = frame.payload
143
+ const channel = frame.channel
144
+
86
145
  switch (frame.type) {
87
- case FRAME_METHOD:
88
- var idAndArgs = methodPattern(payload);
89
- var id = idAndArgs.id;
90
- var fields = decode(id, idAndArgs.args);
91
- return {id: id, channel: frame.channel, fields: fields};
92
- case FRAME_HEADER:
93
- var parts = headerPattern(payload);
94
- var id = parts['class'];
95
- var fields = decode(id, parts.flagsAndfields);
96
- return {id: id, channel: frame.channel,
97
- size: parts.size, fields: fields};
98
- case FRAME_BODY:
99
- return {channel: frame.channel, content: frame.payload};
100
- case FRAME_HEARTBEAT:
101
- return HEARTBEAT;
102
- default:
103
- throw new Error('Unknown frame type ' + frame.type);
146
+ case FRAME_METHOD: {
147
+ const id = payload.readUInt32BE(0)
148
+ const args = payload.subarray(4)
149
+ const fields = decode(id, args)
150
+ return { id, channel, fields }
151
+ }
152
+ case FRAME_HEADER: {
153
+ const id = payload.readUInt16BE(0)
154
+ // const weight = payload.readUInt16BE(2)
155
+ const size = readInt64BE(payload, 4)
156
+ const flagsAndfields = payload.subarray(12)
157
+ const fields = decode(id, flagsAndfields)
158
+ return { id, channel, size, fields }
159
+ }
160
+ case FRAME_BODY:
161
+ return { channel, content: payload }
162
+ case FRAME_HEARTBEAT:
163
+ return HEARTBEAT
164
+ default:
165
+ throw new Error('Unknown frame type ' + frame.type)
104
166
  }
105
167
  }
106
168
 
@@ -110,4 +172,4 @@ module.exports.HEARTBEAT_BUF = Buffer.from([constants.FRAME_HEARTBEAT,
110
172
  0, 0, // channel = 0
111
173
  constants.FRAME_END]);
112
174
 
113
- module.exports.HEARTBEAT = HEARTBEAT;
175
+ module.exports.HEARTBEAT = HEARTBEAT;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "amqplib",
3
3
  "homepage": "http://amqp-node.github.io/amqplib/",
4
4
  "main": "./channel_api.js",
5
- "version": "0.10.5",
5
+ "version": "0.10.7",
6
6
  "description": "An AMQP 0-9-1 (e.g., RabbitMQ) library and client.",
7
7
  "repository": {
8
8
  "type": "git",
@@ -12,7 +12,6 @@
12
12
  "node": ">=10"
13
13
  },
14
14
  "dependencies": {
15
- "@acuminous/bitsyntax": "^0.1.2",
16
15
  "buffer-more-ints": "~1.0.0",
17
16
  "url-parse": "~1.5.10"
18
17
  },
@@ -72,10 +72,14 @@ suite('updateSecret', function() {
72
72
  });
73
73
 
74
74
  function channel_test_fn(method) {
75
- return function(name, chfun) {
75
+ return function(name, options, chfun) {
76
+ if (arguments.length === 2) {
77
+ chfun = options;
78
+ options = {};
79
+ }
76
80
  test(name, function(done) {
77
81
  connect(kCallback(function(c) {
78
- c[method](kCallback(function(ch) {
82
+ c[method](options, kCallback(function(ch) {
79
83
  chfun(ch, done);
80
84
  }, done));
81
85
  }, done));
@@ -210,6 +214,33 @@ suite('sending messages', function() {
210
214
  });
211
215
  });
212
216
 
217
+ var channelOptions = {};
218
+
219
+ channel_test('find high watermark', function(ch, done) {
220
+ var msg = randomString();
221
+ var baseline = 0;
222
+ ch.assertQueue('', {exclusive: true}, function(e, q) {
223
+ if (e !== null) return done(e);
224
+ while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
225
+ baseline++;
226
+ };
227
+ channelOptions.highWaterMark = baseline * 2;
228
+ done();
229
+ })
230
+ });
231
+
232
+ channel_test('set high watermark', channelOptions, function(ch, done) {
233
+ var msg = randomString();
234
+ ch.assertQueue('', {exclusive: true}, function(e, q) {
235
+ if (e !== null) return done(e);
236
+ var ok;
237
+ for (var i = 0; i < channelOptions.highWaterMark; i++) {
238
+ ok = ch.sendToQueue(q.queue, Buffer.from(msg));
239
+ assert.equal(ok, true);
240
+ }
241
+ done();
242
+ });
243
+ });
213
244
  });
214
245
 
215
246
  suite('ConfirmChannel', function() {
@@ -228,6 +259,34 @@ suite('ConfirmChannel', function() {
228
259
  ch.waitForConfirms(done);
229
260
  });
230
261
 
262
+ var channelOptions = {};
263
+
264
+ confirm_channel_test('find high watermark', function(ch, done) {
265
+ var msg = randomString();
266
+ var baseline = 0;
267
+ ch.assertQueue('', {exclusive: true}, function(e, q) {
268
+ if (e !== null) return done(e);
269
+ while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
270
+ baseline++;
271
+ };
272
+ channelOptions.highWaterMark = baseline * 2;
273
+ done();
274
+ })
275
+ });
276
+
277
+ confirm_channel_test('set high watermark', channelOptions, function(ch, done) {
278
+ var msg = randomString();
279
+ ch.assertQueue('', {exclusive: true}, function(e, q) {
280
+ if (e !== null) return done(e);
281
+ var ok;
282
+ for (var i = 0; i < channelOptions.highWaterMark; i++) {
283
+ ok = ch.sendToQueue(q.queue, Buffer.from(msg));
284
+ assert.equal(ok, true);
285
+ }
286
+ done();
287
+ });
288
+ });
289
+
231
290
  });
232
291
 
233
292
  suite("Error handling", function() {
package/test/frame.js CHANGED
@@ -69,12 +69,6 @@ suite("Explicit parsing", function() {
69
69
  0,0,0,0, // garbage ID
70
70
  defs.constants.FRAME_END]);
71
71
 
72
- testBogusFrame('> max frame',
73
- [defs.constants.FRAME_BODY,
74
- 0,0, 0,0,0,6, // too big!
75
- 65,66,67,68,69,70,
76
- defs.constants.FRAME_END]);
77
-
78
72
  });
79
73
 
80
74
  // Now for a bit more fun.