use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use zisk_common::io::{QuicStreamWriter, StreamWrite};
fn read_binary_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut file = File::open(path)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
Ok(buffer)
}
fn main() -> io::Result<()> {
let _ = rustls::crypto::ring::default_provider().install_default();
let args: Vec<String> = std::env::args().collect();
if args.len() != 3 {
eprintln!("Usage: {} <binary_file> <bind_address>", args[0]);
eprintln!("Example: {} hints.bin 127.0.0.1:8080", args[0]);
std::process::exit(1);
}
let file_path = &args[1];
let bind_address = &args[2];
let bind_addr: std::net::SocketAddr = bind_address.parse().map_err(|e| {
io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid bind address: {}", e))
})?;
let file_data = read_binary_file(file_path)?;
println!("Read {} bytes from: {}", file_data.len(), file_path);
println!("========================================");
println!("Hints QUIC Server");
println!("========================================");
println!("Binary file: {}", file_path);
println!("Bind address: {}", bind_addr);
println!();
let mut writer = QuicStreamWriter::new(bind_addr).map_err(io::Error::other)?;
println!("QUIC server created successfully");
println!("Waiting for client connection...");
writer.open().map_err(io::Error::other)?;
println!("Client connected! Starting data transfer...");
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_clone = Arc::clone(&shutdown);
thread::spawn(move || {
println!("Press '0' + Enter to close at any time");
let stdin = io::stdin();
let mut buffer = String::new();
loop {
buffer.clear();
if stdin.read_line(&mut buffer).is_ok() && buffer.trim() == "0" {
println!("Shutdown signal received!");
shutdown_clone.store(true, Ordering::Relaxed);
break;
}
}
});
const HINT_SIZE: usize = 26 * 8; const HINTS_PER_BATCH: usize = 100;
const BATCH_SIZE: usize = HINTS_PER_BATCH * HINT_SIZE;
if file_data.len() < 16 {
eprintln!("Error: File too small (need at least 16 bytes for header+footer)");
return Ok(());
}
let mut offset = 0;
let mut message_num = 0;
loop {
if shutdown.load(Ordering::Relaxed) {
println!("\nShutdown requested, exiting...");
break;
}
if offset >= file_data.len() {
println!("All data sent successfully!");
println!("Connection active. Press '0' to close...");
while !shutdown.load(Ordering::Relaxed) {
thread::sleep(Duration::from_millis(100));
}
break;
}
let (start, end) = if offset == 0 {
(0, 8)
} else if offset + 8 >= file_data.len() {
(file_data.len() - 8, file_data.len())
} else {
let data_end = file_data.len() - 8; let remaining_data = data_end - offset;
let batch_size = std::cmp::min(BATCH_SIZE, remaining_data);
(offset, offset + batch_size)
};
let chunk = &file_data[start..end];
match writer.write(chunk) {
Ok(_) => {
message_num += 1;
println!(
"Message {}: Sent {} bytes (offset {}-{})",
message_num,
chunk.len(),
start,
end
);
offset = end;
}
Err(e) => {
eprintln!("Error writing to QUIC stream: {}", e);
break;
}
}
}
println!("Closing connection...");
let _ = writer.close();
println!("Server shutting down...");
Ok(())
}