amqplib 0.10.8 → 1.0.0
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/.claude/commands/lint-disable.md +9 -0
- package/.claude/commands/lint-enable.md +12 -0
- package/.claude/settings.local.json +16 -0
- package/.github/workflows/publish.yml +1 -1
- package/.github/workflows/test.yml +1 -1
- package/CHANGELOG.md +34 -0
- package/CLAUDE.md +12 -0
- package/Makefile +5 -12
- package/bin/generate-defs.js +382 -369
- package/biome.json +51 -0
- package/callback_api.js +12 -8
- package/channel_api.js +5 -10
- package/examples/direct_reply_to_client.js +9 -5
- package/examples/direct_reply_to_server.js +8 -6
- package/examples/headers.js +13 -10
- package/examples/receive_generator.js +13 -20
- package/examples/send_generators.js +2 -9
- package/examples/ssl.js +2 -3
- package/examples/stream_queues/receive_stream.js +39 -36
- package/examples/stream_queues/send_stream.js +26 -29
- package/examples/tutorials/callback_api/emit_log.js +4 -3
- package/examples/tutorials/callback_api/emit_log_direct.js +5 -4
- package/examples/tutorials/callback_api/emit_log_topic.js +5 -4
- package/examples/tutorials/callback_api/new_task.js +5 -4
- package/examples/tutorials/callback_api/receive.js +16 -10
- package/examples/tutorials/callback_api/receive_logs.js +17 -11
- package/examples/tutorials/callback_api/receive_logs_direct.js +19 -13
- package/examples/tutorials/callback_api/receive_logs_topic.js +19 -14
- package/examples/tutorials/callback_api/rpc_client.js +20 -16
- package/examples/tutorials/callback_api/rpc_server.js +27 -21
- package/examples/tutorials/callback_api/send.js +4 -3
- package/examples/tutorials/callback_api/worker.js +18 -13
- package/examples/tutorials/emit_log.js +4 -6
- package/examples/tutorials/emit_log_direct.js +5 -7
- package/examples/tutorials/emit_log_topic.js +5 -7
- package/examples/tutorials/new_task.js +4 -6
- package/examples/tutorials/receive.js +8 -4
- package/examples/tutorials/receive_logs.js +10 -6
- package/examples/tutorials/receive_logs_direct.js +16 -10
- package/examples/tutorials/receive_logs_topic.js +16 -11
- package/examples/tutorials/rpc_client.js +17 -15
- package/examples/tutorials/rpc_server.js +9 -8
- package/examples/tutorials/send.js +4 -6
- package/examples/tutorials/worker.js +17 -14
- package/examples/waitForConfirms.js +2 -2
- package/lefthook.yml +14 -0
- package/lib/api_args.js +124 -149
- package/lib/bitset.js +28 -18
- package/lib/callback_model.js +134 -190
- package/lib/channel.js +175 -213
- package/lib/channel_model.js +41 -85
- package/lib/codec.js +257 -228
- package/lib/connect.js +74 -82
- package/lib/connection.js +204 -240
- package/lib/credentials.js +21 -29
- package/lib/defs.js +2559 -1927
- package/lib/error.js +5 -6
- package/lib/format.js +15 -33
- package/lib/frame.js +66 -66
- package/lib/heartbeat.js +17 -24
- package/lib/mux.js +32 -41
- package/package.json +8 -5
- package/test/bitset.test.js +65 -0
- package/test/callback_api.test.js +438 -0
- package/test/channel.test.js +592 -0
- package/test/channel_api.test.js +537 -0
- package/test/codec.test.js +172 -0
- package/test/connect.test.js +196 -0
- package/test/connection.test.js +330 -0
- package/test/data.test.js +33 -0
- package/test/frame.test.js +180 -0
- package/test/lib/data.js +249 -0
- package/test/lib/util.js +223 -0
- package/test/mux.test.js +215 -0
- package/test/bitset.js +0 -69
- package/test/callback_api.js +0 -379
- package/test/channel.js +0 -621
- package/test/channel_api.js +0 -606
- package/test/codec.js +0 -237
- package/test/connect.js +0 -197
- package/test/connection.js +0 -390
- package/test/data.js +0 -269
- package/test/frame.js +0 -191
- package/test/mux.js +0 -203
- package/test/util.js +0 -217
package/lib/channel.js
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var closeMsg = require('./format').closeMessage;
|
|
11
|
-
var inspect = require('./format').inspect;
|
|
12
|
-
var methodName = require('./format').methodName;
|
|
13
|
-
var assert = require('assert');
|
|
14
|
-
var EventEmitter = require('events');
|
|
15
|
-
var fmt = require('util').format;
|
|
16
|
-
var IllegalOperationError = require('./error').IllegalOperationError;
|
|
17
|
-
var stackCapture = require('./error').stackCapture;
|
|
1
|
+
const defs = require('./defs');
|
|
2
|
+
const closeMsg = require('./format').closeMessage;
|
|
3
|
+
const inspect = require('./format').inspect;
|
|
4
|
+
const methodName = require('./format').methodName;
|
|
5
|
+
const assert = require('node:assert');
|
|
6
|
+
const EventEmitter = require('node:events');
|
|
7
|
+
const fmt = require('node:util').format;
|
|
8
|
+
const IllegalOperationError = require('./error').IllegalOperationError;
|
|
9
|
+
const stackCapture = require('./error').stackCapture;
|
|
18
10
|
|
|
19
11
|
class Channel extends EventEmitter {
|
|
20
|
-
constructor
|
|
12
|
+
constructor(connection) {
|
|
21
13
|
super();
|
|
22
14
|
|
|
23
15
|
this.connection = connection;
|
|
@@ -28,19 +20,23 @@ class Channel extends EventEmitter {
|
|
|
28
20
|
// for unconfirmed messages
|
|
29
21
|
this.lwm = 1; // the least, unconfirmed deliveryTag
|
|
30
22
|
this.unconfirmed = []; // rolling window of delivery callbacks
|
|
31
|
-
this.on(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
this.on(
|
|
24
|
+
'ack',
|
|
25
|
+
this.handleConfirm.bind(this, (cb) => {
|
|
26
|
+
if (cb) cb(null);
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
this.on(
|
|
30
|
+
'nack',
|
|
31
|
+
this.handleConfirm.bind(this, (cb) => {
|
|
32
|
+
if (cb) cb(new Error('message nacked'));
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
39
35
|
this.on('close', function () {
|
|
40
|
-
|
|
41
|
-
while (cb
|
|
42
|
-
if (cb)
|
|
43
|
-
|
|
36
|
+
let cb = this.unconfirmed.shift();
|
|
37
|
+
while (cb) {
|
|
38
|
+
if (cb) cb(new Error('channel closed'));
|
|
39
|
+
cb = this.unconfirmed.shift();
|
|
44
40
|
}
|
|
45
41
|
});
|
|
46
42
|
// message frame state machine
|
|
@@ -51,7 +47,7 @@ class Channel extends EventEmitter {
|
|
|
51
47
|
this.options = options;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
allocate
|
|
50
|
+
allocate() {
|
|
55
51
|
this.ch = this.connection.freshChannel(this, this.options);
|
|
56
52
|
return this;
|
|
57
53
|
}
|
|
@@ -67,7 +63,7 @@ class Channel extends EventEmitter {
|
|
|
67
63
|
// this reason, I buffer RPCs if the channel is already waiting for a
|
|
68
64
|
// reply.
|
|
69
65
|
// Just send the damn frame.
|
|
70
|
-
sendImmediately
|
|
66
|
+
sendImmediately(method, fields) {
|
|
71
67
|
return this.connection.sendMethod(this.ch, method, fields);
|
|
72
68
|
}
|
|
73
69
|
|
|
@@ -75,70 +71,58 @@ class Channel extends EventEmitter {
|
|
|
75
71
|
// clear a reply, we must send another RPC (and thereby fill
|
|
76
72
|
// this.reply) if there is one waiting. The invariant relevant here
|
|
77
73
|
// and in `accept`.
|
|
78
|
-
sendOrEnqueue
|
|
79
|
-
if (!this.reply) {
|
|
74
|
+
sendOrEnqueue(method, fields, reply) {
|
|
75
|
+
if (!this.reply) {
|
|
76
|
+
// if no reply waiting, we can go
|
|
80
77
|
assert(this.pending.length === 0);
|
|
81
78
|
this.reply = reply;
|
|
82
79
|
this.sendImmediately(method, fields);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
80
|
+
} else {
|
|
85
81
|
this.pending.push({
|
|
86
82
|
method: method,
|
|
87
83
|
fields: fields,
|
|
88
|
-
reply: reply
|
|
84
|
+
reply: reply,
|
|
89
85
|
});
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
88
|
|
|
93
|
-
sendMessage
|
|
94
|
-
return this.connection.sendMessage(
|
|
95
|
-
this.ch,
|
|
96
|
-
defs.BasicPublish, fields,
|
|
97
|
-
defs.BasicProperties, properties,
|
|
98
|
-
content);
|
|
89
|
+
sendMessage(fields, properties, content) {
|
|
90
|
+
return this.connection.sendMessage(this.ch, defs.BasicPublish, fields, defs.BasicProperties, properties, content);
|
|
99
91
|
}
|
|
100
92
|
|
|
101
93
|
// Internal, synchronously resolved RPC; the return value is resolved
|
|
102
94
|
// with the whole frame.
|
|
103
|
-
_rpc
|
|
104
|
-
|
|
95
|
+
_rpc(method, fields, expect, cb) {
|
|
96
|
+
const self = this;
|
|
105
97
|
|
|
106
|
-
function reply
|
|
98
|
+
function reply(err, f) {
|
|
107
99
|
if (err === null) {
|
|
108
100
|
if (f.id === expect) {
|
|
109
101
|
return cb(null, f);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
102
|
+
} else {
|
|
112
103
|
// We have detected a problem, so it's up to us to close the
|
|
113
104
|
// channel
|
|
114
|
-
|
|
105
|
+
const expectedName = methodName(expect);
|
|
115
106
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
self.closeWithError(f.id, fmt('Expected %s; got %s',
|
|
119
|
-
expectedName, methodName(f.id)),
|
|
120
|
-
defs.constants.UNEXPECTED_FRAME, e);
|
|
107
|
+
const e = new Error(fmt('Expected %s; got %s', expectedName, inspect(f, false)));
|
|
108
|
+
self.closeWithError(f.id, fmt('Expected %s; got %s', expectedName, methodName(f.id)), defs.constants.UNEXPECTED_FRAME, e);
|
|
121
109
|
return cb(e);
|
|
122
110
|
}
|
|
123
111
|
}
|
|
124
112
|
|
|
125
|
-
|
|
126
113
|
// An error will be given if, for example, this is waiting to be
|
|
127
114
|
// sent and the connection closes
|
|
128
|
-
else if (err instanceof Error)
|
|
129
|
-
return cb(err);
|
|
130
|
-
|
|
131
|
-
|
|
115
|
+
else if (err instanceof Error) return cb(err);
|
|
132
116
|
// A close frame will be given if this is the RPC awaiting reply
|
|
133
117
|
// and the channel is closed by the server
|
|
134
118
|
else {
|
|
135
119
|
// otherwise, it's a close frame
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
methodName(method), closeMsg(err))
|
|
140
|
-
|
|
141
|
-
|
|
120
|
+
const closeReason = (err.fields.classId << 16) + err.fields.methodId;
|
|
121
|
+
const e =
|
|
122
|
+
method === closeReason
|
|
123
|
+
? fmt('Operation failed: %s; %s', methodName(method), closeMsg(err))
|
|
124
|
+
: fmt('Channel closed by server: %s', closeMsg(err));
|
|
125
|
+
const closeFrameError = new Error(e);
|
|
142
126
|
closeFrameError.code = err.fields.replyCode;
|
|
143
127
|
closeFrameError.classId = err.fields.classId;
|
|
144
128
|
closeFrameError.methodId = err.fields.methodId;
|
|
@@ -150,7 +134,7 @@ class Channel extends EventEmitter {
|
|
|
150
134
|
}
|
|
151
135
|
|
|
152
136
|
// Move to entirely closed state.
|
|
153
|
-
toClosed
|
|
137
|
+
toClosed(capturedStack) {
|
|
154
138
|
this._rejectPending();
|
|
155
139
|
invalidateSend(this, 'Channel closed', capturedStack);
|
|
156
140
|
this.accept = invalidOp('Channel closed', capturedStack);
|
|
@@ -162,61 +146,60 @@ class Channel extends EventEmitter {
|
|
|
162
146
|
// we close the channel. Invokes the continuation once the server has
|
|
163
147
|
// acknowledged the close, but before the channel is moved to the
|
|
164
148
|
// closed state.
|
|
165
|
-
toClosing
|
|
166
|
-
|
|
149
|
+
toClosing(capturedStack, k) {
|
|
150
|
+
const send = this.sendImmediately.bind(this);
|
|
167
151
|
invalidateSend(this, 'Channel closing', capturedStack);
|
|
168
152
|
|
|
169
153
|
this.accept = function (f) {
|
|
170
154
|
if (f.id === defs.ChannelCloseOk) {
|
|
171
|
-
if (k)
|
|
172
|
-
|
|
173
|
-
var s = stackCapture('ChannelCloseOk frame received');
|
|
155
|
+
if (k) k();
|
|
156
|
+
const s = stackCapture('ChannelCloseOk frame received');
|
|
174
157
|
this.toClosed(s);
|
|
175
|
-
}
|
|
176
|
-
else if (f.id === defs.ChannelClose) {
|
|
158
|
+
} else if (f.id === defs.ChannelClose) {
|
|
177
159
|
send(defs.ChannelCloseOk, {});
|
|
178
160
|
}
|
|
179
161
|
// else ignore frame
|
|
180
162
|
};
|
|
181
163
|
}
|
|
182
164
|
|
|
183
|
-
_rejectPending
|
|
184
|
-
function
|
|
185
|
-
r(new Error(
|
|
165
|
+
_rejectPending() {
|
|
166
|
+
function reject(r) {
|
|
167
|
+
r(new Error('Channel ended, no reply will be forthcoming'));
|
|
186
168
|
}
|
|
187
|
-
if (this.reply !== null)
|
|
188
|
-
rej(this.reply);
|
|
169
|
+
if (this.reply !== null) reject(this.reply);
|
|
189
170
|
this.reply = null;
|
|
190
171
|
|
|
191
|
-
|
|
192
|
-
while (discard
|
|
193
|
-
|
|
172
|
+
let discard = this.pending.shift();
|
|
173
|
+
while (discard) {
|
|
174
|
+
if (discard) reject(discard.reply);
|
|
175
|
+
discard = this.pending.shift();
|
|
176
|
+
}
|
|
194
177
|
this.pending = null; // so pushes will break
|
|
195
178
|
}
|
|
196
179
|
|
|
197
|
-
closeBecause
|
|
180
|
+
closeBecause(reason, code, k) {
|
|
198
181
|
this.sendImmediately(defs.ChannelClose, {
|
|
199
182
|
replyText: reason,
|
|
200
183
|
replyCode: code,
|
|
201
|
-
methodId: 0,
|
|
184
|
+
methodId: 0,
|
|
185
|
+
classId: 0,
|
|
202
186
|
});
|
|
203
|
-
|
|
187
|
+
const s = stackCapture(`closeBecause called: ${reason}`);
|
|
204
188
|
this.toClosing(s, k);
|
|
205
189
|
}
|
|
206
190
|
|
|
207
191
|
// If we close because there's been an error, we need to distinguish
|
|
208
192
|
// between what we tell the server (`reason`) and what we report as
|
|
209
193
|
// the cause in the client (`error`).
|
|
210
|
-
closeWithError
|
|
211
|
-
|
|
212
|
-
this.closeBecause(reason, code, function () {
|
|
194
|
+
closeWithError(id, reason, code, error) {
|
|
195
|
+
this.closeBecause(reason, code, () => {
|
|
213
196
|
error.code = code;
|
|
214
197
|
// content frames and consumer errors do not provide a method a class/method ID
|
|
215
198
|
if (id) {
|
|
216
199
|
error.classId = defs.info(id).classId;
|
|
217
200
|
error.methodId = defs.info(id).methodId;
|
|
218
201
|
}
|
|
219
|
-
|
|
202
|
+
this.emit('error', error);
|
|
220
203
|
});
|
|
221
204
|
}
|
|
222
205
|
|
|
@@ -227,38 +210,30 @@ class Channel extends EventEmitter {
|
|
|
227
210
|
// frames.
|
|
228
211
|
// Keep the try/catch localised, in an attempt to avoid disabling
|
|
229
212
|
// optimisation
|
|
230
|
-
acceptMessageFrame
|
|
213
|
+
acceptMessageFrame(f) {
|
|
231
214
|
try {
|
|
232
215
|
this.handleMessage = this.handleMessage(f);
|
|
233
|
-
}
|
|
234
|
-
catch (msg) {
|
|
216
|
+
} catch (msg) {
|
|
235
217
|
if (typeof msg === 'string') {
|
|
236
|
-
this.closeWithError(f.id, msg, defs.constants.UNEXPECTED_FRAME,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
else
|
|
240
|
-
this.closeWithError(f.id, '
|
|
241
|
-
defs.constants.INTERNAL_ERROR, msg);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
this.closeWithError(f.id, 'Internal error while processing message',
|
|
245
|
-
defs.constants.INTERNAL_ERROR,
|
|
246
|
-
new Error(msg.toString()));
|
|
218
|
+
this.closeWithError(f.id, msg, defs.constants.UNEXPECTED_FRAME, new Error(msg));
|
|
219
|
+
} else if (msg instanceof Error) {
|
|
220
|
+
this.closeWithError(f.id, 'Error while processing message', defs.constants.INTERNAL_ERROR, msg);
|
|
221
|
+
} else {
|
|
222
|
+
this.closeWithError(f.id, 'Internal error while processing message', defs.constants.INTERNAL_ERROR, new Error(msg.toString()));
|
|
247
223
|
}
|
|
248
224
|
}
|
|
249
225
|
}
|
|
250
226
|
|
|
251
|
-
handleConfirm
|
|
252
|
-
|
|
253
|
-
|
|
227
|
+
handleConfirm(handle, f) {
|
|
228
|
+
const tag = f.deliveryTag;
|
|
229
|
+
const multi = f.multiple;
|
|
254
230
|
|
|
255
231
|
if (multi) {
|
|
256
|
-
|
|
232
|
+
const confirmed = this.unconfirmed.splice(0, tag - this.lwm + 1);
|
|
257
233
|
this.lwm = tag + 1;
|
|
258
234
|
confirmed.forEach(handle);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
var c;
|
|
235
|
+
} else {
|
|
236
|
+
let c;
|
|
262
237
|
if (tag === this.lwm) {
|
|
263
238
|
c = this.unconfirmed.shift();
|
|
264
239
|
this.lwm++;
|
|
@@ -268,8 +243,7 @@ class Channel extends EventEmitter {
|
|
|
268
243
|
this.unconfirmed.shift();
|
|
269
244
|
this.lwm++;
|
|
270
245
|
}
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
246
|
+
} else {
|
|
273
247
|
c = this.unconfirmed[tag - this.lwm];
|
|
274
248
|
this.unconfirmed[tag - this.lwm] = null;
|
|
275
249
|
}
|
|
@@ -279,78 +253,79 @@ class Channel extends EventEmitter {
|
|
|
279
253
|
}
|
|
280
254
|
}
|
|
281
255
|
|
|
282
|
-
pushConfirmCallback
|
|
256
|
+
pushConfirmCallback(cb) {
|
|
283
257
|
// `null` is used specifically for marking already confirmed slots,
|
|
284
258
|
// so I coerce `undefined` and `null` to false; functions are never
|
|
285
259
|
// falsey.
|
|
286
260
|
this.unconfirmed.push(cb || false);
|
|
287
261
|
}
|
|
288
262
|
|
|
289
|
-
onBufferDrain
|
|
263
|
+
onBufferDrain() {
|
|
290
264
|
this.emit('drain');
|
|
291
265
|
}
|
|
292
266
|
|
|
293
267
|
accept(f) {
|
|
294
|
-
|
|
295
268
|
switch (f.id) {
|
|
296
|
-
|
|
297
269
|
// Message frames
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
270
|
+
case undefined: // content frame!
|
|
271
|
+
case defs.BasicDeliver:
|
|
272
|
+
case defs.BasicReturn:
|
|
273
|
+
case defs.BasicProperties:
|
|
274
|
+
return this.acceptMessageFrame(f);
|
|
303
275
|
|
|
304
276
|
// confirmations, need to do confirm.select first
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
277
|
+
case defs.BasicAck:
|
|
278
|
+
return this.emit('ack', f.fields);
|
|
279
|
+
case defs.BasicNack:
|
|
280
|
+
return this.emit('nack', f.fields);
|
|
281
|
+
case defs.BasicCancel:
|
|
282
|
+
// The broker can send this if e.g., the queue is deleted.
|
|
283
|
+
return this.emit('cancel', f.fields);
|
|
284
|
+
|
|
285
|
+
case defs.ChannelClose: {
|
|
286
|
+
// Any remote closure is an error to us. Reject the pending reply
|
|
287
|
+
// with the close frame, so it can see whether it was that
|
|
288
|
+
// operation that caused it to close.
|
|
289
|
+
if (this.reply) {
|
|
290
|
+
const reply = this.reply;
|
|
291
|
+
this.reply = null;
|
|
292
|
+
reply(f);
|
|
293
|
+
}
|
|
294
|
+
const emsg = `Channel closed by server: ${closeMsg(f)}`;
|
|
295
|
+
this.sendImmediately(defs.ChannelCloseOk, {});
|
|
323
296
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
297
|
+
const error = new Error(emsg);
|
|
298
|
+
error.code = f.fields.replyCode;
|
|
299
|
+
error.classId = f.fields.classId;
|
|
300
|
+
error.methodId = f.fields.methodId;
|
|
301
|
+
this.emit('error', error);
|
|
329
302
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
303
|
+
const s = stackCapture(emsg);
|
|
304
|
+
this.toClosed(s);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
case defs.BasicFlow:
|
|
309
|
+
// RabbitMQ doesn't send this, it just blocks the TCP socket
|
|
310
|
+
return this.closeWithError(f.id, 'Flow not implemented', defs.constants.NOT_IMPLEMENTED, new Error('Flow not implemented'));
|
|
311
|
+
|
|
312
|
+
default: {
|
|
313
|
+
// assume all other things are replies
|
|
314
|
+
// Resolving the reply may lead to another RPC; to make sure we
|
|
315
|
+
// don't hold that up, clear this.reply
|
|
316
|
+
const reply = this.reply;
|
|
317
|
+
this.reply = null;
|
|
318
|
+
// however, maybe there's an RPC waiting to go? If so, that'll
|
|
319
|
+
// fill this.reply again, restoring the invariant. This does rely
|
|
320
|
+
// on any response being recv'ed after resolving the promise,
|
|
321
|
+
// below; hence, I use synchronous defer.
|
|
322
|
+
if (this.pending.length > 0) {
|
|
323
|
+
const send = this.pending.shift();
|
|
324
|
+
this.reply = send.reply;
|
|
325
|
+
this.sendImmediately(send.method, send.fields);
|
|
326
|
+
}
|
|
327
|
+
return reply(null, f);
|
|
352
328
|
}
|
|
353
|
-
return reply(null, f);
|
|
354
329
|
}
|
|
355
330
|
}
|
|
356
331
|
}
|
|
@@ -369,43 +344,40 @@ class Channel extends EventEmitter {
|
|
|
369
344
|
// the exchange.
|
|
370
345
|
|
|
371
346
|
function invalidOp(msg, stack) {
|
|
372
|
-
return
|
|
347
|
+
return () => {
|
|
373
348
|
throw new IllegalOperationError(msg, stack);
|
|
374
349
|
};
|
|
375
350
|
}
|
|
376
351
|
|
|
377
352
|
function invalidateSend(ch, msg, stack) {
|
|
378
|
-
ch.sendImmediately = ch.sendOrEnqueue = ch.sendMessage =
|
|
379
|
-
invalidOp(msg, stack);
|
|
353
|
+
ch.sendImmediately = ch.sendOrEnqueue = ch.sendMessage = invalidOp(msg, stack);
|
|
380
354
|
}
|
|
381
355
|
|
|
382
356
|
// Kick off a message delivery given a BasicDeliver or BasicReturn
|
|
383
357
|
// frame (BasicGet uses the RPC mechanism)
|
|
384
358
|
function acceptDeliveryOrReturn(f) {
|
|
385
|
-
|
|
359
|
+
let event;
|
|
386
360
|
if (f.id === defs.BasicDeliver) event = 'delivery';
|
|
387
361
|
else if (f.id === defs.BasicReturn) event = 'return';
|
|
388
|
-
else throw fmt(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
var self = this;
|
|
392
|
-
var fields = f.fields;
|
|
393
|
-
return acceptMessage(function(message) {
|
|
362
|
+
else throw fmt('Expected BasicDeliver or BasicReturn; got %s', inspect(f));
|
|
363
|
+
const fields = f.fields;
|
|
364
|
+
return acceptMessage((message) => {
|
|
394
365
|
message.fields = fields;
|
|
395
|
-
|
|
366
|
+
this.emit(event, message);
|
|
396
367
|
});
|
|
397
368
|
}
|
|
398
369
|
|
|
399
370
|
// Move to the state of waiting for message frames (headers, then
|
|
400
371
|
// one or more content frames)
|
|
401
372
|
function acceptMessage(continuation) {
|
|
402
|
-
|
|
403
|
-
|
|
373
|
+
let totalSize = 0;
|
|
374
|
+
let remaining = 0;
|
|
375
|
+
let buffers = null;
|
|
404
376
|
|
|
405
|
-
|
|
377
|
+
const message = {
|
|
406
378
|
fields: null,
|
|
407
379
|
properties: null,
|
|
408
|
-
content: null
|
|
380
|
+
content: null,
|
|
409
381
|
};
|
|
410
382
|
|
|
411
383
|
return headers;
|
|
@@ -421,13 +393,11 @@ function acceptMessage(continuation) {
|
|
|
421
393
|
message.content = Buffer.alloc(0);
|
|
422
394
|
continuation(message);
|
|
423
395
|
return acceptDeliveryOrReturn;
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
396
|
+
} else {
|
|
426
397
|
return content;
|
|
427
398
|
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
throw "Expected headers frame after delivery";
|
|
399
|
+
} else {
|
|
400
|
+
throw 'Expected headers frame after delivery';
|
|
431
401
|
}
|
|
432
402
|
}
|
|
433
403
|
|
|
@@ -435,71 +405,63 @@ function acceptMessage(continuation) {
|
|
|
435
405
|
// %%% TODO cancelled messages (sent as zero-length content frame)
|
|
436
406
|
function content(f) {
|
|
437
407
|
if (f.content) {
|
|
438
|
-
|
|
408
|
+
const size = f.content.length;
|
|
439
409
|
remaining -= size;
|
|
440
410
|
if (remaining === 0) {
|
|
441
411
|
if (buffers !== null) {
|
|
442
412
|
buffers.push(f.content);
|
|
443
413
|
message.content = Buffer.concat(buffers);
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
414
|
+
} else {
|
|
446
415
|
message.content = f.content;
|
|
447
416
|
}
|
|
448
417
|
continuation(message);
|
|
449
418
|
return acceptDeliveryOrReturn;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
else {
|
|
456
|
-
if (buffers !== null)
|
|
457
|
-
buffers.push(f.content);
|
|
458
|
-
else
|
|
459
|
-
buffers = [f.content];
|
|
419
|
+
} else if (remaining < 0) {
|
|
420
|
+
throw fmt('Too much content sent! Expected %d bytes', totalSize);
|
|
421
|
+
} else {
|
|
422
|
+
if (buffers !== null) buffers.push(f.content);
|
|
423
|
+
else buffers = [f.content];
|
|
460
424
|
return content;
|
|
461
425
|
}
|
|
462
|
-
}
|
|
463
|
-
else throw "Expected content frame after headers"
|
|
426
|
+
} else throw 'Expected content frame after headers';
|
|
464
427
|
}
|
|
465
428
|
}
|
|
466
429
|
|
|
467
430
|
// This adds just a bit more stuff useful for the APIs, but not
|
|
468
431
|
// low-level machinery.
|
|
469
432
|
class BaseChannel extends Channel {
|
|
470
|
-
constructor
|
|
433
|
+
constructor(connection) {
|
|
471
434
|
super(connection);
|
|
472
435
|
this.consumers = new Map();
|
|
473
436
|
}
|
|
474
437
|
|
|
475
438
|
// Not sure I like the ff, it's going to be changing hidden classes
|
|
476
439
|
// all over the place. On the other hand, whaddya do.
|
|
477
|
-
registerConsumer
|
|
440
|
+
registerConsumer(tag, callback) {
|
|
478
441
|
this.consumers.set(tag, callback);
|
|
479
442
|
}
|
|
480
443
|
|
|
481
|
-
unregisterConsumer
|
|
444
|
+
unregisterConsumer(tag) {
|
|
482
445
|
this.consumers.delete(tag);
|
|
483
446
|
}
|
|
484
447
|
|
|
485
|
-
dispatchMessage
|
|
486
|
-
|
|
487
|
-
|
|
448
|
+
dispatchMessage(fields, message) {
|
|
449
|
+
const consumerTag = fields.consumerTag;
|
|
450
|
+
const consumer = this.consumers.get(consumerTag);
|
|
488
451
|
if (consumer) {
|
|
489
452
|
return consumer(message);
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
453
|
+
} else {
|
|
492
454
|
// %%% Surely a race here
|
|
493
|
-
throw new Error(
|
|
455
|
+
throw new Error(`Unknown consumer: ${consumerTag}`);
|
|
494
456
|
}
|
|
495
457
|
}
|
|
496
458
|
|
|
497
|
-
handleDelivery
|
|
459
|
+
handleDelivery(message) {
|
|
498
460
|
return this.dispatchMessage(message.fields, message);
|
|
499
461
|
}
|
|
500
462
|
|
|
501
|
-
handleCancel
|
|
502
|
-
|
|
463
|
+
handleCancel(fields) {
|
|
464
|
+
const result = this.dispatchMessage(fields, null);
|
|
503
465
|
this.unregisterConsumer(fields.consumerTag);
|
|
504
466
|
return result;
|
|
505
467
|
}
|