Releases: MochengCK/XferRust
Release list
v0.3.2
XferRust v0.3.2 Release Notes
English | 简体中文
Summary
- Fixed HTTP segmented downloads stalling for over ten seconds at 99%: when the last segment's connection went silent, the whole task sat at 99% with zero speed until the regular read-idle timeout (10s) fired, and the tail now uses a shorter timeout
Bug Fixes
- Fixed HTTP segmented downloads stalling for over ten seconds at 99%: the read-idle timeout was a flat 10s, so once the last segment's connection went silently dead (no data and no FIN), every other worker was already parked and the task sat at 99% with zero speed until the 10s timeout fired and the segment was reconnected from its received watermark — with backoff and reconnect, users saw the progress freeze for over ten seconds (most visible near the end, when no other worker is pulling data). The tail now switches to a 3s read-idle timeout once the remaining bytes drop below the end-game threshold (8MiB), so a dead connection is dropped and reconnected much sooner and the final pause drops from over ten seconds to a few
Behavior Changes
- The read-idle timeout during the end game (8MiB or less remaining) is tightened from 10s to 3s: slow servers that deliver the tail in bursts may now be disconnected and reconnected a few extra times (a short read does not consume the fatal failure budget, and the segment resumes from the bytes already received, so the task does not fail). Behavior in the middle of a download is unchanged
XferRust v0.3.2 发布说明
English | 简体中文
摘要
- 修复 HTTP 分片下载收尾阶段「卡在 99% 十几秒」:最后一个分片的连接僵死时,整条任务停在 99% 零速直到常规读空闲超时(10s)才断开重连,现尾声改用更短的超时
问题修复
- 修复 HTTP 分片下载收尾阶段「卡在 99% 十几秒」:分片下载的读空闲超时统一为 10s,而收尾时最后一个分片的连接一旦静默僵死(对端无数据也无 FIN),其余协程早已全部停靠,整条任务就停在 99% 零速,直到 10s 超时触发才断开、从已收水位重连续传——加上退避与重连,使用者看到的就是十几秒不动(剩余越少越明显,因为此时没有任何其他协程在拉数据);现剩余待下字节进入尾声门槛(≤ 8MiB)后改用 3s 读空闲超时,僵死连接更早被断开重连,收尾停顿由十几秒降到数秒
行为变更
- 尾声(剩余 ≤ 8MiB)的读空闲超时由 10s 收紧到 3s:尾部「突发式发数据」的慢服务器可能被多断开几次重连(短读不消耗致命失败预算,断开后从已收水位续传,不会导致任务失败),下载中段的行为不变
v0.3.1
XferRust v0.3.1 Release Notes
English | 简体中文
Summary
- Fixed BT public-swarm downloads being far slower than other clients (same public torrent: <1 MB/s → 17.1 MB/s peak): three root causes — IPv6 peers dropped by trackers, DHT dual-stack binding always failing, and DHT queries always timing out
- IPv6 coverage completed: bare IPv6 literals in non-compact tracker peer lists and the BEP 32
peers6field, IPv4 + IPv6 announces per tracker merged and deduplicated, and dual-stack uTP / UDP tracker / BT listening (none of which could even start on IPv6 hosts) - HTTPS now also trusts the local system root store (corporate proxies and private CAs no longer fail across the board) and
no-proxyreally takes effect - Fixed HTTP split downloads reporting success without downloading a single byte when the control file's segment table is unusable (leaving a truncated file behind while showing as complete); the resume bitfield no longer runs ahead of disk, so a crash recovery with
disk-cacheno longer reproduces "completed holes" - Fixed three DHT protocol errors: responses not validated against the source address (guessing a 16-bit transaction id was enough to inject a forged one),
announce_peeralways sent withimplied_port=1(making the remote node record our DHT port as our BT port), andget_peersencoding IPv6 peers as0.0.0.0 - Fixed the on-disk layout of multi-file torrents with exactly one file (the file was written as
<name>, occupying what should be the directory name; data downloaded by older versions migrates automatically) and thefiles[].path/task.verifyFilespath for single-file torrents (<name>/<name>→<name>) - Fixed NAT-PMP TCP port mapping never working (opcode 6 → 2 per RFC 6886), DHT routing-table persistence always panicking, and peer-id generation panicking whenever a version component has two digits
- Faster cold start and lower idle cost: DHT query timeout 10s → 3s with bootstrap and one iteration of find_node running concurrently, and release builds optimized for speed (see "Build & Release")
New Features
- Per-task custom request headers are now actually sent: the
headeroption (an array like["Referer: https://…", "Cookie: a=b"], or a single CRLF/LF-separated string) and thereferer/user-agentconvenience keys are applied to real HTTP requests — previously they were collected into the task's options and never used when downloading (callers believed they were sent, but they were silently dropped), so every URL that requiresReferer/Cookiefailed with 403. The probe, single-connection and multi-connection split paths all use the same set (sending them on the download but not on the probe makes the probe 403 and misjudge the total length and Range support);Range/Host/Content-Length/Connection/Accept-Encodingare always ignored (overriding them breaks segmenting, resume and virtual-host routing), andOriginis never injected
Bug Fixes
- Fixed IPv6 addresses in non-compact peer lists being dropped: the address and port were concatenated into
ip:portand re-parsed, which always fails for bare IPv6 literals (the port gets swallowed into the address) and discarded the whole peer; peers are now parsed from the IP literal with the port assembled afterwards, so a single invalid entry only skips that entry - Fixed the tracker
peers6field being ignored: added BEP 32 compact parsing (18 bytes per entry = 16-byte IPv6 + 2-byte port, port 0 dropped), with a ragged tail truncated and warned about - Fixed never obtaining IPv6 seeders: hostnames resolve to IPv4 first, so announces were always sent over IPv4 while trackers only return IPv6 peers to IPv6 sources (BEP 7); every tracker is now announced to over both IPv4 and IPv6 concurrently with the results merged and deduplicated — skipped automatically when the host has no IPv6 address or is a bare IP, and the IPv6 result is used as a fallback when the IPv4 announce fails
- Fixed DHT dual-stack nodes always failing to start on macOS: the
[::]bind address was handed to the resolver as a hostname (and then fell back to IPv4-only); it is now parsed as an IP literal (brackets stripped) - Fixed address-family mismatch when sending from / receiving on a dual-stack DHT socket: IPv4 destinations are converted to v4-mapped before sending and v4-mapped sources are normalized back to IPv4, keeping the routing table, known_peers and peer addresses handed to BT as IPv4
- Fixed every DHT KRPC query timing out: callers raced the resident receive loop for the same socket, and responses picked up by the loop were parsed as queries, failed, and silently discarded, leaving callers to time out; pending queries are now registered as "transaction id → channel" and the receive loop dispatches responses by tid (the entry is removed on send failure or timeout)
- Fixed slow DHT cold start: find_node queries for all bootstrap nodes and one iteration now run concurrently
- Fixed uTP being unable to talk to IPv6 peers: the uTP socket was bound to IPv4 only, and on a dual-stack listener every send to an IPv6 / IPv4 peer needs an address-family conversion — without it sendto fails with
Invalid argument (os error 22)and the uTP dial to an IPv6 peer can never succeed (each peer wastes a full handshake timeout, measured at 3s per peer, before falling back to TCP); the socket is now bound dual-stack to[::]with v4-mapped conversion on both send and receive - Fixed BT being unable to listen on IPv6-only hosts (or without IPv6): the TCP listener was bound to
0.0.0.0(tasks failed to start at all on IPv6-only hosts, and dual-stack hosts never received IPv6 inbound connections); it now prefers the dual-stack[::]and adds a plain IPv4 listener on systems withIPV6_V6ONLY=1(e.g. the Windows default); the conflict from that extra bind is ignored when the dual-stack socket already covers IPv4 - Fixed UDP trackers being IPv4-only: the socket was bound to
0.0.0.0, so on IPv6-only hosts everyudp://tracker was unusable and IPv6 trackers failed to send; it is now dual-stack with the same send-side conversion - Fixed v4-mapped peer addresses not being normalized: some trackers / PEX implementations send IPv4 addresses as
::ffff:a.b.c.d, which used to create a second peer record alongside the IPv4 literal and could never be dialed on hosts without an IPv6 route; addresses are now normalized to IPv4 before being stored - Fixed HTTPS failing across the board with private CAs / corporate proxies (TLS interception): TLS only trusted the Mozilla roots compiled into the binary and the local OS certificate store (corporate CAs, user-imported certificates) took no part in verification; system roots are now loaded as well (a union with the built-in roots, leaving public-site verification results unchanged)
- Fixed the
no-proxyoption having no effect: it was stored but never used to build the HTTP client, so in proxied environments LAN / loopback addresses listed inno-proxywere still pushed through the proxy and hosts that were directly reachable failed instead; it now really acts as the proxy's direct-connection exception (when using environment-variable proxies, theNO_PROXYenvironment variable applies) - Fixed HTTP split downloads being reported as finished when the control file's segment table is unusable: when the table was not a strict tiling (gap / overlap / empty) the writer neither fell back nor rebuilt it, so
todocame out as 0 and the download was markedfinished— success without a single byte downloaded, and the target file was not even resized to the correct length; an invalid control file is now treated exactly like "no control file" (the existing file is taken as a contiguous prefix and the remainder is split again) - Fixed the resume bitfield running ahead of disk: with
disk-cacheenabled, piece data lands in an in-memory write-back buffer first while the resume control file is written as soon as a piece completes — so the bitfield could declare pieces complete that had not written a single byte, and after a crash / power loss recovery skipped them, producing "completed holes" (silent corruption). The persisted bitfield now excludes pieces still in the write-back buffer (the only cost is re-downloading those few pieces after a crash), and a clean pause flushes to disk before writing the bitfield - Fixed DHT KRPC responses not being validated against the source address: the pending-query table was keyed by the 16-bit transaction id alone, so with hundreds of concurrent queries in one round (bootstrap + iterative find_node) the birthday bound gives a noticeable chance of a collision delivering a response to the wrong node's waiter, and any host guessing a tid could inject a forged response (peers go straight into the BT dial queue); the key is now "source address + tid" and responses with no waiter no longer enter the query-handling path
- Fixed NAT-PMP TCP port mapping never working: the opcode was written as 6 (neither 1 nor 2), so gateways always answered unsupported and TCP mapping depended entirely on the UPnP fallback; it is now 2 per RFC 6886 §3.3
- Fixed DHT
announce_peeralways being sent withimplied_port=1: BEP 5 says that when the bit is 1 the remote node ignores theportargument and uses the UDP source port, and announces leave through the DHT socket (a different port from the BT listen port) — so the remote node recorded our DHT port as our BT port, spreading a dead address; announces now declareimplied_port=0with the real BT listen port, and the self-address recorded in known_peers uses that port too - Fixed DHT
get_peersreplies encoding IPv6 peers as0.0.0.0: every peer was encoded as a 6-byte IPv4 compact entry, so the 4 IP bytes of an IPv6 address were filled with zeros (a non-zero port still passed the receiver-side filter) and clients receiving it would try to ...
v0.3.0
XferRust v0.3.0 Release Notes
English | 简体中文
Summary
- New disk cache
disk-cache(piece-level write-back buffering), magnet save/load as torrent (bt-save-metadata/bt-load-saved-metadata) - Peer-discovery switches now really take effect:
enable-dht/enable-dht6(BEP 32, DHT over IPv6 dual-stack) /enable-peer-exchange(BEP 11) - HTTP client configuration takes effect:
continueresume switch,user-agent,all-proxy/no-proxyproxy (client rebuilt on change) - New task file-verification RPC
task.verifyFiles(existence / size / streaming hash, executed natively by the engine) - UDP hole punching (
ut_holepunch) aligned with the libtorrent standard — relays through double NATs with standard clients such as qBittorrent - HTTP piece bitmaps, global & per-task rate limiting, per-task average speed, command-line option passthrough
- File selection (
select-file) works end-to-end;.torrentadditions support thebt-file-selectionflow - Piece display upgrades: persisted tri-state bitmap (
partialBitfield), wanted-piece bitmapwantedBitfield, seed durationbt-seed-timethat stops seeding automatically when reached; BT peer IP banning andtask.getPeersper-peer stats / banned group - Fixed phantom progress on unselected files and task progress above 100%: per-file and task progress now share one piece-segment accounting
- Fixed magnet/torrent tasks skipping "awaiting file selection" and downloading directly, garbled Chinese task names, inaccurate average speed
- All non-terminal tasks are restored as paused after restart; per-task rate limits now override the global one
New Features
Disk Cache (disk-cache)
- New global option
disk-cache(bytes or K/M/G suffix such as128M;0= write-through off): BT piece data is first buffered in a memory write-back queue and flushed to disk in FIFO order, reducing small random writes - Read paths serve cached pieces directly from memory without touching disk;
flush_allflushes the cache before syncing files, so the resume bitmap is only persisted after its data is on disk - Pieces larger than the cache limit degrade to direct writes; rewriting the same piece replaces the old entry without double counting; invalid values degrade to a warning without aborting the batch
Magnet Save / Load as Torrent (bt-save-metadata / bt-load-saved-metadata)
- New global option
bt-save-metadata: when magnet metadata arrives it is written to<download-dir>/<hex-infohash>.torrent(top-level dictionary hand-encoded with the original info bytes so the info_hash never shifts; atomic write via temp file + rename) - New global option
bt-load-saved-metadata: on magnet task start, if a same-named.torrentexists in the download dir with a matching info_hash, the metadata is installed directly, skipping the peer fetch (magnet becomes a torrent task instantly) - Tasks created from a
.torrentfile are not re-saved (the original file already exists)
Peer-Discovery Switches (enable-dht / enable-dht6 / enable-peer-exchange)
- New global option
enable-dht(DHT, BEP 5): on by default; when off, BT tasks no longer join the DHT; private torrents never use DHT (the constraint wins) - New global option
enable-dht6(DHT over IPv6, BEP 32): when on, DHT binds a dual-stack socket ([::]), bootstraps IPv6 nodes, replies to IPv6 requesters withnodes6/peers6and parses remote v6 nodes; if the machine has no IPv6 stack and binding fails, it automatically falls back to IPv4 - New global option
enable-peer-exchange(PEX, BEP 11): on by default; when off the extension handshake does not advertiseut_pexand PEX messages are neither sent nor received
HTTP Client Configuration (continue / user-agent / all-proxy / no-proxy)
- New global option
continue(HTTP resume switch, default on): when off, downloads always start from scratch even if the server supports Range; an existing file is kept and a new name is used (aria2 semantics) - New global options
user-agent/all-proxy/no-proxy: the HTTP client is built from them (UA overrides the engine default, proxy is direct or viaall-proxy); changing them rebuilds the client so new connections pick it up immediately - Desktop proxy settings (system / custom) and UA settings now really apply to download traffic
Task BT Identity (bittorrent / infoHash)
- Task status responses (
task.tell/task.list, both the aria2-style and native numeric encodings) now carry abittorrentobject and aninfoHashfield - Non-BT tasks report
bittorrentasnull; when metadata is ready it is{"info": {"name", "hash"}}; while magnet metadata is being fetched it is{}(the frontend shows "fetching metadata") infoHashis the hex encoding of the info dictionary hash: .torrent tasks report the hash computed at parse time, magnet tasks report thebt_info_hashfrom the handshake / metadata exchange
Global Tracker List & Subscription Switch Passthrough
engine.changeOptionsgainsbt-trackers: a string array or newline/comma-separated string with full-replacement semantics, synchronized to all active BT tasks with the same delta semantics as manual add/remove- New switch
auto-update-trackerscontrols tracker subscription auto-update ("false"/"0"both mean off)
Command-Line Runtime Option Passthrough
- The engine command line accepts
--key=valueruntime global options injected into the same store asengine.changeOptions(CLI values override session-restored ones):split,max-connection-per-server,min-split-size, overall rate limits,bt-max-peers,bt-adaptive,bt-seed-mode,bt-seed-ratio,bt-encryption,bt-protocol,bt-listen-port,dht-listen-port,bt-enable-lpd,bt-port-mapping - App-side toggles are auto-mapped:
--enable-upnp/--enable-nat-pmp→bt-port-mapping,--enable-utp→bt-protocol - Any unknown
--key=valueis accepted as a global default (see Behavior Changes), so host applications can pass their entire configuration through the command line
Piece Display, Real-Time Speed & Peer Management
- Download speed now uses a 3-second sliding window refreshed every second: the window average smooths piece-level write jitter while the value updates every second
- BT piece bitfield exposure:
bitfieldreports the real "downloaded pieces" bitmap (aria2-compatible hex encoding), synced at 1 Hz, preserving the last known state while paused - Per-peer bitfield: each peer in
task.getPeersgains abitfieldfield (pieces the peer owns, all ones for seeds) - BT peer IP banning:
task.banPeer(auto-unbans afterdurationseconds,<= 0permanent; immediately disconnects existing connections) /task.unbanPeer; the ban list persists across sessions - Global ban-list option:
engine.changeOptionsgainsbt-ip-ban-list(IP array or newline/comma-separated string, full replacement of permanent bans) - New
task.changeUri(aria2-compatible: removesdelUris/ appendsaddUrisperfileIndexfor waiting/paused tasks, rejected when active) andtask.getServers(HTTP tasks return server entries, BT returns an empty array)
Per-Task Average Speed (averageSpeed)
- Task status responses gain an
averageSpeedfield (bytes/sec): accumulated every second during active download phases; seeding and paused phases neither accumulate nor dilute it - The accumulated data persists across sessions, so the average does not drift after restart-and-resume; the app reads this field directly instead of sampling
File Selection (select-file) End-to-End
task.changeOptionnow actually appliesselect-file(aria2 semantics: comma-separated 1-based file indices, empty = select all): running BT tasks hot-apply it, paused/waiting tasks pick it up on their next startfiles[].selectedreports the real selection: the native encoding previously hardcodedtrue; the aria2 encoding now outputs"true"/"false"- File selection extends to HTTP/HTTPS tasks: single-file layout, persisted and applied on the next start
task.addacceptsselect-filefor pre-selection; invalid/out-of-range values degrade to a warning without aborting the addition.torrentadditions support thebt-file-selectionflow: the same state machine as magnets — auto-pause once metadata is ready, resume viaselect-fileafter the client confirms the selection- Single-file magnets auto-resume: when the layout is a single file, the engine resumes at full selection without user action
HTTP Piece Bitmap & Global Rate Limiting
- HTTP tasks report real piece data:
numPieces/pieceLength/bitfield(aria2-compatible hex encoding) - Piece length is a display granularity decoupled from the segment granularity:
min(min-split-size, max(total/2048, 64KB))— even a few-MB file lights up pieces promptly, large files keep themin-split-sizegranularity - The write side accounts bytes incrementally per landed range on both multi-connection and single-connection paths; the bitmap is rebuilt when a server ignores Range and resends the full body; control-file watermarks pre-fill the bitmap on resume so it stays consistent with disk; omitted when total length is unknown or Range is unsupported
- HTTP global rate limiting: an asynchronous token bucket is injected into every download connection, letting TCP backpressure converge naturally. Previously HTTP downloads were entirely unthrottled
- Speed-limit value parsing upgraded: overall limits accept aria2-style units (
1M/500K/ plain bytes), previously only plain integers - One invalid key no longer aborts the whole settings batch: illegal values in
changeOptionsdegrade to a warning and skip that key
Per-Task Rate Limiting (max-download-limit / max-upload-limit)
task.changeOptionacceptsmax-download-limit/max-upload-limit(aria2 ...
v0.2.0
XferRust v0.2.0 Release Notes
English | 简体中文
Summary: A magnet-experience and BT-stability release — pick which files to download from a table after a magnet link is parsed (download starts only after confirmation), plus fixes for a peer session leak, rate-limiter deadlock and uTP stream corruption, hardening against path traversal and metadata poisoning, and completed ut_metadata serving, tracker reporting and choking behavior. The TUI is fully redesigned as well — the add-task dialog supports a per-task download directory (with a native system folder picker), magnet links start parsing immediately and expand the dialog in place into a file-selection table once metadata is ready, the main layout is rebuilt into a modern flat design, and the global info bar stays visible on task detail pages. Additionally, this release adds full BT seeding lifecycle management (configurable share-ratio auto-stop, continuous seeding until manual stop) and Android platform engine-core cross-compilation support; it also completes BT network discoverability — local peer discovery (LPD), automatic UPnP/NAT-PMP port mapping and configurable BT/DHT listen ports — and fixes the tail-end stall that left HTTP/HTTPS downloads hanging at 99%.
New Features
- Magnet file selection: the engine starts parsing metadata as soon as a magnet link is added; once parsed, the task pauses and a file table is shown (checkbox, file size, selection summary), and only the checked files are downloaded. Pieces spanning the selected/unselected boundary are downloaded whole, as BitTorrent requires
- Magnet tasks are resumable: a task still waiting for file selection reopens the file table after a restart instead of silently downloading everything
- Serve ut_metadata (BEP 9) requests: magnet peers get proper DATA/REJECT responses instead of being ignored
seed_durationis now honored: seeding exits cleanly with a stopped announce when the configured time is up
BT Seeding Lifecycle Management
- BT tasks can be configured to enter a seeding state after download completion (instead of finishing immediately), controlled by the global option
bt-seed-mode(true/false) - Seeding tasks display a live share ratio (uploaded / downloaded); a target share ratio
bt-seed-ratio(e.g.1.5,2.0) can be set — seeding auto-stops when the ratio is reached - Seeding tasks can be paused/resumed, and seeding can be manually stopped (RPC
task.stopSeed/ TUISkey) - Continuous seeding until manual stop is supported (
bt-seed-ratioof0disables auto-stop) - TUI settings page adds seeding mode and target share ratio options; task list/detail views show upload speed and share ratio for seeding tasks
BT Network Discovery & Port Configuration
- Local peer discovery (LPD / BEP 14): each BT task joins the LSD multicast group, periodically announces to and listens on the LAN, so peers on the same network can find each other without trackers; discovered peers enter scheduling with an
lpdsource - Automatic UPnP / NAT-PMP port mapping: when a BT task starts, the engine sets up a TCP + UDP dual mapping on the router (2-hour lease, renewed every 1/3 of the lease, automatic retry next cycle after a single failure) and best-effort removes the mapping on shutdown — better connectability, no more manual router port forwarding
- Configurable BT / DHT listen ports: new global options
bt-listen-port/dht-listen-port(0= random port), applied when a BT task is created; if the listen port or DHT port is taken, the engine falls back to an ephemeral port with a warning instead of disabling listening entirely - New switches
bt-enable-lpd/bt-port-mapping(both on by default) to disable either feature independently;engine.getOptionsexposes the new options andchangeGlobalOptionvalidates port values (0–65535) - The TUI settings page gains the four matching rows: BT / DHT listen port (Enter to edit, validated 0–65535,
0shown as "random") plus Local discovery and Port mapping toggles (←/→ to switch); conns-per-server, min split size and language shift below them
Android Engine-Core Build
- Added Android (aarch64-linux-android / arm64-v8a) cross-compilation support — builds only the engine core (
xferrust), no TUI Cargo.tomlmakes TUI dependencies (crossterm, ratatui, etc.) optional under atuifeature;default = ["tui"]; thexferbinary is markedrequired-features = ["tui"]- CI adds a
build-androidjob: cross-compiles with NDK r27c + API 24 (Android 7.0+); artifactxferrust-android-arm64-v8a.tar.gzis included in releases - New local build script
scripts/build-android.sh: auto-detects NDK host-tag (macOS / Linux), sets CC/CXX/AR/Linker env vars and invokescargo build --no-default-features --bin xferrust
TUI Redesign
- Redesigned add-task dialog: URL + directory fields; an empty directory falls back to the global download directory (per task only). The directory can be typed manually or picked with the native system folder dialog by pressing Enter (macOS / Windows / Linux)
- Magnet links start parsing metadata immediately, no extra confirmation step; the dialog shows live parsing progress (connections / waiting / elapsed). Once metadata is ready, the dialog expands downward into a file-selection table: Space to toggle, A to select all/none, Enter to start downloading the selected files
- The download directory in Settings is also picked with the native system folder dialog
- Task detail pages (BT / non-BT) keep the global info bar at the top (brand logo + global speed / task counters) instead of hiding it
Bug Fixes
- Fix magnet tasks reporting an empty file list while downloading: metadata is back-filled as soon as it is ready, so
filesandnumPiecesare no longer zero - Fix magnet metadata being lost across restarts: the session persists the info dictionary and rebuilds the metadata from it, no need to re-fetch from peers
- Fix inbound peer session leak: passive connections were never unregistered, stalling their assigned pieces for up to 180 s
- Fix rate-limiter deadlock: token bucket guarantees at least one max-size block, limits below 16 KiB/s no longer freeze the connection
- Fix uTP stream corruption: read path applies back-pressure via channel capacity; write path buffers partially accepted bytes instead of dropping them
- Sanitize torrent paths (name / path segments) to prevent path-traversal writes outside the download directory
- Fix scheduler treating 0→0 throughput as a 100% decline — target connection count no longer collapses during cold start
- Fix choking algorithm: wall-clock rounds shared by all sessions, engine-level optimistic unchoke that actually reaches the lucky peer, round-robin uploads while seeding
- Fix tracker announces always reporting zero uploaded bytes (private-tracker ratio tracking works now)
- Fix UDP trackers never receiving stopped/completed events
- Fix HTTP/HTTPS downloads hanging at 99% for over ten seconds before finishing:
- Tail short reads (response body shorter than the requested range) now use a dedicated backoff curve (from 250 ms, capped at 1 s) instead of sharing the 0.5–2 s hard-failure backoff — short reads always make progress when retried from the watermark, and the old backoff accumulated into a long tail under retry storms
- New 10-second read-idle watchdog: a silently stalled connection (dead peer / half-open socket) resumes from the watermark immediately instead of waiting out the 30-second read timeout
- Read timeouts are now classified as renewable transients: they no longer consume the 4-strike failure budget, so tail retries are no longer killed prematurely
UI Redesign
- Flat top bar: a single row with the brand logo on the left and ↓/↑ global speed plus active/waiting/stopped counters on the right, separated by a vertical bar
- Square task-area borders: top/bottom lines start aligned with the logo, end in rounded corners joined to the right vertical line, all four corners left open; the sidebar/content divider leaves headroom above and below
- Sidebar: removed the category heading, selection is now highlighted with a background (no more pointer)
- Task table: a separator line between the header and rows (same color as the border, same width as the columns, symmetric on both sides); header and rows keep one column of padding on each side, symmetric left/right
- Size column is now a compact "completed / total" format, the speed column is narrower, and task names no longer push later columns out of alignment
Other Changes
- New file-selection API:
select_files(gid, file_indices)/get_selected_files(gid), applied live on running tasks;add_uriaccepts abt-file-selectionoption to pause a magnet task after parsing and wait for a selection - Task status exposes a new
awaitingSelectionfield, andfiles[].selectednow reflects the real selection - New seeding status
seedingandseedRatiofield; RPC addstask.stopSeedmethod; session save/restore handles seeding tasks correctly (serialized aswaitingso they re-download on restart) - Failed dial / session addresses are re-queued for a bounded number of retries instead of waiting for the next announce
- A short-read storm exceeding 8 consecutive retries folds into one regular failure under the normal failure budget — the relaxed backoff does not slow down error reporting for permanently broken servers
- Added HTTP tail-truncation + pause/resume regression tests (short-read storms, silent stalls, cancel & resume scenarios)
- DHT hardening: known-peer table capped with FIFO eviction, inbound datagram processing concurrency limited
- Added 27 TUI rendering regression tests (border geometry, column alignment, dialog flow, detail top bar, etc.)
XferRust v0.2.0 发布说明
English | 简体中文
摘要:本...
v0.1.0
XferRust v0.1.0
首个公开版本
一个使用 Rust 构建的现代化、高性能下载引擎。
Important
🎉 XferRust v0.1.0 是本项目的首个公开版本。
此版本标志着 XferRust 正式开始公开开发。
XferRust 是一个使用 Rust 构建的新一代下载引擎,致力于提供出色的 性能(Performance)、可靠性(Reliability) 和 可扩展性(Extensibility)。
The First Public Release
A modern, high-performance download engine built with Rust.
Important
🎉 XferRust v0.1.0 is the first public release of the project.
This release marks the beginning of XferRust — a new generation download engine designed with performance, reliability, and extensibility in mind.
🔗 完整更新记录 / Full Changelog
Full Changelog: https://github.com/MochengCK/XferRust/commits/v0.1.0