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

Deprecate bittorrent-swarm, inline into webtorrent #704

Merged
merged 9 commits into from Mar 29, 2016
@@ -288,7 +288,6 @@ These are the main modules that make up WebTorrent:
| [bittorrent-dht][bittorrent-dht] | [![][bittorrent-dht-ti]][bittorrent-dht-tu] | [![][bittorrent-dht-ni]][bittorrent-dht-nu] | distributed hash table client
| [bittorrent-peerid][bittorrent-peerid] | [![][bittorrent-peerid-ti]][bittorrent-peerid-tu] | [![][bittorrent-peerid-ni]][bittorrent-peerid-nu] | identify client name/version
| [bittorrent-protocol][bittorrent-protocol] | [![][bittorrent-protocol-ti]][bittorrent-protocol-tu] | [![][bittorrent-protocol-ni]][bittorrent-protocol-nu] | bittorrent protocol stream
| [bittorrent-swarm][bittorrent-swarm] | [![][bittorrent-swarm-ti]][bittorrent-swarm-tu] | [![][bittorrent-swarm-ni]][bittorrent-swarm-nu] | bittorrent connection manager
| [bittorrent-tracker][bittorrent-tracker] | [![][bittorrent-tracker-ti]][bittorrent-tracker-tu] | [![][bittorrent-tracker-ni]][bittorrent-tracker-nu] | bittorrent tracker server/client
| [create-torrent][create-torrent] | [![][create-torrent-ti]][create-torrent-tu] | [![][create-torrent-ni]][create-torrent-nu] | create .torrent files
| [magnet-uri][magnet-uri] | [![][magnet-uri-ti]][magnet-uri-tu] | [![][magnet-uri-ni]][magnet-uri-nu] | parse magnet uris
@@ -323,12 +322,6 @@ These are the main modules that make up WebTorrent:
[bittorrent-protocol-ni]: https://img.shields.io/npm/v/bittorrent-protocol.svg
[bittorrent-protocol-nu]: https://www.npmjs.com/package/bittorrent-protocol

[bittorrent-swarm]: https://github.com/feross/bittorrent-swarm
[bittorrent-swarm-ti]: https://img.shields.io/travis/feross/bittorrent-swarm/master.svg
[bittorrent-swarm-tu]: https://travis-ci.org/feross/bittorrent-swarm
[bittorrent-swarm-ni]: https://img.shields.io/npm/v/bittorrent-swarm.svg
[bittorrent-swarm-nu]: https://www.npmjs.com/package/bittorrent-swarm

[bittorrent-tracker]: https://github.com/feross/bittorrent-tracker
[bittorrent-tracker-ti]: https://img.shields.io/travis/feross/bittorrent-tracker/master.svg
[bittorrent-tracker-tu]: https://travis-ci.org/feross/bittorrent-tracker
@@ -201,10 +201,6 @@ Magnet URI of the torrent (string).
Array of all files in the torrent. See documentation for `File` below to learn what
methods/properties files have.

## `torrent.swarm`

The attached [bittorrent-swarm](https://github.com/feross/bittorrent-swarm) instance.

## `torrent.received`

Total bytes received from peers (*including* invalid data).
@@ -251,10 +247,9 @@ Alias for `client.remove(torrent)`.

## `torrent.addPeer(peer)`

Adds a peer to the underlying
[bittorrent-swarm](https://github.com/feross/bittorrent-swarm) instance. Normally, you
don't need to call `torrent.addPeer()`. WebTorrent will automatically find peers using the
tracker servers or DHT. This is just for manually adding a peer to the client.
Adds a peer to the torrent swarm. Normally, you don't need to call `torrent.addPeer()`.
WebTorrent will automatically find peers using the tracker servers or DHT. This is just
for manually adding a peer to the client.

Returns `true` if peer was added, `false` if peer was blocked by the loaded blocklist.

@@ -264,7 +259,11 @@ instance (for WebRTC peers).

## `torrent.addWebSeed(url)`

Adds a web seed to the [bittorrent-swarm](https://github.com/feross/bittorrent-swarm) instance.
Adds a web seed to the torrent swarm. For more information on BitTorrent web seeds, see
[BEP19](http://www.bittorrent.org/beps/bep_0019.html).

In the browser, web seed servers must have proper CORS (Cross-origin resource sharing)
headers so that data can be fetched across domain.

The `url` argument is the web seed URL.

@@ -0,0 +1,254 @@
var debug = require('debug')('webtorrent:peer')
var Wire = require('bittorrent-protocol')

var WebConn = require('./webconn')

var CONNECT_TIMEOUT_TCP = 25000
var CONNECT_TIMEOUT_WEBRTC = 5000
var HANDSHAKE_TIMEOUT = 25000

/**
* WebRTC peer connections start out connected, because WebRTC peers require an
* "introduction" (i.e. WebRTC signaling), and there's no equivalent to an IP address
* that lets you refer to a WebRTC endpoint.
*/
exports.createWebRTCPeer = function (conn, swarm) {
var peer = new Peer(conn.id, 'webrtc')
peer.conn = conn
peer.swarm = swarm

if (peer.conn.connected) {
peer.onConnect()
} else {
peer.conn.once('connect', function () { peer.onConnect() })
peer.conn.once('error', function (err) { peer.destroy(err) })
peer.startConnectTimeout()
}

return peer
}

/**
* Incoming TCP peers start out connected, because the remote peer connected to the
* listening port of the TCP server. Until the remote peer sends a handshake, we don't
* know what swarm the connection is intended for.
*/
exports.createTCPIncomingPeer = function (conn) {
var addr = conn.remoteAddress + ':' + conn.remotePort
var peer = new Peer(addr, 'tcpIncoming')
peer.conn = conn
peer.addr = addr

peer.onConnect()

return peer
}

/**
* Outgoing TCP peers start out with just an IP address. At some point (when there is an
* available connection), the client can attempt to connect to the address.
*/
exports.createTCPOutgoingPeer = function (addr, swarm) {
var peer = new Peer(addr, 'tcpOutgoing')
peer.addr = addr
peer.swarm = swarm

return peer
}

/**
* Peer that represents a Web Seed (BEP17 / BEP19).
*/
exports.createWebSeedPeer = function (url, parsedTorrent, swarm) {
var peer = new Peer(url, 'webSeed')
peer.swarm = swarm
peer.conn = new WebConn(url, parsedTorrent)

peer.onConnect()

return peer
}

/**
* Peer. Represents a peer in the Swarm.
*
* @param {string} id "ip:port" string, peer id (for WebRTC peers), or url (for Web Seeds)
* @param {string} type the type of the peer
*/
function Peer (id, type) {
var self = this
self.id = id
self.type = type

debug('new Peer %s', id)

self.addr = null
self.conn = null
self.swarm = null
self.wire = null

self.connected = false
self.destroyed = false
self.timeout = null // handshake timeout
self.retries = 0 // outgoing TCP connection retry count

self.sentHandshake = false
}

/**
* Called once the peer is connected (i.e. fired 'connect' event)
* @param {Socket} conn
*/
Peer.prototype.onConnect = function () {
var self = this
if (self.destroyed) return
self.connected = true

debug('Peer %s connected', self.id)

clearTimeout(self.connectTimeout)

var conn = self.conn
conn.once('end', function () {
self.destroy()
})
conn.once('close', function () {
self.destroy()
})
conn.once('finish', function () {
self.destroy()
})
conn.once('error', function (err) {
self.destroy(err)
})

var wire = self.wire = new Wire()
wire.type = self.type
wire.once('end', function () {
self.destroy()
})
wire.once('close', function () {
self.destroy()
})
wire.once('finish', function () {
self.destroy()
})
wire.once('error', function (err) {
self.destroy(err)
})

wire.once('handshake', function (infoHash, peerId) {
self.onHandshake(infoHash, peerId)
})
self.startHandshakeTimeout()

conn.pipe(wire).pipe(conn)
if (self.swarm && !self.sentHandshake) self.handshake()
}

/**
* Called when handshake is received from remote peer.
* @param {string} infoHash
* @param {string} peerId
*/
Peer.prototype.onHandshake = function (infoHash, peerId) {
var self = this
if (!self.swarm) return // `self.swarm` not set yet, so do nothing

if (self.swarm.destroyed) return self.destroy(new Error('swarm already destroyed'))
if (infoHash !== self.swarm.infoHash) {
return self.destroy(new Error('unexpected handshake info hash for this swarm'))
}
if (peerId === self.swarm.peerId) {
return self.destroy(new Error('refusing to handshake with self'))
}

debug('Peer %s got handshake %s', self.id, infoHash)

clearTimeout(self.handshakeTimeout)

self.retries = 0

self.wire.on('download', function (downloaded) {
if (self.destroyed) return
self.swarm.downloaded += downloaded
self.swarm.downloadSpeed(downloaded)
self.swarm.emit('download', downloaded)
})

self.wire.on('upload', function (uploaded) {
if (self.destroyed) return
self.swarm.uploaded += uploaded
self.swarm.uploadSpeed(uploaded)
self.swarm.emit('upload', uploaded)
})

self.swarm.wires.push(self.wire)

var addr = self.addr
if (!addr && self.conn.remoteAddress) {
addr = self.conn.remoteAddress + ':' + self.conn.remotePort
}
self.swarm.emit('wire', self.wire, addr)
// swarm could be destroyed in user's 'wire' event handler
if (!self.swarm || self.swarm.destroyed) return

if (!self.sentHandshake) self.handshake()
}

Peer.prototype.handshake = function () {
var self = this
self.wire.handshake(self.swarm.infoHash, self.swarm.peerId, self.swarm.handshakeOpts)
self.sentHandshake = true
}

Peer.prototype.startConnectTimeout = function () {
var self = this
clearTimeout(self.connectTimeout)
self.connectTimeout = setTimeout(function () {
self.destroy(new Error('connect timeout'))
}, self.type === 'webrtc' ? CONNECT_TIMEOUT_WEBRTC : CONNECT_TIMEOUT_TCP)
if (self.connectTimeout.unref) self.connectTimeout.unref()
}

Peer.prototype.startHandshakeTimeout = function () {
var self = this
clearTimeout(self.handshakeTimeout)
self.handshakeTimeout = setTimeout(function () {
self.destroy(new Error('handshake timeout'))
}, HANDSHAKE_TIMEOUT)
if (self.handshakeTimeout.unref) self.handshakeTimeout.unref()
}

Peer.prototype.destroy = function (err) {
var self = this
if (self.destroyed) return
self.destroyed = true
self.connected = false

debug('destroy %s (error: %s)', self.id, err && (err.message || err))

clearTimeout(self.connectTimeout)
clearTimeout(self.handshakeTimeout)

var swarm = self.swarm
var conn = self.conn
var wire = self.wire

self.conn = null
self.swarm = null
self.wire = null

if (swarm && wire) {
var index = swarm.wires.indexOf(wire)
if (index >= 0) swarm.wires.splice(index, 1)
}
if (conn) {
conn.on('error', noop)
conn.destroy()
}
if (wire) wire.destroy()
if (swarm) swarm.removePeer(self.id)
}

function noop () {}
@@ -3,7 +3,6 @@ module.exports = Server
var debug = require('debug')('webtorrent:server')
var http = require('http')
var mime = require('mime')
var prettierBytes = require('prettier-bytes')
var pump = require('pump')
var rangeParser = require('range-parser')
var url = require('url')
@@ -70,7 +69,7 @@ function Server (torrent, opts) {
res.setHeader('Content-Type', 'text/html')
var listHtml = torrent.files.map(function (file, i) {
return '<li><a download="' + file.name + '" href="/' + i + '">' + file.path + '</a> ' +
'(' + prettierBytes(file.length) + ')</li>'
'(' + file.length + ' bytes)</li>'
}).join('<br>')

var html = '<h1>' + torrent.name + '</h1><ol>' + listHtml + '</ol>'
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.