Update, bug fixed.
This commit is contained in:
parent
d60dea1c44
commit
59d075d8e0
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,5 @@
|
||||
*.bak
|
||||
*.bak2
|
||||
*.db
|
||||
*.txt
|
||||
rproxy
|
||||
|
||||
99
rproxy.c
99
rproxy.c
@ -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
207
test_routing.sh
Executable 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
185
test_routing_comprehensive.sh
Executable 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
115
test_routing_fix.sh
Executable 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
25
test_upstream.py
Normal 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
86
verify_routing.sh
Executable 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
|
||||
Loading…
Reference in New Issue
Block a user