feat(init): Initializes the project's basic structure and configuration files.

This commit is contained in:
Gouryella
2025-12-02 16:12:18 +08:00
commit 07eea862d5
66 changed files with 12029 additions and 0 deletions

52
scripts/generate-cert.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Generate self-signed certificate for development/testing using ECDSA
# ECDSA provides better performance and smaller key size than RSA
# WARNING: Do NOT use self-signed certificates in production!
set -e
CERT_DIR="${1:-./certs}"
DOMAIN="${2:-localhost}"
DAYS=365
echo "🔒 Generating self-signed certificate for development..."
echo " Domain: $DOMAIN"
echo " Output directory: $CERT_DIR"
echo ""
# Create directory if it doesn't exist
mkdir -p "$CERT_DIR"
# Generate ECDSA private key (using P-256 curve)
openssl ecparam -genkey -name prime256v1 -out "$CERT_DIR/server.key"
# Generate certificate signing request
openssl req -new -key "$CERT_DIR/server.key" -out "$CERT_DIR/server.csr" \
-subj "/C=US/ST=State/L=City/O=Organization/CN=$DOMAIN"
# Generate self-signed certificate
openssl x509 -req -days $DAYS \
-in "$CERT_DIR/server.csr" \
-signkey "$CERT_DIR/server.key" \
-out "$CERT_DIR/server.crt" \
-extfile <(printf "subjectAltName=DNS:$DOMAIN,DNS:*.$DOMAIN")
# Clean up CSR
rm "$CERT_DIR/server.csr"
echo "✅ Certificate generated successfully!"
echo ""
echo "Files created:"
echo " Certificate: $CERT_DIR/server.crt"
echo " Private Key: $CERT_DIR/server.key"
echo ""
echo "Usage:"
echo " ./bin/drip server \\"
echo " --domain $DOMAIN \\"
echo " --tls-cert $CERT_DIR/server.crt \\"
echo " --tls-key $CERT_DIR/server.key"
echo ""
echo "⚠️ WARNING: This is a self-signed certificate for development only!"
echo " Clients will need to skip certificate verification (insecure)."
echo " For production, use --auto-tls or get a certificate from Let's Encrypt."

1007
scripts/install-server.sh Executable file

File diff suppressed because it is too large Load Diff

642
scripts/install.sh Executable file
View File

@@ -0,0 +1,642 @@
#!/bin/bash
set -e
# ============================================================================
# Configuration
# ============================================================================
DOWNLOAD_BASE_URL="https://"
INSTALL_DIR="${INSTALL_DIR:-}"
VERSION="${VERSION:-latest}"
BINARY_NAME="drip"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
# Language (default: en)
LANG_CODE="${LANG_CODE:-en}"
# ============================================================================
# Internationalization
# ============================================================================
declare -A MSG_EN
declare -A MSG_ZH
# English messages
MSG_EN=(
["banner_title"]="Drip Client - One-Click Installer"
["select_lang"]="Select language / 选择语言"
["lang_en"]="English"
["lang_zh"]="中文"
["checking_os"]="Checking operating system..."
["detected_os"]="Detected OS"
["unsupported_os"]="Unsupported operating system"
["checking_arch"]="Checking system architecture..."
["detected_arch"]="Detected architecture"
["unsupported_arch"]="Unsupported architecture"
["checking_deps"]="Checking dependencies..."
["deps_ok"]="Dependencies check passed"
["downloading"]="Downloading Drip client"
["download_failed"]="Download failed"
["download_ok"]="Download completed"
["select_install_dir"]="Select installation directory"
["option_user"]="User directory (no sudo required)"
["option_system"]="System directory (requires sudo)"
["option_current"]="Current directory"
["option_custom"]="Custom path"
["enter_custom_path"]="Enter custom path"
["installing"]="Installing binary..."
["install_ok"]="Installation completed"
["updating_path"]="Updating PATH..."
["path_updated"]="PATH updated"
["path_note"]="Please restart your terminal or run: source ~/.bashrc"
["config_title"]="Client Configuration"
["configure_now"]="Configure client now?"
["enter_server"]="Enter server address (e.g., tunnel.example.com:8443)"
["server_required"]="Server address is required"
["enter_token"]="Enter authentication token"
["token_required"]="Token is required"
["skip_verify"]="Skip TLS certificate verification? (for self-signed certs)"
["config_saved"]="Configuration saved"
["install_complete"]="Installation completed!"
["usage_title"]="Usage"
["usage_http"]="Expose HTTP service on port 3000"
["usage_tcp"]="Expose TCP service on port 5432"
["usage_config"]="Show/modify configuration"
["usage_daemon"]="Run as background daemon"
["run_test"]="Test connection now?"
["test_running"]="Testing connection..."
["test_success"]="Connection successful"
["test_failed"]="Connection failed"
["yes"]="y"
["no"]="n"
["press_enter"]="Press Enter to continue..."
["windows_note"]="For Windows, please download the .exe file from GitHub Releases"
["already_installed"]="Drip is already installed"
["update_now"]="Update to the latest version?"
["updating"]="Updating..."
["update_ok"]="Update completed"
["verify_install"]="Verifying installation..."
["verify_ok"]="Verification passed"
["verify_failed"]="Verification failed"
["insecure_note"]="Only use --insecure for development/testing"
)
# Chinese messages
MSG_ZH=(
["banner_title"]="Drip 客户端 - 一键安装脚本"
["select_lang"]="Select language / 选择语言"
["lang_en"]="English"
["lang_zh"]="中文"
["checking_os"]="检查操作系统..."
["detected_os"]="检测到操作系统"
["unsupported_os"]="不支持的操作系统"
["checking_arch"]="检查系统架构..."
["detected_arch"]="检测到架构"
["unsupported_arch"]="不支持的架构"
["checking_deps"]="检查依赖..."
["deps_ok"]="依赖检查通过"
["downloading"]="下载 Drip 客户端"
["download_failed"]="下载失败"
["download_ok"]="下载完成"
["select_install_dir"]="选择安装目录"
["option_user"]="用户目录(无需 sudo"
["option_system"]="系统目录(需要 sudo"
["option_current"]="当前目录"
["option_custom"]="自定义路径"
["enter_custom_path"]="输入自定义路径"
["installing"]="安装二进制文件..."
["install_ok"]="安装完成"
["updating_path"]="更新 PATH..."
["path_updated"]="PATH 已更新"
["path_note"]="请重启终端或运行: source ~/.bashrc"
["config_title"]="客户端配置"
["configure_now"]="现在配置客户端?"
["enter_server"]="输入服务器地址例如tunnel.example.com:8443"
["server_required"]="服务器地址是必填项"
["enter_token"]="输入认证令牌"
["token_required"]="认证令牌是必填项"
["skip_verify"]="跳过 TLS 证书验证?(用于自签名证书)"
["config_saved"]="配置已保存"
["install_complete"]="安装完成!"
["usage_title"]="使用方法"
["usage_http"]="暴露本地 3000 端口的 HTTP 服务"
["usage_tcp"]="暴露本地 5432 端口的 TCP 服务"
["usage_config"]="显示/修改配置"
["usage_daemon"]="作为后台守护进程运行"
["run_test"]="现在测试连接?"
["test_running"]="正在测试连接..."
["test_success"]="连接成功"
["test_failed"]="连接失败"
["yes"]="y"
["no"]="n"
["press_enter"]="按 Enter 继续..."
["windows_note"]="Windows 用户请从 GitHub Releases 下载 .exe 文件"
["already_installed"]="Drip 已安装"
["update_now"]="是否更新到最新版本?"
["updating"]="正在更新..."
["update_ok"]="更新完成"
["verify_install"]="验证安装..."
["verify_ok"]="验证通过"
["verify_failed"]="验证失败"
["insecure_note"]="--insecure 仅用于开发/测试环境"
)
# Get message by key
msg() {
local key="$1"
if [[ "$LANG_CODE" == "zh" ]]; then
echo "${MSG_ZH[$key]:-$key}"
else
echo "${MSG_EN[$key]:-$key}"
fi
}
# ============================================================================
# Output functions
# ============================================================================
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[✓]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
print_error() { echo -e "${RED}[✗]${NC} $1"; }
print_step() { echo -e "${CYAN}[→]${NC} $1"; }
# Print banner
print_banner() {
echo -e "${GREEN}"
cat << "EOF"
____ _
/ __ \_____(_)___
/ / / / ___/ / __ \
/ /_/ / / / / /_/ /
/_____/_/ /_/ .___/
/_/
EOF
echo -e "${BOLD}$(msg banner_title)${NC}"
echo ""
}
# ============================================================================
# Language selection
# ============================================================================
select_language() {
echo ""
echo -e "${CYAN}╔════════════════════════════════════════╗${NC}"
echo -e "${CYAN}$(msg select_lang)${NC}"
echo -e "${CYAN}╠════════════════════════════════════════╣${NC}"
echo -e "${CYAN}${NC} ${GREEN}1)${NC} English ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${GREEN}2)${NC} 中文 ${CYAN}${NC}"
echo -e "${CYAN}╚════════════════════════════════════════╝${NC}"
echo ""
read -p "Select [1]: " lang_choice < /dev/tty
case "$lang_choice" in
2)
LANG_CODE="zh"
;;
*)
LANG_CODE="en"
;;
esac
echo ""
}
# ============================================================================
# System checks
# ============================================================================
check_os() {
print_step "$(msg checking_os)"
case "$(uname -s)" in
Linux*)
OS="linux"
;;
Darwin*)
OS="darwin"
;;
MINGW*|MSYS*|CYGWIN*)
OS="windows"
print_warning "$(msg windows_note)"
;;
*)
print_error "$(msg unsupported_os): $(uname -s)"
exit 1
;;
esac
print_success "$(msg detected_os): $OS"
}
check_arch() {
print_step "$(msg checking_arch)"
case "$(uname -m)" in
x86_64|amd64)
ARCH="amd64"
;;
aarch64|arm64)
ARCH="arm64"
;;
armv7l)
ARCH="arm"
;;
i386|i686)
ARCH="386"
;;
*)
print_error "$(msg unsupported_arch): $(uname -m)"
exit 1
;;
esac
print_success "$(msg detected_arch): $ARCH"
}
check_dependencies() {
print_step "$(msg checking_deps)"
# Check for download tool
if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then
print_error "curl or wget is required"
exit 1
fi
print_success "$(msg deps_ok)"
}
get_remote_version() {
# Determine binary name based on OS and ARCH
local binary_file="drip-${OS}-${ARCH}"
if [[ "$OS" == "windows" ]]; then
binary_file="${binary_file}.exe"
fi
local download_url="${DOWNLOAD_BASE_URL}/${VERSION}/${binary_file}"
local tmp_file="/tmp/drip-check-$$"
if command -v curl &> /dev/null; then
curl -fsSL "$download_url" -o "$tmp_file" 2>/dev/null || return 1
else
wget -q "$download_url" -O "$tmp_file" 2>/dev/null || return 1
fi
chmod +x "$tmp_file"
local remote_version=$("$tmp_file" version 2>/dev/null | awk '/Version:/ {print $2}' || echo "unknown")
rm -f "$tmp_file"
echo "$remote_version"
}
check_existing_install() {
if command -v drip &> /dev/null; then
local current_path=$(command -v drip)
local current_version=$(drip version 2>/dev/null | awk '/Version:/ {print $2}' || echo "unknown")
print_warning "$(msg already_installed): $current_path"
print_info "$(msg current_version): $current_version"
# Check remote version
print_step "Checking for updates..."
local remote_version=$(get_remote_version)
if [[ "$remote_version" == "unknown" ]]; then
print_warning "Could not check remote version"
echo ""
read -p "$(msg update_now) [Y/n]: " update_choice < /dev/tty
elif [[ "$current_version" == "$remote_version" ]]; then
print_success "Already up to date ($current_version)"
exit 0
else
print_info "Latest version: $remote_version"
echo ""
read -p "$(msg update_now) [Y/n]: " update_choice < /dev/tty
fi
if [[ "$update_choice" =~ ^[Nn]$ ]]; then
exit 0
fi
INSTALL_DIR=$(dirname "$current_path")
IS_UPDATE=true
fi
}
# ============================================================================
# Download and install
# ============================================================================
get_download_url() {
local binary_name
if [[ "$OS" == "windows" ]]; then
binary_name="drip-windows-${ARCH}.exe"
else
binary_name="drip-${OS}-${ARCH}"
fi
echo "${DOWNLOAD_BASE_URL}/${VERSION}/${binary_name}"
}
download_binary() {
local url=$(get_download_url)
if [[ "$IS_UPDATE" == true ]]; then
print_step "$(msg updating)..."
else
print_step "$(msg downloading)..."
fi
local tmp_file="/tmp/drip-download"
if command -v curl &> /dev/null; then
# Use -# for progress bar instead of -s (silent)
if ! curl -f#L "$url" -o "$tmp_file"; then
print_error "$(msg download_failed): $url"
exit 1
fi
else
# Use --show-progress to display download progress
if ! wget --show-progress "$url" -O "$tmp_file" 2>&1 | grep -v "^$"; then
print_error "$(msg download_failed): $url"
exit 1
fi
fi
chmod +x "$tmp_file"
print_success "$(msg download_ok)"
}
select_install_dir() {
if [[ -n "$INSTALL_DIR" ]]; then
return
fi
echo ""
echo -e "${CYAN}╔════════════════════════════════════════╗${NC}"
echo -e "${CYAN}$(msg select_install_dir) ${CYAN}${NC}"
echo -e "${CYAN}╠════════════════════════════════════════╣${NC}"
echo -e "${CYAN}${NC} ${GREEN}1)${NC} ~/.local/bin $(msg option_user) ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${GREEN}2)${NC} /usr/local/bin $(msg option_system) ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${GREEN}3)${NC} ./ $(msg option_current) ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${GREEN}4)${NC} $(msg option_custom) ${CYAN}${NC}"
echo -e "${CYAN}╚════════════════════════════════════════╝${NC}"
echo ""
read -p "Select [1]: " dir_choice < /dev/tty
case "$dir_choice" in
2)
INSTALL_DIR="/usr/local/bin"
NEED_SUDO=true
;;
3)
INSTALL_DIR="."
;;
4)
read -p "$(msg enter_custom_path): " INSTALL_DIR < /dev/tty
;;
*)
INSTALL_DIR="$HOME/.local/bin"
;;
esac
}
install_binary() {
print_step "$(msg installing)"
# Create directory if needed
if [[ ! -d "$INSTALL_DIR" ]]; then
if [[ "$NEED_SUDO" == true ]]; then
sudo mkdir -p "$INSTALL_DIR"
else
mkdir -p "$INSTALL_DIR"
fi
fi
local target_path="$INSTALL_DIR/$BINARY_NAME"
if [[ "$OS" == "windows" ]]; then
target_path="$INSTALL_DIR/$BINARY_NAME.exe"
fi
# Install binary
if [[ "$NEED_SUDO" == true ]]; then
sudo mv /tmp/drip-download "$target_path"
sudo chmod +x "$target_path"
else
mv /tmp/drip-download "$target_path"
chmod +x "$target_path"
fi
print_success "$(msg install_ok): $target_path"
}
update_path() {
# Skip if already in PATH
if command -v drip &> /dev/null; then
return
fi
# Skip for system directories (usually already in PATH)
if [[ "$INSTALL_DIR" == "/usr/local/bin" ]] || [[ "$INSTALL_DIR" == "/usr/bin" ]]; then
return
fi
print_step "$(msg updating_path)"
local shell_rc=""
local export_line="export PATH=\"\$PATH:$INSTALL_DIR\""
# Determine shell config file
if [[ -n "$ZSH_VERSION" ]] || [[ "$SHELL" == *"zsh"* ]]; then
shell_rc="$HOME/.zshrc"
elif [[ -n "$BASH_VERSION" ]] || [[ "$SHELL" == *"bash"* ]]; then
if [[ "$OS" == "darwin" ]]; then
shell_rc="$HOME/.bash_profile"
else
shell_rc="$HOME/.bashrc"
fi
elif [[ "$SHELL" == *"fish"* ]]; then
shell_rc="$HOME/.config/fish/config.fish"
export_line="set -gx PATH \$PATH $INSTALL_DIR"
fi
if [[ -n "$shell_rc" ]]; then
# Check if already added
if ! grep -q "$INSTALL_DIR" "$shell_rc" 2>/dev/null; then
echo "" >> "$shell_rc"
echo "# Drip client" >> "$shell_rc"
echo "$export_line" >> "$shell_rc"
print_success "$(msg path_updated): $shell_rc"
fi
fi
print_warning "$(msg path_note)"
}
verify_installation() {
print_step "$(msg verify_install)"
local binary_path="$INSTALL_DIR/$BINARY_NAME"
if [[ "$OS" == "windows" ]]; then
binary_path="$INSTALL_DIR/$BINARY_NAME.exe"
fi
if [[ -x "$binary_path" ]]; then
local version=$("$binary_path" version 2>/dev/null | awk '/Version:/ {print $2}' || echo "installed")
print_success "$(msg verify_ok): $version"
else
print_error "$(msg verify_failed)"
exit 1
fi
}
# ============================================================================
# Configuration
# ============================================================================
configure_client() {
echo ""
read -p "$(msg configure_now) [Y/n]: " config_choice < /dev/tty
if [[ "$config_choice" =~ ^[Nn]$ ]]; then
return
fi
echo ""
echo -e "${CYAN}╔════════════════════════════════════════╗${NC}"
echo -e "${CYAN}$(msg config_title) ${CYAN}${NC}"
echo -e "${CYAN}╚════════════════════════════════════════╝${NC}"
echo ""
local binary_path="$INSTALL_DIR/$BINARY_NAME"
# Server address
while true; do
read -p "$(msg enter_server): " SERVER < /dev/tty
if [[ -n "$SERVER" ]]; then
break
fi
print_error "$(msg server_required)"
done
# Token
while true; do
read -p "$(msg enter_token): " TOKEN < /dev/tty
if [[ -n "$TOKEN" ]]; then
break
fi
print_error "$(msg token_required)"
done
# Insecure mode
read -p "$(msg skip_verify) [y/N]: " insecure_choice < /dev/tty
INSECURE=""
if [[ "$insecure_choice" =~ ^[Yy]$ ]]; then
INSECURE="--insecure"
print_warning "$(msg insecure_note)"
fi
# Save configuration
"$binary_path" config set --server "$SERVER" --token "$TOKEN" $INSECURE 2>/dev/null || true
print_success "$(msg config_saved)"
}
# ============================================================================
# Test connection
# ============================================================================
test_connection() {
echo ""
read -p "$(msg run_test) [y/N]: " test_choice < /dev/tty
if [[ ! "$test_choice" =~ ^[Yy]$ ]]; then
return
fi
print_step "$(msg test_running)"
local binary_path="$INSTALL_DIR/$BINARY_NAME"
# Try to validate config
if "$binary_path" config validate 2>/dev/null; then
print_success "$(msg test_success)"
else
print_warning "$(msg test_failed)"
fi
}
# ============================================================================
# Final output
# ============================================================================
show_completion() {
local binary_path="$INSTALL_DIR/$BINARY_NAME"
echo ""
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}$(msg install_complete) ${GREEN}${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${CYAN}$(msg usage_title):${NC}"
echo ""
echo -e " ${GREEN}# $(msg usage_http)${NC}"
echo -e " ${YELLOW}$BINARY_NAME http 3000${NC}"
echo ""
echo -e " ${GREEN}# $(msg usage_tcp)${NC}"
echo -e " ${YELLOW}$BINARY_NAME tcp 5432${NC}"
echo ""
echo -e " ${GREEN}# $(msg usage_config)${NC}"
echo -e " ${YELLOW}$BINARY_NAME config show${NC}"
echo -e " ${YELLOW}$BINARY_NAME config init${NC}"
echo ""
echo -e " ${GREEN}# $(msg usage_daemon)${NC}"
echo -e " ${YELLOW}$BINARY_NAME daemon start http 3000${NC}"
echo -e " ${YELLOW}$BINARY_NAME daemon list${NC}"
echo ""
}
# ============================================================================
# Main
# ============================================================================
main() {
clear
print_banner
select_language
echo -e "${BOLD}────────────────────────────────────────────${NC}"
check_os
check_arch
check_dependencies
check_existing_install
echo ""
download_binary
select_install_dir
install_binary
update_path
verify_installation
# Skip configuration for updates
if [[ "$IS_UPDATE" != true ]]; then
configure_client
test_connection
else
echo ""
local new_version=$("$INSTALL_DIR/$BINARY_NAME" version 2>/dev/null | awk '/Version:/ {print $2}' || echo "installed")
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}$(msg update_ok) ${GREEN}${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
print_info "Version: $new_version"
echo ""
return
fi
show_completion
}
# Run
main "$@"

503
scripts/test/one-click-test.sh Executable file
View File

@@ -0,0 +1,503 @@
#!/bin/bash
# Drip One-Click Performance Test Script
# Automatically starts all services, runs tests, and generates reports
set -e
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
RESULTS_DIR="benchmark-results"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_DIR="/tmp/drip-test-${TIMESTAMP}"
REPORT_FILE="${RESULTS_DIR}/test-report-${TIMESTAMP}.txt"
# Port configuration
HTTP_TEST_PORT=3000
DRIP_SERVER_PORT=8443
PPROF_PORT=6060
# PID file
PIDS_FILE="${LOG_DIR}/pids.txt"
# Create directories
mkdir -p "$RESULTS_DIR"
mkdir -p "$LOG_DIR"
# ============================================
# Helper functions
# ============================================
# All logs go to stderr to avoid being consumed by command substitution $(...)
log_info() {
echo -e "${GREEN}[INFO]${NC} $1" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_step() {
echo -e "\n${BLUE}==>${NC} $1\n" >&2
}
# Cleanup function
cleanup() {
log_step "Cleaning up test environment..."
if [ -f "$PIDS_FILE" ]; then
log_info "Stopping all test processes..."
while read -r pid; do
if ps -p "$pid" > /dev/null 2>&1; then
log_info "Stopping process $pid"
kill "$pid" 2>/dev/null || true
fi
done < "$PIDS_FILE"
rm -f "$PIDS_FILE"
fi
# Extra cleanup: ensure ports are released
pkill -f "python.*${HTTP_TEST_PORT}" 2>/dev/null || true
pkill -f "drip server.*${DRIP_SERVER_PORT}" 2>/dev/null || true
pkill -f "drip http ${HTTP_TEST_PORT}" 2>/dev/null || true
log_info "Cleanup completed"
}
# Register cleanup function
trap cleanup EXIT INT TERM
# Check dependencies
check_dependencies() {
log_step "Checking dependencies..."
local missing=""
if ! command -v wrk &> /dev/null; then
missing="${missing}\n - wrk (brew install wrk)"
fi
if ! command -v python3 &> /dev/null; then
missing="${missing}\n - python3"
fi
if ! command -v openssl &> /dev/null; then
missing="${missing}\n - openssl"
fi
if ! command -v nc &> /dev/null; then
missing="${missing}\n - nc (netcat)"
fi
if [ ! -f "./bin/drip" ]; then
log_error "Cannot find drip executable"
log_info "Please run: make build"
exit 1
fi
if [ -n "$missing" ]; then
log_error "Missing dependencies:${missing}"
exit 1
fi
log_info "✓ All dependencies satisfied"
}
# Generate self-signed ECDSA certificate for testing
generate_test_certs() {
log_step "Generating test TLS certificate (ECDSA)..."
local cert_dir="${LOG_DIR}/certs"
mkdir -p "$cert_dir"
# Generate ECDSA private key (prime256v1 = P-256)
openssl ecparam -name prime256v1 -genkey -noout \
-out "${cert_dir}/server.key" >/dev/null 2>&1
# Generate self-signed certificate with this private key
openssl req -new -x509 \
-key "${cert_dir}/server.key" \
-out "${cert_dir}/server.crt" \
-days 1 \
-subj "/C=US/ST=Test/L=Test/O=Test/CN=localhost" \
>/dev/null 2>&1
if [ -f "${cert_dir}/server.crt" ] && [ -f "${cert_dir}/server.key" ]; then
log_info "✓ ECDSA test certificate generated"
# Note: this echo is the "return value", stdout only outputs this line
echo "${cert_dir}/server.crt ${cert_dir}/server.key"
else
log_error "ECDSA certificate generation failed"
exit 1
fi
}
# Wait for port to be available
wait_for_port() {
local port=$1
local max_wait=${2:-30}
local waited=0
while ! nc -z localhost "$port" 2>/dev/null; do
if [ "$waited" -ge "$max_wait" ]; then
return 1
fi
sleep 1
waited=$((waited + 1))
done
return 0
}
# Start HTTP test server
start_http_server() {
log_step "Starting HTTP test server (port $HTTP_TEST_PORT)..."
# Create simple test server
cat > "${LOG_DIR}/test-server.py" << 'EOF'
import http.server
import socketserver
import json
from datetime import datetime
import sys
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 3000
class TestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
response = {
"status": "ok",
"timestamp": datetime.now().isoformat(),
"message": "Test server response"
}
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(response).encode())
def log_message(self, format, *args):
pass # Silent logging
with socketserver.TCPServer(("", PORT), TestHandler) as httpd:
print(f"Server started on port {PORT}", flush=True)
httpd.serve_forever()
EOF
python3 "${LOG_DIR}/test-server.py" "$HTTP_TEST_PORT" \
> "${LOG_DIR}/http-server.log" 2>&1 &
local pid=$!
echo "$pid" >> "$PIDS_FILE"
if wait_for_port "$HTTP_TEST_PORT" 10; then
log_info "✓ HTTP test server started (PID: $pid)"
else
log_error "HTTP test server failed to start"
exit 1
fi
}
# Start Drip server
start_drip_server() {
log_step "Starting Drip server (port $DRIP_SERVER_PORT)..."
local cert_path=$1
local key_path=$2
./bin/drip server \
--port "$DRIP_SERVER_PORT" \
--domain localhost \
--tls-cert "$cert_path" \
--tls-key "$key_path" \
> "${LOG_DIR}/drip-server.log" 2>&1 &
local pid=$!
echo "$pid" >> "$PIDS_FILE"
if wait_for_port "$DRIP_SERVER_PORT" 10; then
log_info "✓ Drip server started (PID: $pid)"
else
log_error "Drip server failed to start"
cat "${LOG_DIR}/drip-server.log"
exit 1
fi
}
# Start Drip client and extract URL
start_drip_client() {
log_step "Starting Drip client..."
./bin/drip http "$HTTP_TEST_PORT" \
--server "localhost:${DRIP_SERVER_PORT}" \
--insecure \
> "${LOG_DIR}/drip-client.log" 2>&1 &
local pid=$!
echo "$pid" >> "$PIDS_FILE"
# Wait for client to start and extract URL
log_info "Waiting for tunnel to establish..."
sleep 3
# Extract tunnel URL from logs
local tunnel_url=""
local max_attempts=10
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
# Use grep to extract URL starting with https:// and remove ANSI color codes
tunnel_url=$(grep -oE 'https://[a-zA-Z0-9.-]+:[0-9]+' "${LOG_DIR}/drip-client.log" 2>/dev/null | head -1)
if [ -n "$tunnel_url" ]; then
break
fi
sleep 1
attempt=$((attempt + 1))
done
if [ -z "$tunnel_url" ]; then
log_error "Cannot get tunnel URL"
log_info "Client logs:"
cat "${LOG_DIR}/drip-client.log"
exit 1
fi
log_info "✓ Drip client started (PID: $pid)"
log_info "✓ Tunnel URL: $tunnel_url"
# Return URL
echo "$tunnel_url"
}
# Verify connectivity
verify_connectivity() {
local url=$1
log_step "Verifying tunnel connectivity..."
local max_attempts=5
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
if curl -sk --max-time 5 "$url" > /dev/null 2>&1; then
log_info "✓ Tunnel connectivity normal"
return 0
fi
attempt=$((attempt + 1))
log_warn "Attempt $attempt/$max_attempts..."
sleep 2
done
log_error "Tunnel connectivity test failed"
return 1
}
# Run performance tests
run_performance_tests() {
local url=$1
log_step "Starting performance tests..."
# Test 1: Quick benchmark
log_info "[1/3] Quick benchmark (10s)..."
wrk -t 4 -c 50 -d 10s --latency "$url" \
> "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt" 2>&1
# Test 2: Standard load test
log_info "[2/3] Standard load test (30s)..."
wrk -t 8 -c 100 -d 30s --latency "$url" \
> "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt" 2>&1
# Test 3: High concurrency test
log_info "[3/3] High concurrency test (30s)..."
wrk -t 12 -c 400 -d 30s --latency "$url" \
> "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt" 2>&1
log_info "✓ Performance tests completed"
}
# Generate test report
generate_report() {
log_step "Generating test report..."
cat > "$REPORT_FILE" << EOF
========================================
Drip Performance Test Report
========================================
Test Time: $(date)
Test Version: $(./bin/drip version 2>/dev/null | head -1 || echo "unknown")
========================================
Test Environment
========================================
OS: $(uname -s)
CPU Cores: $(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo "unknown")
Memory: $(sysctl -n hw.memsize 2>/dev/null | awk '{print int($1/1024/1024/1024)"GB"}' || echo "unknown")
========================================
Test Results
========================================
EOF
# Parse and add results from each test
if [ -f "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt" ]; then
{
echo "--- Quick Benchmark (10s, 50 connections) ---"
grep "Requests/sec:" "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt"
grep "Transfer/sec:" "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt"
echo ""
grep "Latency" "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt" | head -1
grep -A 3 "Latency Distribution" "${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt"
echo ""
} >> "$REPORT_FILE"
fi
if [ -f "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt" ]; then
{
echo "--- Standard Load Test (30s, 100 connections) ---"
grep "Requests/sec:" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
grep "Transfer/sec:" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
echo ""
grep "Latency" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt" | head -1
grep -A 3 "Latency Distribution" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
echo ""
} >> "$REPORT_FILE"
fi
if [ -f "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt" ]; then
{
echo "--- High Concurrency Test (30s, 400 connections) ---"
grep "Requests/sec:" "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt"
grep "Transfer/sec:" "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt"
echo ""
grep "Latency" "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt" | head -1
grep -A 3 "Latency Distribution" "${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt"
echo ""
} >> "$REPORT_FILE"
fi
# Add performance evaluation
cat >> "$REPORT_FILE" << 'EOF'
========================================
Performance Evaluation Standards
========================================
Excellent (Phase 1 optimization target):
✓ QPS > 5000
✓ P99 latency < 50ms
✓ Error rate = 0%
Good:
✓ QPS > 2000
✓ P99 latency < 100ms
✓ Error rate < 0.1%
Needs Optimization:
✗ QPS < 1000
✗ P99 latency > 200ms
========================================
Detailed Result Files
========================================
EOF
{
echo "Quick test: ${RESULTS_DIR}/quick-benchmark-${TIMESTAMP}.txt"
echo "Standard test: ${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
echo "High concurrency test: ${RESULTS_DIR}/high-concurrency-${TIMESTAMP}.txt"
echo ""
echo "Log directory: ${LOG_DIR}/"
} >> "$REPORT_FILE"
log_info "✓ Test report generated: $REPORT_FILE"
}
# Show report summary
show_summary() {
log_step "Test Results Summary"
if [ -f "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt" ]; then
echo ""
echo "========================================="
echo " Standard Load Test Results"
echo "========================================="
grep "Requests/sec:" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
grep "Transfer/sec:" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
echo ""
grep "50%" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
grep "99%" "${RESULTS_DIR}/standard-benchmark-${TIMESTAMP}.txt"
echo "========================================="
echo ""
fi
log_info "Full report: $REPORT_FILE"
log_info "Detailed results: $RESULTS_DIR/"
}
# ============================================
# Main flow
# ============================================
main() {
clear
echo "========================================="
echo " Drip One-Click Performance Test"
echo "========================================="
echo ""
# Check dependencies
check_dependencies
# Generate test certificate (ECDSA), only capture the last line of stdout with paths
CERT_PATHS=$(generate_test_certs)
CERT_FILE=$(echo "$CERT_PATHS" | awk '{print $1}')
KEY_FILE=$(echo "$CERT_PATHS" | awk '{print $2}')
log_info "Using certificate: $CERT_FILE"
log_info "Using private key: $KEY_FILE"
# Start all services
start_http_server
start_drip_server "$CERT_FILE" "$KEY_FILE"
TUNNEL_URL=$(start_drip_client)
# Verify connectivity
if ! verify_connectivity "$TUNNEL_URL"; then
log_error "Test aborted: tunnel not accessible"
exit 1
fi
# Warm up
log_info "Warming up tunnel (5s)..."
for _ in {1..5}; do
curl -sk "$TUNNEL_URL" > /dev/null 2>&1 || true
sleep 1
done
# Run tests
run_performance_tests "$TUNNEL_URL"
# Generate report
generate_report
# Show summary
show_summary
log_step "Testing completed!"
echo ""
echo "Press any key to view full report, or Ctrl+C to exit..."
read -n 1 -s
cat "$REPORT_FILE"
}
# Run main flow
main