derusted 0.2.0

Programmable HTTPS interception and traffic inspection engine for security-critical applications
Documentation
// HTTP End-to-End Tests
// Phase 5: Real HTTP/HTTPS requests through the proxy

#[cfg(test)]
mod e2e_tests {
    
    use std::process::{Child, Command};
    use std::thread;
    use std::time::Duration;

    struct ProxyTestFixture {
        proxy_process: Option<Child>,
        port: u16,
        token: String,
    }

    impl ProxyTestFixture {
        fn new() -> Self {
            let port = 8443;
            let token = Self::generate_test_token();

            Self {
                proxy_process: None,
                port,
                token,
            }
        }

        fn generate_test_token() -> String {
            // Generate a test JWT token
            // In production, use proper JWT library
            use std::time::{SystemTime, UNIX_EPOCH};

            let payload = serde_json::json!({
                "token_id": "test-token-e2e",
                "user_id": 1,
                "allowed_regions": ["us-east", "eu-west"],
                "exp": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600,
                "iat": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
            });

            // This would normally use jsonwebtoken crate
            // For now, return a placeholder
            "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9pZCI6InRlc3QtdG9rZW4tZTJlIiwidXNlcl9pZCI6MSwiYWxsb3dlZF9yZWdpb25zIjpbInVzLWVhc3QiLCJldS13ZXN0Il0sImV4cCI6MTc2MzcyNzcwNSwiaWF0IjoxNzYzNzI0MTA1fQ.RU7SjgZVT3JUH1h1PYhJEhaXDEXym-BW3QsKw3q5D0c".to_string()
        }

        fn start_proxy(&mut self) -> Result<(), Box<dyn std::error::Error>> {
            // Start proxy in background
            let child = Command::new("./target/release/probe-proxy")
                .env("TLS_CERT_PATH", "/tmp/test-certs/cert.pem")
                .env("TLS_KEY_PATH", "/tmp/test-certs/key.pem")
                .env("PROXY_PORT", self.port.to_string())
                .env("JWT_SECRET", "test_secret_at_least_32_characters!!")
                .env("HTTP_PROXY_ENABLED", "true")
                .spawn()?;

            self.proxy_process = Some(child);

            // Wait for proxy to start
            thread::sleep(Duration::from_secs(2));

            Ok(())
        }

        fn stop_proxy(&mut self) {
            if let Some(mut child) = self.proxy_process.take() {
                let _ = child.kill();
                let _ = child.wait();
            }
        }
    }

    impl Drop for ProxyTestFixture {
        fn drop(&mut self) {
            self.stop_proxy();
        }
    }

    // Manual test - requires running proxy
    #[test]
    #[ignore]
    fn test_proxy_manual_verification() {
        println!("
=== Manual Proxy Test Instructions ===

1. Start the proxy:
   TLS_CERT_PATH=/tmp/test-certs/cert.pem \\
   TLS_KEY_PATH=/tmp/test-certs/key.pem \\
   PROXY_PORT=8443 \\
   JWT_SECRET='test_secret_at_least_32_characters!!' \\
   HTTP_PROXY_ENABLED=true \\
   ./target/release/probe-proxy

2. In another terminal, test HTTP GET:
   TOKEN='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9pZCI6InRlc3QtdG9rZW4tMTIzIiwidXNlcl9pZCI6MSwiYWxsb3dlZF9yZWdpb25zIjpbInVzLWVhc3QiLCJldS13ZXN0Il0sImV4cCI6MTc2MzcyNzcwNSwiaWF0IjoxNzYzNzI0MTA1fQ.RU7SjgZVT3JUH1h1PYhJEhaXDEXym-BW3QsKw3q5D0c'

   printf \"GET http://www.google.com/ HTTP/1.1\\r\\n\" \\
          \"Host: www.google.com\\r\\n\" \\
          \"Proxy-Authorization: Bearer $TOKEN\\r\\n\" \\
          \"Connection: close\\r\\n\\r\\n\" | \\
   openssl s_client -connect localhost:8443 -quiet 2>&1 | head -20

3. Test HTTP POST:
   BODY='{{\"test\":\"data\"}}'
   printf \"POST http://httpbin.org/post HTTP/1.1\\r\\n\" \\
          \"Host: httpbin.org\\r\\n\" \\
          \"Proxy-Authorization: Bearer $TOKEN\\r\\n\" \\
          \"Content-Type: application/json\\r\\n\" \\
          \"Content-Length: ${{#BODY}}\\r\\n\" \\
          \"Connection: close\\r\\n\\r\\n\" \\
          \"$BODY\" | \\
   openssl s_client -connect localhost:8443 -quiet 2>&1 | head -40

4. Test SSRF protection (should get 403):
   printf \"GET http://localhost:22/ HTTP/1.1\\r\\n\" \\
          \"Host: localhost\\r\\n\" \\
          \"Proxy-Authorization: Bearer $TOKEN\\r\\n\" \\
          \"Connection: close\\r\\n\\r\\n\" | \\
   openssl s_client -connect localhost:8443 -quiet 2>&1 | head -10

5. Test missing auth (should get 407):
   printf \"GET http://www.google.com/ HTTP/1.1\\r\\n\" \\
          \"Host: www.google.com\\r\\n\" \\
          \"Connection: close\\r\\n\\r\\n\" | \\
   openssl s_client -connect localhost:8443 -quiet 2>&1 | head -10

=== Expected Results ===
- Test 2: HTTP 200 OK with Google HTML
- Test 3: HTTP 200 OK with JSON echo from httpbin
- Test 4: HTTP 403 Forbidden (SSRF blocked)
- Test 5: HTTP 407 Proxy Authentication Required
        ");
    }
}

/// Load Test Scripts
///
/// These are bash scripts to be run manually for load testing
#[cfg(test)]
mod load_tests {
    #[test]
    #[ignore]
    fn generate_load_test_scripts() {
        let script_dir = "/tmp/proxy-load-tests";
        std::fs::create_dir_all(script_dir).ok();

        // Scenario 1: Basic throughput
        let script1 = r#"#!/bin/bash
# Load Test Scenario 1: Basic Throughput (10k requests, 100 concurrent)

TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9pZCI6InRlc3QtdG9rZW4tMTIzIiwidXNlcl9pZCI6MSwiYWxsb3dlZF9yZWdpb25zIjpbInVzLWVhc3QiLCJldS13ZXN0Il0sImV4cCI6MTc2MzcyNzcwNSwiaWF0IjoxNzYzNzI0MTA1fQ.RU7SjgZVT3JUH1h1PYhJEhaXDEXym-BW3QsKw3q5D0c"

echo "=== Load Test 1: Basic Throughput ==="
echo "Requests: 10,000"
echo "Concurrency: 100"
echo "Target: httpbin.org/get"
echo ""

# Note: This requires modifying h2load to support HTTPS proxy
# Or use a tool like wrk with proxy support

echo "Simulating with curl in loop..."
for i in {1..100}; do
  (printf "GET http://httpbin.org/get HTTP/1.1\r\nHost: httpbin.org\r\nProxy-Authorization: Bearer $TOKEN\r\nConnection: close\r\n\r\n" | \
   openssl s_client -connect localhost:8443 -quiet 2>&1 > /dev/null) &
done
wait

echo "Completed 100 requests"
"#;

        // Scenario 2: High concurrency
        let script2 = r#"#!/bin/bash
# Load Test Scenario 2: High Concurrency (1000 concurrent connections)

TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9pZCI6InRlc3QtdG9rZW4tMTIzIiwidXNlcl9pZCI6MSwiYWxsb3dlZF9yZWdpb25zIjpbInVzLWVhc3QiLCJldS13ZXN0Il0sImV4cCI6MTc2MzcyNzcwNSwiaWF0IjoxNzYzNzI0MTA1fQ.RU7SjgZVT3JUH1h1PYhJEhaXDEXym-BW3QsKw3q5D0c"

echo "=== Load Test 2: High Concurrency ==="
echo "Concurrency: 1000"
echo "Target: httpbin.org/get"
echo ""

# Monitor proxy memory usage
echo "Proxy Memory Usage:"
ps aux | grep probe-proxy | grep -v grep | awk '{print $6 " KB"}'
"#;

        // Scenario 3: Large bodies
        let script3 = r#"#!/bin/bash
# Load Test Scenario 3: Large Response Bodies (10MB responses)

TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9pZCI6InRlc3QtdG9rZW4tMTIzIiwidXNlcl9pZCI6MSwiYWxsb3dlZF9yZWdpb25zIjpbInVzLWVhc3QiLCJldS13ZXN0Il0sImV4cCI6MTc2MzcyNzcwNSwiaWF0IjoxNzYzNzI0MTA1fQ.RU7SjgZVT3JUH1h1PYhJEhaXDEXym-BW3QsKw3q5D0c"

echo "=== Load Test 3: Large Bodies ==="
echo "Body Size: 10MB"
echo "Target: httpbin.org/bytes/10485760"
echo ""

printf "GET http://httpbin.org/bytes/10485760 HTTP/1.1\r\nHost: httpbin.org\r\nProxy-Authorization: Bearer $TOKEN\r\nConnection: close\r\n\r\n" | \
openssl s_client -connect localhost:8443 -quiet 2>&1 | wc -c

echo "Response received and measured"
"#;

        std::fs::write(format!("{}/scenario1_throughput.sh", script_dir), script1).ok();
        std::fs::write(format!("{}/scenario2_concurrency.sh", script_dir), script2).ok();
        std::fs::write(format!("{}/scenario3_large_bodies.sh", script_dir), script3).ok();

        println!("Load test scripts generated in: {}", script_dir);
        println!("Make executable with: chmod +x {}/*.sh", script_dir);
    }
}