Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

10 changed files with 313 additions and 559 deletions

View File

@ -1,2 +0,0 @@
.travis.yml
test/

View File

@ -1,3 +1,4 @@
language: node_js language: node_js
node_js: node_js:
- lts/* - "0.11"
- "0.10"

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) Feross Aboukhadijeh and WebTorrent, LLC Copyright (c) 2013 Feross Aboukhadijeh
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View File

@ -1,44 +1,37 @@
# ut_metadata [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] # ut_metadata [![travis](http://img.shields.io/travis/feross/ut_metadata.svg)](https://travis-ci.org/feross/ut_metadata) [![npm](http://img.shields.io/npm/v/ut_metadata.svg)](https://npmjs.org/package/ut_metadata) [![gittip](http://img.shields.io/gittip/feross.svg)](https://www.gittip.com/feross/)
[travis-image]: https://img.shields.io/travis/webtorrent/ut_metadata/master.svg ### Extension for Peers to Send Metadata Files (BEP 9)
[travis-url]: https://travis-ci.org/webtorrent/ut_metadata
[npm-image]: https://img.shields.io/npm/v/ut_metadata.svg
[npm-url]: https://npmjs.org/package/ut_metadata
[downloads-image]: https://img.shields.io/npm/dm/ut_metadata.svg
[downloads-url]: https://npmjs.org/package/ut_metadata
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[standard-url]: https://standardjs.com
### BitTorrent Extension for Peers to Send Metadata Files (BEP 9) [![browser support](https://ci.testling.com/feross/ut_metadata.png)](https://ci.testling.com/feross/ut_metadata)
JavaScript implementation of the [Extension for Peers to Send Metadata Files (BEP 9)](http://www.bittorrent.org/beps/bep_0009.html). Use with [bittorrent-protocol](https://www.npmjs.com/package/bittorrent-protocol). Node.js implementation of the [Extension for Peers to Send Metadata Files (BEP 9)](http://www.bittorrent.org/beps/bep_0009.html).
The purpose of this extension is to allow clients to join a swarm and complete a download without the need of downloading a .torrent file first. This extension instead allows clients to download the metadata from peers. It makes it possible to support magnet links, a link on a web page only containing enough information to join the swarm (the info hash). The purpose of this extension is to allow clients to join a swarm and complete a download without the need of downloading a .torrent file first. This extension instead allows clients to download the metadata from peers. It makes it possible to support magnet links, a link on a web page only containing enough information to join the swarm (the info hash).
Works in the browser with [browserify](http://browserify.org/)! This module is used by [WebTorrent](http://webtorrent.io). Works in the browser with [browserify](http://browserify.org/)! This module is used by [WebTorrent](http://webtorrent.io).
### install ## install
``` ```
npm install ut_metadata npm install ut_metadata
``` ```
### usage ## usage
This package should be used with [bittorrent-protocol](https://www.npmjs.com/package/bittorrent-protocol), which supports a plugin-like system for extending the protocol with additional functionality. This package should be used with [bittorrent-protocol](https://github.com/feross/bittorrent-protocol), which supports a plugin-like system for extending the protocol with additional functionality.
Say you're already using `bittorrent-protocol`. Your code might look something like this: Say you're already using `bittorrent-protocol`. Your code might look something like this:
```js ```js
const Protocol = require('bittorrent-protocol') var Protocol = require('bittorrent-protocol')
const net = require('net') var net = require('net')
net.createServer(socket => { net.createServer(function (socket) {
var wire = new Protocol() var wire = new Protocol()
socket.pipe(wire).pipe(socket) socket.pipe(wire).pipe(socket)
// handle handshake // handle handshake
wire.on('handshake', (infoHash, peerId) => { wire.on('handshake', function (infoHash, peerId) {
wire.handshake(new Buffer('my info hash'), new Buffer('my peer id')) wire.handshake(new Buffer('my info hash'), new Buffer('my peer id'))
}) })
@ -48,25 +41,21 @@ net.createServer(socket => {
To add support for BEP 9, simply modify your code like this: To add support for BEP 9, simply modify your code like this:
```js ```js
const Protocol = require('bittorrent-protocol') net.createServer(function (socket) {
const net = require('net') var wire = new Protocol()
const ut_metadata = require('ut_metadata')
net.createServer(socket => {
const wire = new Protocol()
socket.pipe(wire).pipe(socket) socket.pipe(wire).pipe(socket)
// initialize the extension // initialize the extension
wire.use(ut_metadata()) wire.use(ut_metadata())
// all `ut_metadata` functionality can now be accessed at wire.ut_metadata // all `ut_metadata` functionality can now be accessed at wire.ext('ut_metadata')
// ask the peer to send us metadata // ask the peer to send us metadata
wire.ut_metadata.fetch() wire.ext('ut_metadata').fetch()
// 'metadata' event will fire when the metadata arrives and is verified to be correct! // 'metadata' event will fire when the metadata arrives and is verified to be correct!
wire.ut_metadata.on('metadata', metadata => { wire.ext('ut_metadata').on('metadata', function (metadata) {
// got metadata! // got metdata!
// Note: the event will not fire if the peer does not support ut_metadata, if they // Note: the event will not fire if the peer does not support ut_metadata, if they
// don't have metadata yet either, if they repeatedly send invalid data, or if they // don't have metadata yet either, if they repeatedly send invalid data, or if they
@ -75,51 +64,44 @@ net.createServer(socket => {
// optionally, listen to the 'warning' event if you want to know that metadata is // optionally, listen to the 'warning' event if you want to know that metadata is
// probably not going to arrive for one of the above reasons. // probably not going to arrive for one of the above reasons.
wire.ut_metadata.on('warning', err => { wire.ext('ut_metadata').on('warning', function (err) {
console.log(err.message) console.log(err.message)
}) })
// handle handshake // handle handshake
wire.on('handshake', (infoHash, peerId) => { wire.on('handshake', function (infoHash, peerId) {
wire.handshake(new Buffer('my info hash'), new Buffer('my peer id')) wire.handshake(new Buffer('my info hash'), new Buffer('my peer id'))
}) })
}).listen(6881) }).listen(6881)
``` ```
### api ## methods
#### `ut_metadata([metadata])` ### fetch
Initialize the extension. If you have the torrent metadata (Buffer), pass it into the
`ut_metadata` constructor so it's made available to the peer.
```js
const metadata = fs.readFileSync(__dirname + '/file.torrent')
wire.use(ut_metadata(metadata))
```
#### `ut_metadata.fetch()`
Ask the peer to send metadata. Ask the peer to send metadata.
#### `ut_metadata.cancel()` ```js
wire.ext('ut_metadata').fetch()
```
### cancel
Stop asking the peer to send metadata. Stop asking the peer to send metadata.
#### `ut_metadata.setMetadata(metadata)` ```js
wire.ext('ut_metadata').cancel()
```
Set the metadata. If you didn't have the metadata at the time `ut_metadata` was ### event: 'metadata'
initialized, but you end up getting it from another peer (or somewhere else), you should
call `setMetadata` so the metadata will be available to the peer.
#### `ut_metadata.on('metadata', function (metadata) {})`
Fired when metadata is available and verified to be correct. Called with a single Fired when metadata is available and verified to be correct. Called with a single
parameter of type Buffer. parameter of type Buffer.
```js ```js
wire.ut_metadata.on('metadata', metadata => { wire.ext('ut_metadata').on('metadata', function (metadata) {
console.log(Buffer.isBuffer(metadata)) // true console.log(Buffer.isBuffer(metadata)) // true
}) })
``` ```
@ -128,19 +110,19 @@ Note: the event will not fire if the peer does not support ut_metadata, if they
don't have metadata yet either, if they repeatedly send invalid data, or if they don't have metadata yet either, if they repeatedly send invalid data, or if they
simply don't respond. simply don't respond.
#### `ut_metadata.on('warning', function (err) {})` ### event: 'warning'
Fired if: Fired if:
- the peer does not support ut_metadata - the peer does not support ut_metadata
- the peer doesn't have metadata yet - the peer doesn't have metadata yet
- the peer repeatedly sent invalid data - the repeatedly sent invalid data
```js ```js
wire.ut_metadata.on('warning', err => { wire.ext('ut_metadata').on('warning', function (err) {
console.log(err.message) console.log(err.message)
}) })
``` ```
### license ## license
MIT. Copyright (c) [Feross Aboukhadijeh](https://feross.org) and [WebTorrent, LLC](https://webtorrent.io). MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org).

194
index.js
View File

@ -1,68 +1,61 @@
/*! ut_metadata. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */ // TODO: add verification
const { EventEmitter } = require('events')
const bencode = require('bencode')
const BitField = require('bitfield').default
const debug = require('debug')('ut_metadata')
const sha1 = require('simple-sha1')
const MAX_METADATA_SIZE = 1E7 // 10 MB var BitField = require('BitField')
const BITFIELD_GROW = 1E3 var bncode = require('bncode')
const PIECE_LENGTH = 1 << 14 // 16 KiB var EventEmitter = require('events').EventEmitter
var inherits = require('inherits')
module.exports = metadata => { var PIECE_LENGTH = 16 * 1024
class utMetadata extends EventEmitter {
constructor (wire) { module.exports = function (metadata) {
super()
inherits(ut_metadata, EventEmitter)
function ut_metadata (wire) {
EventEmitter.call(this)
this._wire = wire this._wire = wire
this._fetching = false
this._metadataComplete = false this._metadataComplete = false
this._metadataSize = null this._metadataSize = null
// how many reject messages to tolerate before quitting this._remainingRejects = null // how many reject messages to tolerate before quitting
this._remainingRejects = null this._fetching = false
this._bitfield = new BitField(0)
// The largest torrent file that I know of is ~1-2MB, which is ~100 if (metadata) {
// pieces. Therefore, cap the bitfield to 10x that (1000 pieces) so a this._gotMetadata(metadata)
// malicious peer can't make it grow to fill all memory.
this._bitfield = new BitField(0, { grow: BITFIELD_GROW })
if (Buffer.isBuffer(metadata)) {
this.setMetadata(metadata)
} }
} }
onHandshake (infoHash, peerId, extensions) { Object.defineProperty(ut_metadata.prototype, '_numPieces', {
this._infoHash = infoHash get: function () {
return Math.ceil(this._metadataSize / PIECE_LENGTH)
}
})
ut_metadata.prototype.onHandshake = function () {
} }
onExtendedHandshake (handshake) { ut_metadata.prototype.onExtendedHandshake = function (handshake) {
if (!handshake.m || !handshake.m.ut_metadata) { if (!handshake.m.ut_metadata) {
return this.emit('warning', new Error('Peer does not support ut_metadata')) return this.emit('warning', new Error('Peer does not support ut_metadata'))
} }
if (!handshake.metadata_size) { if (!handshake.metadata_size) {
return this.emit('warning', new Error('Peer does not have metadata')) return this.emit('warning', new Error('Peer does not have metadata'))
} }
if (typeof handshake.metadata_size !== 'number' ||
MAX_METADATA_SIZE < handshake.metadata_size ||
handshake.metadata_size <= 0) {
return this.emit('warning', new Error('Peer gave invalid metadata size'))
}
this._metadataSize = handshake.metadata_size this._metadataSize = handshake.metadata_size
this._numPieces = Math.ceil(this._metadataSize / PIECE_LENGTH) if (this._fetching) {
this._remainingRejects = this._numPieces * 2
this._requestPieces() this._requestPieces()
} }
}
onMessage (buf) { ut_metadata.prototype.onMessage = function (buf) {
let dict var dict, trailer
let trailer
try { try {
const str = buf.toString() var str = buf.toString()
const trailerIndex = str.indexOf('ee') + 2 var trailerIndex = str.indexOf('ee') + 2
dict = bencode.decode(str.substring(0, trailerIndex)) dict = bncode.decode(str.substring(0, trailerIndex))
trailer = buf.slice(trailerIndex) trailer = buf.slice(trailerIndex)
} catch (err) { } catch (err) {
// drop invalid messages // drop invalid messages
@ -88,11 +81,8 @@ module.exports = metadata => {
} }
} }
/** // Expose high-level, friendly API (fetch/cancel)
* Ask the peer to send metadata. ut_metadata.prototype.fetch = function () {
* @public
*/
fetch () {
if (this._metadataComplete) { if (this._metadataComplete) {
return return
} }
@ -102,85 +92,57 @@ module.exports = metadata => {
} }
} }
/** ut_metadata.prototype.cancel = function () {
* Stop asking the peer to send metadata.
* @public
*/
cancel () {
this._fetching = false this._fetching = false
} }
setMetadata (metadata) { ut_metadata.prototype._gotMetadata = function (_metadata) {
if (this._metadataComplete) return true
debug('set metadata')
// if full torrent dictionary was passed in, pull out just `info` key
try {
const info = bencode.decode(metadata).info
if (info) {
metadata = bencode.encode(info)
}
} catch (err) {}
// check hash
if (this._infoHash && this._infoHash !== sha1.sync(metadata)) {
return false
}
this.cancel() this.cancel()
this.metadata = _metadata
this.metadata = metadata
this._metadataComplete = true this._metadataComplete = true
this._metadataSize = this.metadata.length this._metadataSize = this.metadata.length
this._wire.extendedHandshake.metadata_size = this._metadataSize this._wire.extendedHandshake.metadata_size = this._metadataSize
this.emit('metadata', this.metadata)
this.emit('metadata', bencode.encode({
info: bencode.decode(this.metadata)
}))
return true
} }
_send (dict, trailer) { ut_metadata.prototype._send = function (dict, trailer) {
let buf = bencode.encode(dict) var buf = bncode.encode(dict)
if (Buffer.isBuffer(trailer)) { if (Buffer.isBuffer(trailer)) {
buf = Buffer.concat([buf, trailer]) buf = Buffer.concat([buf, trailer])
} }
this._wire.extended('ut_metadata', buf) this._wire.extended('ut_metadata', buf)
} }
_request (piece) { ut_metadata.prototype._request = function (piece) {
this._send({ msg_type: 0, piece }) this._send({ msg_type: 0, piece: piece })
} }
_data (piece, buf, totalSize) { ut_metadata.prototype._data = function (piece, buf, totalSize) {
const msg = { msg_type: 1, piece } var msg = { msg_type: 1, piece: piece }
if (typeof totalSize === 'number') { if (typeof totalSize === 'number') {
msg.total_size = totalSize msg.total_size = totalSize
} }
this._send(msg, buf) this._send(msg, buf)
} }
_reject (piece) { ut_metadata.prototype._reject = function (piece) {
this._send({ msg_type: 2, piece }) this._send({ msg_type: 2, piece: piece })
} }
_onRequest (piece) { ut_metadata.prototype._onRequest = function (piece) {
if (!this._metadataComplete) { if (this._metadataComplete) {
this._reject(piece) var start = piece * PIECE_LENGTH
return var end = start + PIECE_LENGTH
}
const start = piece * PIECE_LENGTH
let end = start + PIECE_LENGTH
if (end > this._metadataSize) { if (end > this._metadataSize) {
end = this._metadataSize end = this._metadataSize
} }
const buf = this.metadata.slice(start, end) var buf = this.metadata.slice(start, end)
this._data(piece, buf, this._metadataSize) this._data(piece, buf, this._metadataSize)
} }
}
_onData (piece, buf, totalSize) { ut_metadata.prototype._onData = function (piece, buf, totalSize) {
if (buf.length > PIECE_LENGTH || !this._fetching) { if (buf.length > PIECE_LENGTH) {
return return
} }
buf.copy(this.metadata, piece * PIECE_LENGTH) buf.copy(this.metadata, piece * PIECE_LENGTH)
@ -188,10 +150,9 @@ module.exports = metadata => {
this._checkDone() this._checkDone()
} }
_onReject (piece) { ut_metadata.prototype._onReject = function (piece) {
if (this._remainingRejects > 0 && this._fetching) { if (this._remainingRejects > 0 && this._fetching) {
// If we haven't been rejected too much, // If we haven't been rejected too much, then try to request the piece again
// then try to request the piece again
this._request(piece) this._request(piece)
this._remainingRejects -= 1 this._remainingRejects -= 1
} else { } else {
@ -199,17 +160,18 @@ module.exports = metadata => {
} }
} }
_requestPieces () { ut_metadata.prototype._requestPieces = function () {
if (!this._fetching) return this.metadata = new Buffer(this._metadataSize)
this.metadata = Buffer.alloc(this._metadataSize) this._remainingRejects = this._numPieces
for (let piece = 0; piece < this._numPieces; piece++) {
for (var piece = 0; piece < this._numPieces; piece++) {
this._request(piece) this._request(piece)
} }
} }
_checkDone () { ut_metadata.prototype._checkDone = function () {
let done = true var done = true
for (let piece = 0; piece < this._numPieces; piece++) { for (var piece = 0; piece < this._numPieces; piece++) {
if (!this._bitfield.get(piece)) { if (!this._bitfield.get(piece)) {
done = false done = false
break break
@ -217,28 +179,10 @@ module.exports = metadata => {
} }
if (!done) return if (!done) return
// attempt to set metadata -- may fail sha1 check // TODO: verify
const success = this.setMetadata(this.metadata)
if (!success) { this._gotMetadata(this.metadata)
this._failedMetadata()
}
} }
_failedMetadata () { return ut_metadata
// reset bitfield & try again
this._bitfield = new BitField(0, { grow: BITFIELD_GROW })
this._remainingRejects -= this._numPieces
if (this._remainingRejects > 0) {
this._requestPieces()
} else {
this.emit('warning', new Error('Peer sent invalid metadata'))
}
}
}
// Name of the bittorrent-protocol extension
utMetadata.prototype.name = 'ut_metadata'
return utMetadata
} }

View File

@ -1,60 +1,55 @@
{ {
"name": "ut_metadata", "name": "ut_metadata",
"description": "Extension for Peers to Send Metadata Files (BEP 9)", "description": "Extension for Peers to Send Metadata Files (BEP 9)",
"version": "3.5.2", "version": "1.0.0",
"author": { "author": {
"name": "WebTorrent LLC", "name": "Feross Aboukhadijeh",
"email": "feross@webtorrent.io", "email": "feross@feross.org",
"url": "https://webtorrent.io" "url": "http://feross.org/"
}, },
"bugs": { "bugs": {
"url": "https://github.com/webtorrent/ut_metadata/issues" "url": "https://github.com/feross/ut_metadata/issues"
}, },
"dependencies": { "dependencies": {
"bencode": "^2.0.1", "bncode": "^0.5.0",
"bitfield": "^4.0.0", "bitfield": "^0.2.0",
"debug": "^4.2.0", "inherits": "^2.0.1"
"simple-sha1": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"bittorrent-protocol": "^3.1.2", "tape": "2.x",
"brfs": "^2.0.2", "bittorrent-protocol": "^1.0.0",
"standard": "*", "parse-torrent": "^0.6.0"
"tape": "^5.0.1",
"webtorrent-fixtures": "^1.7.3"
}, },
"homepage": "http://webtorrent.io",
"keywords": [ "keywords": [
"Extension for Peers to Send Metadata Files",
"bep",
"bep 9",
"bep_0009",
"bittorrent",
"metadata",
"p2p",
"torrent", "torrent",
"ut_metadata" "bittorrent",
"ut_metadata",
"bep_0009",
"bep 9",
"bep",
"9",
"Extension for Peers to Send Metadata Files",
"metadata",
"p2p"
], ],
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/webtorrent/ut_metadata.git" "url": "git://github.com/feross/ut_metadata.git"
}, },
"scripts": { "scripts": {
"test": "standard && tape test/*.js" "test": "tape test/*.js"
}, },
"funding": [ "testling": {
{ "files": "test/*.js",
"type": "github", "browsers": [
"url": "https://github.com/sponsors/feross" "ie/9..latest",
}, "chrome/25..latest",
{ "firefox/20..latest",
"type": "patreon", "safari/6..latest",
"url": "https://www.patreon.com/feross" "opera/15.0..latest"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
] ]
}
} }

View File

@ -1,34 +1,35 @@
const { leavesMetadata } = require('webtorrent-fixtures') var fs = require('fs')
const bencode = require('bencode') var Protocol = require('bittorrent-protocol')
const Protocol = require('bittorrent-protocol') var ut_metadata = require('../')
const test = require('tape') var test = require('tape')
const utMetadata = require('../')
test('wire.use(utMetadata())', t => { // Used in multiple tests
const wire = new Protocol() var metadata = fs.readFileSync(__dirname + '/torrents/leaves-magnet.torrent')
test('wire.use(ut_metadata())', function (t) {
var wire = new Protocol()
wire.pipe(wire) wire.pipe(wire)
wire.use(utMetadata()) wire.use(ut_metadata())
t.ok(wire.ut_metadata) t.ok(wire.ext('ut_metadata'))
t.ok(wire.ut_metadata.fetch) t.ok(wire.ext('ut_metadata').fetch)
t.ok(wire.ut_metadata.cancel) t.ok(wire.ext('ut_metadata').gotMetadata)
t.notOk(wire.ut_metadata.metadata) t.ok(wire.ext('ut_metadata').cancel)
t.notOk(wire.ext('ut_metadata').metadata)
t.end() t.end()
}) })
test('wire.use(utMetadata(metadata))', t => { test('wire.use(ut_metadata(metadata))', function (t) {
const wire = new Protocol() var wire = new Protocol()
wire.pipe(wire) wire.pipe(wire)
wire.use(utMetadata(leavesMetadata.torrent)) wire.use(ut_metadata(metadata))
t.ok(wire.ut_metadata) t.ok(wire.ext('ut_metadata'))
t.ok(wire.ut_metadata.fetch) t.ok(wire.ext('ut_metadata').fetch)
t.ok(wire.ut_metadata.cancel) t.ok(wire.ext('ut_metadata').gotMetadata)
t.equal( t.ok(wire.ext('ut_metadata').cancel)
wire.ut_metadata.metadata.toString('hex'), t.equal(wire.ext('ut_metadata').metadata, metadata)
bencode.encode(bencode.decode(leavesMetadata.torrent).info).toString('hex')
)
t.end() t.end()
}) })

View File

@ -1,39 +1,37 @@
const { leavesMetadata, sintel } = require('webtorrent-fixtures') var fs = require('fs')
const bencode = require('bencode') var parseTorrent = require('parse-torrent')
const Protocol = require('bittorrent-protocol') var Protocol = require('bittorrent-protocol')
const test = require('tape') var ut_metadata = require('../')
const utMetadata = require('../') var test = require('tape')
const id1 = Buffer.from('01234567890123456789') // Used in multiple tests
const id2 = Buffer.from('12345678901234567890') var metadata = fs.readFileSync(__dirname + '/torrents/leaves-magnet.torrent')
var parsedTorrent = parseTorrent(metadata)
var id1 = new Buffer('01234567890123456789')
var id2 = new Buffer('12345678901234567890')
test('fetch()', t => { test('fetch()', function (t) {
t.plan(3) t.plan(3)
const wire1 = new Protocol() var wire1 = new Protocol()
const wire2 = new Protocol() var wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1) wire1.pipe(wire2).pipe(wire1)
wire1.use(utMetadata(leavesMetadata.torrent)) // wire1 already has metadata wire1.use(ut_metadata(metadata)) // wire1 already has metadata
wire2.use(utMetadata()) // wire2 does not wire2.use(ut_metadata()) // wire2 does not
wire2.ut_metadata.fetch() wire2.ext('ut_metadata').fetch()
wire2.ut_metadata.on('metadata', _metadata => { wire2.ext('ut_metadata').on('metadata', function (_metadata) {
// got metadata! // got metadata!
t.equal( t.deepEqual(_metadata, metadata)
_metadata.toString('hex'),
bencode.encode({
info: bencode.decode(leavesMetadata.torrent).info
}).toString('hex')
)
}) })
wire2.on('handshake', (infoHash, peerId, extensions) => { wire2.on('handshake', function (infoHash, peerId, extensions) {
wire2.handshake(leavesMetadata.parsedTorrent.infoHash, id2) wire2.handshake(parsedTorrent.infoHash, id2)
}) })
wire2.on('extended', ext => { wire2.on('extended', function (ext) {
if (ext === 'handshake') { if (ext === 'handshake') {
t.pass('got extended handshake') t.pass('got extended handshake')
} else if (ext === 'ut_metadata') { } else if (ext === 'ut_metadata') {
@ -46,40 +44,40 @@ test('fetch()', t => {
} }
}) })
wire1.handshake(leavesMetadata.parsedTorrent.infoHash, id1) wire1.handshake(parsedTorrent.infoHash, id1)
}) })
test('fetch() from peer without metadata', t => { test('fetch() from peer without metadata', function (t) {
t.plan(2) t.plan(2)
const wire1 = new Protocol() var wire1 = new Protocol()
const wire2 = new Protocol() var wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1) wire1.pipe(wire2).pipe(wire1)
wire1.use(utMetadata()) // neither wire has metadata wire1.use(ut_metadata()) // neither wire has metadata
wire2.use(utMetadata()) wire2.use(ut_metadata())
wire2.ut_metadata.fetch() wire2.ext('ut_metadata').fetch()
wire2.ut_metadata.on('metadata', () => { wire2.ext('ut_metadata').on('metadata', function () {
t.fail('No "metadata" event should fire') t.fail('No "metadata" event should fire')
}) })
wire1.ut_metadata.onMessage = () => { wire1.ext('ut_metadata').onMessage = function () {
t.fail('No messages should be sent to wire1') t.fail('No messages should be sent to wire1')
// No messages should be sent because wire1 never sent metadata_size // No messages should be sent because wire1 never sent metadata_size in the
// in the extended handshake, so he doesn't have metadata // extended handshake, so he doesn't have metadata
} }
wire2.ut_metadata.on('warning', () => { wire2.ext('ut_metadata').on('warning', function (err) {
t.pass('got warning about peer missing metadata') t.pass('got warning about peer missing metadata')
}) })
wire2.on('handshake', (infoHash, peerId, extensions) => { wire2.on('handshake', function (infoHash, peerId, extensions) {
wire2.handshake(leavesMetadata.parsedTorrent.infoHash, id2) wire2.handshake(parsedTorrent.infoHash, id2)
}) })
wire2.on('extended', ext => { wire2.on('extended', function (ext) {
if (ext === 'handshake') { if (ext === 'handshake') {
t.pass('got extended handshake') t.pass('got extended handshake')
} else if (ext === 'ut_metadata') { } else if (ext === 'ut_metadata') {
@ -89,163 +87,5 @@ test('fetch() from peer without metadata', t => {
} }
}) })
wire1.handshake(leavesMetadata.parsedTorrent.infoHash, id1) wire1.handshake(parsedTorrent.infoHash, id1)
})
test('fetch when peer gets metadata later (setMetadata)', t => {
t.plan(3)
const wire1 = new Protocol()
const wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1)
wire1.use(utMetadata()) // wire1 starts without metadata
process.nextTick(() => {
// wire1 gets metadata later
wire1.ut_metadata.setMetadata(leavesMetadata.torrent)
process.nextTick(() => {
// wire2 does not start with metadata,
// but connects to wire1 after it gets metadata
wire2.use(utMetadata())
wire2.ut_metadata.fetch()
wire2.ut_metadata.on('metadata', _metadata => {
// got metadata!
t.equal(
_metadata.toString('hex'),
bencode.encode({
info: bencode.decode(leavesMetadata.torrent).info
}).toString('hex')
)
})
wire2.on('handshake', (infoHash, peerId, extensions) => {
wire2.handshake(leavesMetadata.parsedTorrent.infoHash, id2)
})
wire2.on('extended', ext => {
if (ext === 'handshake') {
t.pass('got extended handshake')
} else if (ext === 'ut_metadata') {
t.pass('got extended ut_metadata message')
// this is emitted for consistency's sake, but it's ignored
// by the user since the ut_metadata package handles the
// complexities internally
} else {
t.fail('unexpected handshake type')
}
})
wire1.handshake(leavesMetadata.parsedTorrent.infoHash, id1)
})
})
})
test('fetch() large torrent', t => {
t.plan(4)
const wire1 = new Protocol()
const wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1)
wire1.use(utMetadata(sintel.torrent)) // wire1 already has metadata
wire2.use(utMetadata()) // wire2 does not
wire2.ut_metadata.fetch()
wire2.ut_metadata.on('metadata', _metadata => {
// got metadata!
t.equal(
_metadata.toString('hex'),
bencode.encode({
info: bencode.decode(sintel.torrent).info
}).toString('hex')
)
})
wire2.on('handshake', (infoHash, peerId, extensions) => {
wire2.handshake(sintel.parsedTorrent.infoHash, id2)
})
wire2.on('extended', ext => {
if (ext === 'handshake') {
t.pass('got extended handshake')
} else if (ext === 'ut_metadata') {
// note: this should get called twice,
// once for each block of the sintel metadata
t.pass('got extended ut_metadata message')
// this is emitted for consistency's sake, but it's ignored
// by the user since the ut_metadata package handles the
// complexities internally
} else {
t.fail('unexpected handshake type')
}
})
wire1.handshake(sintel.parsedTorrent.infoHash, id1)
})
test('discard invalid metadata', t => {
t.plan(1)
const wire1 = new Protocol()
const wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1)
const invalidMetadata = leavesMetadata.torrent.slice(0)
invalidMetadata[55] = 65 // mess up a byte in the info block
wire1.use(utMetadata(invalidMetadata))
wire2.use(utMetadata())
wire2.ut_metadata.fetch()
wire2.ut_metadata.on('metadata', () => {
t.fail('No "metadata" event should fire')
})
wire2.ut_metadata.on('warning', () => {
t.pass('got warning because peer sent reject too much')
})
wire2.on('handshake', (infoHash, peerId, extensions) => {
wire2.handshake(leavesMetadata.parsedTorrent.infoHash, id2)
})
wire1.handshake(leavesMetadata.parsedTorrent.infoHash, id1)
})
test('stop receiving data after cancel', t => {
t.plan(1)
const wire1 = new Protocol()
const wire2 = new Protocol()
wire1.pipe(wire2).pipe(wire1)
wire1.use(utMetadata(sintel.torrent))
wire2.use(utMetadata())
wire2.ut_metadata.once('metadata', () => {
t.fail('No "metadata" event should fire')
})
wire2.once('handshake', (infoHash, peerId, extensions) => {
wire2.handshake(sintel.parsedTorrent.infoHash, id2)
wire2.ut_metadata.fetch()
})
wire2.on('extended', ext => {
if (ext === 'ut_metadata') {
wire2.ut_metadata.cancel()
}
})
wire1.handshake(sintel.parsedTorrent.infoHash, id1)
process.nextTick(() => t.pass('no metadata received'))
}) })

View File

@ -1,7 +0,0 @@
{
"name": "test",
"version": "0.0.0",
"browserify": {
"transform": ["brfs"]
}
}

Binary file not shown.