264 lines
8.4 KiB
Bash
Executable File
264 lines
8.4 KiB
Bash
Executable File
#!/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
|