Skip to content

Commit ac2c3d0

Browse files
committed
client: Re-use WebSocket connections
Use a socket pool, so tracker clients share WebSocket objects for the same server. In practice, WebSockets are pretty slow to establish, so this gives a nice performance boost, and saves browser resources.
1 parent 340b3fa commit ac2c3d0

File tree

1 file changed

+45
-18
lines changed

1 file changed

+45
-18
lines changed

lib/websocket-tracker.js

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ var Socket = require('simple-websocket')
1111

1212
var common = require('./common')
1313

14+
// Use a socket pool, so tracker clients share WebSocket objects for the same server.
15+
// In practice, WebSockets are pretty slow to establish, so this gives a nice performance
16+
// boost, and saves browser resources.
17+
var socketPool = {}
18+
1419
var RECONNECT_VARIANCE = 30 * 1000
1520
var RECONNECT_MINIMUM = 5 * 1000
1621

@@ -26,9 +31,10 @@ function WebSocketTracker (client, announceUrl, opts) {
2631

2732
self._opts = opts
2833

34+
if (announceUrl[announceUrl.length - 1] === '/') {
35+
announceUrl = announceUrl.substring(0, announceUrl.length - 1)
36+
}
2937
self._announceUrl = announceUrl
30-
if (self._announceUrl[self._announceUrl.length - 1] !== '/') self._announceUrl += '/'
31-
self._announceUrl += '?' + hat(40)
3238

3339
self._peers = {} // peers (offer id -> peer)
3440
self._socket = null
@@ -102,7 +108,11 @@ WebSocketTracker.prototype._openSocket = function () {
102108
self._onSocketDataBound = self._onSocketData.bind(self)
103109
self._onSocketCloseBound = self._onSocketClose.bind(self)
104110

105-
self._socket = new Socket(self._announceUrl)
111+
self._socket = socketPool[self._announceUrl]
112+
if (!self._socket) {
113+
self._socket = socketPool[self._announceUrl] = new Socket(self._announceUrl)
114+
}
115+
106116
self._socket.on('data', self._onSocketDataBound)
107117
self._socket.on('close', self._onSocketCloseBound)
108118
self._socket.on('error', self._onSocketErrorBound)
@@ -117,10 +127,22 @@ WebSocketTracker.prototype._onSocketData = function (data) {
117127
}
118128

119129
if (data.info_hash !== self.client._infoHashBinary) {
120-
return self.client.emit('warning', new Error('Invalid tracker response'))
130+
debug(
131+
'ignoring websocket data from %s for %s (looking for %s: reused socket)',
132+
self._announceUrl, common.binaryToHex(data.info_hash), self.client._infoHashHex
133+
)
134+
return
121135
}
122136

123-
debug('received %s from %s', JSON.stringify(data), self._announceUrl)
137+
if (data.peer_id && data.peer_id === self.client._peerIdBinary) {
138+
// ignore offers/answers from this client
139+
return
140+
}
141+
142+
debug(
143+
'received %s from %s for %s',
144+
JSON.stringify(data), self._announceUrl, self.client._infoHashHex
145+
)
124146

125147
var failure = data['failure reason']
126148
if (failure) return self.client.emit('warning', new Error(failure))
@@ -149,13 +171,8 @@ WebSocketTracker.prototype._onSocketData = function (data) {
149171
})
150172
}
151173

152-
if (self.client._peerIdHex === common.binaryToHex(data.peer_id)) {
153-
// ignore offers/answers from this client
154-
return
155-
}
156-
157174
var peer
158-
if (data.offer) {
175+
if (data.offer && data.peer_id) {
159176
peer = new Peer({
160177
trickle: false,
161178
config: self.client._rtcConfig,
@@ -177,7 +194,7 @@ WebSocketTracker.prototype._onSocketData = function (data) {
177194
self.client.emit('peer', peer)
178195
}
179196

180-
if (data.answer) {
197+
if (data.answer && data.peer_id) {
181198
peer = self._peers[common.binaryToHex(data.offer_id)]
182199
if (peer) {
183200
peer.id = common.binaryToHex(data.peer_id)
@@ -191,18 +208,28 @@ WebSocketTracker.prototype._onSocketData = function (data) {
191208

192209
WebSocketTracker.prototype._onSocketClose = function () {
193210
var self = this
211+
if (self.destroyed) return
194212
self.destroy()
195-
var ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + RECONNECT_MINIMUM
196-
setTimeout(function () {
197-
self._openSocket()
198-
}, ms)
199-
debug('reconnecting socket in %s ms', ms)
213+
self._startReconnectTimer()
200214
}
201215

202216
WebSocketTracker.prototype._onSocketError = function (err) {
203217
var self = this
204218
if (self.destroyed) return
205-
self.client.emit('error', err)
219+
self.destroy()
220+
// errors will often happen if a tracker is offline, so don't treat it as fatal
221+
self.client.emit('warning', err)
222+
self._startReconnectTimer()
223+
}
224+
225+
WebSocketTracker.prototype._startReconnectTimer = function () {
226+
var self = this
227+
var ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + RECONNECT_MINIMUM
228+
setTimeout(function () {
229+
self.destroyed = false
230+
self._openSocket()
231+
}, ms)
232+
debug('reconnecting socket in %s ms', ms)
206233
}
207234

208235
WebSocketTracker.prototype._send = function (params) {

0 commit comments

Comments
 (0)