amqplib 2.2.0 → 2.2.1
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 +8 -0
- package/callback_api.d.ts +2 -2
- package/index.d.ts +1 -1
- package/lib/callback_model.js +3 -22
- package/lib/channel.js +27 -1
- package/lib/channel_model.js +4 -29
- package/lib/properties.d.ts +3 -0
- package/package.json +12 -2
- package/.claude/commands/lint-disable.md +0 -9
- package/.claude/commands/lint-enable.md +0 -12
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -41
- package/.github/workflows/publish.yml +0 -113
- package/.github/workflows/test.yml +0 -54
- package/CLAUDE.md +0 -12
- package/Makefile +0 -42
- package/bin/amqp-rabbitmq-0.9.1.json +0 -483
- package/bin/generate-defs.js +0 -734
- package/biome.json +0 -51
- package/lefthook.yml +0 -17
- package/test/bitset.test.js +0 -65
- package/test/callback_api.test.js +0 -439
- package/test/channel.test.js +0 -918
- package/test/channel_api.test.js +0 -599
- package/test/codec.test.js +0 -171
- package/test/connect.test.js +0 -155
- package/test/connection.test.js +0 -772
- package/test/data.test.js +0 -33
- package/test/frame.test.js +0 -180
- package/test/lib/data.js +0 -249
- package/test/lib/util.js +0 -223
- package/test/mux.test.js +0 -258
- package/test/recovery.test.js +0 -799
- package/test/uri_spec.test.js +0 -210
package/test/codec.test.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
const { describe, it } = require('node:test');
|
|
2
|
-
const assert = require('node:assert');
|
|
3
|
-
const { forAll } = require('claire');
|
|
4
|
-
const codec = require('../lib/codec');
|
|
5
|
-
const defs = require('../lib/defs');
|
|
6
|
-
const amqp = require('./lib/data');
|
|
7
|
-
const { removeExplicitTypes, assertEqualModuloDefaults } = require('./lib/util');
|
|
8
|
-
|
|
9
|
-
// These just test known encodings; to generate the answers I used RabbitMQ's binary generator module.
|
|
10
|
-
const testCases = [
|
|
11
|
-
// integers
|
|
12
|
-
['byte', { byte: 112 }, [4, 98, 121, 116, 101, 98, 112]],
|
|
13
|
-
['byte max value', { byte: 127 }, [4, 98, 121, 116, 101, 98, 127]],
|
|
14
|
-
['byte min value', { byte: -128 }, [4, 98, 121, 116, 101, 98, 128]],
|
|
15
|
-
['< -128 promoted to signed short', { short: -129 }, [5, 115, 104, 111, 114, 116, 115, 255, 127]],
|
|
16
|
-
['> 127 promoted to short', { short: 128 }, [5, 115, 104, 111, 114, 116, 115, 0, 128]],
|
|
17
|
-
['< 2^15 still a short', { short: 0x7fff }, [5, 115, 104, 111, 114, 116, 115, 127, 255]],
|
|
18
|
-
['-2^15 still a short', { short: -0x8000 }, [5, 115, 104, 111, 114, 116, 115, 128, 0]],
|
|
19
|
-
['>= 2^15 promoted to int', { int: 0x8000 }, [3, 105, 110, 116, 73, 0, 0, 128, 0]],
|
|
20
|
-
['< -2^15 promoted to int', { int: -0x8001 }, [3, 105, 110, 116, 73, 255, 255, 127, 255]],
|
|
21
|
-
['< 2^31 still an int', { int: 0x7fffffff }, [3, 105, 110, 116, 73, 127, 255, 255, 255]],
|
|
22
|
-
['>= -2^31 still an int', { int: -0x80000000 }, [3, 105, 110, 116, 73, 128, 0, 0, 0]],
|
|
23
|
-
['>= 2^31 promoted to long', { long: 0x80000000 }, [4, 108, 111, 110, 103, 108, 0, 0, 0, 0, 128, 0, 0, 0]],
|
|
24
|
-
['< -2^31 promoted to long', { long: -0x80000001 }, [4, 108, 111, 110, 103, 108, 255, 255, 255, 255, 127, 255, 255, 255]],
|
|
25
|
-
|
|
26
|
-
// floating point
|
|
27
|
-
['float value', { double: 0.5 }, [6, 100, 111, 117, 98, 108, 101, 100, 63, 224, 0, 0, 0, 0, 0, 0]],
|
|
28
|
-
['negative float value', { double: -0.5 }, [6, 100, 111, 117, 98, 108, 101, 100, 191, 224, 0, 0, 0, 0, 0, 0]],
|
|
29
|
-
// %% test some boundaries of precision?
|
|
30
|
-
|
|
31
|
-
// string
|
|
32
|
-
['string', { string: 'boop' }, [6, 115, 116, 114, 105, 110, 103, 83, 0, 0, 0, 4, 98, 111, 111, 112]],
|
|
33
|
-
|
|
34
|
-
// buffer -> byte array
|
|
35
|
-
['byte array from buffer', { bytes: Buffer.from([1, 2, 3, 4]) }, [5, 98, 121, 116, 101, 115, 120, 0, 0, 0, 4, 1, 2, 3, 4]],
|
|
36
|
-
|
|
37
|
-
// boolean, void
|
|
38
|
-
['true', { bool: true }, [4, 98, 111, 111, 108, 116, 1]],
|
|
39
|
-
['false', { bool: false }, [4, 98, 111, 111, 108, 116, 0]],
|
|
40
|
-
['null', { void: null }, [4, 118, 111, 105, 100, 86]],
|
|
41
|
-
|
|
42
|
-
// array, object
|
|
43
|
-
['array', { array: [6, true, 'foo'] }, [5, 97, 114, 114, 97, 121, 65, 0, 0, 0, 12, 98, 6, 116, 1, 83, 0, 0, 0, 3, 102, 111, 111]],
|
|
44
|
-
[
|
|
45
|
-
'object',
|
|
46
|
-
{ object: { foo: 'bar', baz: 12 } },
|
|
47
|
-
[6, 111, 98, 106, 101, 99, 116, 70, 0, 0, 0, 18, 3, 102, 111, 111, 83, 0, 0, 0, 3, 98, 97, 114, 3, 98, 97, 122, 98, 12],
|
|
48
|
-
],
|
|
49
|
-
|
|
50
|
-
// exotic types
|
|
51
|
-
[
|
|
52
|
-
'timestamp',
|
|
53
|
-
{ timestamp: { '!': 'timestamp', value: 1357212277527 } },
|
|
54
|
-
[9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 84, 0, 0, 1, 60, 0, 39, 219, 23],
|
|
55
|
-
],
|
|
56
|
-
[
|
|
57
|
-
'decimal',
|
|
58
|
-
{ decimal: { '!': 'decimal', value: { digits: 2345, places: 2 } } },
|
|
59
|
-
[7, 100, 101, 99, 105, 109, 97, 108, 68, 2, 0, 0, 9, 41],
|
|
60
|
-
],
|
|
61
|
-
['float', { float: { '!': 'float', value: 0.1 } }, [5, 102, 108, 111, 97, 116, 102, 61, 204, 204, 205]],
|
|
62
|
-
[
|
|
63
|
-
'unsignedbyte',
|
|
64
|
-
{ unsignedbyte: { '!': 'unsignedbyte', value: 255 } },
|
|
65
|
-
[12, 117, 110, 115, 105, 103, 110, 101, 100, 98, 121, 116, 101, 66, 255],
|
|
66
|
-
],
|
|
67
|
-
[
|
|
68
|
-
'unsignedshort',
|
|
69
|
-
{ unsignedshort: { '!': 'unsignedshort', value: 65535 } },
|
|
70
|
-
[13, 117, 110, 115, 105, 103, 110, 101, 100, 115, 104, 111, 114, 116, 117, 255, 255],
|
|
71
|
-
],
|
|
72
|
-
[
|
|
73
|
-
'unsignedint',
|
|
74
|
-
{ unsignedint: { '!': 'unsignedint', value: 4294967295 } },
|
|
75
|
-
[11, 117, 110, 115, 105, 103, 110, 101, 100, 105, 110, 116, 105, 255, 255, 255, 255],
|
|
76
|
-
],
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
function bufferToArray(b) {
|
|
80
|
-
return Array.prototype.slice.call(b);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
describe('Codec', () => {
|
|
84
|
-
|
|
85
|
-
describe('Implicit encodings', () => {
|
|
86
|
-
testCases.forEach(([name, val, expect]) => {
|
|
87
|
-
it(name, () => {
|
|
88
|
-
const buffer = Buffer.alloc(1000);
|
|
89
|
-
const size = codec.encodeTable(buffer, val, 0);
|
|
90
|
-
const result = buffer.subarray(4, size);
|
|
91
|
-
assert.deepEqual(expect, bufferToArray(result));
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Whole frames
|
|
97
|
-
function roundtrip_table(t) {
|
|
98
|
-
const buf = Buffer.alloc(4096);
|
|
99
|
-
const size = codec.encodeTable(buf, t, 0);
|
|
100
|
-
const decoded = codec.decodeFields(buf.subarray(4, size)); // ignore the length-prefix
|
|
101
|
-
try {
|
|
102
|
-
assert.deepEqual(removeExplicitTypes(t), decoded);
|
|
103
|
-
} catch {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function roundtrips(T) {
|
|
110
|
-
return forAll(T).satisfy((v) => roundtrip_table({ value: v }));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
describe('Roundtrip values', () => {
|
|
114
|
-
[
|
|
115
|
-
amqp.Octet,
|
|
116
|
-
amqp.ShortStr,
|
|
117
|
-
amqp.LongStr,
|
|
118
|
-
amqp.UShort,
|
|
119
|
-
amqp.ULong,
|
|
120
|
-
amqp.ULongLong,
|
|
121
|
-
amqp.UShort,
|
|
122
|
-
amqp.Short,
|
|
123
|
-
amqp.Long,
|
|
124
|
-
amqp.Bit,
|
|
125
|
-
amqp.Decimal,
|
|
126
|
-
amqp.Timestamp,
|
|
127
|
-
amqp.UnsignedByte,
|
|
128
|
-
amqp.UnsignedShort,
|
|
129
|
-
amqp.UnsignedInt,
|
|
130
|
-
amqp.Double,
|
|
131
|
-
amqp.Float,
|
|
132
|
-
amqp.FieldArray,
|
|
133
|
-
amqp.FieldTable,
|
|
134
|
-
].forEach((T) => {
|
|
135
|
-
it(`${T.toString()} roundtrip`, roundtrips(T).asTest());
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
function roundtripMethod(Method) {
|
|
140
|
-
return forAll(Method).satisfy((method) => {
|
|
141
|
-
const buf = defs.encodeMethod(method.id, 0, method.fields);
|
|
142
|
-
// FIXME depends on framing, ugh
|
|
143
|
-
const fs1 = defs.decode(method.id, buf.subarray(11, buf.length));
|
|
144
|
-
assertEqualModuloDefaults(method, fs1);
|
|
145
|
-
return true;
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function roundtripProperties(Properties) {
|
|
150
|
-
return forAll(Properties).satisfy((properties) => {
|
|
151
|
-
const buf = defs.encodeProperties(properties.id, 0, properties.size, properties.fields);
|
|
152
|
-
// FIXME depends on framing, ugh
|
|
153
|
-
const fs1 = defs.decode(properties.id, buf.subarray(19, buf.length));
|
|
154
|
-
assert.equal(properties.size, Number(buf.readBigUInt64BE(11)));
|
|
155
|
-
assertEqualModuloDefaults(properties, fs1);
|
|
156
|
-
return true;
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
describe('Roundtrip methods', () => {
|
|
161
|
-
amqp.methods.forEach((Method) => {
|
|
162
|
-
it(`${Method.toString()} roundtrip`, roundtripMethod(Method).asTest());
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('Roundtrip properties', () => {
|
|
167
|
-
amqp.properties.forEach((Properties) => {
|
|
168
|
-
it(`${Properties.toString()} roundtrip`, roundtripProperties(Properties).asTest());
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
package/test/connect.test.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
const { describe, it, afterEach } = require('node:test');
|
|
2
|
-
const assert = require('node:assert');
|
|
3
|
-
const net = require('node:net');
|
|
4
|
-
const connect = require('../lib/connect').connect;
|
|
5
|
-
const defs = require('../lib/defs');
|
|
6
|
-
const { plain, amqplain } = require('../lib/credentials')
|
|
7
|
-
const { latch, runServer } = require('./lib/util');
|
|
8
|
-
|
|
9
|
-
describe('Connect', () => {
|
|
10
|
-
|
|
11
|
-
describe('Connect API', () => {
|
|
12
|
-
|
|
13
|
-
let connection;
|
|
14
|
-
|
|
15
|
-
afterEach((_t, done) => {
|
|
16
|
-
if (connection) connection.close(() => done())
|
|
17
|
-
else done();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('Connection refused', (_t, done) => {
|
|
21
|
-
connect('amqp://localhost:23450', {}, (err) => {
|
|
22
|
-
assert.strictEqual(err.code, 'ECONNREFUSED');
|
|
23
|
-
done();
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// %% this ought to fail the promise, rather than throwing an error
|
|
28
|
-
it('bad URL', (_t, done) => {
|
|
29
|
-
assert.throws(() => connect('blurble'), (err) => {
|
|
30
|
-
assert.match(err.message, /Expected amqp: or amqps: as the protocol/);
|
|
31
|
-
return true;
|
|
32
|
-
});
|
|
33
|
-
done();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('wrongly typed open option', (_t, done) => {
|
|
37
|
-
connect('amqp://localhost?frameMax=NOT_A_NUMBER', {}, (err) => {
|
|
38
|
-
assert.match(err.message, /Field 'frameMax' is the wrong type/);
|
|
39
|
-
done();
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('serverProperties', (_t, done) => {
|
|
44
|
-
connect({ protocol: 'amqp', hostname: 'localhost' }, {}, (err, _connection) => {
|
|
45
|
-
connection = _connection;
|
|
46
|
-
assert.ifError(err);
|
|
47
|
-
assert.equal(connection.serverProperties.product, 'RabbitMQ');
|
|
48
|
-
done();
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('using custom heartbeat option', (_t, done) => {
|
|
53
|
-
connect({ heartbeat: 20 }, {}, (err, _connection) => {
|
|
54
|
-
assert.ifError(err);
|
|
55
|
-
connection = _connection;
|
|
56
|
-
assert.strictEqual(connection.heartbeat, 20);
|
|
57
|
-
done();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('wrongly typed heartbeat option', (_t, done) => {
|
|
62
|
-
connect('amqp://localhost?heartbeat=NOT_A_NUMBER', {}, (err) => {
|
|
63
|
-
assert.match(err.message, /Field 'heartbeat' is the wrong type/);
|
|
64
|
-
done();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('using plain credentials', (_t, done) => {
|
|
69
|
-
connect('amqp://localhost', { credentials: plain('guest', 'guest') }, (err, _connection) => {
|
|
70
|
-
assert.ifError(err);
|
|
71
|
-
connection = _connection;
|
|
72
|
-
done();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('using amqplain credentials', (_t, done) => {
|
|
77
|
-
connect('amqp://localhost', { credentials: amqplain('guest', 'guest') }, (err, _connection) => {
|
|
78
|
-
assert.ifError(err);
|
|
79
|
-
connection = _connection;
|
|
80
|
-
done();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('ipv6', (_t, done) => {
|
|
85
|
-
connect('amqp://[::1]', {}, (err, _connection) => {
|
|
86
|
-
assert.ifError(err);
|
|
87
|
-
connection = _connection;
|
|
88
|
-
done();
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('using unsupported mechanism', (_t, done) => {
|
|
93
|
-
const credentials = { mechanism: 'UNSUPPORTED', response: () => Buffer.from('') };
|
|
94
|
-
connect('amqp://localhost', { credentials }, (err) => {
|
|
95
|
-
assert.match(err.message, /SASL mechanism UNSUPPORTED is not provided by the server/);
|
|
96
|
-
done();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('with a given connection timeout', (_t, done) => {
|
|
101
|
-
const timeoutServer = net.createServer(() => { }).listen(31991);
|
|
102
|
-
|
|
103
|
-
connect('amqp://localhost:31991', { timeout: 50 }, (err) => {
|
|
104
|
-
timeoutServer.close();
|
|
105
|
-
assert.match(err.message, /ETIMEDOUT/);
|
|
106
|
-
done();
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('Errors on connect', () => {
|
|
112
|
-
|
|
113
|
-
let server;
|
|
114
|
-
|
|
115
|
-
afterEach(() => {
|
|
116
|
-
if (server) server.close();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('closes underlying connection on authentication error', (_t, done) => {
|
|
120
|
-
const decrementLatch = latch(2, done);
|
|
121
|
-
server = net.createServer((socket) => {
|
|
122
|
-
socket.once('data', (protocolHeader) => {
|
|
123
|
-
assert.deepStrictEqual(protocolHeader, Buffer.from(`AMQP${String.fromCharCode(0, 0, 9, 1)}`));
|
|
124
|
-
runServer(socket, (send, wait) => {
|
|
125
|
-
send(defs.ConnectionStart, {
|
|
126
|
-
versionMajor: 0,
|
|
127
|
-
versionMinor: 9,
|
|
128
|
-
serverProperties: {},
|
|
129
|
-
mechanisms: Buffer.from('PLAIN'),
|
|
130
|
-
locales: Buffer.from('en_US'),
|
|
131
|
-
});
|
|
132
|
-
wait(defs.ConnectionStartOk)().then(() => {
|
|
133
|
-
send(defs.ConnectionClose, {
|
|
134
|
-
replyCode: 403,
|
|
135
|
-
replyText: 'ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN',
|
|
136
|
-
classId: 0,
|
|
137
|
-
methodId: 0,
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Wait for the connection to be closed after the authentication error
|
|
144
|
-
socket.once('end', () => decrementLatch());
|
|
145
|
-
});
|
|
146
|
-
server.listen(0);
|
|
147
|
-
|
|
148
|
-
connect(`amqp://localhost:${server.address().port}`, {}, (err) => {
|
|
149
|
-
assert.match(err.message, /Handshake terminated by server: 403 \(ACCESS-REFUSED\)/);
|
|
150
|
-
decrementLatch();
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
});
|