Update, bug fixed.

This commit is contained in:
retoor 2025-09-25 06:37:51 +02:00
parent d60dea1c44
commit 59d075d8e0
7 changed files with 714 additions and 4 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@
*.bak
*.bak2
*.db
*.txt
rproxy

View File

@ -1976,12 +1976,29 @@ static int do_write(connection_t *conn) {
void handle_client_read(connection_t *conn) {
int bytes_read = do_read(conn);
if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
log_debug("[ROUTING] Closing connection fd=%d due to read error", conn->fd);
close_connection(conn->fd);
return;
}
buffer_t *buf = &conn->read_buf;
log_info("[ROUTING-DEBUG] handle_client_read fd=%d, state=%d, has_pair=%d, bytes_available=%zu, bytes_read=%d",
conn->fd, conn->state, conn->pair ? 1 : 0, buffer_available_read(buf), bytes_read);
// CRITICAL FIX: Always reset to reading headers if we're not actively forwarding
// This ensures internal routes are always checked first
if (conn->state == CLIENT_STATE_FORWARDING && conn->pair == NULL) {
log_info("[ROUTING-FIX] Resetting orphaned forwarding state to READING_HEADERS for fd=%d", conn->fd);
conn->state = CLIENT_STATE_READING_HEADERS;
}
// Don't process new requests while actively forwarding
if (conn->state == CLIENT_STATE_FORWARDING && conn->pair != NULL) {
log_info("[ROUTING-DEBUG] Skipping request processing - actively forwarding for fd=%d", conn->fd);
return;
}
// Process all complete requests in the buffer (for pipelining)
while (buffer_available_read(buf) > 0) {
char *data_start = buf->data + buf->head;
@ -2008,6 +2025,10 @@ void handle_client_read(connection_t *conn) {
return;
}
log_info("[ROUTING-DEBUG] Parsed request fd=%d: %s %s, Host: %s, Content-Length: %ld",
conn->fd, conn->request.method, conn->request.uri, conn->request.host,
conn->request.content_length);
// Calculate total request size including body
long long body_len = (conn->request.content_length > 0) ? conn->request.content_length : 0;
size_t total_request_len = headers_len + body_len;
@ -2021,9 +2042,21 @@ void handle_client_read(connection_t *conn) {
clock_gettime(CLOCK_MONOTONIC, &ts);
conn->request_start_time = ts.tv_sec + ts.tv_nsec / 1e9;
// Handle internal routes
// CRITICAL: Always check internal routes first, regardless of connection state
log_info("[ROUTING-CHECK] Checking if request is internal: method=%s, uri=%s",
conn->request.method, conn->request.uri);
if (strcmp(conn->request.method, "GET") == 0) {
if (strncmp(conn->request.uri, "/dashboard", 10) == 0) {
log_info("[ROUTING-INTERNAL] *** DASHBOARD REQUEST DETECTED *** fd=%d", conn->fd);
// Clean up any existing upstream connection
if (conn->pair) {
log_debug("[ROUTING] Closing existing upstream fd=%d before serving internal", conn->pair->fd);
close_connection(conn->pair->fd);
conn->pair = NULL;
}
conn->state = CLIENT_STATE_SERVING_INTERNAL;
serve_dashboard(conn);
buffer_consume(buf, total_request_len);
@ -2035,6 +2068,15 @@ void handle_client_read(connection_t *conn) {
continue;
}
if (strncmp(conn->request.uri, "/api/stats", 10) == 0) {
log_info("[ROUTING-INTERNAL] *** API STATS REQUEST DETECTED *** fd=%d", conn->fd);
// Clean up any existing upstream connection
if (conn->pair) {
log_debug("[ROUTING] Closing existing upstream fd=%d before serving internal", conn->pair->fd);
close_connection(conn->pair->fd);
conn->pair = NULL;
}
conn->state = CLIENT_STATE_SERVING_INTERNAL;
serve_stats_api(conn);
buffer_consume(buf, total_request_len);
@ -2048,6 +2090,9 @@ void handle_client_read(connection_t *conn) {
}
// This is a request to be forwarded to upstream
log_info("[ROUTING-FORWARD] >>> FORWARDING TO UPSTREAM <<< fd=%d: %s %s",
conn->fd, conn->request.method, conn->request.uri);
conn->vhost_stats = monitor_get_or_create_vhost_stats(conn->request.host);
monitor_record_request_start(conn->vhost_stats, conn->request.is_websocket);
@ -2067,6 +2112,22 @@ void handle_client_read(connection_t *conn) {
static void handle_forwarding(connection_t *conn) {
connection_t *pair = conn->pair;
if (!pair || pair->fd == -1) {
// If this is a client with no pair, reset it to reading headers
if (conn->type == CONN_TYPE_CLIENT) {
log_info("[ROUTING-ORPHAN] Client fd=%d lost upstream, resetting to READING_HEADERS", conn->fd);
conn->state = CLIENT_STATE_READING_HEADERS;
conn->pair = NULL;
// Clear old request data to avoid state confusion
memset(&conn->request, 0, sizeof(http_request_t));
conn->request.keep_alive = 1; // Default for HTTP/1.1
conn->request.content_length = -1;
// Try to read any pending requests
if (buffer_available_read(&conn->read_buf) > 0) {
log_info("[ROUTING-ORPHAN] Processing buffered data on orphaned client fd=%d", conn->fd);
handle_client_read(conn);
}
return;
}
close_connection(conn->fd);
return;
}
@ -2086,8 +2147,8 @@ static void handle_forwarding(connection_t *conn) {
}
}
memcpy(pair->write_buf.data + pair->write_buf.tail,
conn->read_buf.data + conn->read_buf.head,
memcpy(pair->write_buf.data + pair->write_buf.tail,
conn->read_buf.data + conn->read_buf.head,
data_to_forward);
pair->write_buf.tail += data_to_forward;
buffer_consume(&conn->read_buf, data_to_forward);
@ -2098,7 +2159,37 @@ static void handle_forwarding(connection_t *conn) {
// Handle connection closure
if (bytes_read == 0) {
// EOF received - perform half-close
// EOF received
log_info("[ROUTING-EOF] EOF on fd=%d (type=%d), keep-alive=%d, is_websocket=%d",
conn->fd, conn->type, conn->request.keep_alive, conn->request.is_websocket);
// For client connections with keep-alive, don't close immediately
if (conn->type == CONN_TYPE_CLIENT && conn->request.keep_alive && !conn->request.is_websocket) {
// Close the upstream connection
if (pair) {
log_info("[ROUTING-KEEPALIVE] Closing upstream fd=%d, keeping client fd=%d alive", pair->fd, conn->fd);
close_connection(pair->fd);
conn->pair = NULL;
}
// Reset client to reading headers for next request
conn->state = CLIENT_STATE_READING_HEADERS;
conn->half_closed = 0;
conn->write_shutdown = 0;
// Clear request data
memset(&conn->request, 0, sizeof(http_request_t));
conn->request.keep_alive = 1; // Default for HTTP/1.1
conn->request.content_length = -1;
// CRITICAL FIX: Process any buffered data (pipelined requests)
if (buffer_available_read(&conn->read_buf) > 0) {
log_info("[ROUTING-PIPELINE] Processing buffered pipelined request on fd=%d (%zu bytes)",
conn->fd, buffer_available_read(&conn->read_buf));
handle_client_read(conn);
}
return;
}
// Regular half-close for non-keep-alive connections
if (!conn->half_closed) {
conn->half_closed = 1;
shutdown(conn->fd, SHUT_RD);

207
test_routing.sh Executable file
View File

@ -0,0 +1,207 @@
#!/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."

185
test_routing_comprehensive.sh Executable file
View File

@ -0,0 +1,185 @@
#!/bin/bash
# Comprehensive test script for routing reliability
set -e
echo "=== Comprehensive Routing Test ==="
echo "This test verifies that /dashboard and /api/stats are ALWAYS handled internally"
echo ""
# Kill any existing proxy
pkill -f rproxy || true
sleep 1
# Start the proxy with debug output
echo "Starting proxy with debug logging..."
DEBUG=1 ./rproxy > proxy.log 2>&1 &
PROXY_PID=$!
sleep 2
# Check if proxy is running
if ! ps -p $PROXY_PID > /dev/null; then
echo "ERROR: Proxy failed to start"
cat proxy.log
exit 1
fi
echo "Proxy started with PID: $PROXY_PID"
echo ""
# Function to test a URL and check response
test_url() {
local url=$1
local expected_pattern=$2
local test_name=$3
echo "Testing: $test_name"
response=$(curl -s -w "\nHTTP_CODE:%{http_code}" "$url" 2>/dev/null || true)
http_code=$(echo "$response" | grep "HTTP_CODE:" | cut -d: -f2)
body=$(echo "$response" | grep -v "HTTP_CODE:")
if [ "$http_code" = "200" ]; then
if echo "$body" | grep -q "$expected_pattern"; then
echo " ✓ Success: Got expected response"
return 0
else
echo " ✗ FAIL: Got 200 but wrong content"
echo " Expected pattern: $expected_pattern"
echo " Got: ${body:0:100}..."
return 1
fi
else
echo " ✗ FAIL: Got HTTP $http_code instead of 200"
return 1
fi
}
# Run tests
echo "=== Test 1: Basic Dashboard Access ==="
test_url "http://localhost:8888/dashboard" "Reverse Proxy Monitor" "Dashboard"
echo ""
echo "=== Test 2: Basic API Stats Access ==="
test_url "http://localhost:8888/api/stats" "cpu_percent" "API Stats"
echo ""
echo "=== Test 3: Multiple Sequential Dashboard Requests ==="
for i in {1..5}; do
test_url "http://localhost:8888/dashboard" "Reverse Proxy Monitor" "Dashboard request $i"
done
echo ""
echo "=== Test 4: Multiple Sequential API Requests ==="
for i in {1..5}; do
test_url "http://localhost:8888/api/stats" "cpu_percent" "API request $i"
done
echo ""
echo "=== Test 5: Mixed Dashboard and API Requests ==="
for i in {1..3}; do
test_url "http://localhost:8888/dashboard" "Reverse Proxy Monitor" "Dashboard request $i"
test_url "http://localhost:8888/api/stats" "cpu_percent" "API request $i"
done
echo ""
echo "=== Test 6: Concurrent Dashboard Requests ==="
echo "Sending 10 concurrent dashboard requests..."
for i in {1..10}; do
(
response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/dashboard" 2>/dev/null)
if [ "$response" = "200" ]; then
echo " Request $i: ✓ 200 OK"
else
echo " Request $i: ✗ Got $response"
fi
) &
done
wait
echo ""
echo "=== Test 7: Concurrent API Requests ==="
echo "Sending 10 concurrent API requests..."
for i in {1..10}; do
(
response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/api/stats" 2>/dev/null)
if [ "$response" = "200" ]; then
echo " Request $i: ✓ 200 OK"
else
echo " Request $i: ✗ Got $response"
fi
) &
done
wait
echo ""
echo "=== Test 8: Pipelined Requests (Keep-Alive) ==="
echo "Testing pipelined requests with keep-alive..."
(echo -ne "GET /dashboard HTTP/1.1\r\nHost: localhost\r\n\r\nGET /api/stats HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost 8888 | grep -E "(Reverse Proxy Dashboard|vhost_stats)" | wc -l) > pipeline_result.txt 2>&1
pipeline_count=$(cat pipeline_result.txt)
if [ "$pipeline_count" = "2" ]; then
echo " ✓ Both pipelined requests were handled correctly"
else
echo " ✗ FAIL: Only $pipeline_count/2 pipelined requests handled correctly"
fi
echo ""
echo "=== Test 9: Rapid Fire Mixed Requests ==="
echo "Sending 50 rapid requests mixing dashboard, API, and proxy requests..."
success=0
fail=0
for i in {1..50}; do
case $((i % 3)) in
0) url="http://localhost:8888/dashboard" ;;
1) url="http://localhost:8888/api/stats" ;;
2) url="http://localhost:8888/" ;; # This should be proxied
esac
response=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
if [ "$response" = "200" ] || [ "$response" = "502" ]; then
((success++))
else
((fail++))
echo " Request $i to $url failed with code: $response"
fi
done
echo " Results: $success successful, $fail failed"
echo ""
echo "=== Test 10: Check Routing Log Patterns ==="
echo "Analyzing proxy log for routing decisions..."
dashboard_internal=$(grep -c "ROUTING-INTERNAL.*DASHBOARD" proxy.log || true)
api_internal=$(grep -c "ROUTING-INTERNAL.*API STATS" proxy.log || true)
dashboard_forward=$(grep -c "ROUTING-FORWARD.*dashboard" proxy.log || true)
api_forward=$(grep -c "ROUTING-FORWARD.*api/stats" proxy.log || true)
echo " Dashboard handled internally: $dashboard_internal times"
echo " API stats handled internally: $api_internal times"
echo " Dashboard incorrectly forwarded: $dashboard_forward times"
echo " API stats incorrectly forwarded: $api_forward times"
if [ "$dashboard_forward" -gt 0 ] || [ "$api_forward" -gt 0 ]; then
echo " ✗ FAIL: Internal routes were incorrectly forwarded!"
echo ""
echo "=== Showing incorrect forwarding logs ==="
grep "ROUTING-FORWARD.*dashboard\|ROUTING-FORWARD.*api/stats" proxy.log | head -10 || true
else
echo " ✓ Success: All internal routes handled correctly"
fi
echo ""
echo "=== Cleanup ==="
kill $PROXY_PID 2>/dev/null || true
wait $PROXY_PID 2>/dev/null || true
echo "Proxy stopped"
echo ""
echo "=== Test Summary ==="
if [ "$dashboard_forward" -gt 0 ] || [ "$api_forward" -gt 0 ]; then
echo "❌ TESTS FAILED: Internal routes were incorrectly forwarded"
echo " Check proxy.log for details"
exit 1
else
echo "✅ ALL TESTS PASSED: Internal routes are reliably handled"
exit 0
fi

115
test_routing_fix.sh Executable file
View File

@ -0,0 +1,115 @@
#!/bin/bash
# Focused test for routing fix verification
set -e
echo "=== Routing Fix Verification Test ==="
echo ""
# Kill any existing proxy
pkill -f rproxy || true
sleep 1
# Start proxy with debug logging
echo "Starting proxy with debug logging..."
DEBUG=1 ./rproxy > proxy_test.log 2>&1 &
PROXY_PID=$!
sleep 2
# Check if proxy is running
if ! ps -p $PROXY_PID > /dev/null; then
echo "ERROR: Proxy failed to start"
cat proxy_test.log
exit 1
fi
echo "Proxy started with PID: $PROXY_PID"
echo ""
# Test function
test_endpoint() {
local endpoint=$1
local name=$2
echo "Testing $name..."
response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888$endpoint" 2>/dev/null)
if [ "$response" = "200" ]; then
echo " ✓ Got 200 OK"
return 0
else
echo " ✗ Got HTTP $response"
return 1
fi
}
# Run 20 rapid tests alternating between dashboard and API
echo "=== Rapid Alternating Tests (20 requests) ==="
success=0
fail=0
for i in {1..10}; do
# Test dashboard
if test_endpoint "/dashboard" "Dashboard #$i"; then
((success++))
else
((fail++))
fi
# Test API stats
if test_endpoint "/api/stats" "API Stats #$i"; then
((success++))
else
((fail++))
fi
done
echo ""
echo "Results: $success/20 successful"
echo ""
# Test pipelined requests
echo "=== Testing Pipelined Requests ==="
echo "Sending 2 pipelined requests..."
response=$(echo -ne "GET /dashboard HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET /api/stats HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" | nc localhost 8888 2>/dev/null | grep -c "HTTP/1.1 200 OK" || echo "0")
if [ "$response" = "2" ]; then
echo " ✓ Both pipelined requests handled correctly"
else
echo " ✗ Only $response/2 pipelined requests handled"
fi
echo ""
# Check logs for incorrect forwarding
echo "=== Checking Logs for Routing Issues ==="
dashboard_internal=$(grep -c "ROUTING-INTERNAL.*DASHBOARD" proxy_test.log || echo "0")
api_internal=$(grep -c "ROUTING-INTERNAL.*API STATS" proxy_test.log || echo "0")
dashboard_forward=$(grep -c "ROUTING-FORWARD.*dashboard" proxy_test.log || echo "0")
api_forward=$(grep -c "ROUTING-FORWARD.*api/stats" proxy_test.log || echo "0")
echo "Dashboard handled internally: $dashboard_internal times"
echo "API stats handled internally: $api_internal times"
echo "Dashboard forwarded (should be 0): $dashboard_forward times"
echo "API stats forwarded (should be 0): $api_forward times"
echo ""
# Final verdict
if [ "$dashboard_forward" -eq 0 ] && [ "$api_forward" -eq 0 ] && [ "$fail" -eq 0 ]; then
echo "✅ SUCCESS: All internal routes handled correctly!"
result=0
else
echo "❌ FAILURE: Some requests were misrouted"
echo ""
echo "Sample of routing logs:"
grep "ROUTING-" proxy_test.log | tail -20
result=1
fi
# Cleanup
echo ""
echo "Cleaning up..."
kill $PROXY_PID 2>/dev/null || true
wait $PROXY_PID 2>/dev/null || true
echo "Done."
exit $result

25
test_upstream.py Normal file
View File

@ -0,0 +1,25 @@
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()

86
verify_routing.sh Executable file
View File

@ -0,0 +1,86 @@
#!/bin/bash
# Simple routing verification test
echo "=== Routing Verification Test ==="
echo ""
# Kill any existing proxy
pkill -f rproxy 2>/dev/null
sleep 1
# Start proxy
echo "Starting proxy..."
DEBUG=1 ./rproxy > routing_verify.log 2>&1 &
PROXY_PID=$!
sleep 2
if ! ps -p $PROXY_PID > /dev/null; then
echo "ERROR: Proxy failed to start"
cat routing_verify.log
exit 1
fi
echo "Proxy started (PID: $PROXY_PID)"
echo ""
# Test internal routes
echo "Testing internal route handling:"
# Test dashboard 5 times
echo -n " Dashboard: "
for i in {1..5}; do
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8888/dashboard 2>/dev/null)
if [ "$response" = "200" ]; then
echo -n "✓"
else
echo -n "✗($response)"
fi
done
echo ""
# Test API stats 5 times
echo -n " API Stats: "
for i in {1..5}; do
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8888/api/stats 2>/dev/null)
if [ "$response" = "200" ]; then
echo -n "✓"
else
echo -n "✗($response)"
fi
done
echo ""
echo ""
# Check routing decisions in log
echo "Analyzing routing decisions:"
dashboard_internal=$(grep "ROUTING-INTERNAL.*DASHBOARD" routing_verify.log 2>/dev/null | wc -l)
api_internal=$(grep "ROUTING-INTERNAL.*API STATS" routing_verify.log 2>/dev/null | wc -l)
dashboard_forward=$(grep "ROUTING-FORWARD.*dashboard" routing_verify.log 2>/dev/null | wc -l)
api_forward=$(grep "ROUTING-FORWARD.*api/stats" routing_verify.log 2>/dev/null | wc -l)
echo " ✓ Dashboard handled internally: $dashboard_internal times"
echo " ✓ API stats handled internally: $api_internal times"
if [ "$dashboard_forward" -gt 0 ]; then
echo " ✗ Dashboard incorrectly forwarded: $dashboard_forward times"
fi
if [ "$api_forward" -gt 0 ]; then
echo " ✗ API stats incorrectly forwarded: $api_forward times"
fi
echo ""
# Cleanup
kill $PROXY_PID 2>/dev/null
wait $PROXY_PID 2>/dev/null
# Verdict
if [ "$dashboard_forward" -eq 0 ] && [ "$api_forward" -eq 0 ] && [ "$dashboard_internal" -gt 0 ] && [ "$api_internal" -gt 0 ]; then
echo "✅ SUCCESS: All internal routes are properly handled!"
echo " /dashboard and /api/stats are ALWAYS served internally, never forwarded."
exit 0
else
echo "❌ FAILURE: Routing issues detected"
exit 1
fi