libfw_core/constants.rs
1//! Protocol constants shared between server and client.
2
3/// Fixed chunk size for fragmented transfers (2 MiB).
4pub const CHUNK_SIZE: u64 = 2 * 1024 * 1024;
5
6/// Constant sliding-window size for streaming (64 KiB).
7pub const STREAM_BUF_SIZE: usize = 64 * 1024;
8
9/// Maximum default concurrent in-flight HTTP transfers in the WASM engine.
10///
11/// Used both as the cross-file fan-out bound and as the global permit-pool
12/// size that caps the TOTAL number of parallel chunk/range requests.
13pub const DEFAULT_CONCURRENCY: usize = 4;
14
15/// Default in-flight chunk window for a single file's upload.
16///
17/// A per-file streaming bound: one file schedules up to `upload_window`
18/// chunk tasks at once, each of which then acquires a global permit before
19/// sending, so the TOTAL in-flight HTTP requests never exceed
20/// [`DEFAULT_CONCURRENCY`]. The window keeps a file's read/compress pipeline
21/// saturated on high-latency links without exceeding the global cap.
22pub const DEFAULT_UPLOAD_WINDOW: usize = 8;
23
24/// Default in-flight chunk window for a single file's **download**.
25///
26/// The tus-style parallel download fetches a large file as many concurrent
27/// byte-range GETs, so a single file's throughput is bounded by bandwidth
28/// instead of `chunk_size / RTT` (the same bandwidth-delay-product fill that
29/// [`DEFAULT_UPLOAD_WINDOW`] provides for uploads). Independent of
30/// `concurrency` (cross-file) and of the upload window. Kept modest because
31/// the WASM engine buffers in-flight chunks in a reorder map so it can emit
32/// them to the SDK strictly in order (append-mode writes); the buffer bound
33/// is `window * download_chunk_size`.
34pub const DEFAULT_DOWNLOAD_WINDOW: usize = 4;
35
36/// Default chunk size for parallel (byte-range) downloads — 256 KiB.
37///
38/// Deliberately smaller than the 2 MiB upload chunk: the WASM engine holds
39/// up to `download_window` of these in a reorder buffer while waiting for
40/// them to arrive in order, so `window * chunk_size` (4 × 256 KiB = 1 MiB by
41/// default) is the engine's worst-case extra allocation — comfortably under
42/// the ~2 MiB per-file memory budget.
43pub const DEFAULT_DOWNLOAD_CHUNK_SIZE: u64 = 256 * 1024;
44
45/// A file with fewer remaining bytes than this stays on the sequential
46/// single-connection download path; larger files use the parallel range-GET
47/// path (which has per-request overhead that only pays off at size).
48pub const MIN_PARALLEL_DOWNLOAD_BYTES: u64 = 512 * 1024;
49
50/// Server-side TTL after which an unfinished "session" upload temp (and its
51/// `.blocks` range sidecar) is considered stale and garbage-collected.
52///
53/// tus `Expiration`-style: a client that vanishes mid-upload would otherwise
54/// leave its shared temp behind forever; the server sweeps temps older than
55/// this. Uploads that are actively being written keep their temp fresh, so
56/// this never interrupts an in-flight transfer.
57pub const DEFAULT_SESSION_TTL: std::time::Duration = std::time::Duration::from_secs(24 * 3600);
58
59/// Default maximum retry count for a failed chunk before failing the task.
60pub const MAX_RETRIES: u32 = 3;
61
62/// Default cap for a single upload (100 GiB, configurable by the server).
63pub const DEFAULT_MAX_UPLOAD_SIZE: u64 = 100 * 1024 * 1024 * 1024;
64
65/// HTTP header advertising the compression algorithm on a body stream.
66///
67/// Value is a [`CompressionFormat`](crate::compress::CompressionFormat)
68/// name (e.g. `zrip`). Mirrored by `Content-Encoding` when applicable.
69pub const HEADER_COMPRESS: &str = "x-libfw-compress";
70
71/// HTTP header carrying the requested zrip compression level.
72///
73/// Sent by the client on both upload and download; on download the server
74/// validates/clamps it and **echoes the actual level used** back on the same
75/// header, so a client can never assume its request was honored verbatim.
76/// Absent (older clients) → the server's default level.
77pub const HEADER_COMPRESS_LEVEL: &str = "x-libfw-compress-level";
78
79/// HTTP header carrying JSON-encoded [`FileMeta`](crate::metadata::FileMeta)
80/// for an upload or download.
81pub const HEADER_FILE_META: &str = "x-libfw-file-meta";
82
83/// HTTP header carrying the byte offset to resume an interrupted upload.
84pub const HEADER_OFFSET: &str = "x-libfw-offset";
85
86/// HTTP header marking an upload request as the FINAL chunk of a file.
87///
88/// When present (value `1`/`true`), the server verifies that the resulting
89/// file size equals the declared `x-libfw-file-meta` size before committing,
90/// so a client cannot commit a truncated file. Absent (older clients) skips
91/// the check — the header is optional and backward compatible.
92pub const HEADER_FINAL: &str = "x-libfw-final";
93
94/// HTTP header carrying a per-upload **session id**.
95///
96/// When present, the upload uses the concurrent "session" protocol: each
97/// chunk carries its ABSOLUTE `x-libfw-offset` and is written into a shared
98/// per-session temp file (positional writes), and the final
99/// `x-libfw-final` request commits it. This lets a client pipeline many
100/// chunks in flight (hiding round-trip latency) instead of serializing one
101/// request per chunk. Absent → legacy per-request sequential upload.
102///
103/// A session id should be **stable for a given file version** (derived from
104/// the ETag) so an interrupted upload can be found again on resume: the
105/// shared temp file is keyed by this id, and the server persists which byte
106/// ranges have already been received (see [`HEADER_SESSION_STATUS`]).
107pub const HEADER_SESSION: &str = "x-libfw-session";
108
109/// HTTP header turning a session `POST` into a **status probe**.
110///
111/// When present (value `1`/`probe`) on a session request, the server does
112/// NOT write the body. Instead it opens (creating if needed) the shared
113/// per-session temp and replies with the JSON-encoded byte ranges already
114/// received for that session: `{"ranges": [[start,end], ...]}`. The client
115/// uses this to compute which blocks are still missing and re-send only
116/// those — a BitTorrent-style "only retransmit the broken parts" resume.
117///
118/// Absent (older clients) keeps the previous behavior; legacy servers that
119/// ignore this header simply write nothing for the empty probe body and
120/// return an empty range set, which degrades to a full re-send (idempotent
121/// positional writes make that correct too).
122pub const HEADER_SESSION_STATUS: &str = "x-libfw-session-status";
123
124/// HTTP header carrying the (optional) transfer version handshake.
125pub const HEADER_PROTOCOL: &str = "x-libfw-protocol";
126
127/// Protocol name used in the handshake header.
128pub const PROTOCOL_NAME: &str = "libfw";
129
130/// Protocol version used in the handshake header.
131pub const PROTOCOL_VERSION: &str = "1";
132
133/// The canonical `x-libfw-protocol` header value (e.g. `libfw/1`).
134///
135/// Both the server and the WASM client must agree on this exact value; the
136/// server rejects (426) requests that explicitly advertise a different one.
137/// Keep in sync with [`PROTOCOL_NAME`] / [`PROTOCOL_VERSION`].
138pub fn protocol_header_value() -> &'static str {
139 "libfw/1"
140}
141
142/// Whether a received `x-libfw-protocol` handshake value is compatible with
143/// this build of the library.
144pub fn protocol_compatible(value: &str) -> bool {
145 value.trim().eq_ignore_ascii_case(protocol_header_value())
146}