Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING: Many fixes; all leaks fixed #762

Merged
merged 12 commits into from Apr 22, 2016

cleanup torrent reference leaks

  • Loading branch information
feross committed Apr 21, 2016
commit a47d2ce4b27896a4c2e2e3820f69c712c2a0de3e
@@ -33,9 +33,8 @@
- Do not return existing torrent object when duplicate torrent is added. Fire an
`'error'` event instead.

- Memory leak of `Torrent` object caused by `RarityMap`

- Memory leak of `Torrent` object caused by `TCPPool`
- Memory leaks of `Torrent` object caused by various internal subclasses of WebTorrent,
including `RarityMap`, `TCPPool`, `WebConn`, `Server`.

- `client.ratio` and `torrent.ratio` are now calculated as `uploaded / received` instead
of `uploaded / downloaded`.
@@ -12,19 +12,22 @@ function Server (torrent, opts) {
var server = http.createServer(opts)

var sockets = []
var pendingReady = []
var closed = false

server.on('connection', function (socket) {
socket.setTimeout(36000000)
sockets.push(socket)
socket.on('close', function () {
arrayRemove(sockets, sockets.indexOf(socket))
})
})
server.on('connection', onConnection)
server.on('request', onRequest)

var _close = server.close
server.close = function (cb) {
closed = true
torrent = null
server.removeListener('connection', onConnection)
server.removeListener('request', onRequest)
while (pendingReady.length) {
var onReady = pendingReady.pop()
torrent.removeListener('ready', onReady)
}
_close.call(server, cb)
}

@@ -38,7 +41,15 @@ function Server (torrent, opts) {
else server.close(cb)
}

server.on('request', function (req, res) {
function onConnection (socket) {
socket.setTimeout(36000000)
sockets.push(socket)
socket.once('close', function () {
arrayRemove(sockets, sockets.indexOf(socket))
})
}

function onRequest (req, res) {
debug('onRequest')

// Allow CORS requests to specify arbitrary headers, e.g. 'Range',
@@ -61,10 +72,15 @@ function Server (torrent, opts) {
var pathname = url.parse(req.url).pathname
if (pathname === '/favicon.ico') return res.end()

if (torrent.ready) onReady()
else torrent.once('ready', onReady)
if (torrent.ready) {
onReady()
} else {
pendingReady.push(onReady)
torrent.once('ready', onReady)
}

function onReady () {
arrayRemove(pendingReady, pendingReady.indexOf(onReady))
if (pathname === '/') {
res.setHeader('Content-Type', 'text/html')
var listHtml = torrent.files.map(function (file, i) {
@@ -112,7 +128,7 @@ function Server (torrent, opts) {
if (req.method === 'HEAD') res.end()
pump(file.createReadStream(range), res)
}
})
}

return server
}
@@ -1,6 +1,3 @@
// TODO: cleanup events
// TODO: cleanup reference to parsedTorrent (i.e. Torrent object)

module.exports = WebConn

var BitField = require('bitfield')
@@ -15,37 +12,41 @@ inherits(WebConn, Wire)
/**
* Converts requests for torrent blocks into http range requests.
* @param {string} url web seed url
* @param {Object} parsedTorrent
* @param {Object} torrent
*/
function WebConn (url, parsedTorrent) {
var self = this
function WebConn (url, torrent) {
Wire.call(this)

self.url = url
self.webPeerId = sha1.sync(url)
self.parsedTorrent = parsedTorrent
this.url = url
this.webPeerId = sha1.sync(url)
this._torrent = torrent

this._init()
}

WebConn.prototype._init = function () {
var self = this
self.setKeepAlive(true)

self.on('handshake', function (infoHash, peerId) {
self.once('handshake', function (infoHash, peerId) {
if (self.destroyed) return
self.handshake(infoHash, self.webPeerId)
var numPieces = self.parsedTorrent.pieces.length
var numPieces = self._torrent.pieces.length
var bitfield = new BitField(numPieces)
for (var i = 0; i <= numPieces; i++) {
bitfield.set(i, true)
}
self.bitfield(bitfield)
})

self.on('choke', function () { debug('choke') })
self.on('unchoke', function () { debug('unchoke') })

self.once('interested', function () {
debug('interested')
self.unchoke()
})
self.on('uninterested', function () { debug('uninterested') })

self.on('uninterested', function () { debug('uninterested') })
self.on('choke', function () { debug('choke') })
self.on('unchoke', function () { debug('unchoke') })
self.on('bitfield', function () { debug('bitfield') })

self.on('request', function (pieceIndex, offset, length, callback) {
@@ -56,14 +57,14 @@ function WebConn (url, parsedTorrent) {

WebConn.prototype.httpRequest = function (pieceIndex, offset, length, cb) {
var self = this
var pieceOffset = pieceIndex * self.parsedTorrent.pieceLength
var pieceOffset = pieceIndex * self._torrent.pieceLength
var rangeStart = pieceOffset + offset /* offset within whole torrent */
var rangeEnd = rangeStart + length - 1

// Web seed URL format:
// For single-file torrents, make HTTP range requests directly to the web seed URL
// For multi-file torrents, add the torrent folder and file name to the URL
var files = self.parsedTorrent.files
var files = self._torrent.files
var requests
if (files.length <= 1) {
requests = [{
@@ -138,3 +139,8 @@ WebConn.prototype.httpRequest = function (pieceIndex, offset, length, cb) {
})
})
}

WebConn.prototype.destroy = function () {
Wire.prototype.destroy.call(this)
this._torrent = null
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.