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!(
245 "{} Protocol {}",
246 "╰".dimmed(),
247 if nbd {
248 "NBD".magenta()
249 } else {
250 "HTTP".magenta()
251 }
252 );
253 println!();
254 }
255
256 tokio::runtime::Builder::new_multi_thread()
257 .enable_all()
258 .build()?
259 .block_on(async {
260 let backend = Arc::new(MmapBackend::new(std::path::Path::new(&hexz_path))?);
261 let snap = HexzFile::open(backend, None)?;
262
263 if nbd {
264 hexz_server::serve_nbd(snap, port, bind).await
265 } else {
266 hexz_server::serve_http(snap, port, bind).await
267 }
268 })
269}