Skip to content

Commit 070b379

Browse files
ammarioclaude
andcommitted
feat: add --server mode for standalone proxy operation
Implements a new --server flag that allows httpjail to run as a standalone HTTP/HTTPS proxy server without executing any commands. This enables use cases where httpjail acts as a persistent proxy service that applications can connect to. Changes: - Add --server CLI flag with appropriate conflict checks - Implement server mode logic with graceful shutdown handling - Update README with usage examples and documentation - Mark feature as completed in TODO list 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 38cb1ae commit 070b379

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ cargo install httpjail
2323
## MVP TODO
2424

2525
- [ ] Update README to be more reflective of AI agent restrictions
26-
- [ ] Add a `--server` mode that runs the proxy server but doesn't execute the command
26+
- [x] Add a `--server` mode that runs the proxy server but doesn't execute the command
2727
- [ ] Expand test cases to include WebSockets
2828

2929
## Quick Start
@@ -43,6 +43,10 @@ httpjail -r "allow-get: api\.github\.com" -r "deny: .*" -- git pull
4343

4444
# Use config file for complex rules
4545
httpjail --config rules.txt -- python script.py
46+
47+
# Run as standalone proxy server (no command execution)
48+
httpjail --server -r "allow: .*"
49+
# Then set HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8443 in your application
4650
```
4751

4852
## Architecture Overview
@@ -172,6 +176,11 @@ httpjail --dry-run --config rules.txt -- ./app
172176
# Verbose logging
173177
httpjail -vvv -r "allow: .*" -- curl https://example.com
174178

179+
# Server mode - run as standalone proxy without executing commands
180+
httpjail --server -r "allow: github\.com" -r "deny: .*"
181+
# Proxy listens on localhost:8080 (HTTP) and localhost:8443 (HTTPS)
182+
# Configure applications to use these proxy endpoints
183+
175184
```
176185

177186
## TLS Interception

src/main.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,17 @@ struct Args {
6464
#[arg(long = "cleanup", hide = true)]
6565
cleanup: bool,
6666

67+
/// Run as standalone proxy server (without executing a command)
68+
#[arg(
69+
long = "server",
70+
conflicts_with = "cleanup",
71+
conflicts_with = "weak",
72+
conflicts_with = "timeout"
73+
)]
74+
server: bool,
75+
6776
/// Command and arguments to execute
68-
#[arg(trailing_var_arg = true, required_unless_present = "cleanup")]
77+
#[arg(trailing_var_arg = true, required_unless_present_any = ["cleanup", "server"])]
6978
command: Vec<String>,
7079
}
7180

@@ -306,6 +315,11 @@ async fn main() -> Result<()> {
306315
return Ok(());
307316
}
308317

318+
// Handle server mode
319+
if args.server {
320+
info!("Starting httpjail in server mode");
321+
}
322+
309323
// Build rules from command line arguments
310324
let rules = build_rules(&args)?;
311325
let rule_engine = RuleEngine::new(rules, args.dry_run, args.log_only);
@@ -345,6 +359,38 @@ async fn main() -> Result<()> {
345359
actual_http_port, actual_https_port
346360
);
347361

362+
// In server mode, just run the proxy server
363+
if args.server {
364+
// Set up signal handler for graceful shutdown
365+
let shutdown = Arc::new(AtomicBool::new(false));
366+
let shutdown_clone = shutdown.clone();
367+
368+
ctrlc::set_handler(move || {
369+
if !shutdown_clone.load(Ordering::SeqCst) {
370+
info!("Received interrupt signal, shutting down server...");
371+
shutdown_clone.store(true, Ordering::SeqCst);
372+
std::process::exit(0);
373+
}
374+
})
375+
.expect("Error setting signal handler");
376+
377+
info!(
378+
"Server running on ports {} (HTTP) and {} (HTTPS). Press Ctrl+C to stop.",
379+
actual_http_port, actual_https_port
380+
);
381+
382+
// Keep the server running until interrupted
383+
loop {
384+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
385+
if shutdown.load(Ordering::SeqCst) {
386+
break;
387+
}
388+
}
389+
390+
return Ok(());
391+
}
392+
393+
// Normal mode: create jail and execute command
348394
// Create jail configuration with actual bound ports
349395
let mut jail_config = JailConfig::new();
350396
jail_config.http_proxy_port = actual_http_port;
@@ -539,6 +585,7 @@ mod tests {
539585
timeout: None,
540586
no_jail_cleanup: false,
541587
cleanup: false,
588+
server: false,
542589
command: vec![],
543590
};
544591

0 commit comments

Comments
 (0)