Skip to main content

hexz_cli/cmd/sys/
serve.rs

1//! HTTP server for exposing Hexz archives over network protocols.
2//!
3//! This command starts a server that exposes Hexz archive data over various
4//! network protocols (HTTP, NBD, S3), enabling remote access without local
5//! archive files. Supports daemon mode for background operation and is designed
6//! for high-performance remote archive access.
7//!
8//! # Server Modes
9//!
10//! ## HTTP Mode (Default)
11//!
12//! **Protocol:** HTTP/1.1 with Range request support
13//! **Port:** Configurable (default: varies by invocation)
14//! **Endpoints:**
15//! - `GET /disk` - Serves disk image stream
16//! - `GET /memory` - Serves memory dump stream (if present)
17//! - `GET /info` - Returns archive metadata (JSON)
18//!
19//! **Range Request Support:**
20//! - Supports `Range: bytes=start-end` header
21//! - Enables efficient random access and partial reads
22//! - Used by HTTP storage backend for transparent remote mounting
23//!
24//! **Use Cases:**
25//! - Remote archive access over networks
26//! - HTTP storage backend testing
27//! - Web-based archive browsing
28//! - Container registry-like archive distribution
29//!
30//! **Performance:**
31//! - Sequential reads: ~100-500 MB/s (network-bound)
32//! - Random reads: ~1000-5000 IOPS (depends on network latency)
33//! - Concurrent connections: Handled via Tokio async runtime
34//!
35//! ## NBD Mode (`--nbd`)
36//!
37//! **Protocol:** NBD (Network Block Device) protocol
38//! **Port:** Configurable
39//! **Features:**
40//! - Block device semantics (read-only currently)
41//! - Compatible with standard NBD clients (`nbd-client`)
42//! - Lower overhead than HTTP for sequential access
43//!
44//! **Use Cases:**
45//! - Remote block device mounting
46//! - Performance testing and benchmarking
47//! - Integration with NBD-aware tools
48//!
49//! **Client Usage:**
50//! ```bash
51//! # Start NBD server
52//! hexz serve archive.hxz --nbd --port 10809
53//!
54//! # Connect from client
55//! sudo nbd-client server-ip 10809 /dev/nbd0
56//! sudo mount /dev/nbd0 /mnt
57//! ```
58//!
59//! ## S3 Gateway Mode (`--s3`)
60//!
61//! **Protocol:** S3-compatible REST API
62//! **Status:** Not yet implemented
63//! **Planned Features:**
64//! - S3 `GetObject` with range support
65//! - Bucket/object listing
66//! - Compatible with S3 storage backend
67//!
68//! # Server Configuration
69//!
70//! **Port Binding:**
71//! - Binds to `127.0.0.1:<port>` (localhost only)
72//! - Default ports vary by mode (consult CLI help)
73//! - Ensure firewall rules allow inbound connections
74//!
75//! **Daemon Mode:**
76//! - Detaches from terminal and runs in background
77//! - Logs redirected to `/tmp/hexz-serve.log` and `/tmp/hexz-serve.err`
78//! - Working directory: Current directory (not `/`)
79//! - No PID file created (use systemd or similar for management)
80//!
81//! **Archive Loading:**
82//! - Opens archive read-only
83//! - Loads compression dictionary if present
84//! - Initializes appropriate decompressor (LZ4 or Zstd)
85//! - No caching configured (each request decompresses on-demand)
86//!
87//! # Security Considerations
88//!
89//! **WARNING:** This server has no authentication or encryption.
90//!
91//! - **Do not expose to untrusted networks**
92//! - Use behind a reverse proxy (nginx, haproxy) for production
93//! - Consider TLS termination at proxy level
94//! - Implement authentication at proxy layer
95//!
96//! **Recommendations:**
97//! - Bind to localhost (`127.0.0.1`) for local access
98//! - Use SSH tunneling for remote access: `ssh -L 8080:localhost:8080 server`
99//! - Deploy behind VPN for internal network access
100//! - Use firewall rules to restrict access
101//!
102//! # Performance Tuning
103//!
104//! **Concurrency:**
105//! - Uses Tokio multi-threaded runtime (all CPU cores)
106//! - Each connection handled concurrently
107//! - No per-connection memory overhead beyond request buffers
108//!
109//! **Compression:**
110//! - Decompression happens on-demand for each request
111//! - No server-side caching (stateless design)
112//! - LZ4: ~800-2000 MB/s decompression per core
113//! - Zstd: ~400-800 MB/s decompression per core
114//!
115//! **Network:**
116//! - Performance limited by network bandwidth and latency
117//! - Use 10 Gbps Ethernet or faster for multi-GB archives
118//! - Consider proximity to clients (same datacenter/region)
119//!
120//! # Common Usage Patterns
121//!
122//! ```bash
123//! # Start HTTP server on port 8080
124//! hexz serve archive.hxz --port 8080
125//!
126//! # Start as daemon (background process)
127//! hexz serve archive.hxz --port 8080 --daemon
128//!
129//! # Start NBD server
130//! hexz serve archive.hxz --nbd --port 10809
131//!
132//! # Access from remote client
133//! curl -H "Range: bytes=0-1024" http://server:8080/disk
134//!
135//! # Mount via HTTP backend
136//! hexz mount http://server:8080 /mnt
137//! ```
138
139use anyhow::Result;
140use colored::Colorize;
141use daemonize::Daemonize;
142use hexz_core::Archive as HexzFile;
143use hexz_store::local::MmapBackend;
144use std::fs::File;
145use std::sync::Arc;
146
147/// Executes the serve command to start a network server.
148///
149/// Opens a Hexz archive and starts a server that exposes it over HTTP, NBD,
150/// or S3 protocol. The server runs until interrupted (Ctrl+C) or, in daemon mode,
151/// until explicitly killed.
152///
153/// # Arguments
154///
155/// * `hexz_path` - Path to the `.hxz` archive file to serve
156/// * `port` - TCP port to bind to
157/// * `daemon` - If true, daemonize the process and run in background
158/// * `nbd` - If true, use NBD protocol; otherwise use HTTP
159/// * `s3` - If true, use S3 gateway mode (not yet implemented)
160///
161/// # Server Startup Sequence
162///
163/// 1. **Daemonization** (if requested):
164///    - Detach from terminal
165///    - Redirect stdout to `/tmp/hexz-serve.log`
166///    - Redirect stderr to `/tmp/hexz-serve.err`
167///
168/// 2. **Archive Loading**:
169///    - Open archive file via `MmapBackend`
170///    - Read and parse header
171///    - Load compression dictionary if present
172///    - Initialize decompressor (LZ4 or Zstd)
173///
174/// 3. **Server Start**:
175///    - Create Tokio multi-threaded runtime
176///    - Start HTTP or NBD server on specified port
177///    - Listen for incoming connections
178///
179/// 4. **Serving**:
180///    - Handle requests until process is interrupted
181///    - Each request decompresses data on-demand
182///    - No persistent state or caching
183///
184/// # Protocol Selection
185///
186/// - If `nbd=true`: Start NBD server via `hexz_server::serve_nbd()`
187/// - If `s3=true`: Reserved for future S3 gateway (currently prints error)
188/// - Otherwise: Start HTTP server via `hexz_server::serve_http()`
189///
190/// # Errors
191///
192/// Returns an error if:
193/// - Archive file cannot be opened or read
194/// - Header or dictionary cannot be parsed (corrupted archive)
195/// - Port is already in use (address binding fails)
196/// - Daemonization fails (resource limits, permissions)
197/// - Server fails to start (Tokio runtime error)
198///
199/// # Examples
200///
201/// ```no_run
202/// use hexz_cli::cmd::sys::serve;
203///
204/// // Start HTTP server on port 8080
205/// serve::run(
206///     "archive.hxz",
207///     8080,
208///     "127.0.0.1",
209///     false, // not daemon
210///     false, // HTTP mode
211/// )?;
212///
213/// // Start NBD server as daemon
214/// serve::run(
215///     "archive.hxz",
216///     10809,
217///     "127.0.0.1",
218///     true,  // daemon mode
219///     true,  // NBD mode
220/// )?;
221/// # Ok::<(), anyhow::Error>(())
222/// ```
223pub fn run(hexz_path: &str, port: u16, bind: &str, daemon: bool, nbd: bool) -> Result<()> {
224    if daemon {
225        let log_dir = std::env::var("XDG_RUNTIME_DIR")
226            .or_else(|_| std::env::var("TMPDIR"))
227            .unwrap_or_else(|_| "/tmp".to_string());
228        let stdout = File::create(format!("{log_dir}/hexz-serve.log"))
229            .or_else(|_| File::create("/dev/null"))?;
230        let stderr = File::create(format!("{log_dir}/hexz-serve.err"))
231            .or_else(|_| File::create("/dev/null"))?;
232
233        Daemonize::new()
234            .working_directory(".")
235            .stdout(stdout)
236            .stderr(stderr)
237            .start()?;
238    }
239
240    if !daemon {
241        println!("{} Starting Hexz server", "╭".dimmed());
242        println!("{} Port      {}", "│".dimmed(), port.to_string().cyan());
243        println!("{} Bind      {}", "│".dimmed(), bind.cyan());
244        println!("{} Protocol  {}", "╰".dimmed(), if nbd { "NBD".magenta() } else { "HTTP".magenta() });
245        println!();
246    }
247
248    tokio::runtime::Builder::new_multi_thread()
249        .enable_all()
250        .build()?
251        .block_on(async {
252            let backend = Arc::new(MmapBackend::new(std::path::Path::new(&hexz_path))?);
253            let snap = HexzFile::open(backend, None)?;
254
255            if nbd {
256                hexz_server::serve_nbd(snap, port, bind).await
257            } else {
258                hexz_server::serve_http(snap, port, bind).await
259            }
260        })
261}