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
This commit is contained in:
Feross Aboukhadijeh 2015-07-08 10:13:52 -07:00
parent f13accfc42
commit 89b3fb3086
3 changed files with 119 additions and 32 deletions

View File

@ -113,16 +113,18 @@ var server = new Server({
http: true, // enable http server? [default=true] http: true, // enable http server? [default=true]
ws: true, // enable websocket server? [default=false] ws: true, // enable websocket server? [default=false]
filter: function (infoHash, params, cb) { filter: function (infoHash, params, cb) {
// black/whitelist for disallowing/allowing torrents [default=allow all] // Blacklist/whitelist function for allowing/disallowing torrents. If this option is
// this example only allows this one torrent // omitted, all torrents are allowed. It is possible to interface with a database or
cb(infoHash === 'aaa67059ed6bd08362da625b3ae77f6f4a075aaa') // external system before deciding to allow/deny, because this function is async.
// it's possible to interface with a database or external system before // It is possible to block by peer id (whitelisting torrent clients) or by secret
// deciding, because this function is async // 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 // This example only allows one torrent.
// by secret key, as you get full access to the original http/udp
// request parameters in `params` var allowed = (infoHash === 'aaa67059ed6bd08362da625b3ae77f6f4a075aaa')
cb(allowed)
}) })
}) })

View File

@ -385,11 +385,13 @@ Server.prototype._onAnnounce = function (params, cb) {
function createSwarm () { function createSwarm () {
if (self._filter) { if (self._filter) {
self._filter(params.info_hash, params, function (allowed) { 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) swarm = self.createSwarm(params.info_hash)
announce() announce()
} else {
cb(new Error('disallowed info_hash'))
} }
}) })
} else { } else {
@ -432,7 +434,7 @@ Server.prototype._onAnnounce = function (params, cb) {
}) })
} // else, return full peer objects (used for websocket responses) } // else, return full peer objects (used for websocket responses)
cb(err, response) cb(null, response)
}) })
} }

View File

@ -13,7 +13,7 @@ var parsedLeaves = parseTorrent(leaves)
var peerId = new Buffer('01234567890123456789') var peerId = new Buffer('01234567890123456789')
function testFilterOption (t, serverType) { function testFilterOption (t, serverType) {
t.plan(6) t.plan(8)
var opts = serverType === 'http' ? { udp: false } : { http: false } var opts = serverType === 'http' ? { udp: false } : { http: false }
opts.filter = function (infoHash, params, cb) { opts.filter = function (infoHash, params, cb) {
process.nextTick(function () { process.nextTick(function () {
@ -44,7 +44,8 @@ function testFilterOption (t, serverType) {
client.once('warning', function (err) { client.once('warning', function (err) {
t.ok(/disallowed info_hash/.test(err.message), 'got client warning') t.ok(/disallowed info_hash/.test(err.message), 'got client warning')
client.destroy() client.destroy(function () {
t.pass('client destroyed')
client = new Client(peerId, port, parsedLeaves) client = new Client(peerId, port, parsedLeaves)
client.on('error', function (err) { client.on('error', function (err) {
@ -56,7 +57,9 @@ function testFilterOption (t, serverType) {
client.on('update', function () { client.on('update', function () {
t.pass('got announce') t.pass('got announce')
client.destroy() client.destroy(function () {
t.pass('client destroyed')
})
server.close(function () { server.close(function () {
t.pass('server closed') t.pass('server closed')
}) })
@ -68,6 +71,7 @@ function testFilterOption (t, serverType) {
client.start() client.start()
}) })
})
server.once('warning', function (err) { server.once('warning', function (err) {
t.ok(/disallowed info_hash/.test(err.message), 'got server warning') t.ok(/disallowed info_hash/.test(err.message), 'got server warning')
@ -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) { test('udp: filter option blocks tracker from tracking torrent', function (t) {
testFilterOption(t, 'udp') 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')
})