comq 0.25.1 → 0.26.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/docs/headers.md +16 -1
- package/features/properties.feature +39 -0
- package/features/shards.virtual.feature +89 -0
- package/features/steps/context.js +29 -2
- package/features/steps/hooks.js +1 -0
- package/features/steps/names.js +245 -0
- package/features/steps/networks.js +23 -5
- package/features/steps/rpc.js +14 -2
- package/package.json +1 -1
- package/readme.md +102 -4
- package/source/channel.js +147 -2
- package/source/connect.js +18 -15
- package/source/connection.js +8 -5
- package/source/events.js +7 -0
- package/source/io.js +31 -9
- package/source/shards/channel.js +289 -19
- package/source/shards/connection.js +404 -18
- package/source/shards/expand.js +57 -0
- package/source/shards/index.js +2 -0
- package/source/shards/resolve.js +134 -0
- package/source/shards/watch.js +257 -0
- package/source/singleton.js +3 -2
- package/test/amqplib.mock.js +1 -0
- package/test/assert.test.js +7 -2
- package/test/channel.quiet.test.js +301 -0
- package/test/connect.test.js +49 -8
- package/test/connection.mock.js +3 -0
- package/test/connection.test.js +15 -0
- package/test/io.call.test.js +1 -1
- package/test/io.diagnostics.test.js +1 -1
- package/test/io.reply.test.js +12 -1
- package/test/io.request.test.js +18 -0
- package/test/io.shards.test.js +85 -0
- package/test/shards/channel.test.js +411 -0
- package/test/shards/connection.test.js +372 -17
- package/test/shards/expand.test.js +58 -0
- package/test/shards/resolve.test.js +73 -0
- package/test/shards/watch.test.js +264 -0
- package/test/singleton.test.js +1 -1
- package/types/channel.d.ts +23 -0
- package/types/connection.d.ts +13 -2
- package/types/diagnostic.d.ts +2 -1
- package/types/io.d.ts +17 -1
- package/types/shards.d.ts +58 -0
package/docs/headers.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Message Properties
|
|
2
2
|
|
|
3
|
-
Events may be
|
|
3
|
+
Events and Requests may be sent and received with custom properties using an optional `properties`
|
|
4
|
+
argument.
|
|
4
5
|
|
|
5
6
|
## Emission
|
|
6
7
|
|
|
@@ -16,6 +17,20 @@ to [amqplib.publish](https://amqp-node.github.io/amqplib/channel_api.html#channe
|
|
|
16
17
|
`consumer` function's signature
|
|
17
18
|
is `async? (payload: any, [properties: comq.amqp.Properties]): void`
|
|
18
19
|
|
|
20
|
+
## Request
|
|
21
|
+
|
|
22
|
+
`async IO.request(queue: string, payload: any, [properties: comq.amqp.Properties]): any`
|
|
23
|
+
|
|
24
|
+
`replyTo` and `correlationId` are set by comq whatever is passed, and `contentType` is the
|
|
25
|
+
encoding of the Request. A Request takes no `timeout` and no `signal`.
|
|
26
|
+
|
|
27
|
+
## Reply
|
|
28
|
+
|
|
29
|
+
`async IO.reply(queue: string, producer): void`
|
|
30
|
+
|
|
31
|
+
`producer` function's signature
|
|
32
|
+
is `async? (payload: any, [properties: comq.amqp.Properties]): any`
|
|
33
|
+
|
|
19
34
|
## Headers set by comq
|
|
20
35
|
|
|
21
36
|
A message that failed at least once carries headers comq wrote, and a consumer sees them among
|
|
@@ -27,3 +27,42 @@ Feature: Message properties
|
|
|
27
27
|
"""yaml
|
|
28
28
|
appId: test
|
|
29
29
|
"""
|
|
30
|
+
|
|
31
|
+
Scenario: Sending a request with headers
|
|
32
|
+
Given function replying `greet` queue:
|
|
33
|
+
"""
|
|
34
|
+
({ name }, properties) => properties.headers.greeting + ' ' + name
|
|
35
|
+
"""
|
|
36
|
+
When the consumer sends the following request to the `greet` queue with properties:
|
|
37
|
+
"""yaml
|
|
38
|
+
payload:
|
|
39
|
+
name: world
|
|
40
|
+
properties:
|
|
41
|
+
headers:
|
|
42
|
+
greeting: hello
|
|
43
|
+
"""
|
|
44
|
+
Then the consumer receives the reply:
|
|
45
|
+
"""yaml
|
|
46
|
+
hello world
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
Scenario: A retried request keeps its headers
|
|
50
|
+
Given function replying `greet` queue:
|
|
51
|
+
"""
|
|
52
|
+
(_, properties) => {
|
|
53
|
+
if (properties.headers['x-comq-attempt'] === undefined) throw new Error('once')
|
|
54
|
+
|
|
55
|
+
return properties.headers.greeting
|
|
56
|
+
}
|
|
57
|
+
"""
|
|
58
|
+
When the consumer sends the following request to the `greet` queue with properties:
|
|
59
|
+
"""yaml
|
|
60
|
+
payload: {}
|
|
61
|
+
properties:
|
|
62
|
+
headers:
|
|
63
|
+
greeting: hello
|
|
64
|
+
"""
|
|
65
|
+
Then the consumer receives the reply:
|
|
66
|
+
"""yaml
|
|
67
|
+
hello
|
|
68
|
+
"""
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Feature: Virtual shards
|
|
2
|
+
|
|
3
|
+
The URLs of a sharded connection are names, of which several may stand for one broker.
|
|
4
|
+
A connection is made to each broker, and the brokers are followed as the names move.
|
|
5
|
+
|
|
6
|
+
Scenario: Names sharing a broker share a connection
|
|
7
|
+
Given 4 names, half of them standing for each broker
|
|
8
|
+
When I connect to the names
|
|
9
|
+
Then 1 connection is open to each broker
|
|
10
|
+
|
|
11
|
+
Scenario: Names given as a range
|
|
12
|
+
Given 4 names, half of them standing for each broker
|
|
13
|
+
When I connect to the range of the names
|
|
14
|
+
Then 1 connection is open to each broker
|
|
15
|
+
|
|
16
|
+
Scenario: Requests over the names of one broker
|
|
17
|
+
Given 4 names standing for the first broker
|
|
18
|
+
And an active connection to the names
|
|
19
|
+
And a producer replying `virtual_one` queue
|
|
20
|
+
When the consumer sends 20 requests to the `virtual_one` queue
|
|
21
|
+
Then all replies have been received
|
|
22
|
+
And 1 connection is open to the first broker
|
|
23
|
+
And 0 connections are open to the second broker
|
|
24
|
+
|
|
25
|
+
Scenario: A name moves to another broker
|
|
26
|
+
Given 4 names standing for the first broker
|
|
27
|
+
And an active connection to the names
|
|
28
|
+
And a producer replying `virtual_moved` queue
|
|
29
|
+
And tasks from the `virtual_moved_tasks` queue are being processed
|
|
30
|
+
When half of the names move to the second broker
|
|
31
|
+
Then the second broker has joined
|
|
32
|
+
And 1 connection is open to each broker
|
|
33
|
+
When the consumer sends 50 requests to the `virtual_moved` queue
|
|
34
|
+
Then all replies have been received
|
|
35
|
+
When a task is enqueued to the `virtual_moved_tasks` queue of the second broker by another connection
|
|
36
|
+
Then the task is processed
|
|
37
|
+
|
|
38
|
+
Scenario: A broker no name stands for is drained before it is left
|
|
39
|
+
Given 4 names standing for the first broker
|
|
40
|
+
And a broker lingers for 300ms
|
|
41
|
+
And an active connection to the names
|
|
42
|
+
And tasks from the `virtual_left_behind` queue are being processed
|
|
43
|
+
And a producer replying `virtual_slow` queue in 2000ms
|
|
44
|
+
When the consumer sends 5 requests to the `virtual_slow` queue
|
|
45
|
+
And the names move to the second broker
|
|
46
|
+
Then the second broker has joined
|
|
47
|
+
And the first broker is retired
|
|
48
|
+
When a task is enqueued to the `virtual_left_behind` queue of the first broker by another connection
|
|
49
|
+
Then the task is processed
|
|
50
|
+
When after 1000ms
|
|
51
|
+
Then the first broker is not left
|
|
52
|
+
And all replies have been received
|
|
53
|
+
And the first broker is left
|
|
54
|
+
And 0 connections are open to the first broker
|
|
55
|
+
When the consumer sends 20 requests to the `virtual_slow` queue
|
|
56
|
+
Then all replies have been received
|
|
57
|
+
|
|
58
|
+
Scenario: Names observed while they are moving are not followed
|
|
59
|
+
Given 4 names standing for the first broker
|
|
60
|
+
And an active connection to the names
|
|
61
|
+
When the names keep moving between the brokers for 1500ms
|
|
62
|
+
And after 500ms
|
|
63
|
+
Then no broker has joined or retired
|
|
64
|
+
And 0 connections are open to the second broker
|
|
65
|
+
|
|
66
|
+
Scenario: A broker that is out of reach is followed at once
|
|
67
|
+
Given 4 names standing for the first broker
|
|
68
|
+
And the names settle in 60000ms
|
|
69
|
+
And an active connection to the names
|
|
70
|
+
And a producer replying `virtual_followed` queue
|
|
71
|
+
When the first broker is out of reach
|
|
72
|
+
And the names move to the second broker
|
|
73
|
+
Then the second broker has joined
|
|
74
|
+
When the consumer sends 20 requests to the `virtual_followed` queue
|
|
75
|
+
Then all replies have been received
|
|
76
|
+
|
|
77
|
+
Scenario: A retiring broker that is named again is back
|
|
78
|
+
Given 4 names standing for the first broker
|
|
79
|
+
And a broker lingers for 5000ms
|
|
80
|
+
And an active connection to the names
|
|
81
|
+
And a producer replying `virtual_back` queue
|
|
82
|
+
When the names move to the second broker
|
|
83
|
+
Then the first broker is retired
|
|
84
|
+
When the names move to the first broker
|
|
85
|
+
Then the first broker has joined
|
|
86
|
+
And the second broker is retired
|
|
87
|
+
And the first broker is not left
|
|
88
|
+
When the consumer sends 20 requests to the `virtual_back` queue
|
|
89
|
+
Then all replies have been received
|
|
@@ -42,6 +42,15 @@ class Context extends World {
|
|
|
42
42
|
/** @type {comq.features.Network[]} */
|
|
43
43
|
networks = []
|
|
44
44
|
|
|
45
|
+
/** @type {Record<string, string>} the address each name of a shard resolves to */
|
|
46
|
+
names = {}
|
|
47
|
+
|
|
48
|
+
/** @type {comq.shards.Timing} */
|
|
49
|
+
resolution = { interval: 50, settle: 300, linger: 600 }
|
|
50
|
+
|
|
51
|
+
/** @type {Record<'join' | 'retire' | 'leave', string[]>} the addresses of the brokers that did */
|
|
52
|
+
brokers = { join: [], retire: [], leave: [] }
|
|
53
|
+
|
|
45
54
|
async connect (user, password) {
|
|
46
55
|
const urls = this.#urls(user, password)
|
|
47
56
|
|
|
@@ -54,6 +63,23 @@ class Context extends World {
|
|
|
54
63
|
await this.#connect(urls, assert)
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Connects to the names of the shards, which a broker is found behind at whatever port the
|
|
68
|
+
* names are given: see `names.js`.
|
|
69
|
+
*
|
|
70
|
+
* @param {string[]} hosts
|
|
71
|
+
* @param {number} port
|
|
72
|
+
*/
|
|
73
|
+
async connectNames (hosts, port) {
|
|
74
|
+
const urls = hosts.map((host) => PROTOCOL + USER + ':' + PASSWORD + '@' + host + ':' + port)
|
|
75
|
+
|
|
76
|
+
await this.#connect(urls, connect, { ...TOPOLOGY, resolution: this.resolution })
|
|
77
|
+
|
|
78
|
+
for (const event of Object.keys(this.brokers)) {
|
|
79
|
+
this.io.diagnose(event, (_index, address) => this.brokers[event].push(address))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
async unplug () {
|
|
58
84
|
await Promise.all(this.networks.map((network) => network.close()))
|
|
59
85
|
|
|
@@ -78,14 +104,15 @@ class Context extends World {
|
|
|
78
104
|
/**
|
|
79
105
|
* @param {string[]} urls
|
|
80
106
|
* @param {comq.Connect} [method]
|
|
107
|
+
* @param {comq.Options} [options]
|
|
81
108
|
* @return {Promise<void>}
|
|
82
109
|
*/
|
|
83
|
-
async #connect (urls, method = connect) {
|
|
110
|
+
async #connect (urls, method = connect, options = TOPOLOGY) {
|
|
84
111
|
if (this.io !== undefined) await this.disconnect()
|
|
85
112
|
|
|
86
113
|
// the retry delay is a topology setting, so the suite need not wait out a
|
|
87
114
|
// production one to see the mechanism work
|
|
88
|
-
this.io = await method(...urls,
|
|
115
|
+
this.io = await method(...urls, options)
|
|
89
116
|
this.connected = true
|
|
90
117
|
|
|
91
118
|
for (const event of EVENTS) this.io.diagnose(event, () => (this.events[event] = true))
|
package/features/steps/hooks.js
CHANGED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert')
|
|
4
|
+
const { Given, When, Then, defineParameterType } = require('@cucumber/cucumber')
|
|
5
|
+
const { connect } = require('../../')
|
|
6
|
+
const { timeout, until } = require('../../test/helpers')
|
|
7
|
+
const { BROKERS_AMOUNT, getAddress, USER, PASSWORD } = require('./brokers')
|
|
8
|
+
const { Network } = require('./networks')
|
|
9
|
+
|
|
10
|
+
defineParameterType({
|
|
11
|
+
name: 'broker',
|
|
12
|
+
regexp: /(first|second)/,
|
|
13
|
+
transformer: (value) => ORDINALS.indexOf(value)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
Given('{number} names standing for the {broker} broker',
|
|
17
|
+
/**
|
|
18
|
+
* @param {number} amount
|
|
19
|
+
* @param {number} n
|
|
20
|
+
* @this {comq.features.Context}
|
|
21
|
+
*/
|
|
22
|
+
async function (amount, n) {
|
|
23
|
+
await expose.call(this)
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < amount; i++) this.names[name(i)] = ADDRESSES[n]
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
Given('{number} names, half of them standing for each broker',
|
|
29
|
+
/**
|
|
30
|
+
* @param {number} amount
|
|
31
|
+
* @this {comq.features.Context}
|
|
32
|
+
*/
|
|
33
|
+
async function (amount) {
|
|
34
|
+
await expose.call(this)
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < amount; i++) this.names[name(i)] = ADDRESSES[i < amount / 2 ? 0 : 1]
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
Given('the names settle in {number}ms',
|
|
40
|
+
/**
|
|
41
|
+
* @param {number} settle
|
|
42
|
+
* @this {comq.features.Context}
|
|
43
|
+
*/
|
|
44
|
+
function (settle) {
|
|
45
|
+
this.resolution.settle = settle
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
Given('a broker lingers for {number}ms',
|
|
49
|
+
/**
|
|
50
|
+
* @param {number} linger
|
|
51
|
+
* @this {comq.features.Context}
|
|
52
|
+
*/
|
|
53
|
+
function (linger) {
|
|
54
|
+
this.resolution.linger = linger
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
Given('an active connection to the names', connectNames)
|
|
58
|
+
|
|
59
|
+
When('I connect to the names', connectNames)
|
|
60
|
+
|
|
61
|
+
When('I connect to the range of the names',
|
|
62
|
+
/**
|
|
63
|
+
* @this {comq.features.Context}
|
|
64
|
+
*/
|
|
65
|
+
async function () {
|
|
66
|
+
const amount = Object.keys(this.names).length
|
|
67
|
+
|
|
68
|
+
await this.connectNames([name(`[0..${amount}]`)], PORT)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
When('half of the names move to the {broker} broker',
|
|
72
|
+
/**
|
|
73
|
+
* @param {number} n
|
|
74
|
+
* @this {comq.features.Context}
|
|
75
|
+
*/
|
|
76
|
+
function (n) {
|
|
77
|
+
const names = Object.keys(this.names)
|
|
78
|
+
|
|
79
|
+
for (const name of names.slice(names.length / 2)) this.names[name] = ADDRESSES[n]
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
When('the names move to the {broker} broker', move)
|
|
83
|
+
|
|
84
|
+
When('the names keep moving between the brokers for {number}ms',
|
|
85
|
+
/**
|
|
86
|
+
* @param {number} duration
|
|
87
|
+
* @this {comq.features.Context}
|
|
88
|
+
*/
|
|
89
|
+
async function (duration) {
|
|
90
|
+
const flips = Math.floor(duration / FLIP_MS)
|
|
91
|
+
|
|
92
|
+
// an odd number of moves would leave the names moved
|
|
93
|
+
for (let i = 0; i < flips + flips % 2; i++) {
|
|
94
|
+
move.call(this, (i + 1) % 2)
|
|
95
|
+
|
|
96
|
+
await timeout(FLIP_MS)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
When('the {broker} broker is out of reach',
|
|
101
|
+
/**
|
|
102
|
+
* @param {number} n
|
|
103
|
+
* @this {comq.features.Context}
|
|
104
|
+
*/
|
|
105
|
+
function (n) {
|
|
106
|
+
this.networks[n].cut()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
When('a task is enqueued to the {token} queue of the {broker} broker by another connection',
|
|
110
|
+
/**
|
|
111
|
+
* @param {string} queue
|
|
112
|
+
* @param {number} n
|
|
113
|
+
* @this {comq.features.Context}
|
|
114
|
+
*/
|
|
115
|
+
async function (queue, n) {
|
|
116
|
+
const io = await connect('amqp://' + USER + ':' + PASSWORD + '@' + getAddress(n))
|
|
117
|
+
|
|
118
|
+
this.enqueued = 'left behind'
|
|
119
|
+
|
|
120
|
+
await io.enqueue(queue, this.enqueued)
|
|
121
|
+
await io.close()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
Then('the task is processed',
|
|
125
|
+
/**
|
|
126
|
+
* @this {comq.features.Context}
|
|
127
|
+
*/
|
|
128
|
+
async function () {
|
|
129
|
+
const processed = await until(() => this.processed === this.enqueued)
|
|
130
|
+
|
|
131
|
+
assert.equal(processed, true, 'the task was not processed')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
Then('the {broker} broker has joined', heard('join'))
|
|
135
|
+
|
|
136
|
+
Then('the {broker} broker is retired', heard('retire'))
|
|
137
|
+
|
|
138
|
+
Then('the {broker} broker is left', heard('leave'))
|
|
139
|
+
|
|
140
|
+
Then('the {broker} broker is not left',
|
|
141
|
+
/**
|
|
142
|
+
* @param {number} n
|
|
143
|
+
* @this {comq.features.Context}
|
|
144
|
+
*/
|
|
145
|
+
function (n) {
|
|
146
|
+
assert.equal(this.brokers.leave.includes(ADDRESSES[n]), false, 'the broker is left')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
Then('no broker has joined or retired',
|
|
150
|
+
/**
|
|
151
|
+
* @this {comq.features.Context}
|
|
152
|
+
*/
|
|
153
|
+
function () {
|
|
154
|
+
assert.deepEqual(this.brokers, { join: [], retire: [], leave: [] })
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
Then('{number} connection(s) is/are open to the {broker} broker', open)
|
|
158
|
+
|
|
159
|
+
Then('{number} connection is open to each broker',
|
|
160
|
+
/**
|
|
161
|
+
* @param {number} amount
|
|
162
|
+
* @this {comq.features.Context}
|
|
163
|
+
*/
|
|
164
|
+
async function (amount) {
|
|
165
|
+
for (let n = 0; n < BROKERS_AMOUNT; n++) await open.call(this, amount, n)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Each broker is given an address of its own, at one port: a name resolves to an address and
|
|
170
|
+
* says nothing of a port, and the brokers of the suite differ in nothing but theirs.
|
|
171
|
+
*
|
|
172
|
+
* @this {comq.features.Context}
|
|
173
|
+
*/
|
|
174
|
+
async function expose () {
|
|
175
|
+
if (this.networks.length > 0) return
|
|
176
|
+
|
|
177
|
+
for (let n = 0; n < BROKERS_AMOUNT; n++) {
|
|
178
|
+
const network = new Network(n, ADDRESSES[n], PORT)
|
|
179
|
+
|
|
180
|
+
await network.open()
|
|
181
|
+
|
|
182
|
+
this.networks[n] = network
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
global.COMQ_TESTING_LOOKUP = async (host) => ({ addresses: [this.names[host]], ttl: 0 })
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @this {comq.features.Context}
|
|
190
|
+
*/
|
|
191
|
+
async function connectNames () {
|
|
192
|
+
await this.connectNames(Object.keys(this.names), PORT)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {number} n
|
|
197
|
+
* @this {comq.features.Context}
|
|
198
|
+
*/
|
|
199
|
+
function move (n) {
|
|
200
|
+
for (const name of Object.keys(this.names)) this.names[name] = ADDRESSES[n]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @param {number} amount
|
|
205
|
+
* @param {number} n
|
|
206
|
+
* @this {comq.features.Context}
|
|
207
|
+
*/
|
|
208
|
+
async function open (amount, n) {
|
|
209
|
+
await until(() => this.networks[n].connections === amount)
|
|
210
|
+
|
|
211
|
+
assert.equal(this.networks[n].connections, amount)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {'join' | 'retire' | 'leave'} event
|
|
216
|
+
*/
|
|
217
|
+
function heard (event) {
|
|
218
|
+
/**
|
|
219
|
+
* @param {number} n
|
|
220
|
+
* @this {comq.features.Context}
|
|
221
|
+
*/
|
|
222
|
+
return async function (n) {
|
|
223
|
+
const happened = await until(() => this.brokers[event].includes(ADDRESSES[n]), 10_000)
|
|
224
|
+
|
|
225
|
+
assert.equal(happened, true, `the broker did not ${event}`)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {number | string} i
|
|
231
|
+
* @return {string}
|
|
232
|
+
*/
|
|
233
|
+
function name (i) {
|
|
234
|
+
return `shard-${i}.test`
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const ORDINALS = ['first', 'second']
|
|
238
|
+
|
|
239
|
+
/** Loopback addresses, which every one of 127.0.0.0/8 is. */
|
|
240
|
+
const ADDRESSES = ['127.0.0.2', '127.0.0.3']
|
|
241
|
+
|
|
242
|
+
const PORT = 56790
|
|
243
|
+
|
|
244
|
+
/** Sooner than the names settle. */
|
|
245
|
+
const FLIP_MS = 100
|
|
@@ -25,24 +25,42 @@ class Network {
|
|
|
25
25
|
|
|
26
26
|
#refusing = false
|
|
27
27
|
|
|
28
|
+
/** @type {string} */
|
|
29
|
+
#listenHost
|
|
30
|
+
|
|
31
|
+
/** @type {number} */
|
|
32
|
+
#listenPort
|
|
33
|
+
|
|
28
34
|
/**
|
|
29
35
|
* @param {number} [n] broker index
|
|
36
|
+
* @param {string} [host] the address the broker is found at through this network
|
|
37
|
+
* @param {number} [port] any that is free unless given
|
|
30
38
|
*/
|
|
31
|
-
constructor (n = 0) {
|
|
32
|
-
const [
|
|
39
|
+
constructor (n = 0, host = '127.0.0.1', port = 0) {
|
|
40
|
+
const [brokerHost, brokerPort] = getAddress(n).split(':')
|
|
33
41
|
|
|
34
|
-
this.#host =
|
|
35
|
-
this.#port = Number(
|
|
42
|
+
this.#host = brokerHost
|
|
43
|
+
this.#port = Number(brokerPort)
|
|
44
|
+
this.#listenHost = host
|
|
45
|
+
this.#listenPort = port
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
get address () {
|
|
39
49
|
return 'localhost:' + this.#server.address().port
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
/** How many connections are open through this network. */
|
|
53
|
+
get connections () {
|
|
54
|
+
return this.#tunnels.size
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
async open () {
|
|
43
58
|
this.#server = net.createServer(this.#tunnel)
|
|
44
59
|
|
|
45
|
-
await new Promise((resolve) =>
|
|
60
|
+
await new Promise((resolve, reject) => {
|
|
61
|
+
this.#server.once('error', reject)
|
|
62
|
+
this.#server.listen(this.#listenPort, this.#listenHost, resolve)
|
|
63
|
+
})
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
/**
|
package/features/steps/rpc.js
CHANGED
|
@@ -144,6 +144,18 @@ When('the consumer sends the following request to the {token} queue:',
|
|
|
144
144
|
await send.call(this, queue, payload)
|
|
145
145
|
})
|
|
146
146
|
|
|
147
|
+
When('the consumer sends the following request to the {token} queue with properties:',
|
|
148
|
+
/**
|
|
149
|
+
* @param {string} queue
|
|
150
|
+
* @param {string} yaml
|
|
151
|
+
* @this {comq.features.Context}
|
|
152
|
+
*/
|
|
153
|
+
async function (queue, yaml) {
|
|
154
|
+
const { payload, properties } = parse(yaml)
|
|
155
|
+
|
|
156
|
+
await send.call(this, queue, payload, properties)
|
|
157
|
+
})
|
|
158
|
+
|
|
147
159
|
When('the consumer sends a request to the {token} queue',
|
|
148
160
|
/**
|
|
149
161
|
* @param {string} queue
|
|
@@ -567,10 +579,10 @@ function sendMany (amount, queue) {
|
|
|
567
579
|
}
|
|
568
580
|
}
|
|
569
581
|
|
|
570
|
-
async function send (queue, payload) {
|
|
582
|
+
async function send (queue, payload, properties) {
|
|
571
583
|
if (this.expected) await this.expected
|
|
572
584
|
|
|
573
|
-
this.reply = this.io.request(queue, payload)
|
|
585
|
+
this.reply = this.io.request(queue, payload, properties)
|
|
574
586
|
}
|
|
575
587
|
|
|
576
588
|
/**
|