cratetorrent Documentation

repository·master·Indexed 19 days ago

https://github.com/vimpunk/cratetorrent

A BitTorrent V1 engine library and CLI implemented in Rust. It provides an asynchronous I/O engine for downloading and seeding torrents, featuring a TUI-based CLI tool, a configurable engine via EngineConf and TorrentConf, and a PeerSession system for managing BitTorrent v1 wire protocol communication.

Tokens
14.3K
Snippets
36
Records
64
Agent score
67%

What's inside cratetorrent

  1. Handle request timeouts in peer sessions

    master

    Peer sessions implement dynamic timeout mechanisms for block requests to prevent the download from getting stuck if a peer fails to respond.

    • Purpose: If a peer doesn't send a requested block within the window, the session can attempt to request it from a different peer.
    • Dynamic Adjustment: Each peer has a unique timeout value derived from a weighted running average of round-trip times (RTT). New samples have a weight of $1/20$ to guard against jitter.
    • Minimum Threshold: To prevent premature timeouts during minor network hiccups, the minimum timeout is set to 2 seconds.
  2. Understand the error handling model

    master

    Cratetorrent distinguishes between two types of errors to prevent accidental system halts. When consuming the API, you must handle these differently:

    1. Fatal Errors: These represent critical system failures (e.g., mpsc channel failures) that cause the engine to stop. These are returned as the general Error type.
    2. Fallible Errors: These are transient or recoverable issues (e.g., network IO failures, disk IO failures like running out of space) that should not stop the engine. These are communicated via alert channels rather than being returned as a Result::Err that would trigger a ? operator halt.

    By separating these, the engine ensures that a single disk write failure doesn't abort the entire event loop. Non-fatal errors (like WriteError) are routed to the responsible entity for logging or re-attempting, while only fatal Error types are intended to propagate through the main execution loop.

  3. Understand the Peer Session Tick

    master

    Every second, the peer session runs an update loop (a "session tick") to perform periodic maintenance:

    • Stats Collection: Collects current download/upload rates, calculates running averages, and resets per-round counters.
    • Slow Start Management: Checks if the session should exit slow start mode.
    • Timeout Processing: Executes the timeout procedure for pending requests.
    • State Updates: Sends state change updates to the owning torrent.
  4. How piece downloads work in Cratetorrent

    master

    A piece download tracks the completion status of an ongoing piece download. This abstraction is used to optimize download performance through several mechanisms:

    • Sharing block requests: Allowing peers to share block requests within a piece.
    • Peer request timeouts: Managing when a peer's request for a block should be timed out.
    • End game mode: Optimizing the final stages of a download (Note: end game mode is currently not implemented).

    Piece downloads are stored within the Torrent object and are shared across all peers in that torrent. When a peer initiates a new download, it adds the download instance to the shared Torrent object, enabling other peers to join the same download process.

  5. How the Disk entity and its task-based architecture work

    master

    The Disk entity (located in the disk module) manages all disk storage operations, including file allocation, hashing downloaded pieces, and reading blocks for seeding.

    To prevent blocking the main torrent engine or peer sessions, Disk runs as a separate task. Communication with the Disk task is handled via two tiers of mpsc channels:

    1. Global Alert Port: Used for high-level results, such as the result of allocating a new torrent. The Engine listens here to receive the torrent-specific alert port after allocation.
    2. Per-Torrent Alert Port: Used for torrent-specific results, such as notifying a Torrent that a piece has been successfully written to disk.

    Workflow for downloading a piece:

    1. PeerSession sends block write commands to Disk via a handle.
    2. Disk buffers blocks in memory until a piece is complete.
    3. Once complete, Disk hashes the piece.
    4. If the hash is valid, Disk writes the piece to disk and notifies the Torrent via its specific alert channel.
    /* Conceptual workflow summary */
    // 1. Engine -> Disk (Allocate Torrent) -> Global Alert Port
    // 2. Disk -> Engine (Return Torrent Alert Port)
    // 3. PeerSession -> Disk (Write Block) -> Per-Torrent Alert Port
  6. Understand the metainfo file structure

    master

    A torrent's metadata is stored in a UTF-8 encoded metainfo file, which is a bencoded dictionary. To start a download or upload, you must provide this file to the client.

    Key top-level keys:

    • announce: The URL of the torrent's tracker (currently disregarded by the engine).
    • info: The core torrent information.

    Inside the info dictionary:

    • name: The name of the file or directory.
    • piece length: The size of each piece in bytes (typically a power of two).
    • pieces: A list of SHA1 hashes representing the expected value of each file piece.
    • length: (Single-file mode) The total length of the file.
    • files: (Multi-file mode) A list of dictionaries, each containing length and path (relative to the download root).
  7. Mapping torrent pieces to files on disk

    master

    Because a single piece in a torrent can span multiple files, Disk must determine how to partition a piece before writing it.

    Cratetorrent uses a dynamic approach: it computes which files a piece intersects with at the moment the piece download starts. This minimizes memory overhead compared to pre-computing all intersections for all files.

    The Partitioning Algorithm:

    1. Find the first file where [file.start_offset, file.end_offset).contains(piece.start_offset).
    2. If no file is found, the piece is invalid/empty.
    3. Initialize a range of file indices starting with that file.
    4. Iterate through subsequent files to see if [piece.start_offset..piece.end_offset].contains(file.start_offset) is true. Record these indices.
    5. For each identified file, calculate the specific slice (offset and length) within that file that overlaps with the piece's byte range.
  8. Optimize throughput with the Download Pipeline

    master

    To saturate the network link, the peer session maintains an "optimal request queue size" based on the Bandwidth-Delay Product (BDP). This ensures there are always enough outstanding requests to keep the pipe full.

    Calculating Request Queue Size

    The number of outstanding block requests ($Q$) is calculated as:

    Q = B * D / 16 KiB

    Where:

    • $B$ is the current download rate (bytes per second).
    • $D$ is the link latency (currently hard-coded to 1 second).
    • $16 ext{ KiB}$ is the standard block size.

    Slow Start

    To quickly discover link capacity without wasting bandwidth, the session uses a slow start mechanism. The target request queue size starts low and increases by one with every received block (effectively doubling the queue size with each complete round trip). The session exits slow start when the download rate increases by less than $10 ext{ kB/s}$.

  9. How block fetching and seeding works

    master

    The engine manages data movement through specific lifecycles for fetching and seeding blocks:

    Block Fetching (Downloading)

    1. PeerSession requests n blocks from a peer.
    2. DiskHandle::write_block is called for each received block, sending a message via the command channel to the Disk task to place the block in the write buffer.
    3. Once a piece is complete, it is hashed. If the hash matches the expected value, it is saved to disk.
    4. The result (written blocks or an IO error) is sent back to the Torrent via a channel.
    5. Torrent processes the message and forwards it back to the PeerSession.

    Seeding a Block (Uploading)

    1. PeerSession receives a block request from a peer.
    2. The system checks seeding conditions and sends a block read request to the disk task.
    3. The system checks if the block's piece is in the read cache.
    4. If not cached, the entire piece is read from disk into the read cache.
    5. The block is returned via a sender to the peer session.
    6. The peer receives the block, provided no cancellation messages were received during the process.
  10. Efficiently saving blocks using Vectored IO

    master

    To minimize the overhead of system calls and context switches, Disk uses Vectored IO via the pwritev syscall. Instead of writing each 16 KiB block individually, Disk flushes a vector of byte arrays (iovecs) in a single operation.

    Handling File Boundaries: When a piece spans multiple files, a single block might be split across a file boundary. To prevent pwritev from writing data past the end of a file (which would incorrectly expand the file size), Disk uses the iovecs module to manage these boundaries:

    • If a block spans a boundary, the iovecs abstraction handles trimming the slice of data sent to the syscall.
    • It uses a strategy of keeping metadata about trimmed iovecs to avoid unnecessary allocations, ensuring high performance.
    • After each write, the slice of iovecs is trimmed by the number of bytes actually written to ensure the next file in the sequence receives the correct subsequent data.
  11. How the Torrent and Peer sessions interact

    master

    The engine manages downloads through a hierarchy of components:

    1. Engine: Orchestrates all components.
    2. Torrent: Coordinates the download, manages the piece picker, and maintains connections to seeds. It runs a periodic 'tick' (every 1 second) to collect stats and manage state.
    3. Peer Session: Each connection to a peer is spawned as a separate asynchronous task.

    Communication Pattern: Because peer sessions run in separate tasks, the Torrent and Peer Session communicate using asynchronous mpsc (multi-producer, single-consumer) channels. This avoids lifetime and synchronization issues with the borrow checker. Peer sessions send 'state change' messages to the torrent during their own tick routines, allowing the torrent to aggregate stats (like throughput and piece availability) and make management decisions (like unchoking peers).