#!/usr/bin/env bash # # test_xray_connection.sh — Test xray juris-xhttp outbound # Tests: connectivity, latency (SLA), throughput # set -euo pipefail XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64" TEST_CONFIG="/tmp/test-juris-sla.json" SOCKS_PORT=11080 REMOTE_IP="83.99.190.32" XRAY_PID="" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' cleanup() { if [[ -n "$XRAY_PID" ]] && kill -0 "$XRAY_PID" 2>/dev/null; then kill "$XRAY_PID" 2>/dev/null wait "$XRAY_PID" 2>/dev/null || true fi rm -f "$TEST_CONFIG" } trap cleanup EXIT header() { echo "" echo -e "${BOLD}${CYAN}=== $1 ===${NC}" } pass() { echo -e " ${GREEN}PASS${NC} $1"; } fail() { echo -e " ${RED}FAIL${NC} $1"; } info() { echo -e " ${YELLOW}INFO${NC} $1"; } # ── Write temporary xray config ────────────────────────────────────────────── cat > "$TEST_CONFIG" << 'EOF' { "log": { "loglevel": "error" }, "inbounds": [{ "listen": "127.0.0.1", "port": 11080, "protocol": "socks", "settings": { "auth": "noauth", "udp": true }, "tag": "test-socks" }], "outbounds": [{ "tag": "juris-xhttp", "protocol": "vless", "settings": { "vnext": [{ "address": "share.alogins.net", "port": 443, "users": [{ "id": "6e422ab5-070a-43f6-8241-38cd56d23d24", "flow": "", "encryption": "none" }] }] }, "streamSettings": { "network": "xhttp", "security": "reality", "realitySettings": { "fingerprint": "chrome", "serverName": "www.delfi.lv", "publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw", "shortId": "6036d37d12c443c4", "spiderX": "/" }, "xhttpSettings": { "path": "/xt-6036d37d", "host": "", "mode": "auto", "extra": { "xPaddingBytes": "100-1000", "xmux": { "maxConcurrency": "16-32", "maxConnections": 0, "cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0, "hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000" } } } } }] } EOF # ── Start xray ─────────────────────────────────────────────────────────────── header "Starting test xray instance (SOCKS on 127.0.0.1:$SOCKS_PORT)" $XRAY_BIN -test -config "$TEST_CONFIG" > /dev/null 2>&1 pass "Config validation OK" $XRAY_BIN -config "$TEST_CONFIG" > /dev/null 2>&1 & XRAY_PID=$! sleep 2 if kill -0 "$XRAY_PID" 2>/dev/null; then pass "Xray started (PID $XRAY_PID)" else fail "Xray failed to start" exit 1 fi # ── 1. Connectivity test ───────────────────────────────────────────────────── header "1. Connectivity" PROXY="socks5h://127.0.0.1:$SOCKS_PORT" ERRORS=0 # Test: exit IP EXIT_IP=$(curl -s --connect-timeout 10 --max-time 15 -x "$PROXY" https://ifconfig.me 2>/dev/null || echo "FAIL") if [[ "$EXIT_IP" == "$REMOTE_IP" ]]; then pass "Exit IP: $EXIT_IP (matches remote server)" else fail "Exit IP: '$EXIT_IP' (expected $REMOTE_IP)" ERRORS=$((ERRORS + 1)) fi # Test: HTTPS connectivity HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 15 -x "$PROXY" https://www.google.com 2>/dev/null || echo "000") if [[ "$HTTP_CODE" == "200" ]]; then pass "HTTPS to google.com: HTTP $HTTP_CODE" else fail "HTTPS to google.com: HTTP $HTTP_CODE" ERRORS=$((ERRORS + 1)) fi # Test: DNS resolution through tunnel DNS_IP=$(curl -s --connect-timeout 10 --max-time 15 -x "$PROXY" https://dns.google/resolve?name=example.com\&type=A 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['Answer'][0]['data'])" 2>/dev/null || echo "FAIL") if [[ "$DNS_IP" != "FAIL" ]]; then pass "DNS resolution through tunnel: example.com → $DNS_IP" else fail "DNS resolution through tunnel" ERRORS=$((ERRORS + 1)) fi # ── 2. Latency (SLA) ───────────────────────────────────────────────────────── header "2. Latency (SLA)" LATENCIES=() LATENCY_ERRORS=0 SAMPLES=10 info "Running $SAMPLES requests to measure round-trip time..." for i in $(seq 1 $SAMPLES); do T=$(curl -s -o /dev/null -w '%{time_total}' --connect-timeout 10 --max-time 15 -x "$PROXY" https://www.gstatic.com/generate_204 2>/dev/null || echo "0") MS=$(echo "$T * 1000" | bc 2>/dev/null | cut -d. -f1) if [[ -n "$MS" && "$MS" -gt 0 ]]; then LATENCIES+=("$MS") printf " [%2d/%d] %s ms\n" "$i" "$SAMPLES" "$MS" else LATENCY_ERRORS=$((LATENCY_ERRORS + 1)) printf " [%2d/%d] ${RED}timeout${NC}\n" "$i" "$SAMPLES" fi done if [[ ${#LATENCIES[@]} -gt 0 ]]; then # Calculate min/avg/max/p95 SORTED=($(printf '%s\n' "${LATENCIES[@]}" | sort -n)) COUNT=${#SORTED[@]} MIN=${SORTED[0]} MAX=${SORTED[$((COUNT - 1))]} SUM=0 for v in "${SORTED[@]}"; do SUM=$((SUM + v)); done AVG=$((SUM / COUNT)) P95_IDX=$(echo "($COUNT * 95 + 99) / 100 - 1" | bc) [[ $P95_IDX -ge $COUNT ]] && P95_IDX=$((COUNT - 1)) P95=${SORTED[$P95_IDX]} echo "" info "Latency summary ($COUNT/$SAMPLES successful):" echo -e " ${BOLD} Min: ${MIN} ms${NC}" echo -e " ${BOLD} Avg: ${AVG} ms${NC}" echo -e " ${BOLD} P95: ${P95} ms${NC}" echo -e " ${BOLD} Max: ${MAX} ms${NC}" if [[ $AVG -lt 300 ]]; then pass "Average latency ${AVG}ms < 300ms threshold" elif [[ $AVG -lt 500 ]]; then info "Average latency ${AVG}ms — acceptable but elevated" else fail "Average latency ${AVG}ms exceeds 500ms" ERRORS=$((ERRORS + 1)) fi else fail "All latency samples failed" ERRORS=$((ERRORS + 1)) fi # ── 3. Throughput ───────────────────────────────────────────────────────────── header "3. Throughput" info "Downloading 10MB test file through tunnel..." DL_RESULT=$(curl -s -o /dev/null -w '%{size_download} %{time_total} %{speed_download}' \ --connect-timeout 15 --max-time 60 \ -x "$PROXY" \ "http://speedtest.tele2.net/10MB.zip" 2>/dev/null || echo "0 0 0") DL_BYTES=$(echo "$DL_RESULT" | awk '{print $1}') DL_TIME=$(echo "$DL_RESULT" | awk '{print $2}') DL_SPEED=$(echo "$DL_RESULT" | awk '{print $3}') if [[ "$DL_BYTES" -gt 0 ]] 2>/dev/null; then DL_MB=$(echo "scale=2; $DL_BYTES / 1048576" | bc) DL_MBPS=$(echo "scale=2; $DL_SPEED * 8 / 1048576" | bc) DL_TIME_S=$(echo "scale=2; $DL_TIME" | bc) pass "Downloaded ${DL_MB} MB in ${DL_TIME_S}s" echo -e " ${BOLD} Download speed: ${DL_MBPS} Mbps${NC}" else fail "Download test failed" ERRORS=$((ERRORS + 1)) fi info "Uploading 5MB test payload through tunnel..." # Generate 5MB of random data and POST it UL_RESULT=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | \ curl -s -o /dev/null -w '%{size_upload} %{time_total} %{speed_upload}' \ --connect-timeout 15 --max-time 60 \ -x "$PROXY" \ -X POST -H "Content-Type: application/octet-stream" \ --data-binary @- \ "http://speedtest.tele2.net/upload.php" 2>/dev/null || echo "0 0 0") UL_BYTES=$(echo "$UL_RESULT" | awk '{print $1}') UL_TIME=$(echo "$UL_RESULT" | awk '{print $2}') UL_SPEED=$(echo "$UL_RESULT" | awk '{print $3}') if [[ "$UL_BYTES" -gt 0 ]] 2>/dev/null; then UL_MB=$(echo "scale=2; $UL_BYTES / 1048576" | bc) UL_MBPS=$(echo "scale=2; $UL_SPEED * 8 / 1048576" | bc) UL_TIME_S=$(echo "scale=2; $UL_TIME" | bc) pass "Uploaded ${UL_MB} MB in ${UL_TIME_S}s" echo -e " ${BOLD} Upload speed: ${UL_MBPS} Mbps${NC}" else fail "Upload test failed" ERRORS=$((ERRORS + 1)) fi # ── Summary ─────────────────────────────────────────────────────────────────── header "Summary" if [[ $ERRORS -eq 0 ]]; then echo -e " ${GREEN}${BOLD}ALL TESTS PASSED${NC}" else echo -e " ${RED}${BOLD}$ERRORS TEST(S) FAILED${NC}" fi echo "" exit $ERRORS