124 lines
4.4 KiB
Bash
Executable File
124 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
readonly RC_VERSION='1.14.0-rc.5'
|
|
readonly IMAGE="harbor-singbox-client-rc-test:${RC_VERSION}"
|
|
FIXTURES="$(mktemp -d)"
|
|
SUFFIX="${FIXTURES##*/}"
|
|
readonly SUFFIX="${SUFFIX//[^[:alnum:]]/}"
|
|
readonly NETWORK="harbor-singbox-rc-${SUFFIX}"
|
|
readonly TARGET="harbor-singbox-rc-target-${SUFFIX}"
|
|
readonly PROXY="harbor-singbox-rc-proxy-${SUFFIX}"
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
NETWORK_CREATED=false
|
|
TARGET_CREATED=false
|
|
PROXY_CREATED=false
|
|
|
|
cleanup() {
|
|
[[ "$PROXY_CREATED" == true ]] && docker rm -f "$PROXY" >/dev/null 2>&1 || true
|
|
[[ "$TARGET_CREATED" == true ]] && docker rm -f "$TARGET" >/dev/null 2>&1 || true
|
|
[[ "$NETWORK_CREATED" == true ]] && docker network rm "$NETWORK" >/dev/null 2>&1 || true
|
|
docker image rm "$IMAGE" >/dev/null 2>&1 || true
|
|
rm -rf "$FIXTURES"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
check_config() {
|
|
local name="$1"
|
|
local output
|
|
local unexpected
|
|
|
|
if ! output="$(docker run --rm \
|
|
-v "$FIXTURES:/fixtures:ro" \
|
|
--entrypoint sing-box \
|
|
"$IMAGE" check -c "/fixtures/${name}.json" 2>&1)"; then
|
|
printf '%s\n' "$output" >&2
|
|
return 1
|
|
fi
|
|
unexpected="$(printf '%s\n' "$output" \
|
|
| grep -Ei 'warn|deprecated' \
|
|
| grep -Evi 'independent_cache.*DNS option is deprecated' || true)"
|
|
if [[ -n "$unexpected" ]]; then
|
|
printf 'unexpected warning for %s:\n%s\n' "$name" "$unexpected" >&2
|
|
return 1
|
|
fi
|
|
printf 'PASS config %s\n' "$name"
|
|
}
|
|
|
|
docker build \
|
|
--build-arg "SINGBOX_VERSION=${RC_VERSION}" \
|
|
-t "$IMAGE" \
|
|
-f "$ROOT/Dockerfile.client" \
|
|
"$ROOT"
|
|
|
|
version_output="$(docker run --rm --entrypoint sing-box "$IMAGE" version)"
|
|
printf '%s\n' "$version_output"
|
|
grep -Fxq "sing-box version ${RC_VERSION}" <<< "$version_output"
|
|
|
|
cat > "$FIXTURES/generate-configs.mjs" <<'EOF'
|
|
import fs from 'node:fs';
|
|
import { parseSubscriptionBody } from '/app/dist/server/subscription.js';
|
|
import { buildGatewayConfig } from '/app/dist/server/singbox.js';
|
|
|
|
const reality = 'vless://00000000-0000-4000-8000-000000000001@reality.example.test:443?security=reality&type=tcp&pbk=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sid=0123456789abcdef&sni=cover.example.test&fp=chrome#Reality';
|
|
const websocket = 'vless://00000000-0000-4000-8000-000000000002@ws.example.test:8443?encryption=none&security=tls&sni=edge.example.test&fp=firefox&alpn=h2%2Chttp%2F1.1&type=ws&host=edge.example.test&path=%2Fsocket%3Fed%3D2048#TLS%20WS';
|
|
|
|
function generated(link, clientDirect = false) {
|
|
const parsed = parseSubscriptionBody(Buffer.from(link).toString('base64'));
|
|
return buildGatewayConfig(parsed.config, parsed.servers[0].id, { clientDirect });
|
|
}
|
|
|
|
for (const [name, config] of [
|
|
['local-vpn', generated(websocket)],
|
|
['gateway-direct', generated(websocket, true)],
|
|
['vless-reality', generated(reality)],
|
|
['vless-tls-websocket', generated(websocket)],
|
|
]) {
|
|
fs.writeFileSync(`/fixtures/${name}.json`, JSON.stringify(config));
|
|
}
|
|
EOF
|
|
|
|
docker run --rm \
|
|
-e APP_MODE=client \
|
|
-e PROXY_PORT=18081 \
|
|
-e DIAGNOSTICS_PROXY_PORT=18082 \
|
|
-e DATA_DIR=/tmp/harbor-rc \
|
|
-e SING_BOX_CACHE=/tmp/harbor-rc-cache.db \
|
|
-v "$FIXTURES:/fixtures" \
|
|
--entrypoint node \
|
|
"$IMAGE" /fixtures/generate-configs.mjs
|
|
|
|
check_config local-vpn
|
|
check_config gateway-direct
|
|
check_config vless-reality
|
|
check_config vless-tls-websocket
|
|
|
|
docker network create "$NETWORK" >/dev/null
|
|
NETWORK_CREATED=true
|
|
docker create --name "$TARGET" --network "$NETWORK" --entrypoint node "$IMAGE" \
|
|
-e 'require("node:http").createServer((request,response)=>response.end(request.url)).listen(18080,"0.0.0.0")' >/dev/null
|
|
TARGET_CREATED=true
|
|
docker start "$TARGET" >/dev/null
|
|
docker create --name "$PROXY" --network "$NETWORK" \
|
|
-v "$FIXTURES:/fixtures:ro" --entrypoint sing-box "$IMAGE" run -c /fixtures/gateway-direct.json >/dev/null
|
|
PROXY_CREATED=true
|
|
docker start "$PROXY" >/dev/null
|
|
|
|
for _ in {1..30}; do
|
|
if docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
|
|
--noproxy '' -fsS -x "http://${PROXY}:18081" "http://${TARGET}:18080/http" | grep -Fxq '/http'; then
|
|
break
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
|
|
http_body="$(docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
|
|
--noproxy '' -fsS -x "http://${PROXY}:18081" "http://${TARGET}:18080/http")"
|
|
socks_body="$(docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
|
|
--noproxy '' -fsS --socks5-hostname "${PROXY}:18081" "http://${TARGET}:18080/socks")"
|
|
|
|
[[ "$http_body" == '/http' ]]
|
|
[[ "$socks_body" == '/socks' ]]
|
|
printf 'PASS mixed inbound HTTP\nPASS mixed inbound SOCKS5\n'
|