fetch-dht/routes/fetch.js
2020-04-12 16:33:32 -05:00

88 lines
2.3 KiB
JavaScript

const Router = require('express-promise-router');
const bencode = require('bencode');
const qs = require('querystring');
const peerListFormatters = require('../lib/peerListFormatters');
const bqs = require('../lib/binaryQuerystring');
const INFO_HASH_REGEX = new RegExp('^[0-9a-f]{40}$', 'i');
module.exports = ({ dht }) => {
const app = Router();
app.get(
'/',
async (
{
log,
originalUrl,
query: { info_hash: infoHash, compact, json, event = '' },
},
res,
) => {
// worst case if every character is percent encoded, or best case if every _is_ decoded
if (!infoHash || infoHash.length > 60 || infoHash < 20) {
res
.status(400)
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
return;
}
if (infoHash.length !== 20) {
infoHash = bqs.fromUrl(originalUrl).info_hash;
if (!infoHash || infoHash.length !== 20) {
res
.status(400)
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
log({ status: 'refused', type: 'dhtGateway' });
return;
}
}
infoHash = Buffer.from(infoHash, 'latin1').toString('hex');
if (!infoHash.match(INFO_HASH_REGEX)) {
res
.status(400)
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
log({ status: 'refused', type: 'dhtGateway' });
return;
}
let format = 'bencoded';
if (compact === '1') {
format = 'compact';
} else if (json === '1') {
format = 'json';
}
try {
let peers = [];
// a completed or stopped torrent does not need peers
if (event !== 'completed' && event !== 'stopped') {
peers = await dht.lookup(infoHash);
}
// this sends the response as well as formatting it
peerListFormatters(peers, res, format);
log({
status: 'served',
type: 'dhtGateway',
format,
count: peers.length,
});
} catch (e) {
res
.status(500)
.end(bencode.encode({ 'failure reason': 'internal error' }));
log({
status: 'failed',
type: 'dhtGateway',
format,
error: e.message,
stack: e.stack,
});
}
},
);
return app;
};