Skip to content

Latest commit

 

History

233 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FX-Torrent

Build Crates License: Apache-2.0 Documentation codecov

FX-Torrent is the most complete BitTorrent implementation fully written in Rust, which supports both Linux, MacOS, and Windows. It supports most of the Bittorrent protocol specifications, such as multi-file torrents, validating existing files, resuming torrent files, and is based on the libtorrent library for functionality and naming convention.

Getting Started

Create a new FxSession which manages one or more torrents. A Torrent can be created from a magnet link, torrent file, or passing the raw TorrentMetadata.

create a new session with torrent

// The fx-torrent crate makes use of async tokio runtimes
// this requires that new sessions and torrents need to be created within a tokio runtime
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    let session = FxSession::builder()
        .config(
            SessionConfig::builder()
                .base_path("/downloads")
                .client_name("MyClient")
                .build(),
        )
        .default_extensions()
        .dht(DhtTracker::builder()
            .default_routing_nodes()
            .build()
            .await?)
        .build()?;

    // 1. Add a torrent via Magnet URI
    let magnet_torrent = session
        .add_torrent_from_uri("magnet:?xt=urn:btih:...", TorrentFlags::default())
        .await;

    // 2. Add a torrent from a local .torrent file
    let file_torrent = session
        .add_torrent_from_uri("/path/to/file.torrent", TorrentFlags::default())
        .await;

    // 3. Add a torrent from raw metadata bytes
    let data: &[u8] = &[0; 1024]; // Replace with actual bencoded bytes
    let metadata = TorrentMetadata::try_from(data)?;
    let metadata_torrent = session
        .add_torrent_from_metadata(metadata, TorrentFlags::Paused)
        .await;

    Ok(())
}

For more examples, see the examples directory.

CLI example

The CLI example makes use of most of the functionality provided by the library and can be used to download torrents from magnet links or torrent files. The CLI also allows the introspection of the DHT network and Trackers.

The example is built on top of the Ratatui as terminal UI library.

Tracing & Tokio Unstable

The CLI example enables the tracing feature by default to support the tokio-console subscriber. This requires the tokio_unstable configuration flag to be passed to the compiler.

RUSTFLAGS="--cfg tokio_unstable" cargo run --example cli

If you do not wish to use experimental tokio features, you must disable the tracing feature in the example.

Streaming

The fx_torrent engine supports sequential file streaming through FileStream. This allows you to stream file data (such as media files) over the network before the entire torrent finishes downloading.

Because FileStream implements the standard futures::Stream trait, it can be easily integrated into asynchronous loops or piped directly into web frameworks like axum.

Basic Usage

fn example() {
    let torrent = Torrent::request()
        .build()
        .unwrap();
    let file = torrent.file_by_name("example.mp4").await.unwrap();

    let mut stream = torrent.stream(&file).await.unwrap();
    while let Some(bytes) = stream.next().await {
        // use the bytes here
    }
}

Axum Integration Example

fn axum_example(filename: &str) -> Response<Body> {
    let torrent = Torrent::request()
         .build()
         .unwrap();
    let file = torrent.file_by_name(filename).await.unwrap();

    let stream = torrent.stream(&file).await.unwrap();
    Response::builder()
        .body(Body::from_stream(Box::into_pin(stream)))
}

Extensions

The fx_torrent crate is designed to be highly extensible. You can modify the core behavior of components by implementing and registering "extension" traits. These allow for custom logic in peer communication and data persistence.

Peer Extension

Peer extensions allow you to extend the BitTorrent protocol with custom messaging and handshake capabilities, following the BEP 10 specification.

To modify peer protocol behavior, implement the peer::extension::Extension trait. Once implemented, these extensions can be attached to individual torrents or globally across a session.

example peer extension

#[derive(Debug)]
pub struct MyPeerExtension;
impl Extension for MyPeerExtension {
    fn name(&self) -> &str {
        "my-extension"
    }

    // Additional trait methods
}

fn example() {
    // 1. Peer extension directly in a torrent
    let torrent = Torrent::request()
        .extension(|| MyPeerExtension.into())
        .build()
        .unwrap();

    // 2. Peer extension in a session
    let session = FxSession::builder()
        .extension(|| MyPeerExtension.into())
        .build()
        .unwrap();
}

Storage Extension

Storage extensions allow you to customize how data is read from and written to disk (or memory). This is useful for implementing custom caching layers, encrypted storage, or cloud-backed persistence.

To create your own storage backend, implement the storage::Extension trait.

example storage extension

#[derive(Debug)]
pub struct MyStorageExtension;
impl MyStorageExtension {
    pub fn new(_params: StorageParams) -> Self {
        Self
    }
}
impl Extension for MyStorageExtension {
    async fn read(&self, buffer: &mut [u8], piece: &PieceIndex, offset: usize) -> Result<usize> {
        // Read piece data from storage
        Ok(0)
    }

    // Additional trait methods
}

fn example() {
    // 1. Storage extension directly in a torrent
    let torrent = Torrent::request()
        .storage(|params| MyStorageExtension::new(params).into())
        .build()
        .unwrap();

    // 2. Storage extension in a session
    let session = FxSession::builder()
        .storage(|params| MyStorageExtension::new(params).into())
        .build().unwrap();
}

Operation Extension

Operation extensions are tick-based tasks invoked by the TorrentContext. These operations are executed sequentially in an order-dependent chain, meaning the sequence in which you register them determines their execution priority.

example operation extension

#[derive(Debug)]
pub struct MyOperation;
#[async_trait]
impl Extension for MyOperation {
    /// The `tick` method is called periodically by the torrent engine.
    async fn tick(&self, context: &mut TorrentContext, peer_discoveries: &[PeerDiscovery]) -> TorrentOperationResult {
        // Logic for your custom operation goes here
        TorrentOperationResult::Continue
    }

    // Additional trait methods
}

fn example() {
    // 1. Operation extension directly in a torrent
    let torrent = Torrent::request()
        .operation(MyOperation.into())
        .build()
        .unwrap();

    // 2. Operation extension in a session
    let session = FxSession::builder()
        .operation(|| MyOperation.into())
        .build()
        .unwrap();
}

Piece Picker Extension

Piece picker extensions allow you to customize or completely override the core piece selection algorithm. Piece selection tasks are executed either periodically via a background ticker or instantly on-demand when requested by a peer.

To implement a custom piece picker algorithm, implement the fx_torrent::piece_picker::Extension trait.

example piece picker extension

#[derive(Debug)]
pub struct MyPiecePicker;
#[async_trait]
impl Extension for MyPiecePicker {
    async fn pick_pieces(&mut self, peer: &Peer) {
        // Your custom piece picking algorithm goes here
    }

    async fn tick<'a>(&'a mut self, peers: Vec<&'a Peer>) {
        // Tick-based piece picking logic goes here
    }

    // Additional trait methods
}

fn example () {
    // 1. Piece picker extension directly in a torrent
    let torrent = Torrent::request()
        .piece_picker(|
            torrent: InnerTorrent,
            data_pool: DataPool,
            storage: Storage,
            options: PickerOptions| MyPiecePicker.into())
        .build()
        .unwrap();

    // 2. Piece picker extension in a session
    let session = FxSession::builder()
        .piece_picker(|
            torrent: InnerTorrent,
            data_pool: DataPool,
            storage: Storage,
            options: PickerOptions| MyPiecePicker.into())
        .build()
        .unwrap();
}

Piece Picker Strategy Extension

The fx_torrent::piece_picker::FxPiecePicker architecture allows sub-strategies to be sequentially stacked or overridden. These strategies operate in an order-dependent chain, meaning their registration order explicitly dictates execution priority during the piece picking lifecycle.

#[derive(Debug)]
pub struct MyStrategy;
impl Extension for MyStrategy {
    async fn pick_pieces<'a>(
        &self,
        peer: &Peer,
        blocks: &'a Vec<PiecePickerBlock>,
        target_queue_len: usize,
        suggested_pieces: &[PieceIndex],
        is_end_game: bool,
        options: PickerOptions,
    ) -> Vec<&'a PiecePickerBlock> {
        // Your custom piece picking logic goes here
        vec![]
    }
}

fn example() {
    let torrent = Torrent::request()
        .piece_picker(|
            torrent: InnerTorrent,
            data_pool: DataPool,
            storage: Storage,
            options: PickerOptions| FxPiecePicker::new(
            torrent,
            data_pool,
            storage,
            vec![
                MyStrategy.into(),
                PriorityStrategy::new().into(),
            ],
            32 * 1024 * 1024
        ))
        .build()
        .unwrap();
}

Features

  • BEP3 - The BitTorrent Protocol Specification
  • BEP4 - Assigned Numbers
  • BEP5 - DHT Protocol
  • BEP6 - Fast Extension
  • BEP7 - IPv6 Tracker Extension
  • BEP9 - Extension for Peers to Send Metadata Files
  • BEP10 - Extension Protocol
  • BEP11 - Peer Exchange (PEX)
  • BEP12 - Multitracker Metadata Extension
  • BEP14 - Local Service Discovery
  • BEP15 - UDP Tracker Protocol for BitTorrent
  • BEP19 - WebSeed - HTTP/FTP Seeding (GetRight style)
  • BEP20 - Peer ID Conventions
  • BEP21 - Extension for partial seeds
  • BEP24 - Tracker Returns External IP
  • BEP29 - uTorrent transport protocol
  • BEP32 - BitTorrent DHT Extensions for IPv6
  • BEP33 - DHT scrape
  • BEP40 - Canonical Peer Priority
  • BEP42 - DHT Security extension
  • BEP43 - Read-only DHT Nodes
  • BEP44 - Storing arbitrary data in the DHT
  • BEP47 - Padding files and extended file attributes
  • BEP48 - Tracker Protocol Extension: Scrape
  • BEP51 - DHT Infohash Indexing
  • BEP52 - The BitTorrent Protocol Specification v2 (WIP)
  • BEP53 - Magnets
  • BEP54 - The lt_donthave extension
  • BEP55 - Holepunch extension

License

This project is licensed under the Apache-2.0 license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

About

FX Torrent is a feature rich Bittorrent protocol implementation written in rust supporting Linux, MacOS and Windows

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages