Recover from unexpected events better

When you restart the tracker server, all peers it's tracking are
discarded. Then, clients that are already running will continue to send
`update` events, but the tracker throws them away because it was
expected to get a `start` event first.

This should only be a warning, and not a fatal error. I just made the
following changes:

- unexpected `started` event (for peer already in swarm) is treated as
an `update`
- unexpected `stopped` event is discarded
- unexpected `completed` event is treated as a `start`
- unexpected `update` event (from peer not in swarm) is treated as
`start`

Fixes #42
This commit is contained in:
Feross Aboukhadijeh 2014-11-26 15:27:02 +08:00
parent b44715d37a
commit 026f2c2de0

231
server.js
View File

@ -138,7 +138,6 @@ Server.prototype._getSwarm = function (binaryInfoHash) {
Server.prototype._onHttpRequest = function (req, res) { Server.prototype._onHttpRequest = function (req, res) {
var self = this var self = this
var warning
var s = req.url.split('?') var s = req.url.split('?')
var params = common.querystringParse(s[1]) var params = common.querystringParse(s[1])
var response var response
@ -153,6 +152,9 @@ Server.prototype._onHttpRequest = function (req, res) {
if (peerId.length !== 20) return error('invalid peer_id') if (peerId.length !== 20) return error('invalid peer_id')
if (!port) return error('invalid port') if (!port) return error('invalid port')
var left = Number(params.left)
var compact = Number(params.compact)
var ip = self._trustProxy var ip = self._trustProxy
? req.headers['x-forwarded-for'] || req.connection.remoteAddress ? req.headers['x-forwarded-for'] || req.connection.remoteAddress
: req.connection.remoteAddress.replace(REMOVE_IPV6_RE, '') // force ipv4 : req.connection.remoteAddress.replace(REMOVE_IPV6_RE, '') // force ipv4
@ -165,76 +167,76 @@ Server.prototype._onHttpRequest = function (req, res) {
MAX_ANNOUNCE_PEERS MAX_ANNOUNCE_PEERS
) )
var start = function () {
if (peer) {
debug('unexpected `started` event from peer that is already in swarm')
return update() // treat as an update
}
if (left === 0) swarm.complete += 1
else swarm.incomplete += 1
swarm.peers[addr] = {
ip: ip,
port: port,
peerId: peerId
}
self.emit('start', addr)
}
var stop = function () {
if (!peer) {
debug('unexpected `stopped` event from peer that is not in swarm')
return // do nothing
}
if (peer.complete) swarm.complete -= 1
else swarm.incomplete -= 1
swarm.peers[addr] = null
self.emit('stop', addr)
}
var complete = function () {
if (!peer) {
debug('unexpected `completed` event from peer that is not in swarm')
return start() // treat as a start
}
if (peer.complete) {
debug('unexpected `completed` event from peer that is already marked as completed')
return // do nothing
}
swarm.complete += 1
swarm.incomplete -= 1
peer.complete = true
self.emit('complete', addr)
}
var update = function () {
if (!peer) {
debug('unexpected `update` event from peer that is not in swarm')
return start() // treat as a start
}
self.emit('update', addr)
}
switch (params.event) { switch (params.event) {
case 'started': case 'started':
if (peer) { start()
warning = 'unexpected `started` event from peer that is already in swarm'
break
}
if (Number(params.left) === 0) {
swarm.complete += 1
} else {
swarm.incomplete += 1
}
swarm.peers[addr] = {
ip: ip,
port: port,
peerId: peerId
}
self.emit('start', addr)
break break
case 'stopped': case 'stopped':
if (!peer) { stop()
warning = 'unexpected `stopped` event from peer that is not in swarm'
break
}
if (peer.complete) {
swarm.complete -= 1
} else {
swarm.incomplete -= 1
}
swarm.peers[addr] = null
self.emit('stop', addr)
break break
case 'completed': case 'completed':
if (!peer) { complete()
warning = 'unexpected `completed` event from peer that is not in swarm'
break
}
if (peer.complete) {
warning = 'unexpected `completed` event from peer that is already marked as completed'
break
}
swarm.complete += 1
swarm.incomplete -= 1
peer.complete = true
self.emit('complete', addr)
break break
case '': case undefined: // update
case '': // update update()
case undefined:
if (!peer) {
warning = 'unexpected `update` event from peer that is not in swarm'
break
}
self.emit('update', addr)
break break
default: default:
return error('invalid event') // early return return error('invalid event') // early return
} }
if (left === 0) peer.complete = true
// send peers // send peers
var peers = Number(params.compact) === 1 var peers = compact === 1
? self._getPeersCompact(swarm, numWant) ? self._getPeersCompact(swarm, numWant)
: self._getPeers(swarm, numWant) : self._getPeers(swarm, numWant)
@ -245,9 +247,6 @@ Server.prototype._onHttpRequest = function (req, res) {
interval: self._intervalMs interval: self._intervalMs
} }
if (warning) {
response['warning message'] = warning
}
res.end(bencode.encode(response)) res.end(bencode.encode(response))
debug('sent response %s', response) debug('sent response %s', response)
@ -361,74 +360,74 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
// 512 bytes which is not safe // 512 bytes which is not safe
numWant = Math.min(numWant || NUM_ANNOUNCE_PEERS, MAX_ANNOUNCE_PEERS) numWant = Math.min(numWant || NUM_ANNOUNCE_PEERS, MAX_ANNOUNCE_PEERS)
var warning var start = function () {
if (peer) {
debug('unexpected `started` event from peer that is already in swarm')
return update() // treat as an update
}
if (left === 0) swarm.complete += 1
else swarm.incomplete += 1
swarm.peers[addr] = {
ip: ip,
port: port,
peerId: peerId
}
self.emit('start', addr)
}
var stop = function () {
if (!peer) {
debug('unexpected `stopped` event from peer that is not in swarm')
return // do nothing
}
if (peer.complete) swarm.complete -= 1
else swarm.incomplete -= 1
swarm.peers[addr] = null
self.emit('stop', addr)
}
var complete = function () {
if (!peer) {
debug('unexpected `completed` event from peer that is not in swarm')
return start() // treat as a start
}
if (peer.complete) {
debug('unexpected `completed` event from peer that is already marked as completed')
return // do nothing
}
swarm.complete += 1
swarm.incomplete -= 1
peer.complete = true
self.emit('complete', addr)
}
var update = function () {
if (!peer) {
debug('unexpected `update` event from peer that is not in swarm')
return start() // treat as a start
}
self.emit('update', addr)
}
switch (event) { switch (event) {
case common.EVENTS.started: case common.EVENTS.started:
if (peer) { start()
warning = 'unexpected `started` event from peer that is already in swarm'
break
}
if (left === 0) {
swarm.complete += 1
} else {
swarm.incomplete += 1
}
swarm.peers[addr] = {
ip: ip,
port: port,
peerId: peerId
}
self.emit('start', addr)
break break
case common.EVENTS.stopped: case common.EVENTS.stopped:
if (!peer) { stop()
warning = 'unexpected `stopped` event from peer that is not in swarm'
break
}
if (peer.complete) {
swarm.complete -= 1
} else {
swarm.incomplete -= 1
}
swarm.peers[addr] = null
self.emit('stop', addr)
break break
case common.EVENTS.completed: case common.EVENTS.completed:
if (!peer) { complete()
warning = 'unexpected `completed` event from peer that is not in swarm'
break
}
if (peer.complete) {
warning = 'unexpected `completed` event from peer that is already marked as completed'
break
}
swarm.complete += 1
swarm.incomplete -= 1
peer.complete = true
self.emit('complete', addr)
break break
case common.EVENTS.update: // update case common.EVENTS.update: // update
if (!peer) { update()
warning = 'unexpected `update` event from peer that is not in swarm'
break
}
self.emit('update', addr)
break break
default: default:
return error('invalid event') // early return return error('invalid event') // early return
} }
if (left === 0) peer.complete = true
// send peers // send peers
var peers = self._getPeersCompact(swarm, numWant) var peers = self._getPeersCompact(swarm, numWant)