From 89b3fb3086d401bb15b283888ec9d69b72c1e379 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Wed, 8 Jul 2015 10:13:52 -0700 Subject: [PATCH] support custom filter error messages In addition to returning a boolean (`true` for allowed, `false` for disallowed), you can return an `Error` object to disallow and provide a custom reason. Fixes #85 --- README.md | 18 ++++---- server.js | 10 ++-- test/filter.js | 123 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index aae8665..c3eb095 100644 --- a/README.md +++ b/README.md @@ -113,16 +113,18 @@ var server = new Server({ http: true, // enable http server? [default=true] ws: true, // enable websocket server? [default=false] filter: function (infoHash, params, cb) { - // black/whitelist for disallowing/allowing torrents [default=allow all] - // this example only allows this one torrent - cb(infoHash === 'aaa67059ed6bd08362da625b3ae77f6f4a075aaa') + // Blacklist/whitelist function for allowing/disallowing torrents. If this option is + // omitted, all torrents are allowed. It is possible to interface with a database or + // external system before deciding to allow/deny, because this function is async. - // it's possible to interface with a database or external system before - // deciding, because this function is async + // It is possible to block by peer id (whitelisting torrent clients) or by secret + // key (private trackers). Full access to the original HTTP/UDP request parameters + // are available n `params`. - // it's possible to block by peer id (whitelisting torrent clients) or - // by secret key, as you get full access to the original http/udp - // request parameters in `params` + // This example only allows one torrent. + + var allowed = (infoHash === 'aaa67059ed6bd08362da625b3ae77f6f4a075aaa') + cb(allowed) }) }) diff --git a/server.js b/server.js index 87f09d0..c3c485e 100644 --- a/server.js +++ b/server.js @@ -385,11 +385,13 @@ Server.prototype._onAnnounce = function (params, cb) { function createSwarm () { if (self._filter) { self._filter(params.info_hash, params, function (allowed) { - if (allowed) { + if (allowed instanceof Error) { + cb(allowed) + } else if (!allowed) { + cb(new Error('disallowed info_hash')) + } else { swarm = self.createSwarm(params.info_hash) announce() - } else { - cb(new Error('disallowed info_hash')) } }) } else { @@ -432,7 +434,7 @@ Server.prototype._onAnnounce = function (params, cb) { }) } // else, return full peer objects (used for websocket responses) - cb(err, response) + cb(null, response) }) } diff --git a/test/filter.js b/test/filter.js index 04aedd4..7be9bb2 100644 --- a/test/filter.js +++ b/test/filter.js @@ -13,7 +13,7 @@ var parsedLeaves = parseTorrent(leaves) var peerId = new Buffer('01234567890123456789') function testFilterOption (t, serverType) { - t.plan(6) + t.plan(8) var opts = serverType === 'http' ? { udp: false } : { http: false } opts.filter = function (infoHash, params, cb) { process.nextTick(function () { @@ -44,29 +44,33 @@ function testFilterOption (t, serverType) { client.once('warning', function (err) { t.ok(/disallowed info_hash/.test(err.message), 'got client warning') - client.destroy() - client = new Client(peerId, port, parsedLeaves) + client.destroy(function () { + t.pass('client destroyed') + client = new Client(peerId, port, parsedLeaves) - client.on('error', function (err) { - t.error(err) - }) - client.on('warning', function (err) { - t.error(err) - }) - - client.on('update', function () { - t.pass('got announce') - client.destroy() - server.close(function () { - t.pass('server closed') + client.on('error', function (err) { + t.error(err) + }) + client.on('warning', function (err) { + t.error(err) }) - }) - server.on('start', function () { - t.equal(Object.keys(server.torrents).length, 1) - }) + client.on('update', function () { + t.pass('got announce') + client.destroy(function () { + t.pass('client destroyed') + }) + server.close(function () { + t.pass('server closed') + }) + }) - client.start() + server.on('start', function () { + t.equal(Object.keys(server.torrents).length, 1) + }) + + client.start() + }) }) server.once('warning', function (err) { @@ -85,3 +89,82 @@ test('http: filter option blocks tracker from tracking torrent', function (t) { test('udp: filter option blocks tracker from tracking torrent', function (t) { testFilterOption(t, 'udp') }) + +function testFilterCustomError (t, serverType) { + t.plan(8) + var opts = serverType === 'http' ? { udp: false } : { http: false } + opts.filter = function (infoHash, params, cb) { + process.nextTick(function () { + if (infoHash === parsedBitlove.infoHash) cb(new Error('bitlove blocked')) + else cb(true) + }) + } + var server = new Server(opts) + + server.on('error', function (err) { + t.error(err) + }) + + server.listen(0, function () { + var port = server[serverType].address().port + var announceUrl = serverType === 'http' + ? 'http://127.0.0.1:' + port + '/announce' + : 'udp://127.0.0.1:' + port + + parsedBitlove.announce = [ announceUrl ] + parsedLeaves.announce = [ announceUrl ] + + var client = new Client(peerId, port, parsedBitlove) + + client.on('error', function (err) { + t.error(err) + }) + + client.once('warning', function (err) { + t.ok(/bitlove blocked/.test(err.message), 'got client warning') + + client.destroy(function () { + t.pass('client destroyed') + client = new Client(peerId, port, parsedLeaves) + + client.on('error', function (err) { + t.error(err) + }) + client.on('warning', function (err) { + t.error(err) + }) + + client.on('update', function () { + t.pass('got announce') + client.destroy(function () { + t.pass('client destroyed') + }) + server.close(function () { + t.pass('server closed') + }) + }) + + server.on('start', function () { + t.equal(Object.keys(server.torrents).length, 1) + }) + + client.start() + }) + }) + + server.once('warning', function (err) { + t.ok(/bitlove blocked/.test(err.message), 'got server warning') + t.equal(Object.keys(server.torrents).length, 0) + }) + + client.start() + }) +} + +test('http: filter option with custom error', function (t) { + testFilterCustomError(t, 'http') +}) + +test('udp: filter option filter option with custom error', function (t) { + testFilterCustomError(t, 'udp') +})