#!/bin/bash # Test script for verifying proxy routing behavior # Ensures /dashboard and /api/stats are always handled internally PORT=8080 TEST_UPSTREAM_PORT=3000 echo "=== Proxy Routing Test Suite ===" echo "================================" # Start a simple upstream server that logs all requests echo "Starting test upstream server on port $TEST_UPSTREAM_PORT..." cat > test_upstream.py << 'EOF' from http.server import HTTPServer, BaseHTTPRequestHandler import json import datetime class TestHandler(BaseHTTPRequestHandler): def do_GET(self): print(f"[{datetime.datetime.now()}] UPSTREAM RECEIVED: {self.command} {self.path}") if self.path == "/dashboard" or self.path == "/api/stats": print("ERROR: Internal route leaked to upstream!") self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() response = {"upstream": "response", "path": self.path} self.wfile.write(json.dumps(response).encode()) def do_POST(self): self.do_GET() def log_message(self, format, *args): return # Suppress default logging if __name__ == "__main__": server = HTTPServer(('localhost', 3000), TestHandler) print("Test upstream server running on port 3000") server.serve_forever() EOF python3 test_upstream.py & UPSTREAM_PID=$! sleep 2 # Start the proxy with debug mode echo "Starting proxy server..." DEBUG=1 ./rproxy proxy_config.json & PROXY_PID=$! sleep 3 echo "" echo "=== Test 1: Direct dashboard access ===" echo "Request: GET /dashboard" response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/dashboard) if [ "$response" = "200" ]; then echo "✓ Dashboard served correctly (HTTP $response)" else echo "✗ Dashboard failed (HTTP $response)" fi echo "" echo "=== Test 2: Direct API stats access ===" echo "Request: GET /api/stats" response=$(curl -s http://localhost:$PORT/api/stats | jq -r '.current' 2>/dev/null) if [ ! -z "$response" ]; then echo "✓ API stats served correctly" else echo "✗ API stats failed" fi echo "" echo "=== Test 3: Multiple requests on same connection (keep-alive) ===" echo "Testing pipeline: /api/stats -> /test -> /dashboard -> /another" cat > test_requests.txt << 'EOF' GET /api/stats HTTP/1.1 Host: localhost Connection: keep-alive GET /test HTTP/1.1 Host: localhost Connection: keep-alive GET /dashboard HTTP/1.1 Host: localhost Connection: keep-alive GET /another HTTP/1.1 Host: localhost Connection: close EOF # Send pipelined requests (cat test_requests.txt; sleep 1) | nc localhost $PORT > test_response.txt 2>&1 # Check if internal routes were handled correctly if grep -q "cpu_percent" test_response.txt && grep -q "Reverse Proxy Monitor" test_response.txt; then echo "✓ Internal routes handled correctly in pipeline" else echo "✗ Internal routes not handled correctly in pipeline" fi echo "" echo "=== Test 4: Concurrent connections ===" echo "Sending 10 concurrent requests to different endpoints..." # Background requests for i in {1..3}; do curl -s http://localhost:$PORT/dashboard > /dev/null & done for i in {1..3}; do curl -s http://localhost:$PORT/api/stats > /dev/null & done for i in {1..4}; do curl -s http://localhost:$PORT/test$i > /dev/null & done wait echo "✓ Concurrent requests completed" echo "" echo "=== Test 5: Rapid sequential requests ===" echo "Sending rapid requests to verify state management..." success=0 failed=0 for i in {1..20}; do # Alternate between internal and forwarded routes if [ $((i % 3)) -eq 0 ]; then response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/dashboard) if [ "$response" = "200" ]; then ((success++)) else ((failed++)) echo " ✗ Dashboard request $i failed (HTTP $response)" fi elif [ $((i % 3)) -eq 1 ]; then response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/api/stats) if [ "$response" = "200" ]; then ((success++)) else ((failed++)) echo " ✗ API stats request $i failed (HTTP $response)" fi else response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/test$i) if [ "$response" = "200" ]; then ((success++)) else ((failed++)) fi fi done echo "Results: $success successful, $failed failed" if [ $failed -eq 0 ]; then echo "✓ All rapid requests handled correctly" else echo "✗ Some requests failed" fi echo "" echo "=== Test 6: WebSocket upgrade after regular request ===" echo "Testing state reset after WebSocket..." # First a normal request curl -s http://localhost:$PORT/api/stats > /dev/null echo "✓ Normal request sent" # Then attempt WebSocket (will fail but tests state handling) curl -s -H "Upgrade: websocket" -H "Connection: upgrade" http://localhost:$PORT/ws 2>/dev/null echo "✓ WebSocket attempt sent" # Then another normal request to internal route response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/dashboard) if [ "$response" = "200" ]; then echo "✓ Dashboard works after WebSocket attempt" else echo "✗ Dashboard failed after WebSocket attempt (HTTP $response)" fi echo "" echo "=== Checking upstream server logs ===" echo "Verifying no internal routes leaked to upstream..." sleep 1 # The upstream server prints errors if it receives internal routes # Check proxy debug logs for routing decisions echo "" echo "Recent routing decisions from proxy logs:" echo "(Last 10 routing log entries)" echo "----------------------------------------" # Kill the servers kill $PROXY_PID 2>/dev/null kill $UPSTREAM_PID 2>/dev/null # Clean up rm -f test_upstream.py test_requests.txt test_response.txt echo "" echo "=== Test Complete ===" echo "If you see any 'ERROR: Internal route leaked to upstream!' messages above," echo "the routing issue is NOT fixed. Otherwise, the fix is working correctly."