amqplib 1.0.5 → 1.0.6
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 +3 -0
- package/Makefile +1 -1
- package/lib/callback_model.js +12 -13
- package/lib/channel.js +15 -13
- package/lib/channel_model.js +4 -4
- package/package.json +1 -1
- package/test/callback_api.test.js +2 -23
- package/test/channel_api.test.js +42 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Change log for amqplib
|
|
2
2
|
|
|
3
|
+
## v1.0.6
|
|
4
|
+
- Fix channel.get() not invoking callback with error on channel close; previously only an error event was emitted (fixes #832). **Note:** if you use the callback API, ensure your channel.get() callbacks handle errors — they will now be invoked in error cases where previously they were not. If you use the promise API, the returned promise now rejects with a proper Error object (with .code, .classId and .methodId properties) rather than a raw close frame.
|
|
5
|
+
|
|
3
6
|
## v1.0.5
|
|
4
7
|
- Fix ConfirmChannel callbacks silently dropped on channel close when some publishes had no callback (fixes #191)
|
|
5
8
|
|
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='18.
|
|
5
|
+
NODEJS_VERSIONS='18.20.0' '20.10.0' '22.14.0' '24.7.0'
|
|
6
6
|
|
|
7
7
|
UGLIFY=./node_modules/.bin/uglifyjs
|
|
8
8
|
|
package/lib/callback_model.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const defs = require('./defs');
|
|
2
2
|
const EventEmitter = require('node:events');
|
|
3
|
-
const BaseChannel = require('./channel').BaseChannel;
|
|
4
|
-
const acceptMessage = require('./channel').acceptMessage;
|
|
5
3
|
const Args = require('./api_args');
|
|
4
|
+
const { BaseChannel, acceptMessage, convertCloseFrameToError } = require('./channel');
|
|
6
5
|
|
|
7
6
|
class CallbackModel extends EventEmitter {
|
|
8
7
|
constructor(connection) {
|
|
@@ -179,17 +178,17 @@ class Channel extends BaseChannel {
|
|
|
179
178
|
const fields = Args.get(queue, options);
|
|
180
179
|
const cb = callbackWrapper(this, cb0);
|
|
181
180
|
this.sendOrEnqueue(defs.BasicGet, fields, (err, f) => {
|
|
182
|
-
if (err
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
}
|
|
181
|
+
if (err instanceof Error) return cb(err);
|
|
182
|
+
if (err) return cb(convertCloseFrameToError(defs.BasicGet, err));
|
|
183
|
+
if (f.id === defs.BasicGetEmpty) {
|
|
184
|
+
cb(null, false);
|
|
185
|
+
} else if (f.id === defs.BasicGetOk) {
|
|
186
|
+
this.handleMessage = acceptMessage((m) => {
|
|
187
|
+
m.fields = f.fields;
|
|
188
|
+
cb(null, m);
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
cb(new Error(`Unexpected response to BasicGet: ${inspect(f)}`));
|
|
193
192
|
}
|
|
194
193
|
});
|
|
195
194
|
return this;
|
package/lib/channel.js
CHANGED
|
@@ -116,19 +116,7 @@ class Channel extends EventEmitter {
|
|
|
116
116
|
else if (err instanceof Error) return cb(err);
|
|
117
117
|
// A close frame will be given if this is the RPC awaiting reply
|
|
118
118
|
// and the channel is closed by the server
|
|
119
|
-
else
|
|
120
|
-
// otherwise, it's a close frame
|
|
121
|
-
const closeReason = (err.fields.classId << 16) + err.fields.methodId;
|
|
122
|
-
const e =
|
|
123
|
-
method === closeReason
|
|
124
|
-
? fmt('Operation failed: %s; %s', methodName(method), closeMsg(err))
|
|
125
|
-
: fmt('Channel closed by server: %s', closeMsg(err));
|
|
126
|
-
const closeFrameError = new Error(e);
|
|
127
|
-
closeFrameError.code = err.fields.replyCode;
|
|
128
|
-
closeFrameError.classId = err.fields.classId;
|
|
129
|
-
closeFrameError.methodId = err.fields.methodId;
|
|
130
|
-
return cb(closeFrameError);
|
|
131
|
-
}
|
|
119
|
+
else return cb(convertCloseFrameToError(method, err));
|
|
132
120
|
}
|
|
133
121
|
|
|
134
122
|
this.sendOrEnqueue(method, fields, reply);
|
|
@@ -428,6 +416,19 @@ function acceptMessage(continuation) {
|
|
|
428
416
|
}
|
|
429
417
|
}
|
|
430
418
|
|
|
419
|
+
function convertCloseFrameToError(method, closeFrame) {
|
|
420
|
+
const closeReason = (closeFrame.fields.classId << 16) + closeFrame.fields.methodId;
|
|
421
|
+
const e =
|
|
422
|
+
method === closeReason
|
|
423
|
+
? fmt('Operation failed: %s; %s', methodName(method), closeMsg(closeFrame))
|
|
424
|
+
: fmt('Channel closed by server: %s', closeMsg(closeFrame));
|
|
425
|
+
const error = new Error(e);
|
|
426
|
+
error.code = closeFrame.fields.replyCode;
|
|
427
|
+
error.classId = closeFrame.fields.classId;
|
|
428
|
+
error.methodId = closeFrame.fields.methodId;
|
|
429
|
+
return error;
|
|
430
|
+
}
|
|
431
|
+
|
|
431
432
|
// This adds just a bit more stuff useful for the APIs, but not
|
|
432
433
|
// low-level machinery.
|
|
433
434
|
class BaseChannel extends Channel {
|
|
@@ -471,3 +472,4 @@ class BaseChannel extends Channel {
|
|
|
471
472
|
module.exports.acceptMessage = acceptMessage;
|
|
472
473
|
module.exports.BaseChannel = BaseChannel;
|
|
473
474
|
module.exports.Channel = Channel;
|
|
475
|
+
module.exports.convertCloseFrameToError = convertCloseFrameToError;
|
package/lib/channel_model.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const EventEmitter = require('node:events');
|
|
2
|
-
const promisify = require('node:util')
|
|
2
|
+
const { promisify } = require('node:util');
|
|
3
3
|
const defs = require('./defs');
|
|
4
|
-
const { BaseChannel } = require('./channel');
|
|
5
|
-
const { acceptMessage } = require('./channel');
|
|
6
4
|
const Args = require('./api_args');
|
|
5
|
+
const { BaseChannel, acceptMessage, convertCloseFrameToError } = require('./channel');
|
|
7
6
|
const { inspect } = require('./format');
|
|
8
7
|
|
|
9
8
|
class ChannelModel extends EventEmitter {
|
|
@@ -159,7 +158,8 @@ class Channel extends BaseChannel {
|
|
|
159
158
|
const fields = Args.get(queue, options);
|
|
160
159
|
return new Promise((resolve, reject) => {
|
|
161
160
|
this.sendOrEnqueue(defs.BasicGet, fields, (err, f) => {
|
|
162
|
-
if (err) return reject(err);
|
|
161
|
+
if (err instanceof Error) return reject(err);
|
|
162
|
+
if (err) return reject(convertCloseFrameToError(defs.BasicGet, err));
|
|
163
163
|
if (f.id === defs.BasicGetEmpty) {
|
|
164
164
|
return resolve(false);
|
|
165
165
|
} else if (f.id === defs.BasicGetOk) {
|
package/package.json
CHANGED
|
@@ -401,39 +401,18 @@ describe('Callback API', () => {
|
|
|
401
401
|
});
|
|
402
402
|
});
|
|
403
403
|
|
|
404
|
-
/*
|
|
405
|
-
When this test was refactored as part of the move to the node test framework
|
|
406
|
-
it failed because only the domain error handler was ever invoked and not the
|
|
407
|
-
channel.get error callback.
|
|
408
|
-
|
|
409
|
-
The original test passed because twice.first (refactored to use util.latch) short circuits on error rather
|
|
410
|
-
than waiting for twice.second to be invoked. I have verified that the pre-refactored
|
|
411
|
-
amqplib does not invoke the channel.get callback when the queue does not exist.
|
|
412
|
-
See lib/callback_model.js
|
|
413
|
-
*/
|
|
414
404
|
error_test('Get from non-queue invokes error', (ch, done, dom) => {
|
|
415
405
|
const decrementLatch = latch(2, () => done());
|
|
416
406
|
dom.on('error', (err) => {
|
|
417
407
|
assert.match(err.message, /404 \(NOT-FOUND\)/);
|
|
418
|
-
decrementLatch(
|
|
408
|
+
decrementLatch();
|
|
419
409
|
});
|
|
420
410
|
ch.get('', {}, (err) => {
|
|
421
411
|
assert.match(err.message, /404 \(NOT-FOUND\)/)
|
|
422
|
-
decrementLatch(
|
|
412
|
+
decrementLatch()
|
|
423
413
|
});
|
|
424
414
|
});
|
|
425
415
|
|
|
426
|
-
/*
|
|
427
|
-
When this test was refactored as part of the move to the node test framework
|
|
428
|
-
it failed because only the domain error handler was ever invoked and not the
|
|
429
|
-
channel.consume error callback. Unlike the above channel.get test,
|
|
430
|
-
channel.consume does invoke the channel.consume callback with an error, but
|
|
431
|
-
the original test did not supply one, mistakenly providing a message handler
|
|
432
|
-
function instead.
|
|
433
|
-
|
|
434
|
-
The original test passed because twice.first (refactored to use util.latch) short circuits on error
|
|
435
|
-
rather than waiting for twice.second to be invoked.
|
|
436
|
-
*/
|
|
437
416
|
error_test('Consume from non-queue invokes error', (ch, done, dom) => {
|
|
438
417
|
const decrementLatch = latch(2, done);
|
|
439
418
|
dom.on('error', (err) => {
|
package/test/channel_api.test.js
CHANGED
|
@@ -176,6 +176,48 @@ describe('sendMessage', () => {
|
|
|
176
176
|
});
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
+
describe('get', () => {
|
|
180
|
+
it('returns false when queue is empty', () => {
|
|
181
|
+
return withChannel((ch) =>
|
|
182
|
+
ch.assertQueue('test.get-empty', QUEUE_OPTS)
|
|
183
|
+
.then(() => ch.purgeQueue('test.get-empty'))
|
|
184
|
+
.then(() => ch.get('test.get-empty', { noAck: true }))
|
|
185
|
+
.then((m) => assert.strictEqual(false, m))
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns a message when queue has messages', () => {
|
|
190
|
+
const msg = randomString();
|
|
191
|
+
return withChannel((ch) =>
|
|
192
|
+
ch.assertQueue('test.get-message', QUEUE_OPTS)
|
|
193
|
+
.then(() => ch.purgeQueue('test.get-message'))
|
|
194
|
+
.then(() => {
|
|
195
|
+
ch.sendToQueue('test.get-message', Buffer.from(msg));
|
|
196
|
+
return waitForMessages('test.get-message');
|
|
197
|
+
})
|
|
198
|
+
.then(() => ch.get('test.get-message', { noAck: true }))
|
|
199
|
+
.then((m) => {
|
|
200
|
+
assert(m);
|
|
201
|
+
assert.equal(msg, m.content.toString());
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('rejects with a proper error when queue does not exist', () => {
|
|
207
|
+
return withChannel((ch) => {
|
|
208
|
+
ch.once('error', ignore);
|
|
209
|
+
return assert.rejects(() => ch.get('', {}), (err) => {
|
|
210
|
+
assert(err instanceof Error);
|
|
211
|
+
assert.match(err.message, /NOT_FOUND/);
|
|
212
|
+
assert.strictEqual(404, err.code);
|
|
213
|
+
assert.strictEqual(60, err.classId);
|
|
214
|
+
assert.strictEqual(70, err.methodId);
|
|
215
|
+
return true;
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
179
221
|
describe('binding, consuming', () => {
|
|
180
222
|
// bind, publish, get
|
|
181
223
|
it('route message', () => {
|