diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2c8b087 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +deploy/dev/script/*.sh text eol=lf diff --git a/deploy/dev/script/test-rl.sh b/deploy/dev/script/test-rl.sh new file mode 100644 index 0000000..5ac6611 --- /dev/null +++ b/deploy/dev/script/test-rl.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# 测试 Envoy Lua 双 Cookie XSRF + 登录接口 + 限流头 +# 用法: +# ./test-login-xsrf-rl.sh [BASE_URL] [WARM_PATH] [LOGIN_PATH] [COUNT] [INTERVAL] +# 环境变量: +# INSECURE=1 # https 自签时跳过校验 +# TOKEN_HEADER=xsrf-token +# USERNAME=testUser +# PASSWORD=string +# PHONE=string +# REMEMBER=true # true/false +# DATA='{"username":"..."}' # 直接提供完整 JSON,将覆盖上面四项 +# +# 例子: +# ./test-login-xsrf-rl.sh http://127.0.0.1:8080 /healthz /api/v1/auth/login 25 0.1 + +set -euo pipefail + +BASE_URL="${1:-http://127.0.0.1:8080}" +WARM_PATH="${2:-/healthz}" +LOGIN_PATH="${3:-/api/v1/auth/login}" +COUNT="${4:-20}" +INTERVAL="${5:-0.05}" + +METHOD="POST" +CONTENT_TYPE="application/json" + +TOKEN_HEADER="${TOKEN_HEADER:-xsrf-token}" +TOKEN_COOKIE="__Host-XSRF-TOKEN" +GUARD_COOKIE="__Host-XSRF-GUARD" + +USERNAME="${USERNAME:-testUser}" +PASSWORD="${PASSWORD:-string}" +PHONE="${PHONE:-string}" +REMEMBER="${REMEMBER:-true}" + +INSECURE="${INSECURE:-}" + +CURL_COMMON=(-sS -L) +[[ -n "${INSECURE}" ]] && CURL_COMMON+=(-k) + +tmpdir="$(mktemp -d)" +trap 'rm -rf "$tmpdir"' EXIT +hdr="$tmpdir/hdr" + +echo "# Base=${BASE_URL} Warm=${WARM_PATH} Login=${LOGIN_PATH} Count=${COUNT} Interval=${INTERVAL}s" + +# 构造请求体(优先使用 DATA;否则用变量拼装) +build_data() { + if [[ -n "${DATA:-}" ]]; then + DATA_PAYLOAD="${DATA}" + return + fi + + # 规范化 REMEMBER 为 true/false + case "${REMEMBER,,}" in + true) REMEMBER=true ;; + false) REMEMBER=false ;; + *) REMEMBER=true ;; + esac + + if command -v jq >/dev/null 2>&1; then + DATA_PAYLOAD="$( + jq -n \ + --arg username "$USERNAME" \ + --arg password "$PASSWORD" \ + --arg phone "$PHONE" \ + --argjson remember "$REMEMBER" \ + '{username:$username,password:$password,phone:$phone,remember:$remember}' + )" + else + # 无 jq 的简易拼装(注意变量里如含双引号会破坏 JSON,生产建议安装 jq) + DATA_PAYLOAD=$(cat </dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) + REMAIN=$(grep -i '^x-ratelimit-remaining:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) + RESET=$(grep -i '^x-ratelimit-reset:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) + RLED=$(grep -i '^x-envoy-ratelimited:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) + printf "limit=%s remaining=%s reset=%s ratelimited=%s" "${LIMIT:--}" "${REMAIN:--}" "${RESET:--}" "${RLED:-no}" +} + +do_request() { + local with_header="$1" # yes/no + local url="${BASE_URL}${LOGIN_PATH}" + rm -f "$hdr" + + if [[ "$with_header" == "yes" ]]; then + CODE=$(curl "${CURL_COMMON[@]}" -X "$METHOD" \ + -H "Content-Type: ${CONTENT_TYPE}" \ + -H "Cookie: ${COOKIE_HEADER}" \ + -H "${TOKEN_HEADER}: ${TOKEN}" \ + --data "$DATA_PAYLOAD" \ + -D "$hdr" -o /dev/null -w "%{http_code}" \ + "$url") + else + CODE=$(curl "${CURL_COMMON[@]}" -X "$METHOD" \ + -H "Content-Type: ${CONTENT_TYPE}" \ + -H "Cookie: ${COOKIE_HEADER}" \ + --data "$DATA_PAYLOAD" \ + -D "$hdr" -o /dev/null -w "%{http_code}" \ + "$url") + fi + + printf "%s code=%s " "$(date '+%H:%M:%S')" "${CODE}" + extract_rl_headers + printf "\n" +} + +# 1) 构造登录请求体 +build_data +echo "# 登录请求体: ${DATA_PAYLOAD}" + +# 2) 预热拿 Cookie +warm_set_cookies + +# 3) 负例:不带 xsrf-token 头(应 403) +echo "# 负例:不带 XSRF 头,预期 403" +do_request "no" + +# 4) 正例:带齐 Cookie + xsrf-token,循环观察限流头 +echo "# 正例:带齐 Cookie 与 XSRF 头,开始压测以观测限流头" +for i in $(seq 1 "${COUNT}"); do + printf "#%02d " "$i" + do_request "yes" + sleep "${INTERVAL}" +done