mirror of
https://github.com/webtorrent/bittorrent-tracker.git
synced 2024-12-12 18:36:28 +00:00
fix: drop buffer (#465)
This commit is contained in:
parent
6864ef9a24
commit
c99eb89208
24
client.js
24
client.js
@ -4,6 +4,7 @@ import once from 'once'
|
|||||||
import parallel from 'run-parallel'
|
import parallel from 'run-parallel'
|
||||||
import Peer from 'simple-peer'
|
import Peer from 'simple-peer'
|
||||||
import queueMicrotask from 'queue-microtask'
|
import queueMicrotask from 'queue-microtask'
|
||||||
|
import { hex2arr, hex2bin, text2arr, arr2hex, arr2text } from 'uint8-util'
|
||||||
|
|
||||||
import common from './lib/common.js'
|
import common from './lib/common.js'
|
||||||
import HTTPTracker from './lib/client/http-tracker.js' // empty object in browser
|
import HTTPTracker from './lib/client/http-tracker.js' // empty object in browser
|
||||||
@ -18,8 +19,8 @@ const debug = Debug('bittorrent-tracker:client')
|
|||||||
* Find torrent peers, to help a torrent client participate in a torrent swarm.
|
* Find torrent peers, to help a torrent client participate in a torrent swarm.
|
||||||
*
|
*
|
||||||
* @param {Object} opts options object
|
* @param {Object} opts options object
|
||||||
* @param {string|Buffer} opts.infoHash torrent info hash
|
* @param {string|Uint8Array} opts.infoHash torrent info hash
|
||||||
* @param {string|Buffer} opts.peerId peer id
|
* @param {string|Uint8Array} opts.peerId peer id
|
||||||
* @param {string|Array.<string>} opts.announce announce
|
* @param {string|Array.<string>} opts.announce announce
|
||||||
* @param {number} opts.port torrent client listening port
|
* @param {number} opts.port torrent client listening port
|
||||||
* @param {function} opts.getAnnounceOpts callback to provide data to tracker
|
* @param {function} opts.getAnnounceOpts callback to provide data to tracker
|
||||||
@ -39,15 +40,15 @@ class Client extends EventEmitter {
|
|||||||
|
|
||||||
this.peerId = typeof opts.peerId === 'string'
|
this.peerId = typeof opts.peerId === 'string'
|
||||||
? opts.peerId
|
? opts.peerId
|
||||||
: opts.peerId.toString('hex')
|
: arr2hex(opts.peerId)
|
||||||
this._peerIdBuffer = Buffer.from(this.peerId, 'hex')
|
this._peerIdBuffer = hex2arr(this.peerId)
|
||||||
this._peerIdBinary = this._peerIdBuffer.toString('binary')
|
this._peerIdBinary = hex2bin(this.peerId)
|
||||||
|
|
||||||
this.infoHash = typeof opts.infoHash === 'string'
|
this.infoHash = typeof opts.infoHash === 'string'
|
||||||
? opts.infoHash.toLowerCase()
|
? opts.infoHash.toLowerCase()
|
||||||
: opts.infoHash.toString('hex')
|
: arr2hex(opts.infoHash)
|
||||||
this._infoHashBuffer = Buffer.from(this.infoHash, 'hex')
|
this._infoHashBuffer = hex2arr(this.infoHash)
|
||||||
this._infoHashBinary = this._infoHashBuffer.toString('binary')
|
this._infoHashBinary = hex2bin(this.infoHash)
|
||||||
|
|
||||||
debug('new client %s', this.infoHash)
|
debug('new client %s', this.infoHash)
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ class Client extends EventEmitter {
|
|||||||
|
|
||||||
// Remove trailing slash from trackers to catch duplicates
|
// Remove trailing slash from trackers to catch duplicates
|
||||||
announce = announce.map(announceUrl => {
|
announce = announce.map(announceUrl => {
|
||||||
announceUrl = announceUrl.toString()
|
announceUrl = arr2text(announceUrl)
|
||||||
if (announceUrl[announceUrl.length - 1] === '/') {
|
if (announceUrl[announceUrl.length - 1] === '/') {
|
||||||
announceUrl = announceUrl.substring(0, announceUrl.length - 1)
|
announceUrl = announceUrl.substring(0, announceUrl.length - 1)
|
||||||
}
|
}
|
||||||
@ -260,7 +261,7 @@ Client.scrape = (opts, cb) => {
|
|||||||
|
|
||||||
const clientOpts = Object.assign({}, opts, {
|
const clientOpts = Object.assign({}, opts, {
|
||||||
infoHash: Array.isArray(opts.infoHash) ? opts.infoHash[0] : opts.infoHash,
|
infoHash: Array.isArray(opts.infoHash) ? opts.infoHash[0] : opts.infoHash,
|
||||||
peerId: Buffer.from('01234567890123456789'), // dummy value
|
peerId: text2arr('01234567890123456789'), // dummy value
|
||||||
port: 6881 // dummy value
|
port: 6881 // dummy value
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -284,9 +285,6 @@ Client.scrape = (opts, cb) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
opts.infoHash = Array.isArray(opts.infoHash)
|
|
||||||
? opts.infoHash.map(infoHash => Buffer.from(infoHash, 'hex'))
|
|
||||||
: Buffer.from(opts.infoHash, 'hex')
|
|
||||||
client.scrape({ infoHash: opts.infoHash })
|
client.scrape({ infoHash: opts.infoHash })
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import clone from 'clone'
|
|||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import get from 'simple-get'
|
import get from 'simple-get'
|
||||||
import Socks from 'socks'
|
import Socks from 'socks'
|
||||||
|
import { bin2hex, hex2bin, arr2text } from 'uint8-util'
|
||||||
|
|
||||||
import common from '../common.js'
|
import common from '../common.js'
|
||||||
import Tracker from './tracker.js'
|
import Tracker from './tracker.js'
|
||||||
@ -65,8 +66,8 @@ class HTTPTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
const infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
||||||
? opts.infoHash.map(infoHash => infoHash.toString('binary'))
|
? opts.infoHash.map(infoHash => hex2bin(infoHash))
|
||||||
: (opts.infoHash && opts.infoHash.toString('binary')) || this.client._infoHashBinary
|
: (opts.infoHash && hex2bin(opts.infoHash)) || this.client._infoHashBinary
|
||||||
const params = {
|
const params = {
|
||||||
info_hash: infoHashes
|
info_hash: infoHashes
|
||||||
}
|
}
|
||||||
@ -159,13 +160,13 @@ class HTTPTracker extends Tracker {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
return cb(new Error(`Error decoding tracker response: ${err.message}`))
|
return cb(new Error(`Error decoding tracker response: ${err.message}`))
|
||||||
}
|
}
|
||||||
const failure = data['failure reason'] && Buffer.from(data['failure reason']).toString()
|
const failure = data['failure reason'] && arr2text(data['failure reason'])
|
||||||
if (failure) {
|
if (failure) {
|
||||||
debug(`failure from ${requestUrl} (${failure})`)
|
debug(`failure from ${requestUrl} (${failure})`)
|
||||||
return cb(new Error(failure))
|
return cb(new Error(failure))
|
||||||
}
|
}
|
||||||
|
|
||||||
const warning = data['warning message'] && Buffer.from(data['warning message']).toString()
|
const warning = data['warning message'] && arr2text(data['warning message'])
|
||||||
if (warning) {
|
if (warning) {
|
||||||
debug(`warning from ${requestUrl} (${warning})`)
|
debug(`warning from ${requestUrl} (${warning})`)
|
||||||
self.client.emit('warning', new Error(warning))
|
self.client.emit('warning', new Error(warning))
|
||||||
@ -189,7 +190,7 @@ class HTTPTracker extends Tracker {
|
|||||||
|
|
||||||
const response = Object.assign({}, data, {
|
const response = Object.assign({}, data, {
|
||||||
announce: this.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(data.info_hash)
|
infoHash: bin2hex(data.info_hash || String(data.info_hash))
|
||||||
})
|
})
|
||||||
this.client.emit('update', response)
|
this.client.emit('update', response)
|
||||||
|
|
||||||
@ -248,7 +249,7 @@ class HTTPTracker extends Tracker {
|
|||||||
// (separate from announce interval)
|
// (separate from announce interval)
|
||||||
const response = Object.assign(data[infoHash], {
|
const response = Object.assign(data[infoHash], {
|
||||||
announce: this.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(infoHash)
|
infoHash: bin2hex(infoHash)
|
||||||
})
|
})
|
||||||
this.client.emit('scrape', response)
|
this.client.emit('scrape', response)
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import arrayRemove from 'unordered-array-remove'
|
import arrayRemove from 'unordered-array-remove'
|
||||||
import BN from 'bn.js'
|
|
||||||
import clone from 'clone'
|
import clone from 'clone'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import dgram from 'dgram'
|
import dgram from 'dgram'
|
||||||
import randombytes from 'randombytes'
|
|
||||||
import Socks from 'socks'
|
import Socks from 'socks'
|
||||||
|
import { concat, hex2arr, randomBytes } from 'uint8-util'
|
||||||
|
|
||||||
import common from '../common.js'
|
import common from '../common.js'
|
||||||
import Tracker from './tracker.js'
|
import Tracker from './tracker.js'
|
||||||
@ -131,7 +130,7 @@ class UDPTracker extends Tracker {
|
|||||||
}, common.REQUEST_TIMEOUT)
|
}, common.REQUEST_TIMEOUT)
|
||||||
if (timeout.unref) timeout.unref()
|
if (timeout.unref) timeout.unref()
|
||||||
|
|
||||||
send(Buffer.concat([
|
send(concat([
|
||||||
common.CONNECTION_ID,
|
common.CONNECTION_ID,
|
||||||
common.toUInt32(common.ACTIONS.CONNECT),
|
common.toUInt32(common.ACTIONS.CONNECT),
|
||||||
transactionId
|
transactionId
|
||||||
@ -175,7 +174,8 @@ class UDPTracker extends Tracker {
|
|||||||
|
|
||||||
function onSocketMessage (msg) {
|
function onSocketMessage (msg) {
|
||||||
if (proxySocket) msg = msg.slice(10)
|
if (proxySocket) msg = msg.slice(10)
|
||||||
if (msg.length < 8 || msg.readUInt32BE(4) !== transactionId.readUInt32BE(0)) {
|
const view = new DataView(transactionId.buffer)
|
||||||
|
if (msg.length < 8 || msg.readUInt32BE(4) !== view.getUint32(0)) {
|
||||||
return onError(new Error('tracker sent invalid transaction id'))
|
return onError(new Error('tracker sent invalid transaction id'))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,14 +270,14 @@ class UDPTracker extends Tracker {
|
|||||||
function announce (connectionId, opts) {
|
function announce (connectionId, opts) {
|
||||||
transactionId = genTransactionId()
|
transactionId = genTransactionId()
|
||||||
|
|
||||||
send(Buffer.concat([
|
send(concat([
|
||||||
connectionId,
|
connectionId,
|
||||||
common.toUInt32(common.ACTIONS.ANNOUNCE),
|
common.toUInt32(common.ACTIONS.ANNOUNCE),
|
||||||
transactionId,
|
transactionId,
|
||||||
self.client._infoHashBuffer,
|
self.client._infoHashBuffer,
|
||||||
self.client._peerIdBuffer,
|
self.client._peerIdBuffer,
|
||||||
toUInt64(opts.downloaded),
|
toUInt64(opts.downloaded),
|
||||||
opts.left != null ? toUInt64(opts.left) : Buffer.from('FFFFFFFFFFFFFFFF', 'hex'),
|
opts.left != null ? toUInt64(opts.left) : hex2arr('ffffffffffffffff'),
|
||||||
toUInt64(opts.uploaded),
|
toUInt64(opts.uploaded),
|
||||||
common.toUInt32(common.EVENTS[opts.event] || 0),
|
common.toUInt32(common.EVENTS[opts.event] || 0),
|
||||||
common.toUInt32(0), // ip address (optional)
|
common.toUInt32(0), // ip address (optional)
|
||||||
@ -291,10 +291,10 @@ class UDPTracker extends Tracker {
|
|||||||
transactionId = genTransactionId()
|
transactionId = genTransactionId()
|
||||||
|
|
||||||
const infoHash = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
const infoHash = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
||||||
? Buffer.concat(opts.infoHash)
|
? concat(opts.infoHash)
|
||||||
: (opts.infoHash || self.client._infoHashBuffer)
|
: (opts.infoHash || self.client._infoHashBuffer)
|
||||||
|
|
||||||
send(Buffer.concat([
|
send(concat([
|
||||||
connectionId,
|
connectionId,
|
||||||
common.toUInt32(common.ACTIONS.SCRAPE),
|
common.toUInt32(common.ACTIONS.SCRAPE),
|
||||||
transactionId,
|
transactionId,
|
||||||
@ -307,12 +307,13 @@ class UDPTracker extends Tracker {
|
|||||||
UDPTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 60 * 1000 // 30 minutes
|
UDPTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 60 * 1000 // 30 minutes
|
||||||
|
|
||||||
function genTransactionId () {
|
function genTransactionId () {
|
||||||
return randombytes(4)
|
return randomBytes(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toUInt16 (n) {
|
function toUInt16 (n) {
|
||||||
const buf = Buffer.allocUnsafe(2)
|
const buf = new Uint8Array(2)
|
||||||
buf.writeUInt16BE(n, 0)
|
const view = new DataView(buf.buffer)
|
||||||
|
view.setUint16(0, n)
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,13 +321,12 @@ const MAX_UINT = 4294967295
|
|||||||
|
|
||||||
function toUInt64 (n) {
|
function toUInt64 (n) {
|
||||||
if (n > MAX_UINT || typeof n === 'string') {
|
if (n > MAX_UINT || typeof n === 'string') {
|
||||||
const bytes = new BN(n).toArray()
|
const buf = new Uint8Array(8)
|
||||||
while (bytes.length < 8) {
|
const view = new DataView(buf.buffer)
|
||||||
bytes.unshift(0)
|
view.setBigUint64(0, n)
|
||||||
}
|
return buf
|
||||||
return Buffer.from(bytes)
|
|
||||||
}
|
}
|
||||||
return Buffer.concat([common.toUInt32(0), common.toUInt32(n)])
|
return concat([new Uint8Array(4), common.toUInt32(n)])
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop () {}
|
function noop () {}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import clone from 'clone'
|
import clone from 'clone'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import Peer from 'simple-peer'
|
import Peer from 'simple-peer'
|
||||||
import randombytes from 'randombytes'
|
|
||||||
import Socket from '@thaunknown/simple-websocket'
|
import Socket from '@thaunknown/simple-websocket'
|
||||||
import Socks from 'socks'
|
import Socks from 'socks'
|
||||||
|
import { arr2text, arr2hex, hex2bin, bin2hex, randomBytes } from 'uint8-util'
|
||||||
|
|
||||||
import common from '../common.js'
|
import { DESTROY_TIMEOUT } from '../common.js'
|
||||||
import Tracker from './tracker.js'
|
import Tracker from './tracker.js'
|
||||||
|
|
||||||
const debug = Debug('bittorrent-tracker:websocket-tracker')
|
const debug = Debug('bittorrent-tracker:websocket-tracker')
|
||||||
@ -80,8 +80,8 @@ class WebSocketTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
const infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
|
||||||
? opts.infoHash.map(infoHash => infoHash.toString('binary'))
|
? opts.infoHash.map(infoHash => hex2bin(infoHash))
|
||||||
: (opts.infoHash && opts.infoHash.toString('binary')) || this.client._infoHashBinary
|
: (opts.infoHash && hex2bin(opts.infoHash)) || this.client._infoHashBinary
|
||||||
const params = {
|
const params = {
|
||||||
action: 'scrape',
|
action: 'scrape',
|
||||||
info_hash: infoHashes
|
info_hash: infoHashes
|
||||||
@ -138,7 +138,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
|
|
||||||
// Otherwise, wait a short time for potential responses to come in from the
|
// Otherwise, wait a short time for potential responses to come in from the
|
||||||
// server, then force close the socket.
|
// server, then force close the socket.
|
||||||
timeout = setTimeout(destroyCleanup, common.DESTROY_TIMEOUT)
|
timeout = setTimeout(destroyCleanup, DESTROY_TIMEOUT)
|
||||||
|
|
||||||
// But, if a response comes from the server before the timeout fires, do cleanup
|
// But, if a response comes from the server before the timeout fires, do cleanup
|
||||||
// right away.
|
// right away.
|
||||||
@ -214,7 +214,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
this.expectingResponse = false
|
this.expectingResponse = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(Buffer.from(data))
|
data = JSON.parse(arr2text(data))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.client.emit('warning', new Error('Invalid tracker response'))
|
this.client.emit('warning', new Error('Invalid tracker response'))
|
||||||
return
|
return
|
||||||
@ -233,7 +233,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
if (data.info_hash !== this.client._infoHashBinary) {
|
if (data.info_hash !== this.client._infoHashBinary) {
|
||||||
debug(
|
debug(
|
||||||
'ignoring websocket data from %s for %s (looking for %s: reused socket)',
|
'ignoring websocket data from %s for %s (looking for %s: reused socket)',
|
||||||
this.announceUrl, common.binaryToHex(data.info_hash), this.client.infoHash
|
this.announceUrl, bin2hex(data.info_hash), this.client.infoHash
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
if (data.complete != null) {
|
if (data.complete != null) {
|
||||||
const response = Object.assign({}, data, {
|
const response = Object.assign({}, data, {
|
||||||
announce: this.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(data.info_hash)
|
infoHash: bin2hex(data.info_hash)
|
||||||
})
|
})
|
||||||
this.client.emit('update', response)
|
this.client.emit('update', response)
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
if (data.offer && data.peer_id) {
|
if (data.offer && data.peer_id) {
|
||||||
debug('creating peer (from remote offer)')
|
debug('creating peer (from remote offer)')
|
||||||
peer = this._createPeer()
|
peer = this._createPeer()
|
||||||
peer.id = common.binaryToHex(data.peer_id)
|
peer.id = bin2hex(data.peer_id)
|
||||||
peer.once('signal', answer => {
|
peer.once('signal', answer => {
|
||||||
const params = {
|
const params = {
|
||||||
action: 'announce',
|
action: 'announce',
|
||||||
@ -293,10 +293,10 @@ class WebSocketTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.answer && data.peer_id) {
|
if (data.answer && data.peer_id) {
|
||||||
const offerId = common.binaryToHex(data.offer_id)
|
const offerId = bin2hex(data.offer_id)
|
||||||
peer = this.peers[offerId]
|
peer = this.peers[offerId]
|
||||||
if (peer) {
|
if (peer) {
|
||||||
peer.id = common.binaryToHex(data.peer_id)
|
peer.id = bin2hex(data.peer_id)
|
||||||
this.client.emit('peer', peer)
|
this.client.emit('peer', peer)
|
||||||
peer.signal(data.answer)
|
peer.signal(data.answer)
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ class WebSocketTracker extends Tracker {
|
|||||||
// (separate from announce interval)
|
// (separate from announce interval)
|
||||||
const response = Object.assign(data[infoHash], {
|
const response = Object.assign(data[infoHash], {
|
||||||
announce: this.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(infoHash)
|
infoHash: bin2hex(infoHash)
|
||||||
})
|
})
|
||||||
this.client.emit('scrape', response)
|
this.client.emit('scrape', response)
|
||||||
})
|
})
|
||||||
@ -376,13 +376,13 @@ class WebSocketTracker extends Tracker {
|
|||||||
checkDone()
|
checkDone()
|
||||||
|
|
||||||
function generateOffer () {
|
function generateOffer () {
|
||||||
const offerId = randombytes(20).toString('hex')
|
const offerId = arr2hex(randomBytes(20))
|
||||||
debug('creating peer (from _generateOffers)')
|
debug('creating peer (from _generateOffers)')
|
||||||
const peer = self.peers[offerId] = self._createPeer({ initiator: true })
|
const peer = self.peers[offerId] = self._createPeer({ initiator: true })
|
||||||
peer.once('signal', offer => {
|
peer.once('signal', offer => {
|
||||||
offers.push({
|
offers.push({
|
||||||
offer,
|
offer,
|
||||||
offer_id: common.hexToBinary(offerId)
|
offer_id: hex2bin(offerId)
|
||||||
})
|
})
|
||||||
checkDone()
|
checkDone()
|
||||||
})
|
})
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import querystring from 'querystring'
|
import querystring from 'querystring'
|
||||||
|
import { concat } from 'uint8-util'
|
||||||
|
|
||||||
export const IPV4_RE = /^[\d.]+$/
|
export const IPV4_RE = /^[\d.]+$/
|
||||||
export const IPV6_RE = /^[\da-fA-F:]+$/
|
export const IPV6_RE = /^[\da-fA-F:]+$/
|
||||||
export const REMOVE_IPV4_MAPPED_IPV6_RE = /^::ffff:/
|
export const REMOVE_IPV4_MAPPED_IPV6_RE = /^::ffff:/
|
||||||
|
|
||||||
export const CONNECTION_ID = Buffer.concat([toUInt32(0x417), toUInt32(0x27101980)])
|
export const CONNECTION_ID = concat([toUInt32(0x417), toUInt32(0x27101980)])
|
||||||
export const ACTIONS = { CONNECT: 0, ANNOUNCE: 1, SCRAPE: 2, ERROR: 3 }
|
export const ACTIONS = { CONNECT: 0, ANNOUNCE: 1, SCRAPE: 2, ERROR: 3 }
|
||||||
export const EVENTS = { update: 0, completed: 1, started: 2, stopped: 3, paused: 4 }
|
export const EVENTS = { update: 0, completed: 1, started: 2, stopped: 3, paused: 4 }
|
||||||
export const EVENT_IDS = {
|
export const EVENT_IDS = {
|
||||||
@ -40,8 +41,9 @@ export const REQUEST_TIMEOUT = 15000
|
|||||||
export const DESTROY_TIMEOUT = 1000
|
export const DESTROY_TIMEOUT = 1000
|
||||||
|
|
||||||
export function toUInt32 (n) {
|
export function toUInt32 (n) {
|
||||||
const buf = Buffer.allocUnsafe(4)
|
const buf = new Uint8Array(4)
|
||||||
buf.writeUInt32BE(n, 0)
|
const view = new DataView(buf.buffer)
|
||||||
|
view.setUint32(0, n)
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,24 +2,11 @@
|
|||||||
* Functions/constants needed by both the client and server.
|
* Functions/constants needed by both the client and server.
|
||||||
*/
|
*/
|
||||||
import * as common from './common-node.js'
|
import * as common from './common-node.js'
|
||||||
|
export * from './common-node.js'
|
||||||
|
|
||||||
export const DEFAULT_ANNOUNCE_PEERS = 50
|
export const DEFAULT_ANNOUNCE_PEERS = 50
|
||||||
export const MAX_ANNOUNCE_PEERS = 82
|
export const MAX_ANNOUNCE_PEERS = 82
|
||||||
|
|
||||||
export const binaryToHex = str => {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
str = String(str)
|
|
||||||
}
|
|
||||||
return Buffer.from(str, 'binary').toString('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hexToBinary = str => {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
str = String(str)
|
|
||||||
}
|
|
||||||
return Buffer.from(str, 'hex').toString('binary')
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: Fix for WHATWG URL object not parsing non-standard URL schemes like
|
// HACK: Fix for WHATWG URL object not parsing non-standard URL schemes like
|
||||||
// 'udp:'. Just replace it with 'http:' since we only need a few properties.
|
// 'udp:'. Just replace it with 'http:' since we only need a few properties.
|
||||||
//
|
//
|
||||||
@ -49,8 +36,6 @@ export const parseUrl = str => {
|
|||||||
export default {
|
export default {
|
||||||
DEFAULT_ANNOUNCE_PEERS,
|
DEFAULT_ANNOUNCE_PEERS,
|
||||||
MAX_ANNOUNCE_PEERS,
|
MAX_ANNOUNCE_PEERS,
|
||||||
binaryToHex,
|
|
||||||
hexToBinary,
|
|
||||||
parseUrl,
|
parseUrl,
|
||||||
...common
|
...common
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import { bin2hex } from 'uint8-util'
|
||||||
|
|
||||||
import common from '../common.js'
|
import common from '../common.js'
|
||||||
|
|
||||||
export default parseHttpRequest
|
export default function (req, opts) {
|
||||||
|
|
||||||
function parseHttpRequest (req, opts) {
|
|
||||||
if (!opts) opts = {}
|
if (!opts) opts = {}
|
||||||
const s = req.url.split('?')
|
const s = req.url.split('?')
|
||||||
const params = common.querystringParse(s[1])
|
const params = common.querystringParse(s[1])
|
||||||
@ -14,12 +14,12 @@ function parseHttpRequest (req, opts) {
|
|||||||
if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) {
|
if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) {
|
||||||
throw new Error('invalid info_hash')
|
throw new Error('invalid info_hash')
|
||||||
}
|
}
|
||||||
params.info_hash = common.binaryToHex(params.info_hash)
|
params.info_hash = bin2hex(params.info_hash)
|
||||||
|
|
||||||
if (typeof params.peer_id !== 'string' || params.peer_id.length !== 20) {
|
if (typeof params.peer_id !== 'string' || params.peer_id.length !== 20) {
|
||||||
throw new Error('invalid peer_id')
|
throw new Error('invalid peer_id')
|
||||||
}
|
}
|
||||||
params.peer_id = common.binaryToHex(params.peer_id)
|
params.peer_id = bin2hex(params.peer_id)
|
||||||
|
|
||||||
params.port = Number(params.port)
|
params.port = Number(params.port)
|
||||||
if (!params.port) throw new Error('invalid port')
|
if (!params.port) throw new Error('invalid port')
|
||||||
@ -56,7 +56,7 @@ function parseHttpRequest (req, opts) {
|
|||||||
if (typeof binaryInfoHash !== 'string' || binaryInfoHash.length !== 20) {
|
if (typeof binaryInfoHash !== 'string' || binaryInfoHash.length !== 20) {
|
||||||
throw new Error('invalid info_hash')
|
throw new Error('invalid info_hash')
|
||||||
}
|
}
|
||||||
return common.binaryToHex(binaryInfoHash)
|
return bin2hex(binaryInfoHash)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import ipLib from 'ip'
|
import ipLib from 'ip'
|
||||||
import common from '../common.js'
|
import common from '../common.js'
|
||||||
|
import { equal } from 'uint8-util'
|
||||||
|
|
||||||
export default parseUdpRequest
|
export default function (msg, rinfo) {
|
||||||
|
|
||||||
function parseUdpRequest (msg, rinfo) {
|
|
||||||
if (msg.length < 16) throw new Error('received packet is too short')
|
if (msg.length < 16) throw new Error('received packet is too short')
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@ -13,7 +12,7 @@ function parseUdpRequest (msg, rinfo) {
|
|||||||
type: 'udp'
|
type: 'udp'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common.CONNECTION_ID.equals(params.connectionId)) {
|
if (!equal(common.CONNECTION_ID, params.connectionId)) {
|
||||||
throw new Error('received packet with invalid connection id')
|
throw new Error('received packet with invalid connection id')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import { bin2hex } from 'uint8-util'
|
||||||
|
|
||||||
import common from '../common.js'
|
import common from '../common.js'
|
||||||
|
|
||||||
export default parseWebSocketRequest
|
export default function (socket, opts, params) {
|
||||||
|
|
||||||
function parseWebSocketRequest (socket, opts, params) {
|
|
||||||
if (!opts) opts = {}
|
if (!opts) opts = {}
|
||||||
params = JSON.parse(params) // may throw
|
params = JSON.parse(params) // may throw
|
||||||
|
|
||||||
@ -14,18 +14,18 @@ function parseWebSocketRequest (socket, opts, params) {
|
|||||||
if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) {
|
if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) {
|
||||||
throw new Error('invalid info_hash')
|
throw new Error('invalid info_hash')
|
||||||
}
|
}
|
||||||
params.info_hash = common.binaryToHex(params.info_hash)
|
params.info_hash = bin2hex(params.info_hash)
|
||||||
|
|
||||||
if (typeof params.peer_id !== 'string' || params.peer_id.length !== 20) {
|
if (typeof params.peer_id !== 'string' || params.peer_id.length !== 20) {
|
||||||
throw new Error('invalid peer_id')
|
throw new Error('invalid peer_id')
|
||||||
}
|
}
|
||||||
params.peer_id = common.binaryToHex(params.peer_id)
|
params.peer_id = bin2hex(params.peer_id)
|
||||||
|
|
||||||
if (params.answer) {
|
if (params.answer) {
|
||||||
if (typeof params.to_peer_id !== 'string' || params.to_peer_id.length !== 20) {
|
if (typeof params.to_peer_id !== 'string' || params.to_peer_id.length !== 20) {
|
||||||
throw new Error('invalid `to_peer_id` (required with `answer`)')
|
throw new Error('invalid `to_peer_id` (required with `answer`)')
|
||||||
}
|
}
|
||||||
params.to_peer_id = common.binaryToHex(params.to_peer_id)
|
params.to_peer_id = bin2hex(params.to_peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
params.left = Number(params.left)
|
params.left = Number(params.left)
|
||||||
@ -45,7 +45,7 @@ function parseWebSocketRequest (socket, opts, params) {
|
|||||||
if (typeof binaryInfoHash !== 'string' || binaryInfoHash.length !== 20) {
|
if (typeof binaryInfoHash !== 'string' || binaryInfoHash.length !== 20) {
|
||||||
throw new Error('invalid info_hash')
|
throw new Error('invalid info_hash')
|
||||||
}
|
}
|
||||||
return common.binaryToHex(binaryInfoHash)
|
return bin2hex(binaryInfoHash)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
"@thaunknown/simple-websocket": "^9.1.0",
|
"@thaunknown/simple-websocket": "^9.1.0",
|
||||||
"bencode": "^3.0.3",
|
"bencode": "^3.0.3",
|
||||||
"bittorrent-peerid": "^1.3.3",
|
"bittorrent-peerid": "^1.3.3",
|
||||||
"bn.js": "^5.2.0",
|
|
||||||
"chrome-dgram": "^3.0.6",
|
"chrome-dgram": "^3.0.6",
|
||||||
"clone": "^2.0.0",
|
"clone": "^2.0.0",
|
||||||
"compact2string": "^1.4.1",
|
"compact2string": "^1.4.1",
|
||||||
@ -41,13 +40,13 @@
|
|||||||
"once": "^1.4.0",
|
"once": "^1.4.0",
|
||||||
"queue-microtask": "^1.2.3",
|
"queue-microtask": "^1.2.3",
|
||||||
"random-iterate": "^1.0.1",
|
"random-iterate": "^1.0.1",
|
||||||
"randombytes": "^2.1.0",
|
|
||||||
"run-parallel": "^1.2.0",
|
"run-parallel": "^1.2.0",
|
||||||
"run-series": "^1.1.9",
|
"run-series": "^1.1.9",
|
||||||
"simple-get": "^4.0.0",
|
"simple-get": "^4.0.0",
|
||||||
"simple-peer": "^9.11.0",
|
"simple-peer": "^9.11.0",
|
||||||
"socks": "^2.0.0",
|
"socks": "^2.0.0",
|
||||||
"string2compact": "^2.0.0",
|
"string2compact": "^2.0.0",
|
||||||
|
"uint8-util": "^2.1.9",
|
||||||
"unordered-array-remove": "^1.0.2",
|
"unordered-array-remove": "^1.0.2",
|
||||||
"ws": "^8.0.0"
|
"ws": "^8.0.0"
|
||||||
},
|
},
|
||||||
|
17
server.js
17
server.js
@ -7,6 +7,7 @@ import peerid from 'bittorrent-peerid'
|
|||||||
import series from 'run-series'
|
import series from 'run-series'
|
||||||
import string2compact from 'string2compact'
|
import string2compact from 'string2compact'
|
||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
|
import { hex2bin } from 'uint8-util'
|
||||||
|
|
||||||
import common from './lib/common.js'
|
import common from './lib/common.js'
|
||||||
import Swarm from './lib/server/swarm.js'
|
import Swarm from './lib/server/swarm.js'
|
||||||
@ -488,7 +489,7 @@ class Server extends EventEmitter {
|
|||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
action: params.action === common.ACTIONS.ANNOUNCE ? 'announce' : 'scrape',
|
action: params.action === common.ACTIONS.ANNOUNCE ? 'announce' : 'scrape',
|
||||||
'failure reason': err.message,
|
'failure reason': err.message,
|
||||||
info_hash: common.hexToBinary(params.info_hash)
|
info_hash: hex2bin(params.info_hash)
|
||||||
}), socket.onSend)
|
}), socket.onSend)
|
||||||
|
|
||||||
this.emit('warning', err)
|
this.emit('warning', err)
|
||||||
@ -506,7 +507,7 @@ class Server extends EventEmitter {
|
|||||||
socket.infoHashes.push(params.info_hash)
|
socket.infoHashes.push(params.info_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.info_hash = common.hexToBinary(params.info_hash)
|
response.info_hash = hex2bin(params.info_hash)
|
||||||
|
|
||||||
// WebSocket tracker should have a shorter interval – default: 2 minutes
|
// WebSocket tracker should have a shorter interval – default: 2 minutes
|
||||||
response.interval = Math.ceil(this.intervalMs / 1000 / 5)
|
response.interval = Math.ceil(this.intervalMs / 1000 / 5)
|
||||||
@ -526,8 +527,8 @@ class Server extends EventEmitter {
|
|||||||
action: 'announce',
|
action: 'announce',
|
||||||
offer: params.offers[i].offer,
|
offer: params.offers[i].offer,
|
||||||
offer_id: params.offers[i].offer_id,
|
offer_id: params.offers[i].offer_id,
|
||||||
peer_id: common.hexToBinary(params.peer_id),
|
peer_id: hex2bin(params.peer_id),
|
||||||
info_hash: common.hexToBinary(params.info_hash)
|
info_hash: hex2bin(params.info_hash)
|
||||||
}), peer.socket.onSend)
|
}), peer.socket.onSend)
|
||||||
debug('sent offer to %s from %s', peer.peerId, params.peer_id)
|
debug('sent offer to %s from %s', peer.peerId, params.peer_id)
|
||||||
})
|
})
|
||||||
@ -559,8 +560,8 @@ class Server extends EventEmitter {
|
|||||||
action: 'announce',
|
action: 'announce',
|
||||||
answer: params.answer,
|
answer: params.answer,
|
||||||
offer_id: params.offer_id,
|
offer_id: params.offer_id,
|
||||||
peer_id: common.hexToBinary(params.peer_id),
|
peer_id: hex2bin(params.peer_id),
|
||||||
info_hash: common.hexToBinary(params.info_hash)
|
info_hash: hex2bin(params.info_hash)
|
||||||
}), toPeer.socket.onSend)
|
}), toPeer.socket.onSend)
|
||||||
debug('sent answer to %s from %s', toPeer.peerId, params.peer_id)
|
debug('sent answer to %s from %s', toPeer.peerId, params.peer_id)
|
||||||
|
|
||||||
@ -685,7 +686,7 @@ class Server extends EventEmitter {
|
|||||||
} else if (params.compact === 0) {
|
} else if (params.compact === 0) {
|
||||||
// IPv6 peers are not separate for non-compact responses
|
// IPv6 peers are not separate for non-compact responses
|
||||||
response.peers = response.peers.map(peer => ({
|
response.peers = response.peers.map(peer => ({
|
||||||
'peer id': common.hexToBinary(peer.peerId),
|
'peer id': hex2bin(peer.peerId),
|
||||||
ip: peer.ip,
|
ip: peer.ip,
|
||||||
port: peer.port
|
port: peer.port
|
||||||
}))
|
}))
|
||||||
@ -729,7 +730,7 @@ class Server extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
results.forEach(result => {
|
results.forEach(result => {
|
||||||
response.files[common.hexToBinary(result.infoHash)] = {
|
response.files[hex2bin(result.infoHash)] = {
|
||||||
complete: result.complete || 0,
|
complete: result.complete || 0,
|
||||||
incomplete: result.incomplete || 0,
|
incomplete: result.incomplete || 0,
|
||||||
downloaded: result.complete || 0 // TODO: this only provides a lower-bound
|
downloaded: result.complete || 0 // TODO: this only provides a lower-bound
|
||||||
|
@ -5,6 +5,7 @@ import commonLib from '../lib/common.js'
|
|||||||
import fixtures from 'webtorrent-fixtures'
|
import fixtures from 'webtorrent-fixtures'
|
||||||
import get from 'simple-get'
|
import get from 'simple-get'
|
||||||
import test from 'tape'
|
import test from 'tape'
|
||||||
|
import { hex2bin } from 'uint8-util'
|
||||||
|
|
||||||
const peerId = Buffer.from('01234567890123456789')
|
const peerId = Buffer.from('01234567890123456789')
|
||||||
|
|
||||||
@ -152,8 +153,8 @@ test('udp: MULTI scrape using Client.scrape static method', t => {
|
|||||||
test('server: multiple info_hash scrape (manual http request)', t => {
|
test('server: multiple info_hash scrape (manual http request)', t => {
|
||||||
t.plan(13)
|
t.plan(13)
|
||||||
|
|
||||||
const binaryInfoHash1 = commonLib.hexToBinary(fixtures.leaves.parsedTorrent.infoHash)
|
const binaryInfoHash1 = hex2bin(fixtures.leaves.parsedTorrent.infoHash)
|
||||||
const binaryInfoHash2 = commonLib.hexToBinary(fixtures.alice.parsedTorrent.infoHash)
|
const binaryInfoHash2 = hex2bin(fixtures.alice.parsedTorrent.infoHash)
|
||||||
|
|
||||||
common.createServer(t, 'http', (server, announceUrl) => {
|
common.createServer(t, 'http', (server, announceUrl) => {
|
||||||
const scrapeUrl = announceUrl.replace('/announce', '/scrape')
|
const scrapeUrl = announceUrl.replace('/announce', '/scrape')
|
||||||
@ -189,7 +190,7 @@ test('server: multiple info_hash scrape (manual http request)', t => {
|
|||||||
test('server: all info_hash scrape (manual http request)', t => {
|
test('server: all info_hash scrape (manual http request)', t => {
|
||||||
t.plan(10)
|
t.plan(10)
|
||||||
|
|
||||||
const binaryInfoHash = commonLib.hexToBinary(fixtures.leaves.parsedTorrent.infoHash)
|
const binaryInfoHash = hex2bin(fixtures.leaves.parsedTorrent.infoHash)
|
||||||
|
|
||||||
common.createServer(t, 'http', (server, announceUrl) => {
|
common.createServer(t, 'http', (server, announceUrl) => {
|
||||||
const scrapeUrl = announceUrl.replace('/announce', '/scrape')
|
const scrapeUrl = announceUrl.replace('/announce', '/scrape')
|
||||||
|
Loading…
Reference in New Issue
Block a user