hypercore 10.35.1 → 10.35.3
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 +1 -2
- package/lib/bitfield.js +25 -1
- package/lib/core.js +29 -10
- package/lib/remote-bitfield.js +2 -2
- package/lib/replicator.js +70 -22
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -60,7 +60,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
60
60
|
this.crypto = opts.crypto || hypercoreCrypto
|
|
61
61
|
this.core = null
|
|
62
62
|
this.replicator = null
|
|
63
|
-
this.inflightRange = opts.inflightRange || null
|
|
64
63
|
this.encryption = null
|
|
65
64
|
this.extensions = new Map()
|
|
66
65
|
this.cache = createCache(opts.cache)
|
|
@@ -405,7 +404,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
405
404
|
eagerUpgrade: true,
|
|
406
405
|
notDownloadingLinger: opts.notDownloadingLinger,
|
|
407
406
|
allowFork: opts.allowFork !== false,
|
|
408
|
-
inflightRange:
|
|
407
|
+
inflightRange: opts.inflightRange,
|
|
409
408
|
onpeerupdate: this._onpeerupdate.bind(this),
|
|
410
409
|
onupload: this._onupload.bind(this),
|
|
411
410
|
oninvalid: this._oninvalid.bind(this)
|
package/lib/bitfield.js
CHANGED
|
@@ -213,6 +213,30 @@ module.exports = class Bitfield {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
toBuffer (length) {
|
|
217
|
+
const pages = Math.ceil(length / BITS_PER_PAGE)
|
|
218
|
+
const buffer = b4a.allocUnsafe(pages * BYTES_PER_PAGE)
|
|
219
|
+
|
|
220
|
+
for (let i = 0; i < pages; i++) {
|
|
221
|
+
const page = this._pages.get(i)
|
|
222
|
+
const offset = i * BYTES_PER_PAGE
|
|
223
|
+
|
|
224
|
+
if (page) {
|
|
225
|
+
const buf = b4a.from(
|
|
226
|
+
page.bitfield.buffer,
|
|
227
|
+
page.bitfield.byteOffset,
|
|
228
|
+
page.bitfield.byteLength
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
buffer.set(buf, offset)
|
|
232
|
+
} else {
|
|
233
|
+
buffer.fill(0, offset, offset + BYTES_PER_PAGE)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return buffer
|
|
238
|
+
}
|
|
239
|
+
|
|
216
240
|
getBitfield (index, length) {
|
|
217
241
|
const j = index & (BITS_PER_PAGE - 1)
|
|
218
242
|
const i = (index - j) / BITS_PER_PAGE
|
|
@@ -303,7 +327,7 @@ module.exports = class Bitfield {
|
|
|
303
327
|
i++
|
|
304
328
|
}
|
|
305
329
|
|
|
306
|
-
return val ? -1 :
|
|
330
|
+
return val ? -1 : this._segments.maxLength * BITS_PER_SEGMENT
|
|
307
331
|
}
|
|
308
332
|
|
|
309
333
|
firstSet (position) {
|
package/lib/core.js
CHANGED
|
@@ -6,6 +6,7 @@ const Mutex = require('./mutex')
|
|
|
6
6
|
const MerkleTree = require('./merkle-tree')
|
|
7
7
|
const BlockStore = require('./block-store')
|
|
8
8
|
const Bitfield = require('./bitfield')
|
|
9
|
+
const RemoteBitfield = require('./remote-bitfield')
|
|
9
10
|
const Info = require('./info')
|
|
10
11
|
const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors')
|
|
11
12
|
const m = require('./messages')
|
|
@@ -30,6 +31,7 @@ module.exports = class Core {
|
|
|
30
31
|
this.truncating = 0
|
|
31
32
|
this.updating = false
|
|
32
33
|
this.closed = false
|
|
34
|
+
this.skipBitfield = null
|
|
33
35
|
|
|
34
36
|
this._manifestFlushed = !!header.manifest
|
|
35
37
|
this._maxOplogSize = 65536
|
|
@@ -142,7 +144,7 @@ module.exports = class Core {
|
|
|
142
144
|
const prologue = header.manifest ? header.manifest.prologue : null
|
|
143
145
|
|
|
144
146
|
const tree = await MerkleTree.open(treeFile, { crypto, prologue, ...header.tree })
|
|
145
|
-
const bitfield = await Bitfield.open(bitfieldFile
|
|
147
|
+
const bitfield = await Bitfield.open(bitfieldFile)
|
|
146
148
|
const blocks = new BlockStore(dataFile, tree)
|
|
147
149
|
|
|
148
150
|
if (overwrite) {
|
|
@@ -258,7 +260,7 @@ module.exports = class Core {
|
|
|
258
260
|
const [offset] = await src.tree.byteRange(2 * segmentStart)
|
|
259
261
|
await this.blocks.putBatch(segmentStart, segment, offset)
|
|
260
262
|
|
|
261
|
-
this.
|
|
263
|
+
this._setBitfieldRange(segmentStart, segmentEnd - segmentStart, true)
|
|
262
264
|
|
|
263
265
|
pos = segmentEnd + 1
|
|
264
266
|
|
|
@@ -300,7 +302,7 @@ module.exports = class Core {
|
|
|
300
302
|
const blocks = additional.length === missing ? additional : additional.slice(offset, offset + missing)
|
|
301
303
|
|
|
302
304
|
await this.blocks.putBatch(source.length, blocks, source.byteLength)
|
|
303
|
-
this.
|
|
305
|
+
this._setBitfieldRange(source.length, missing, true)
|
|
304
306
|
|
|
305
307
|
updates.push({
|
|
306
308
|
drop: false,
|
|
@@ -464,7 +466,7 @@ module.exports = class Core {
|
|
|
464
466
|
|
|
465
467
|
await this.oplog.append([entry], false)
|
|
466
468
|
|
|
467
|
-
this.
|
|
469
|
+
this._setBitfieldRange(batch.ancestors, len - batch.ancestors, false)
|
|
468
470
|
batch.commit()
|
|
469
471
|
|
|
470
472
|
// TODO: (see below todo)
|
|
@@ -491,7 +493,7 @@ module.exports = class Core {
|
|
|
491
493
|
|
|
492
494
|
await this.oplog.append([entry], false)
|
|
493
495
|
|
|
494
|
-
this.
|
|
496
|
+
this._setBitfieldRange(start, end - start, false)
|
|
495
497
|
|
|
496
498
|
if (start < this.header.hints.contiguousLength) {
|
|
497
499
|
this.header.hints.contiguousLength = start
|
|
@@ -588,7 +590,7 @@ module.exports = class Core {
|
|
|
588
590
|
await this.blocks.putBatch(treeLength, adding > values.length ? values.slice(0, adding) : values, byteOffset)
|
|
589
591
|
await this.oplog.append([entry], false)
|
|
590
592
|
|
|
591
|
-
this.
|
|
593
|
+
this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true)
|
|
592
594
|
batch.commit()
|
|
593
595
|
|
|
594
596
|
if (batch.upgraded) {
|
|
@@ -654,7 +656,7 @@ module.exports = class Core {
|
|
|
654
656
|
|
|
655
657
|
await this.oplog.append([entry], false)
|
|
656
658
|
|
|
657
|
-
this.
|
|
659
|
+
this._setBitfieldRange(batch.ancestors, batch.length - batch.ancestors, true)
|
|
658
660
|
batch.commit()
|
|
659
661
|
|
|
660
662
|
this.header.tree.length = batch.length
|
|
@@ -719,7 +721,7 @@ module.exports = class Core {
|
|
|
719
721
|
let status = 0b0001
|
|
720
722
|
|
|
721
723
|
if (bitfield) {
|
|
722
|
-
this.
|
|
724
|
+
this._setBitfield(bitfield.start, true)
|
|
723
725
|
status |= updateContig(this.header, bitfield, this.bitfield)
|
|
724
726
|
}
|
|
725
727
|
|
|
@@ -781,7 +783,7 @@ module.exports = class Core {
|
|
|
781
783
|
let status = 0
|
|
782
784
|
|
|
783
785
|
if (bitfield) {
|
|
784
|
-
this.
|
|
786
|
+
this._setBitfield(bitfield.start, true)
|
|
785
787
|
status = updateContig(this.header, bitfield, this.bitfield)
|
|
786
788
|
}
|
|
787
789
|
|
|
@@ -898,7 +900,7 @@ module.exports = class Core {
|
|
|
898
900
|
|
|
899
901
|
await this.oplog.append([entry], false)
|
|
900
902
|
|
|
901
|
-
this.
|
|
903
|
+
this._setBitfieldRange(batch.ancestors, this.tree.length - batch.ancestors, false)
|
|
902
904
|
addReorgHint(this.header.hints.reorgs, this.tree, batch)
|
|
903
905
|
batch.commit()
|
|
904
906
|
|
|
@@ -920,6 +922,23 @@ module.exports = class Core {
|
|
|
920
922
|
await this._flushOplog()
|
|
921
923
|
}
|
|
922
924
|
|
|
925
|
+
openSkipBitfield () {
|
|
926
|
+
if (this.skipBitfield !== null) return this.skipBitfield
|
|
927
|
+
this.skipBitfield = new RemoteBitfield()
|
|
928
|
+
this.skipBitfield.insert(0, this.bitfield.toBuffer(this.tree.length))
|
|
929
|
+
return this.skipBitfield
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
_setBitfield (index, value) {
|
|
933
|
+
this.bitfield.set(index, value)
|
|
934
|
+
if (this.skipBitfield !== null) this.skipBitfield.set(index, value)
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
_setBitfieldRange (start, length, value) {
|
|
938
|
+
this.bitfield.setRange(start, length, value)
|
|
939
|
+
if (this.skipBitfield !== null) this.skipBitfield.setRange(start, length, value)
|
|
940
|
+
}
|
|
941
|
+
|
|
923
942
|
async close () {
|
|
924
943
|
this.closed = true
|
|
925
944
|
await this._mutex.destroy()
|
package/lib/remote-bitfield.js
CHANGED
|
@@ -116,7 +116,7 @@ class RemoteBitfieldSegment {
|
|
|
116
116
|
i++
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
return -1
|
|
119
|
+
return (val || this.pagesLength === PAGES_PER_SEGMENT) ? -1 : this.pagesLength * BITS_PER_PAGE
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
findLast (val, position) {
|
|
@@ -230,7 +230,7 @@ module.exports = class RemoteBitfield {
|
|
|
230
230
|
i++
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
return val ? -1 :
|
|
233
|
+
return val ? -1 : this._maxSegments * BITS_PER_SEGMENT
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
firstSet (position) {
|
package/lib/replicator.js
CHANGED
|
@@ -33,7 +33,7 @@ const m = require('./messages')
|
|
|
33
33
|
const caps = require('./caps')
|
|
34
34
|
const { createTracer } = require('hypertrace')
|
|
35
35
|
|
|
36
|
-
const DEFAULT_MAX_INFLIGHT = [
|
|
36
|
+
const DEFAULT_MAX_INFLIGHT = [16, 512]
|
|
37
37
|
const SCALE_LATENCY = 50
|
|
38
38
|
const DEFAULT_SEGMENT_SIZE = 256 * 1024 * 8 // 256 KiB in bits
|
|
39
39
|
const NOT_DOWNLOADING_SLACK = 20000 + (Math.random() * 20000) | 0
|
|
@@ -369,8 +369,8 @@ class Peer {
|
|
|
369
369
|
const stream = this.stream.rawStream
|
|
370
370
|
if (!stream.udx) return Math.min(this.inflightRange[1], this.inflightRange[0] * 3)
|
|
371
371
|
|
|
372
|
-
const scale = stream.rtt <= SCALE_LATENCY ? 1 : stream.rtt / SCALE_LATENCY
|
|
373
|
-
return Math.round(Math.min(this.inflightRange[1], this.inflightRange[0] * scale))
|
|
372
|
+
const scale = stream.rtt <= SCALE_LATENCY ? 1 : stream.rtt / SCALE_LATENCY * Math.min(1, 2 / this.replicator.peers.length)
|
|
373
|
+
return Math.max(this.inflightRange[0], Math.round(Math.min(this.inflightRange[1], this.inflightRange[0] * scale)))
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
signalUpgrade () {
|
|
@@ -378,6 +378,10 @@ class Peer {
|
|
|
378
378
|
else this.sendSync()
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
_markInflight (index) {
|
|
382
|
+
this.missingBlocks.set(index, false)
|
|
383
|
+
}
|
|
384
|
+
|
|
381
385
|
broadcastRange (start, length, drop) {
|
|
382
386
|
if (drop) this._unclearLocalRange(start, length)
|
|
383
387
|
else this._clearLocalRange(start, length)
|
|
@@ -662,11 +666,12 @@ class Peer {
|
|
|
662
666
|
}
|
|
663
667
|
|
|
664
668
|
_cancelRequest (id) {
|
|
665
|
-
const
|
|
666
|
-
if (!
|
|
669
|
+
const req = this.replicator._inflight.get(id)
|
|
670
|
+
if (!req) return
|
|
667
671
|
|
|
668
672
|
this.inflight--
|
|
669
673
|
this.replicator._removeInflight(id)
|
|
674
|
+
if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)
|
|
670
675
|
|
|
671
676
|
this.wireCancel.send({ request: id })
|
|
672
677
|
}
|
|
@@ -716,6 +721,8 @@ class Peer {
|
|
|
716
721
|
if (reorg === true) return await this.replicator._onreorgdata(this, req, data)
|
|
717
722
|
} catch (err) {
|
|
718
723
|
safetyCatch(err)
|
|
724
|
+
if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)
|
|
725
|
+
|
|
719
726
|
this.paused = true
|
|
720
727
|
this.replicator.oninvalid(err, req, data, this)
|
|
721
728
|
return
|
|
@@ -730,6 +737,7 @@ class Peer {
|
|
|
730
737
|
}
|
|
731
738
|
} catch (err) {
|
|
732
739
|
safetyCatch(err)
|
|
740
|
+
if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)
|
|
733
741
|
|
|
734
742
|
if (err.code === 'WRITE_FAILED') {
|
|
735
743
|
// For example, we don't want to keep pulling data when storage is full
|
|
@@ -788,8 +796,10 @@ class Peer {
|
|
|
788
796
|
}
|
|
789
797
|
|
|
790
798
|
_clearLocalRange (start, length) {
|
|
799
|
+
const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield
|
|
800
|
+
|
|
791
801
|
if (length === 1) {
|
|
792
|
-
this.missingBlocks.set(start, this._remoteHasBlock(start) && !
|
|
802
|
+
this.missingBlocks.set(start, this._remoteHasBlock(start) && !bitfield.get(start))
|
|
793
803
|
return
|
|
794
804
|
}
|
|
795
805
|
|
|
@@ -809,7 +819,7 @@ class Peer {
|
|
|
809
819
|
|
|
810
820
|
const end = start + Math.min(length, this.core.tree.length)
|
|
811
821
|
while (start < end) {
|
|
812
|
-
const local =
|
|
822
|
+
const local = bitfield.getBitfield(start)
|
|
813
823
|
|
|
814
824
|
if (local && local.bitfield) {
|
|
815
825
|
this.missingBlocks.clear(start, local.bitfield)
|
|
@@ -819,9 +829,14 @@ class Peer {
|
|
|
819
829
|
}
|
|
820
830
|
}
|
|
821
831
|
|
|
832
|
+
_resetMissingBlock (index) {
|
|
833
|
+
const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield
|
|
834
|
+
this.missingBlocks.set(index, this._remoteHasBlock(index) && !bitfield.get(index))
|
|
835
|
+
}
|
|
836
|
+
|
|
822
837
|
_unclearLocalRange (start, length) {
|
|
823
838
|
if (length === 1) {
|
|
824
|
-
this.
|
|
839
|
+
this._resetMissingBlock(start)
|
|
825
840
|
return
|
|
826
841
|
}
|
|
827
842
|
|
|
@@ -856,8 +871,9 @@ class Peer {
|
|
|
856
871
|
if (start === 0 && drop === false && length > this._remoteContiguousLength) {
|
|
857
872
|
this._remoteContiguousLength = length
|
|
858
873
|
} else if (length === 1) {
|
|
874
|
+
const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield
|
|
859
875
|
this.remoteBitfield.set(start, has)
|
|
860
|
-
this.missingBlocks.set(start, has && !
|
|
876
|
+
this.missingBlocks.set(start, has && !bitfield.get(start))
|
|
861
877
|
} else {
|
|
862
878
|
const rangeStart = this.remoteBitfield.findFirst(!has, start)
|
|
863
879
|
const rangeLength = length - (rangeStart - start)
|
|
@@ -1034,6 +1050,14 @@ class Peer {
|
|
|
1034
1050
|
return index < this._remoteContiguousLength || this.remoteBitfield.get(index) === true
|
|
1035
1051
|
}
|
|
1036
1052
|
|
|
1053
|
+
_sendBlockRequest (req, b) {
|
|
1054
|
+
req.block = { index: b.index, nodes: 0 }
|
|
1055
|
+
this.replicator._markInflight(b.index)
|
|
1056
|
+
|
|
1057
|
+
b.inflight.push(req)
|
|
1058
|
+
this._send(req)
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1037
1061
|
_requestBlock (b) {
|
|
1038
1062
|
const { length, fork } = this.core.tree
|
|
1039
1063
|
|
|
@@ -1049,10 +1073,7 @@ class Peer {
|
|
|
1049
1073
|
const req = this._makeRequest(b.index >= length, b.priority)
|
|
1050
1074
|
if (req === null) return false
|
|
1051
1075
|
|
|
1052
|
-
req
|
|
1053
|
-
|
|
1054
|
-
b.inflight.push(req)
|
|
1055
|
-
this._send(req)
|
|
1076
|
+
this._sendBlockRequest(req, b)
|
|
1056
1077
|
|
|
1057
1078
|
return true
|
|
1058
1079
|
}
|
|
@@ -1061,7 +1082,10 @@ class Peer {
|
|
|
1061
1082
|
if (this.core.bitfield.get(index) === true || !this._hasTreeParent(index)) return false
|
|
1062
1083
|
|
|
1063
1084
|
const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
|
|
1064
|
-
if (b.inflight.length > 0)
|
|
1085
|
+
if (b.inflight.length > 0) {
|
|
1086
|
+
this.missingBlocks.set(index, false) // in case we missed some states just set them ondemand, nbd
|
|
1087
|
+
return false
|
|
1088
|
+
}
|
|
1065
1089
|
|
|
1066
1090
|
const req = this._makeRequest(index >= length, b.priority)
|
|
1067
1091
|
|
|
@@ -1071,10 +1095,7 @@ class Peer {
|
|
|
1071
1095
|
return false
|
|
1072
1096
|
}
|
|
1073
1097
|
|
|
1074
|
-
req
|
|
1075
|
-
|
|
1076
|
-
b.inflight.push(req)
|
|
1077
|
-
this._send(req)
|
|
1098
|
+
this._sendBlockRequest(req, b)
|
|
1078
1099
|
|
|
1079
1100
|
// Don't think this will ever happen, as the pending queue is drained before the range queue
|
|
1080
1101
|
// but doesn't hurt to check this explicitly here also.
|
|
@@ -1084,7 +1105,8 @@ class Peer {
|
|
|
1084
1105
|
|
|
1085
1106
|
_findNext (i) {
|
|
1086
1107
|
if (i < this._remoteContiguousLength) {
|
|
1087
|
-
|
|
1108
|
+
if (this.core.skipBitfield === null) this.replicator._openSkipBitfield()
|
|
1109
|
+
i = this.core.skipBitfield.findFirst(false, i)
|
|
1088
1110
|
if (i < this._remoteContiguousLength && i > -1) return i
|
|
1089
1111
|
i = this._remoteContiguousLength
|
|
1090
1112
|
}
|
|
@@ -1583,9 +1605,8 @@ module.exports = class Replicator {
|
|
|
1583
1605
|
|
|
1584
1606
|
_removeInflight (id) {
|
|
1585
1607
|
this._inflight.remove(id)
|
|
1586
|
-
if (this.isDownloading() ===
|
|
1587
|
-
|
|
1588
|
-
}
|
|
1608
|
+
if (this.isDownloading() === true) return
|
|
1609
|
+
for (const peer of this.peers) peer.signalUpgrade()
|
|
1589
1610
|
}
|
|
1590
1611
|
|
|
1591
1612
|
_removePeer (peer) {
|
|
@@ -1788,6 +1809,7 @@ module.exports = class Replicator {
|
|
|
1788
1809
|
_clearRequest (peer, req) {
|
|
1789
1810
|
if (req.block !== null) {
|
|
1790
1811
|
this._clearInflightBlock(this._blocks, req)
|
|
1812
|
+
this._unmarkInflight(req.block.index)
|
|
1791
1813
|
}
|
|
1792
1814
|
|
|
1793
1815
|
if (req.hash !== null) {
|
|
@@ -1812,6 +1834,28 @@ module.exports = class Replicator {
|
|
|
1812
1834
|
this.updateAll()
|
|
1813
1835
|
}
|
|
1814
1836
|
|
|
1837
|
+
_openSkipBitfield () {
|
|
1838
|
+
// technically the skip bitfield gets bits cleared if .clear() is called
|
|
1839
|
+
// also which might be in inflight also, but that just results in that section being overcalled shortly
|
|
1840
|
+
// worst case, so ok for now
|
|
1841
|
+
|
|
1842
|
+
const bitfield = this.core.openSkipBitfield()
|
|
1843
|
+
|
|
1844
|
+
for (const req of this._inflight) {
|
|
1845
|
+
if (req.block) bitfield.set(req.block.index, true) // skip
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
_markInflight (index) {
|
|
1850
|
+
if (this.core.skipBitfield !== null) this.core.skipBitfield.set(index, true)
|
|
1851
|
+
for (const peer of this.peers) peer._markInflight(index)
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
_unmarkInflight (index) {
|
|
1855
|
+
if (this.core.skipBitfield !== null) this.core.skipBitfield.set(index, this.core.bitfield.get(index))
|
|
1856
|
+
for (const peer of this.peers) peer._resetMissingBlock(index)
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1815
1859
|
_ondata (peer, req, data) {
|
|
1816
1860
|
if (data.block !== null) {
|
|
1817
1861
|
this._resolveBlockRequest(this._blocks, data.block.index, data.block.value, req)
|
|
@@ -2247,3 +2291,7 @@ function onwireextension (m, c) {
|
|
|
2247
2291
|
function setDownloadingLater (repl, downloading, session) {
|
|
2248
2292
|
repl.setDownloadingNow(downloading, session)
|
|
2249
2293
|
}
|
|
2294
|
+
|
|
2295
|
+
function isBlockRequest (req) {
|
|
2296
|
+
return req !== null && req.block !== null
|
|
2297
|
+
}
|