• Stars
    star
    342
  • Rank 123,697 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 11 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple, robust, BitTorrent peer wire protocol implementation

bittorrent-protocol travis npm downloads javascript style guide

Simple, robust, BitTorrent wire protocol implementation

Node.js implementation of the BitTorrent peer wire protocol. The protocol is the main communication layer for BitTorrent file transfer.

Also works in the browser with browserify! This module is used by WebTorrent.

install

npm install bittorrent-protocol

usage

The protocol is implemented as a duplex stream, so all you have to do is pipe to and from it.

duplex streams a.pipe(b).pipe(a)
duplex streams a.pipe(b).pipe(a)

(Images from the "harnessing streams" talk by substack.)

import Protocol from 'bittorrent-protocol'
import net from 'net'

net.createServer(socket => {
	const wire = new Protocol()

	// pipe to and from the protocol
	socket.pipe(wire).pipe(socket)

	wire.on('handshake', (infoHash, peerId) => {
    // receive a handshake (infoHash and peerId are hex strings)

		// lets emit a handshake of our own as well
		wire.handshake('my info hash (hex)', 'my peer id (hex)')
	})

	wire.on('unchoke', () => {
		console.log('peer is no longer choking us: ' + wire.peerChoking)
	})
}).listen(6881)

methods

handshaking

Send and receive a handshake from the peer. This is the first message.

// send a handshake to the peer
wire.handshake(infoHash, peerId, { dht: true })
wire.on('handshake', (infoHash, peerId, extensions) => {
	// receive a handshake (infoHash and peerId are hex strings)
  console.log(extensions.dht) // supports DHT (BEP-0005)
  console.log(extensions.extended) // supports extension protocol (BEP-0010)
})

For wire.handshake(), the infoHash and the peerId should be 20 bytes (hex-encoded string or Buffer).

choking

Check if you or the peer is choking.

wire.peerChoking // is the peer choking us?
wire.amChoking // are we choking the peer?

wire.on('choke', () => {
	// the peer is now choking us
})
wire.on('unchoke', () => {
	// peer is no longer choking us
})

interested

See if you or the peer is interested.

wire.peerInterested // is the peer interested in us?
wire.amInterested // are we interested in the peer?

wire.on('interested', () => {
	// peer is now interested
})
wire.on('uninterested', () => {
	// peer is no longer interested
})

bitfield

Exchange piece information with the peer.

// send a bitfield to the peer
wire.bitfield(buffer)
wire.on('bitfield', bitfield => {
	// bitfield received from the peer
})

// send a have message indicating that you have a piece
wire.have(pieceIndex)
wire.on('have', pieceIndex => {
	// peer has sent you a have message
})

You can always see which pieces the peer has

wire.peerPieces.get(i) // returns true if peer has piece i

wire.peerPieces is a BitField, see docs.

requests

Send and respond to requests for pieces.

// request a block from a peer
wire.request(pieceIndex, offset, length, (err, block) => {
	if (err) {
		// there was an error (peer has started choking us etc)
		return
	}
	// got block
})

// cancel a request to a peer
wire.cancel(pieceIndex, offset, length)

// receive a request from a peer
wire.on('request', (pieceIndex, offset, length, callback) => {
	// ... read block ...
	callback(null, block) // respond back to the peer
})

wire.requests     // list of requests we currently have pending {piece, offset, length}
wire.peerRequests // list of requests the peer currently have pending {piece, offset, length}

You can set a request timeout if you want to.

wire.setTimeout(5000) // head request should take a most 5s to finish

If the timeout is triggered the request callback is called with an error and a timeout event is emitted.

dht and port

You can set the extensions flag dht in the handshake to true if you participate in the torrent dht. Afterwards you can send your dht port.

// send your port to the peer
wire.port(dhtPort)
wire.on('port', dhtPort => {
	// peer has sent a port to us
})

You can check to see if the peer supports extensions.

wire.peerExtensions.dht // supports DHT (bep_0005)
wire.peerExtensions.extended // supports extended messages (bep_0005)

keep-alive

You can enable the keep-alive ping (triggered every 60s).

// starts the keep alive
wire.setKeepAlive(true)
wire.on('keep-alive', () => {
	// peer sent a keep alive - just ignore it
})

fast extension (BEP 6)

This module has built-in support for the BitTorrent Fast Extension (BEP 6).

The Fast Extension introduces several messages to make the protocol more efficient: have-none, have-all, suggest, reject, and allowed-fast.

wire.handshake(infoHash, peerId, { fast: true })

wire.hasFast // true if Fast Extension is available, required to call the following methods

wire.haveNone() // instead of wire.bitfield(buffer) with an all-zero buffer
wire.on('have-none', () => {
  // instead of bitfield with an all-zero buffer
})

wire.haveAll() // instead of wire.bitfield(buffer) with an all-one buffer
wire.on('have-all', () => {
  // instead of bitfield with an all-one buffer
})

wire.suggest(pieceIndex) // suggest requesting a piece to the peer
wire.on('suggest', (pieceIndex) => {
  // peer suggests requesting piece
})

wire.on('allowed-fast', (pieceIndex) => {
  // piece may be obtained from peer while choked
})

wire.peerAllowedFastSet // list of allowed-fast pieces

// Note rejection is handled automatically on choke or request error
wire.reject(pieceIndex, offset, length) // reject a request
wire.on('reject', (pieceIndex, offset, length) => {
  // peer rejected a request
})

extension protocol (BEP 10)

This module has built-in support for the BitTorrent Extension Protocol (BEP 10).

The intention of BEP 10 is to provide a simple and thin transport for extensions to the bittorrent protocol. Most extensions to the protocol use BEP 10 so they can add new features to the protocol without interfering with the standard bittorrent protocol or clients that don't support the new extension.

An example of a BitTorrent extension that uses BEP 10 is ut_metadata (BEP 9), the extension that allows magnet uris to work.

wire.extended(code, buffer)

This package, bittorrent-protocol, also provides an extension API to make it easy to add extensions to this module using the "extension protocol" (BEP 10). For example, to support ut_metadata (BEP 9), you need only install the ut_metadata npm module and call wire.use(). See the Extension API section for more information.

transfer stats

Check how many bytes you have uploaded and download, and current speed

wire.uploaded // number of bytes uploaded
wire.downloaded // number of bytes downloaded

wire.uploadSpeed() // upload speed - bytes per second
wire.downloadSpeed() // download speed - bytes per second

wire.on('download', numberOfBytes => {
  ...
})
wire.on('upload', numberOfBytes => {
  ...
})

extension api

This package supports a simple extension API so you can extend the default protocol functionality with common protocol extensions like ut_metadata (magnet uris).

Here are the bittorrent-protocol extensions that we know about:

  • ut_metadata - Extension for Peers to Send Metadata Files (BEP 9)
  • ut_pex - Extension for Peer Discovery (PEX)
  • Add yours here! Send a pull request!

In short, an extension can register itself with at a certain name, which will be added to the extended protocol handshake sent to the remote peer. Extensions can also hook events like 'handshake' and 'extended'. To use an extension, simply require it and call wire.use().

Here is an example of the ut_metadata extension being used with bittorrent-protocol:

import Protocol from 'bittorrent-protocol'
import net from 'net'
import ut_metadata from 'ut_metadata'

net.createServer(socket => {
  const wire = new Protocol()
  socket.pipe(wire).pipe(socket)

  // initialize the extension
  wire.use(ut_metadata())

  // all `ut_metadata` functionality can now be accessed at wire.ut_metadata

  // ask the peer to send us metadata
  wire.ut_metadata.fetch()

  // 'metadata' event will fire when the metadata arrives and is verified to be correct!
  wire.ut_metadata.on('metadata', metadata => {
    // got metadata!

    // 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
    // simply don't respond.
  })

  // 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.
  wire.ut_metadata.on('warning', err => {
    console.log(err.message)
  })

  // handle handshake
  wire.on('handshake', (infoHash, peerId) => {
    // receive a handshake (infoHash and peerId are hex strings)
    wire.handshake(new Buffer('my info hash'), new Buffer('my peer id'))
  })

}).listen(6881)

If you want to write your own extension, take a look at the ut_metadata index.js file to see how it's done.

license

MIT. Copyright (c) Feross Aboukhadijeh, Mathias Buus, and WebTorrent, LLC.

More Repositories

1

webtorrent

⚑️ Streaming torrent client for the web
JavaScript
29,385
star
2

webtorrent-desktop

❀️ Streaming torrent app for Mac, Windows, and Linux
JavaScript
9,659
star
3

instant.io

πŸš€ Streaming file transfer over WebTorrent (torrents on the web)
JavaScript
3,423
star
4

bittorrent-tracker

🌊 Simple, robust, BitTorrent tracker (client & server) implementation
JavaScript
1,744
star
5

bittorrent-dht

πŸ•Έ Simple, robust, BitTorrent DHT implementation
JavaScript
1,216
star
6

webtorrent-cli

WebTorrent, the streaming torrent client. For the command line.
JavaScript
1,154
star
7

webtorrent-hybrid

WebTorrent (with WebRTC support in Node.js)
JavaScript
518
star
8

parse-torrent

Parse a torrent identifier (magnet uri, .torrent file, info hash)
JavaScript
438
star
9

create-torrent

Create .torrent files
JavaScript
342
star
10

magnet-uri

Parse a magnet URI and return an object of keys/values
JavaScript
221
star
11

torrent-discovery

Discover BitTorrent and WebTorrent peers
JavaScript
216
star
12

node-bencode

bencode de/encoder for nodejs
JavaScript
165
star
13

webtorrent.io

The code that runs the WebTorrent website
JavaScript
125
star
14

ut_metadata

BitTorrent Extension for Peers to Send Metadata Files (BEP 9)
JavaScript
110
star
15

bittorrent-peerid

Map a BitTorrent peer ID to a human-readable client name and version
JavaScript
65
star
16

parse-torrent-file

DEPRECATED: Parse a .torrent file and return an object of keys/values
JavaScript
63
star
17

ut_pex

Implementation of ut_pex bittorrent protocol (PEX) for webtorrent
JavaScript
58
star
18

workshop

Learn WebTorrent and WebRTC in a guided workshop!
48
star
19

bittorrent-lsd

Local Service Discovery (BEP14) implementation
JavaScript
35
star
20

fs-chunk-store

Filesystem (fs) chunk store that is abstract-chunk-store compliant
JavaScript
33
star
21

torrent-piece

Torrent piece abstraction
JavaScript
23
star
22

webtorrent-fixtures

Sample torrent files for the WebTorrent test suite
JavaScript
22
star
23

load-ip-set

download and parse ip-set (blocklist) files
JavaScript
22
star
24

lt_donthave

The BitTorrent lt_donthave extension (BEP 54)
JavaScript
11
star
25

addr-to-ip-port

Convert an "address:port" string to an array [address:string, port:number]
JavaScript
10
star
26

bep53-range

Parse and compose Magnet URI extension (BEP53) ranges
JavaScript
9
star
27

string2compact

Convert 'hostname:port' strings to BitTorrent's compact ip/host binary returned by Trackers
JavaScript
9
star
28

renovate-config

Renovate's config
1
star
29

semantic-release-config

JavaScript
1
star
30

.github

1
star