-
-
Notifications
You must be signed in to change notification settings - Fork 206
Description
What version of this package are you using?
Git
What problem do you want to solve?
Well, indexing. This isn't the right library for that. Still, it can benefit from a bep 51 implementation.
What do you think is the correct solution to this problem?
Not this library.
Are you willing to submit a pull request to implement this change?
Not really. This isn't my line of expertise. I can help though.
I made a rough (ai assisted) BEP 51 implementation that does work!
Here's the diff against only the client.js file from git:
473a474,514
> // --- NEW: Public BEP 51 method ---
> bep51 (peer, cb) {
> if (!peer || (!peer.host && !peer.address) || !peer.port) {
> throw new Error('`peer` argument must have host/address and port');
> }
> if (!cb) cb = noop;
> const self = this;
>
> // The 'target' for sample_infohashes is typically the querying node's ID,
> // or a random ID if we want to get a diverse sample from different parts of the DHT.
> // For a direct query to a peer, using our own ID as target is reasonable.
> const target = this._rpc.id;
>
> const message = {
> q: 'sample_infohashes',
> a: {
> id: this._rpc.id,
> target: target
> }
> };
>
> self._debug('sending bep51 query to %s:%d', peer.host || peer.address, peer.port);
>
> this._rpc.query(peer, message, (err, response, fromNode) => {
> if (err) {
> self._debug('bep51 query to %s:%d failed: %s', peer.host || peer.address, peer.port, err.message);
> return cb(err);
> }
>
> // Process the response using the shared helper.
> // The helper will emit 'announce' events if samples are found.
> const processed = self._processSampleInfohashesResponse(response, fromNode, target);
> if (!processed) {
> self._debug('bep51 response from %s:%d did not contain samples', peer.host || peer.address, peer.port);
> }
>
> cb(null); // Indicate completion, regardless of whether samples were found.
> });
> }
> // --- END NEW ---
>
524a566,570
>
> // --- NEW: Handle sample_infohashes query ---
> case 'sample_infohashes':
> return this._onsampleinfohashes(query, peer)
> // --- END NEW ---
643a690,709
> // --- NEW: _onsampleinfohashes method for server-side BEP 51 responses ---
> _onsampleinfohashes (query, peer) {
> const target = query.a.target;
> if (!target) {
> return this._rpc.error(peer, query, [203, '`sample_infohashes` missing required `a.target` field']);
> }
>
> const nodes = this._rpc.nodes.closest(target);
>
> const response = {
> id: this._rpc.id,
> interval: 21600,
> num: 0,
> samples: null
> };
>
> this._rpc.response(peer, query, response, nodes);
> }
> // --- END NEW ---
>
664a731,755
> // --- NEW: Helper method to process BEP 51 sample_infohashes responses ---
> _processSampleInfohashesResponse (message, node, targetInfoHash = null) {
> const r = message.r;
> if (!r) return false;
>
> // A BEP 51 response is identified by the presence of 'samples', 'num', and 'interval' fields.
> if (r.samples && Buffer.isBuffer(r.samples) &&
> typeof r.num === 'number' && typeof r.interval === 'number') {
> this._debug('received BEP 51 samples from %s:%d (num: %d)', node.host || node.address, node.port, r.num);
>
> const samplesBuffer = r.samples;
> const extractedInfoHashes = [];
> for (let i = 0; i < samplesBuffer.length; i += 20) {
> extractedInfoHashes.push(samplesBuffer.slice(i, i + 20));
> }
>
> for (const infoHash of extractedInfoHashes) {
> this.emit('announce', { host: node.host || node.address, port: node.port }, infoHash, node);
> }
> return true; // Indicates BEP 51 response was processed
> }
> return false; // Not a BEP 51 response
> }
> // --- END NEW ---
>
685,686c776,783
< if (message.r.token && message.r.id && Buffer.isBuffer(message.r.id) && message.r.id.length === self._hashLength) {
< self._debug('found node %s (target: %s)', message.r.id, target)
---
> // --- UPDATED: Call the new helper method for BEP 51 responses ---
> self._processSampleInfohashesResponse(message, node, target);
> // --- END UPDATED ---
>
> // Original logic for adding nodes to the routing table:
> const r = message.r;
> if (r.token && r.id && Buffer.isBuffer(r.id) && r.id.length === self._hashLength) {
> self._debug('found node %s (target: %s)', r.id, target);
688c785
< id: message.r.id,
---
> id: r.id,
691c788
< token: message.r.token
---
> token: r.token
Now if you send a .bep51(peer)
to a given peer, and if that peer supports bep 51 and provides some sample infohashes then each of them will be made known to you using the normal announce emit. That works quite well!
My intent here is crawling/indexing where this library apparently just isn't suited (too slow). The implementation probably is a bit rough and not up to the standards of this library. So i'm just sharing it here for others to look at and potentially work on for inclusion in the library itself.
This change also implements the sample_infohashes
RPC (as in when you are asked to provide sample_infohashes
. It's a stub implementation. For this to work you'd need to have a list of infohashes to return which is quite outside the scope of this library. The library could opt to register a hook to fill that list somehow when sample_infohashes
is requested i guess. That would make it as generic as possible.
Enjoy :)