dht-rpc 6.3.2 → 6.4.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/index.js CHANGED
@@ -8,6 +8,7 @@ const NatSampler = require('nat-sampler')
8
8
  const b4a = require('b4a')
9
9
  const IO = require('./lib/io')
10
10
  const Query = require('./lib/query')
11
+ const Session = require('./lib/session')
11
12
  const peer = require('./lib/peer')
12
13
  const { UNKNOWN_COMMAND, INVALID_TOKEN } = require('./lib/errors')
13
14
  const { PING, PING_NAT, FIND_NODE, DOWN_HINT } = require('./lib/commands')
@@ -150,15 +151,19 @@ class DHT extends EventEmitter {
150
151
 
151
152
  if (opts && opts.size && opts.size > 0) value = b4a.alloc(opts.size)
152
153
 
153
- const req = this.io.createRequest({ id: null, host, port }, null, true, PING, null, value)
154
+ const req = this.io.createRequest({ id: null, host, port }, null, true, PING, null, value, (opts && opts.session) || null)
154
155
  return this._requestToPromise(req, opts)
155
156
  }
156
157
 
157
158
  request ({ token = null, command, target = null, value = null }, { host, port }, opts) {
158
- const req = this.io.createRequest({ id: null, host, port }, token, false, command, target, value)
159
+ const req = this.io.createRequest({ id: null, host, port }, token, false, command, target, value, (opts && opts.session) || null)
159
160
  return this._requestToPromise(req, opts)
160
161
  }
161
162
 
163
+ session () {
164
+ return new Session(this)
165
+ }
166
+
162
167
  _requestToPromise (req, opts) {
163
168
  if (req === null) return Promise.reject(new Error('Node destroyed'))
164
169
 
@@ -212,7 +217,7 @@ class DHT extends EventEmitter {
212
217
  const value = b4a.allocUnsafe(2)
213
218
  c.uint16.encode({ start: 0, end: 2, buffer: value }, self.io.serverSocket.address().port)
214
219
 
215
- self._request(data.from, true, PING_NAT, null, value, () => { testNat = true }, noop)
220
+ self._request(data.from, true, PING_NAT, null, value, null, () => { testNat = true }, noop)
216
221
  }
217
222
  }
218
223
 
@@ -227,8 +232,8 @@ class DHT extends EventEmitter {
227
232
  return this.io.destroy()
228
233
  }
229
234
 
230
- _request (to, internal, command, target, value, onresponse, onerror) {
231
- const req = this.io.createRequest(to, null, internal, command, target, value)
235
+ _request (to, internal, command, target, value, session, onresponse, onerror) {
236
+ const req = this.io.createRequest(to, null, internal, command, target, value, session)
232
237
  if (req === null) return null
233
238
 
234
239
  req.onresponse = onresponse
@@ -358,7 +363,7 @@ class DHT extends EventEmitter {
358
363
  oldNode.pinged = this._tick
359
364
 
360
365
  this._repinging++
361
- this._request({ id: null, host: oldNode.host, port: oldNode.port }, true, PING, null, null, onsuccess, onswap)
366
+ this._request({ id: null, host: oldNode.host, port: oldNode.port }, true, PING, null, null, null, onsuccess, onswap)
362
367
 
363
368
  function onsuccess (m) {
364
369
  if (oldNode.seen <= lastSeen) return onswap()
@@ -475,7 +480,7 @@ class DHT extends EventEmitter {
475
480
  }
476
481
 
477
482
  this._checks++
478
- this._request({ id: null, host: node.host, port: node.port }, true, PING, null, null, onresponse, onerror)
483
+ this._request({ id: null, host: node.host, port: node.port }, true, PING, null, null, null, onresponse, onerror)
479
484
  }
480
485
 
481
486
  _ontick () {
@@ -699,7 +704,7 @@ function requestAll (dht, internal, command, value, nodes) {
699
704
 
700
705
  return new Promise((resolve) => {
701
706
  for (const node of nodes) {
702
- const req = dht._request(node, internal, command, null, value, onsuccess, onerror)
707
+ const req = dht._request(node, internal, command, null, value, null, onsuccess, onerror)
703
708
  if (!req) return resolve(replies)
704
709
  }
705
710
 
package/lib/io.js CHANGED
@@ -69,6 +69,8 @@ module.exports = class IO {
69
69
  if (i === this.inflight.length - 1) this.inflight.pop()
70
70
  else this.inflight[i] = this.inflight.pop()
71
71
 
72
+ if (req.session) req.session._detach(req)
73
+
72
74
  // TODO: Auto retry here if errors.INVALID_TOKEN is returned?
73
75
 
74
76
  if (req._timeout) {
@@ -118,6 +120,8 @@ module.exports = class IO {
118
120
  req._timeout = null
119
121
  req.destroyed = true
120
122
 
123
+ if (req.session) req.session._detach(req)
124
+
121
125
  req.onerror(errors.createDestroyedError(), req)
122
126
  }
123
127
 
@@ -199,7 +203,7 @@ module.exports = class IO {
199
203
  }
200
204
  }
201
205
 
202
- createRequest (to, token, internal, command, target, value) {
206
+ createRequest (to, token, internal, command, target, value, session) {
203
207
  if (this._destroying !== null) return null
204
208
 
205
209
  if (this._tid === 65536) this._tid = 0
@@ -207,14 +211,15 @@ module.exports = class IO {
207
211
  const tid = this._tid++
208
212
  const socket = this.firewalled ? this.clientSocket : this.serverSocket
209
213
 
210
- const req = new Request(this, socket, tid, null, to, token, internal, command, target, value)
214
+ const req = new Request(this, socket, tid, null, to, token, internal, command, target, value, session)
211
215
  this.inflight.push(req)
216
+ if (session) session._attach(req)
212
217
  return req
213
218
  }
214
219
  }
215
220
 
216
221
  class Request {
217
- constructor (io, socket, tid, from, to, token, internal, command, target, value) {
222
+ constructor (io, socket, tid, from, to, token, internal, command, target, value, session) {
218
223
  this.socket = socket
219
224
  this.tid = tid
220
225
  this.from = from
@@ -224,6 +229,8 @@ class Request {
224
229
  this.target = target
225
230
  this.value = value
226
231
  this.internal = internal
232
+ this.session = session
233
+ this.index = -1
227
234
  this.sent = 0
228
235
  this.retries = 3
229
236
  this.destroyed = false
@@ -251,7 +258,7 @@ class Request {
251
258
 
252
259
  if (id !== null) from.id = validateId(id, from)
253
260
 
254
- return new Request(io, socket, tid, from, to, token, internal, command, target, value)
261
+ return new Request(io, socket, tid, from, to, token, internal, command, target, value, null)
255
262
  } catch {
256
263
  return null
257
264
  }
@@ -317,6 +324,8 @@ class Request {
317
324
  if (i === this._io.inflight.length - 1) this._io.inflight.pop()
318
325
  else this._io.inflight[i] = this._io.inflight.pop()
319
326
 
327
+ if (this.session) this.session._detach(this)
328
+
320
329
  this.onerror(err || errors.createDestroyedError(), this)
321
330
  }
322
331
 
package/lib/query.js CHANGED
@@ -31,6 +31,8 @@ module.exports = class Query extends Readable {
31
31
  this._fromTable = false
32
32
  this._commit = opts.commit === true ? autoCommit : (opts.commit || null)
33
33
  this._commiting = false
34
+ this._session = opts.session || dht.session()
35
+ this._autoDestroySession = !opts.session
34
36
 
35
37
  this._onvisitbound = this._onvisit.bind(this)
36
38
  this._onerrorbound = this._onerror.bind(this)
@@ -284,7 +286,7 @@ module.exports = class Query extends Readable {
284
286
  _downHint (node, down) {
285
287
  const state = { start: 0, end: 6, buffer: b4a.allocUnsafe(6) }
286
288
  peer.ipv4.encode(state, down)
287
- this.dht._request(node, true, DOWN_HINT, null, state.buffer, noop, noop)
289
+ this.dht._request(node, true, DOWN_HINT, null, state.buffer, this._session, noop, noop)
288
290
  }
289
291
 
290
292
  _pushClosest (m) {
@@ -318,13 +320,18 @@ module.exports = class Query extends Readable {
318
320
  _visit (to) {
319
321
  this.inflight++
320
322
 
321
- const req = this.dht._request(to, this.internal, this.command, this.target, this.value, this._onvisitbound, this._onerrorbound)
323
+ const req = this.dht._request(to, this.internal, this.command, this.target, this.value, this._session, this._onvisitbound, this._onerrorbound)
322
324
  if (req === null) {
323
325
  this.destroy(new Error('Node was destroyed'))
324
326
  return
325
327
  }
326
328
  req.oncycle = this._oncyclebound
327
329
  }
330
+
331
+ _destroy (cb) {
332
+ if (this._autoDestroySession) this._session.destroy()
333
+ cb(null)
334
+ }
328
335
  }
329
336
 
330
337
  function autoCommit (reply, dht, query) {
package/lib/session.js ADDED
@@ -0,0 +1,41 @@
1
+ module.exports = class Session {
2
+ constructor (dht) {
3
+ this.dht = dht
4
+ this.inflight = []
5
+ }
6
+
7
+ _attach (req) {
8
+ req.index = this.inflight.push(req) - 1
9
+ }
10
+
11
+ _detach (req) {
12
+ const i = req.index
13
+ if (i === -1) return
14
+ req.index = -1
15
+
16
+ if (i === this.inflight.length - 1) this.inflight.pop()
17
+ else {
18
+ const req = this.inflight[i] = this.inflight.pop()
19
+ req.index = i
20
+ }
21
+ }
22
+
23
+ query ({ target, command, value }, opts = {}) {
24
+ return this.dht.query({ target, command, value }, { ...opts, session: this })
25
+ }
26
+
27
+ request ({ token, command, target, value }, { host, port }, opts = {}) {
28
+ return this.dht.request({ token, command, target, value }, { host, port }, { ...opts, session: this })
29
+ }
30
+
31
+ ping ({ host, port }, opts = {}) {
32
+ return this.dht.ping({ host, port }, { ...opts, session: this })
33
+ }
34
+
35
+ destroy (err) {
36
+ while (this.inflight.length) {
37
+ const req = this.inflight[0]
38
+ req.destroy(err)
39
+ }
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dht-rpc",
3
- "version": "6.3.2",
3
+ "version": "6.4.0",
4
4
  "description": "Make RPC calls over a Kademlia based DHT",
5
5
  "main": "index.js",
6
6
  "dependencies": {
package/test.js CHANGED
@@ -396,6 +396,27 @@ test('filter nodes from routing table', async function (t) {
396
396
  t.absent(node.table.has(b.id), 'should not have b')
397
397
  })
398
398
 
399
+ test('request session, destroy all', async function (t) {
400
+ const [, a, b] = await makeSwarm(3, t)
401
+
402
+ a.on('request', () => t.fail())
403
+
404
+ const s = b.session()
405
+ const p = [
406
+ s.request({ command: 42 }, a),
407
+ s.request({ command: 42 }, a)
408
+ ]
409
+
410
+ const err = new Error('destroyed')
411
+
412
+ s.destroy(err)
413
+
414
+ for (const { status, reason } of await Promise.allSettled(p)) {
415
+ t.is(status, 'rejected')
416
+ t.is(reason, err)
417
+ }
418
+ })
419
+
399
420
  async function freePort () {
400
421
  const udx = new UDX()
401
422
  const sock = udx.createSocket()