diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..706fd07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +.vscode diff --git a/README_UNIFIED.md b/README_UNIFIED.md new file mode 100644 index 0000000..bd31c69 --- /dev/null +++ b/README_UNIFIED.md @@ -0,0 +1,250 @@ +# GoProxy 统一代理 + +GoProxy统一代理是一个高性能、功能丰富的Go代理实现,支持正向代理、反向代理和透明代理模式。本项目整合了原有的多种代理实现,提供了统一、简洁的接口和更完善的功能。 + +## 特性 + +- **多种代理模式**:支持正向代理、反向代理和透明代理模式 +- **HTTP/HTTPS支持**:完整支持HTTP和HTTPS协议 +- **WebSocket支持**:支持WebSocket协议的代理转发 +- **负载均衡**:内置多种负载均衡算法,包括轮询、随机等 +- **健康检查**:支持对后端服务进行健康检查,自动剔除不健康的节点 +- **缓存机制**:支持HTTP响应缓存,提高访问速度 +- **DNS解析**:支持自定义DNS解析和DNS缓存 +- **规则路由**:支持基于规则的请求路由和转发 +- **监控指标**:内置指标收集,方便监控和调优 +- **优雅关闭**:支持优雅启动和关闭,保证请求不丢失 +- **热重载**:支持配置热重载,无需重启服务 + +## 安装 + +```bash +go get github.com/darkit/goproxy +``` + +## 快速开始 + +### 创建正向代理 + +```go +package main + +import ( + "log" + "net/http" + + "github.com/darkit/goproxy" +) + +func main() { + // 创建正向代理 + proxy, err := goproxy.NewForwardProxy(":8080") + if err != nil { + log.Fatalf("创建代理失败: %v", err) + } + + // 启动HTTP服务器 + log.Println("正向代理启动在 :8080") + if err := http.ListenAndServe(":8080", proxy); err != nil { + log.Fatalf("服务器启动失败: %v", err) + } +} +``` + +### 创建反向代理 + +```go +package main + +import ( + "log" + "net/http" + + "github.com/darkit/goproxy" +) + +func main() { + // 创建反向代理 + proxy, err := goproxy.NewReverseProxy(":8080", "http://example.com") + if err != nil { + log.Fatalf("创建代理失败: %v", err) + } + + // 启动HTTP服务器 + log.Println("反向代理启动在 :8080,转发到 http://example.com") + if err := http.ListenAndServe(":8080", proxy); err != nil { + log.Fatalf("服务器启动失败: %v", err) + } +} +``` + +### 创建带负载均衡的反向代理 + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/darkit/goproxy" + "github.com/darkit/goproxy/config" + "github.com/darkit/goproxy/pkg/loadbalance" +) + +func main() { + // 创建负载均衡器 + lb := loadbalance.NewRoundRobinBalancer() + lb.Add("http://backend1:8080", 1) + lb.Add("http://backend2:8080", 1) + lb.Add("http://backend3:8080", 1) + + // 创建统一配置 + cfg := config.DefaultUnifiedConfig() + cfg.ListenAddr = ":8080" + cfg.ProxyMode = config.ModeReverse + cfg.EnableLoadBalancing = true + + // 创建代理 + proxy, err := goproxy.CreateUnifiedProxy( + goproxy.WithUnifiedConfig(cfg), + goproxy.WithUnifiedLoadBalancer(lb), + ) + if err != nil { + log.Fatalf("创建代理失败: %v", err) + } + + // 启动HTTP服务器 + log.Println("负载均衡反向代理启动在 :8080") + if err := http.ListenAndServe(":8080", proxy); err != nil { + log.Fatalf("服务器启动失败: %v", err) + } +} +``` + +## 命令行工具 + +GoProxy 提供了一个功能齐全的命令行工具,支持各种代理模式和配置选项。 + +```bash +# 启动正向代理 +go run cmd/unified_proxy/main.go -mode forward -listen :8080 + +# 启动反向代理 +go run cmd/unified_proxy/main.go -mode reverse -listen :8080 -target http://example.com + +# 启动带负载均衡的反向代理 +go run cmd/unified_proxy/main.go -mode reverse -listen :8080 -backends "http://backend1:8080,http://backend2:8080" -health-check -lb-strategy round-robin + +# 启动带HTTPS的反向代理 +go run cmd/unified_proxy/main.go -mode reverse -listen :443 -target http://example.com -https -cert server.crt -key server.key + +# 启动带缓存的代理 +go run cmd/unified_proxy/main.go -mode forward -listen :8080 -cache -cache-ttl 5m +``` + +## 配置选项 + +### 通用选项 + +- `-mode`:代理模式 (forward, reverse, transparent) +- `-listen`:监听地址 +- `-https`:启用HTTPS +- `-cert`:TLS证书文件 +- `-key`:TLS密钥文件 +- `-cache`:启用响应缓存 +- `-cache-ttl`:缓存过期时间 +- `-insecure`:跳过SSL验证 +- `-verbose`:输出详细日志 + +### 反向代理选项 + +- `-target`:目标服务地址 +- `-rules`:规则文件路径 + +### 负载均衡选项 + +- `-backends`:后端服务器列表,用逗号分隔 +- `-lb-strategy`:负载均衡策略 (round-robin, random, weighted) +- `-health-check`:启用健康检查 +- `-health-interval`:健康检查间隔时间 + +## 高级用法 + +### 自定义委托 + +通过实现`Delegate`接口,可以在代理处理请求的各个阶段进行干预: + +```go +type CustomDelegate struct { + goproxy.DefaultDelegate +} + +func (d *CustomDelegate) BeforeRequest(ctx *goproxy.Context) { + // 在请求发送前修改请求 + ctx.Req.Header.Set("X-Custom-Header", "value") +} + +func (d *CustomDelegate) BeforeResponse(ctx *goproxy.Context, resp *http.Response, err error) { + // 在返回响应前修改响应 + if resp != nil { + resp.Header.Set("X-Response-Time", time.Now().String()) + } +} + +// 使用自定义委托 +proxy, err := goproxy.CreateUnifiedProxy( + goproxy.WithUnifiedDelegate(&CustomDelegate{}), +) +``` + +### 带规则的反向代理 + +可以使用JSON规则文件定义复杂的路由规则: + +```json +{ + "rules": [ + { + "path": "/api/", + "target": "http://api-server:8000", + "strip_prefix": true + }, + { + "host": "admin.example.com", + "target": "http://admin-server:9000" + }, + { + "default": true, + "target": "http://default-server:8080" + } + ] +} +``` + +## 性能优化 + +- **连接池**:启用连接池可以减少连接建立的开销 +- **DNS缓存**:启用DNS缓存可以减少DNS解析的时间 +- **响应缓存**:对于频繁访问的静态资源,启用响应缓存可以大幅提高性能 +- **压缩**:启用压缩可以减少传输数据量,但会增加CPU开销 + +## 监控与指标 + +GoProxy内置了指标收集功能,可以方便地监控代理的运行状态: + +- 请求计数 +- 错误计数 +- 响应时间 +- 缓存命中率 +- 连接数 +- 并发数 + +## 贡献 + +欢迎贡献代码、报告问题或提出改进建议。请先阅读CONTRIBUTING.md了解贡献流程。 + +## 许可证 + +本项目采用MIT许可证,详见LICENSE文件。 \ No newline at end of file diff --git a/cmd/cmd_reverse_proxy.go b/cmd/cmd_reverse_proxy/cmd_reverse_proxy.go similarity index 100% rename from cmd/cmd_reverse_proxy.go rename to cmd/cmd_reverse_proxy/cmd_reverse_proxy.go diff --git a/cmd/functional_options_proxy.go b/cmd/functional_options_proxy/functional_options_proxy.go similarity index 92% rename from cmd/functional_options_proxy.go rename to cmd/functional_options_proxy/functional_options_proxy.go index 543aef0..9d300b5 100644 --- a/cmd/functional_options_proxy.go +++ b/cmd/functional_options_proxy/functional_options_proxy.go @@ -14,8 +14,7 @@ import ( "github.com/darkit/goproxy/pkg/metrics" ) -// 这是functional_options_proxy的main函数,改名避免与其他文件冲突 -func main_functional_options() { +func main() { // 创建基本配置 cfg := config.DefaultConfig() @@ -93,8 +92,3 @@ func main_functional_options() { log.Fatalf("服务器启动失败: %v", err) } } - -// 添加实际的main函数,调用上面的示例函数 -func main() { - main_functional_options() -} diff --git a/cmd/https_reverse_proxy.go b/cmd/https_reverse_proxy/https_reverse_proxy.go similarity index 100% rename from cmd/https_reverse_proxy.go rename to cmd/https_reverse_proxy/https_reverse_proxy.go diff --git a/cmd/middleware_reverse_proxy.go b/cmd/middleware_reverse_proxy/middleware_reverse_proxy.go similarity index 100% rename from cmd/middleware_reverse_proxy.go rename to cmd/middleware_reverse_proxy/middleware_reverse_proxy.go diff --git a/cmd/reverse_proxy_example.go b/cmd/reverse_proxy_example/reverse_proxy_example.go similarity index 100% rename from cmd/reverse_proxy_example.go rename to cmd/reverse_proxy_example/reverse_proxy_example.go diff --git a/cmd/proxy_rules.json b/cmd/rules_reverse_proxy/proxy_rules.json similarity index 100% rename from cmd/proxy_rules.json rename to cmd/rules_reverse_proxy/proxy_rules.json diff --git a/cmd/rules_reverse_proxy.go b/cmd/rules_reverse_proxy/rules_reverse_proxy.go similarity index 96% rename from cmd/rules_reverse_proxy.go rename to cmd/rules_reverse_proxy/rules_reverse_proxy.go index cb4c8ea..c1224c5 100644 --- a/cmd/rules_reverse_proxy.go +++ b/cmd/rules_reverse_proxy/rules_reverse_proxy.go @@ -42,7 +42,7 @@ func main() { cfg.PreserveClientIP = true // 保留客户端IP cfg.AddXForwardedFor = true // 添加X-Forwarded-For头 cfg.AddXRealIP = true // 添加X-Real-IP头 - cfg.TargetAddr = "www.chipeak.com" + cfg.TargetAddr = "https://git.zishuo.net" // 创建代理实例 proxy := goproxy.New(&goproxy.Options{ @@ -114,7 +114,7 @@ func createExampleRulesFile(filename string) { ] }` - err := os.WriteFile(filename, []byte(content), 0644) + err := os.WriteFile(filename, []byte(content), 0o644) if err != nil { log.Fatalf("创建规则文件失败: %v", err) } diff --git a/cmd/simple_reverse_proxy.go b/cmd/simple_reverse_proxy.go deleted file mode 100644 index e52605f..0000000 --- a/cmd/simple_reverse_proxy.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" - - "github.com/darkit/goproxy" - "github.com/darkit/goproxy/config" -) - -func main() { - // 创建基本配置 - cfg := config.DefaultConfig() - - // 配置反向代理 - cfg.ReverseProxy = true // 启用反向代理模式 - cfg.ListenAddr = ":8080" // 监听本地8080端口 - cfg.TargetAddr = "http://example.com" // 转发到example.com - - // 启用一些基本功能 - cfg.PreserveClientIP = true // 保留客户端IP - cfg.AddXForwardedFor = true // 添加X-Forwarded-For头 - - // 创建代理实例 - proxy := goproxy.New(&goproxy.Options{ - Config: cfg, - }) - - // 启动服务器 - fmt.Println("反向代理启动在 :8080,转发到 http://example.com") - if err := http.ListenAndServe(":8080", proxy); err != nil { - log.Fatalf("服务器启动失败: %v", err) - } -} diff --git a/cmd/simple_reverse_proxy_fixed.go b/cmd/simple_reverse_proxy/simple_reverse_proxy.go similarity index 100% rename from cmd/simple_reverse_proxy_fixed.go rename to cmd/simple_reverse_proxy/simple_reverse_proxy.go diff --git a/cmd/unified_proxy/main.go b/cmd/unified_proxy/main.go new file mode 100644 index 0000000..e5d579c --- /dev/null +++ b/cmd/unified_proxy/main.go @@ -0,0 +1,258 @@ +package main + +import ( + "context" + "errors" + "flag" + "log/slog" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/darkit/goproxy" + "github.com/darkit/goproxy/config" + "github.com/darkit/goproxy/pkg/cache" + "github.com/darkit/goproxy/pkg/dns" + "github.com/darkit/goproxy/pkg/healthcheck" + "github.com/darkit/goproxy/pkg/loadbalance" + "github.com/darkit/goproxy/pkg/metrics" +) + +func main() { + // 通用选项 + proxyMode := flag.String("mode", "forward", "代理模式 [forward, reverse, transparent]") + listenAddr := flag.String("listen", ":8080", "监听地址") + targetAddr := flag.String("target", "http://192.168.1.240", "目标地址 (只在反向代理模式下使用)") + enableHTTPS := flag.Bool("https", false, "是否启用HTTPS") + certFile := flag.String("cert", "", "证书文件路径") + keyFile := flag.String("key", "", "密钥文件路径") + enableCache := flag.Bool("cache", false, "是否启用响应缓存") + cacheTTL := flag.Duration("cache-ttl", 5*time.Minute, "缓存过期时间") + insecure := flag.Bool("insecure", false, "是否跳过证书验证") + + // 反向代理选项 + rulesFile := flag.String("rules", "", "规则文件路径 (只在反向代理模式下使用)") + + // 负载均衡选项 + backends := flag.String("backends", "", "后端服务器列表,用逗号分隔") + lbStrategy := flag.String("lb-strategy", "round-robin", "负载均衡策略 [round-robin, random, weighted]") + healthCheck := flag.Bool("health-check", false, "是否启用健康检查") + healthInterval := flag.Duration("health-interval", 10*time.Second, "健康检查间隔时间") + + // 其他选项 + verbose := flag.Bool("verbose", false, "是否输出详细日志") + + // 解析命令行参数 + flag.Parse() + + // 设置日志级别 + var logLevel slog.Level + if *verbose { + logLevel = slog.LevelDebug + } else { + logLevel = slog.LevelInfo + } + + logLevelVar := &logLevel + + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + Level: logLevelVar, + })) + slog.SetDefault(logger) + + // 创建统一配置 + cfg := config.DefaultUnifiedConfig() + cfg.ListenAddr = *listenAddr + cfg.Logger = logger + + // 设置代理模式 + switch strings.ToLower(*proxyMode) { + case "forward", "proxy": + cfg.ProxyMode = config.ModeForward + case "reverse": + cfg.ProxyMode = config.ModeReverse + if *targetAddr != "" { + cfg.TargetAddr = *targetAddr + } + if *rulesFile != "" { + cfg.RulesFile = *rulesFile + } + case "transparent": + cfg.ProxyMode = config.ModeTransparent + default: + logger.Error("不支持的代理模式", "mode", *proxyMode) + os.Exit(1) + } + + // HTTPS设置 + cfg.EnableHTTPS = *enableHTTPS + if *enableHTTPS { + if *certFile == "" || *keyFile == "" { + logger.Error("启用HTTPS需要提供证书和密钥文件") + os.Exit(1) + } + cfg.TLSCert = *certFile + cfg.TLSKey = *keyFile + } + + // 缓存设置 + cfg.EnableCache = *enableCache + cfg.CacheTTL = *cacheTTL + + // SSL验证设置 + cfg.InsecureSkipVerify = *insecure + + // 添加X-Forwarded-For头 + cfg.AddXForwardedFor = true + // 添加X-Real-IP头 + cfg.AddXRealIP = true + + // 负载均衡设置 + if *backends != "" { + backendList := strings.Split(*backends, ",") + cfg.EnableLoadBalancing = true + cfg.Backends = backendList + + // 健康检查设置 + if *healthCheck { + cfg.EnableHealthCheck = true + cfg.HealthCheckInterval = *healthInterval + cfg.HealthCheckTimeout = 5 * time.Second + } + } + + // 创建统一代理选项 + opts := &goproxy.UnifiedOptions{ + Config: cfg, + } + + // 根据需要添加额外组件 + + // 添加缓存 + if *enableCache { + opts.HTTPCache = cache.NewMemoryCache(*cacheTTL, time.Minute, 10000) + } + + // 添加负载均衡器 + if cfg.EnableLoadBalancing { + var lb loadbalance.LoadBalancer + + switch strings.ToLower(*lbStrategy) { + case "round-robin": + lb = loadbalance.NewRoundRobinBalancer() + case "random": + lb = loadbalance.NewRandomBalancer() + case "weighted": + lb = loadbalance.NewRoundRobinBalancer() // 实际上是加权轮询,但简化处理 + default: + logger.Warn("不支持的负载均衡策略,使用默认的轮询策略", "strategy", *lbStrategy) + lb = loadbalance.NewRoundRobinBalancer() + } + + // 添加后端服务器 + for _, backend := range cfg.Backends { + lb.Add(backend, 1) + } + + opts.LoadBalancer = lb + + // 添加健康检查器 + if cfg.EnableHealthCheck { + healthCfg := &healthcheck.Config{ + Interval: cfg.HealthCheckInterval, + Timeout: cfg.HealthCheckTimeout, + MaxFails: 3, + MinSuccess: 1, + } + + healthChecker := healthcheck.NewHealthChecker(healthCfg) + for _, backend := range cfg.Backends { + healthChecker.AddTarget(backend) + } + + opts.HealthChecker = healthChecker + + // 启动健康检查 + healthChecker.Start() + defer healthChecker.Stop() + } + } + + // 添加DNS解析器 + if cfg.ProxyMode == config.ModeReverse { + resolver := dns.NewResolver( + dns.WithFallback(true), // 如果找不到自定义规则,回退到系统DNS + dns.WithTTL(5*time.Minute), // 设置缓存TTL + ) + // 将解析器添加到选项中 + opts.DNSResolver = resolver + logger.Info("已创建自定义DNS解析器", "fallback", true, "ttl", "5m") + } + + // 添加监控指标 + opts.Metrics = metrics.NewSimpleMetrics() + + // 创建统一代理 + proxy, err := goproxy.NewUnifiedProxy(opts) + if err != nil { + logger.Error("创建代理失败", "error", err) + os.Exit(1) + } + + // 打印启动信息 + logger.Info("代理服务器启动", "mode", cfg.ProxyMode, "listen", cfg.ListenAddr, "https", cfg.EnableHTTPS) + + if cfg.ProxyMode == config.ModeReverse { + logger.Info("反向代理设置", "target", cfg.TargetAddr, "rules", cfg.RulesFile) + } + + if cfg.EnableLoadBalancing { + logger.Info("负载均衡设置", "strategy", *lbStrategy, "backends", cfg.Backends, "health_check", cfg.EnableHealthCheck) + } + + // 创建HTTP服务器 + server := &http.Server{ + Addr: cfg.ListenAddr, + Handler: proxy, + } + + // 启动HTTP服务器 + go func() { + var err error + if cfg.EnableHTTPS { + err = server.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey) + } else { + err = server.ListenAndServe() + } + + if err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error("服务器运行失败", "error", err) + os.Exit(1) + } + }() + + // 等待中断信号 + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + + logger.Info("正在关闭服务器...") + + // 优雅关闭 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := server.Shutdown(ctx); err != nil { + logger.Error("服务器关闭失败", "error", err) + } + + // 关闭代理 + if err := proxy.Close(); err != nil { + logger.Error("代理关闭失败", "error", err) + } + + logger.Info("服务器已关闭") +} diff --git a/config/unified_config.go b/config/unified_config.go new file mode 100644 index 0000000..f418602 --- /dev/null +++ b/config/unified_config.go @@ -0,0 +1,254 @@ +package config + +import ( + "log/slog" + "time" + + "github.com/darkit/goproxy/pkg/reverse" +) + +// ProxyMode 代理模式 +type ProxyMode string + +const ( + // ModeForward 正向代理模式 + ModeForward ProxyMode = "forward" + // ModeReverse 反向代理模式 + ModeReverse ProxyMode = "reverse" + // ModeTransparent 透明代理模式 + ModeTransparent ProxyMode = "transparent" +) + +// UnifiedConfig 统一代理配置 +type UnifiedConfig struct { + // 基本配置 + ListenAddr string `json:"listen_addr" yaml:"listen_addr" toml:"listen_addr"` // 监听地址 + ProxyMode ProxyMode `json:"proxy_mode" yaml:"proxy_mode" toml:"proxy_mode"` // 代理模式:forward, reverse, transparent + TargetAddr string `json:"target_addr" yaml:"target_addr" toml:"target_addr"` // 目标地址(反向代理使用) + EnableHTTPS bool `json:"enable_https" yaml:"enable_https" toml:"enable_https"` // 是否启用HTTPS + DecryptHTTPS bool `json:"decrypt_https" yaml:"decrypt_https" toml:"decrypt_https"` // 是否解密HTTPS(正向代理使用) + TLSCert string `json:"tls_cert" yaml:"tls_cert" toml:"tls_cert"` // TLS证书文件路径 + TLSKey string `json:"tls_key" yaml:"tls_key" toml:"tls_key"` // TLS密钥文件路径 + CACert string `json:"ca_cert" yaml:"ca_cert" toml:"ca_cert"` // CA证书文件路径(用于生成动态证书) + CAKey string `json:"ca_key" yaml:"ca_key" toml:"ca_key"` // CA密钥文件路径(用于生成动态证书) + UseECDSA bool `json:"use_ecdsa" yaml:"use_ecdsa" toml:"use_ecdsa"` // 是否使用ECDSA生成证书(默认使用RSA) + InsecureSkipVerify bool `json:"insecure_skip_verify" yaml:"insecure_skip_verify" toml:"insecure_skip_verify"` // 是否跳过TLS证书验证 + + // 连接配置 + DisableKeepAlive bool `json:"disable_keep_alive" yaml:"disable_keep_alive" toml:"disable_keep_alive"` // 是否禁用连接复用 + RequestTimeout time.Duration `json:"request_timeout" yaml:"request_timeout" toml:"request_timeout"` // 请求超时时间 + IdleTimeout time.Duration `json:"idle_timeout" yaml:"idle_timeout" toml:"idle_timeout"` // 连接空闲超时时间 + MaxIdleConns int `json:"max_idle_conns" yaml:"max_idle_conns" toml:"max_idle_conns"` // 最大空闲连接数 + EnableConnectionPool bool `json:"enable_connection_pool" yaml:"enable_connection_pool" toml:"enable_connection_pool"` // 是否启用连接池 + ConnectionPoolSize int `json:"connection_pool_size" yaml:"connection_pool_size" toml:"connection_pool_size"` // 连接池大小 + MaxConnections int `json:"max_connections" yaml:"max_connections" toml:"max_connections"` // 最大连接数 + EnableWebSocket bool `json:"enable_websocket" yaml:"enable_websocket" toml:"enable_websocket"` // 是否启用WebSocket + WebSocketIntercept bool `json:"websocket_intercept" yaml:"websocket_intercept" toml:"websocket_intercept"` // 是否拦截WebSocket + SupportWebSocketUpgrade bool `json:"support_websocket_upgrade" yaml:"support_websocket_upgrade" toml:"support_websocket_upgrade"` // 是否支持Websocket升级 + + // 缓存配置 + EnableCache bool `json:"enable_cache" yaml:"enable_cache" toml:"enable_cache"` // 是否启用响应缓存 + CacheTTL time.Duration `json:"cache_ttl" yaml:"cache_ttl" toml:"cache_ttl"` // 缓存过期时间 + DNSCacheTTL time.Duration `json:"dns_cache_ttl" yaml:"dns_cache_ttl" toml:"dns_cache_ttl"` // DNS缓存过期时间 + + // 重试配置 + EnableRetry bool `json:"enable_retry" yaml:"enable_retry" toml:"enable_retry"` // 是否启用重试机制 + MaxRetries int `json:"max_retries" yaml:"max_retries" toml:"max_retries"` // 最大重试次数 + RetryBackoff time.Duration `json:"retry_backoff" yaml:"retry_backoff" toml:"retry_backoff"` // 重试间隔基数 + MaxRetryBackoff time.Duration `json:"max_retry_backoff" yaml:"max_retry_backoff" toml:"max_retry_backoff"` // 最大重试间隔 + + // HTTP头部操作 + EnableCompression bool `json:"enable_compression" yaml:"enable_compression" toml:"enable_compression"` // 是否启用压缩 + EnableCORS bool `json:"enable_cors" yaml:"enable_cors" toml:"enable_cors"` // 是否启用CORS + PreserveClientIP bool `json:"preserve_client_ip" yaml:"preserve_client_ip" toml:"preserve_client_ip"` // 是否保留客户端IP + RewriteHostHeader bool `json:"rewrite_host_header" yaml:"rewrite_host_header" toml:"rewrite_host_header"` // 重写Host头 + AddXForwardedFor bool `json:"add_x_forwarded_for" yaml:"add_x_forwarded_for" toml:"add_x_forwarded_for"` // 是否添加X-Forwarded-For头 + AddXRealIP bool `json:"add_x_real_ip" yaml:"add_x_real_ip" toml:"add_x_real_ip"` // 是否添加X-Real-IP头 + + // 负载均衡配置 + EnableLoadBalancing bool `json:"enable_load_balancing" yaml:"enable_load_balancing" toml:"enable_load_balancing"` // 是否启用负载均衡 + Backends []string `json:"backends" yaml:"backends" toml:"backends"` // 负载均衡后端列表 + EnableHealthCheck bool `json:"enable_health_check" yaml:"enable_health_check" toml:"enable_health_check"` // 是否启用健康检查 + HealthCheckInterval time.Duration `json:"health_check_interval" yaml:"health_check_interval" toml:"health_check_interval"` // 健康检查间隔时间 + HealthCheckTimeout time.Duration `json:"health_check_timeout" yaml:"health_check_timeout" toml:"health_check_timeout"` // 健康检查超时时间 + + // 限流配置 + EnableRateLimit bool `json:"enable_rate_limit" yaml:"enable_rate_limit" toml:"enable_rate_limit"` // 是否启用限流 + RateLimit float64 `json:"rate_limit" yaml:"rate_limit" toml:"rate_limit"` // 每秒请求速率限制 + MaxBurst int `json:"max_burst" yaml:"max_burst" toml:"max_burst"` // 并发请求峰值限制 + + // 监控和跟踪 + EnableMetrics bool `json:"enable_metrics" yaml:"enable_metrics" toml:"enable_metrics"` // 是否启用监控指标 + EnableTracing bool `json:"enable_tracing" yaml:"enable_tracing" toml:"enable_tracing"` // 是否启用请求追踪 + + // 反向代理特有配置 + RulesFile string `json:"rules_file" yaml:"rules_file" toml:"rules_file"` // 规则文件路径 + + // 日志 + Logger *slog.Logger `json:"-" yaml:"-" toml:"-"` // 日志记录器 +} + +// DefaultUnifiedConfig 返回默认统一配置 +func DefaultUnifiedConfig() *UnifiedConfig { + return &UnifiedConfig{ + ListenAddr: ":8080", + ProxyMode: ModeForward, // 默认为正向代理 + DecryptHTTPS: false, + EnableHTTPS: false, + UseECDSA: false, + InsecureSkipVerify: false, + + RequestTimeout: 30 * time.Second, + IdleTimeout: 90 * time.Second, + MaxIdleConns: 100, + EnableConnectionPool: true, + ConnectionPoolSize: 100, + MaxConnections: 1000, + EnableWebSocket: true, + WebSocketIntercept: false, + SupportWebSocketUpgrade: true, + + EnableCache: false, + CacheTTL: 5 * time.Minute, + DNSCacheTTL: 5 * time.Minute, + + EnableRetry: true, + MaxRetries: 3, + RetryBackoff: time.Second, + MaxRetryBackoff: 10 * time.Second, + + EnableCompression: true, + EnableCORS: true, + PreserveClientIP: true, + RewriteHostHeader: false, + AddXForwardedFor: true, + AddXRealIP: true, + + EnableLoadBalancing: false, + Backends: []string{}, + EnableHealthCheck: false, + HealthCheckInterval: 30 * time.Second, + HealthCheckTimeout: 5 * time.Second, + + EnableRateLimit: false, + RateLimit: 0, // 0 表示不限流 + MaxBurst: 50, + + EnableMetrics: false, + EnableTracing: false, + + Logger: slog.Default(), + } +} + +// CreateLegacyConfig 将统一配置转换为旧的配置格式(兼容性用) +func (uc *UnifiedConfig) CreateLegacyConfig() *Config { + cfg := DefaultConfig() + + // 基本配置 + cfg.ListenAddr = uc.ListenAddr + cfg.TargetAddr = uc.TargetAddr + cfg.DecryptHTTPS = uc.DecryptHTTPS + cfg.CACert = uc.CACert + cfg.CAKey = uc.CAKey + cfg.UseECDSA = uc.UseECDSA + cfg.TLSCert = uc.TLSCert + cfg.TLSKey = uc.TLSKey + cfg.InsecureSkipVerify = uc.InsecureSkipVerify + + // 连接配置 + cfg.DisableKeepAlive = uc.DisableKeepAlive + cfg.RequestTimeout = uc.RequestTimeout + cfg.EnableCache = uc.EnableCache + cfg.IdleTimeout = uc.IdleTimeout + cfg.MaxIdleConns = uc.MaxIdleConns + + // 缓存配置 + cfg.DNSCacheTTL = uc.DNSCacheTTL + cfg.CacheTTL = uc.CacheTTL + + // 重试配置 + cfg.EnableRetry = uc.EnableRetry + cfg.MaxRetries = uc.MaxRetries + cfg.BaseBackoff = uc.RetryBackoff + cfg.MaxBackoff = uc.MaxRetryBackoff + + // 限流配置 + cfg.RateLimit = uc.RateLimit + + // 其他配置 + cfg.EnableCORS = uc.EnableCORS + + // 负载均衡配置 + cfg.EnableLoadBalancing = uc.EnableLoadBalancing + cfg.Backends = uc.Backends + cfg.EnableRateLimit = uc.EnableRateLimit + cfg.MaxBurst = uc.MaxBurst + cfg.MaxConnections = uc.MaxConnections + cfg.EnableConnectionPool = uc.EnableConnectionPool + cfg.ConnectionPoolSize = uc.ConnectionPoolSize + cfg.EnableHealthCheck = uc.EnableHealthCheck + cfg.HealthCheckInterval = uc.HealthCheckInterval + cfg.HealthCheckTimeout = uc.HealthCheckTimeout + cfg.EnableMetrics = uc.EnableMetrics + cfg.EnableTracing = uc.EnableTracing + cfg.WebSocketIntercept = uc.WebSocketIntercept + cfg.ReverseProxy = uc.ProxyMode == ModeReverse + cfg.ReverseProxyRulesFile = uc.RulesFile + cfg.PreserveClientIP = uc.PreserveClientIP + cfg.EnableCompression = uc.EnableCompression + cfg.RewriteHostHeader = uc.RewriteHostHeader + cfg.AddXForwardedFor = uc.AddXForwardedFor + cfg.AddXRealIP = uc.AddXRealIP + cfg.SupportWebSocketUpgrade = uc.SupportWebSocketUpgrade + cfg.Logger = uc.Logger + + return cfg +} + +// CreateReverseConfig 将统一配置转换为反向代理配置 +func (uc *UnifiedConfig) CreateReverseConfig() *reverse.Config { + cfg := reverse.DefaultConfig() + + // 基础配置 + cfg.BaseConfig.ListenAddr = uc.ListenAddr + cfg.BaseConfig.TargetAddr = uc.TargetAddr + cfg.BaseConfig.EnableHTTPS = uc.EnableHTTPS + if uc.TLSCert != "" && uc.TLSKey != "" { + cfg.BaseConfig.TLSConfig = &reverse.TLSConfig{ + CertFile: uc.TLSCert, + KeyFile: uc.TLSKey, + InsecureSkipVerify: uc.InsecureSkipVerify, + UseECDSA: uc.UseECDSA, + } + } + cfg.BaseConfig.EnableWebSocket = uc.EnableWebSocket + cfg.BaseConfig.EnableCompression = uc.EnableCompression + cfg.BaseConfig.EnableCORS = uc.EnableCORS + cfg.BaseConfig.PreserveClientIP = uc.PreserveClientIP + cfg.BaseConfig.AddXForwardedFor = uc.AddXForwardedFor + cfg.BaseConfig.AddXRealIP = uc.AddXRealIP + + // 其他配置 + cfg.RulesFile = uc.RulesFile + cfg.InsecureSkipVerify = uc.InsecureSkipVerify + cfg.EnableHealthCheck = uc.EnableHealthCheck + cfg.HealthCheckInterval = uc.HealthCheckInterval + cfg.HealthCheckTimeout = uc.HealthCheckTimeout + cfg.EnableRetry = uc.EnableRetry + cfg.MaxRetries = uc.MaxRetries + cfg.RetryBackoff = uc.RetryBackoff + cfg.MaxRetryBackoff = uc.MaxRetryBackoff + cfg.EnableMetrics = uc.EnableMetrics + cfg.EnableTracing = uc.EnableTracing + cfg.WebSocketIntercept = uc.WebSocketIntercept + cfg.DNSCacheTTL = uc.DNSCacheTTL + cfg.EnableCache = uc.EnableCache + cfg.CacheTTL = uc.CacheTTL + cfg.EnableConnectionPool = uc.EnableConnectionPool + cfg.ConnectionPoolSize = uc.ConnectionPoolSize + cfg.IdleTimeout = uc.IdleTimeout + cfg.RequestTimeout = uc.RequestTimeout + + return cfg +} diff --git a/coverage.out b/coverage.out deleted file mode 100644 index 53f3923..0000000 --- a/coverage.out +++ /dev/null @@ -1,225 +0,0 @@ -mode: set -github.com/darkit/goproxy/pkg/dns/config.go:24.55,26.16 2 1 -github.com/darkit/goproxy/pkg/dns/config.go:26.16,28.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:29.2,33.42 4 1 -github.com/darkit/goproxy/pkg/dns/config.go:33.42,35.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:37.2,37.12 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:41.56,43.16 2 1 -github.com/darkit/goproxy/pkg/dns/config.go:43.16,45.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:46.2,55.47 4 1 -github.com/darkit/goproxy/pkg/dns/config.go:55.47,57.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:59.2,59.20 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:63.61,65.16 2 1 -github.com/darkit/goproxy/pkg/dns/config.go:65.16,67.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:68.2,78.21 5 1 -github.com/darkit/goproxy/pkg/dns/config.go:78.21,83.49 3 1 -github.com/darkit/goproxy/pkg/dns/config.go:83.49,84.12 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:87.3,88.22 2 1 -github.com/darkit/goproxy/pkg/dns/config.go:88.22,89.12 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:92.3,97.21 4 1 -github.com/darkit/goproxy/pkg/dns/config.go:97.21,98.12 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:101.3,106.20 4 1 -github.com/darkit/goproxy/pkg/dns/config.go:106.20,108.4 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:110.3,110.34 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:110.34,112.38 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:112.38,113.10 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:117.4,117.34 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:121.2,121.38 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:121.38,123.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:125.2,125.20 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:129.63,131.20 2 1 -github.com/darkit/goproxy/pkg/dns/config.go:131.20,133.3 1 1 -github.com/darkit/goproxy/pkg/dns/config.go:133.8,135.3 1 0 -github.com/darkit/goproxy/pkg/dns/config.go:137.2,145.17 3 1 -github.com/darkit/goproxy/pkg/dns/config.go:149.43,151.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:19.43,26.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:29.61,32.2 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:35.65,38.2 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:41.55,44.2 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:47.94,50.16 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:50.16,54.3 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:57.2,58.16 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:58.16,60.3 1 0 -github.com/darkit/goproxy/pkg/dns/dialer.go:63.2,63.17 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:63.17,65.17 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:65.17,67.4 1 0 -github.com/darkit/goproxy/pkg/dns/dialer.go:70.3,70.24 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:70.24,72.4 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:74.3,74.21 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:78.2,84.71 2 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:88.66,90.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:93.26,95.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:98.98,100.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:103.52,105.2 1 1 -github.com/darkit/goproxy/pkg/dns/dialer.go:108.111,110.2 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:16.39,21.2 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:24.57,29.2 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:32.49,34.30 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:34.30,36.22 2 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:36.22,38.4 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:41.3,45.17 3 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:45.17,47.4 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:49.3,49.44 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:53.2,53.28 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:57.36,58.16 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:58.16,60.3 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:61.2,61.13 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:65.70,66.16 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:66.16,68.3 1 1 -github.com/darkit/goproxy/pkg/dns/endpoint.go:69.2,69.48 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:73.53,84.33 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:84.33,86.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:88.2,88.10 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:92.63,94.16 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:94.16,96.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:97.2,97.25 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:101.91,106.64 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:106.64,109.3 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:112.2,112.60 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:112.60,115.3 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:118.2,118.36 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:118.36,119.41 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:119.41,122.4 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:124.2,127.16 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:127.16,129.17 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:129.17,131.4 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:134.3,135.28 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:135.28,136.39 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:136.39,138.10 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:142.3,142.15 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:142.15,144.4 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:147.3,148.22 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:148.22,150.4 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:153.3,160.23 4 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:163.2,163.76 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:167.91,168.25 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:168.25,170.3 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:173.2,173.25 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:173.25,175.22 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:175.22,177.4 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:178.3,178.18 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:182.2,183.22 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:184.18,186.68 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:187.14,189.68 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:190.22,192.26 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:195.2,195.40 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:195.40,197.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:198.2,198.17 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:202.65,206.39 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:206.39,207.48 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:207.48,209.4 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:212.2,212.12 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:216.64,218.41 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:218.41,220.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:223.2,223.38 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:223.38,225.29 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:225.29,226.12 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:230.3,230.38 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:230.38,232.4 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:235.2,235.13 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:239.53,241.2 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:244.71,245.28 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:245.28,247.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:249.2,253.50 4 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:253.50,255.31 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:255.31,256.54 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:256.54,258.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:260.3,260.54 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:261.8,263.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:264.2,264.12 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:268.71,270.2 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:273.89,274.28 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:274.28,276.3 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:279.2,279.44 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:279.44,281.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:284.2,290.39 4 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:290.39,291.37 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:291.37,294.37 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:294.37,295.55 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:295.55,297.6 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:300.4,301.14 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:306.2,315.12 3 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:319.52,324.34 3 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:324.34,327.3 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:330.2,330.39 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:330.39,331.27 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:331.27,335.4 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:338.2,338.44 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:342.74,347.42 3 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:347.42,349.31 2 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:349.31,350.36 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:350.36,352.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:354.3,354.29 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:354.29,356.4 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:356.9,358.4 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:359.3,359.13 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:363.2,363.39 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:363.39,364.27 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:364.27,366.37 2 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:366.37,367.37 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:367.37,369.6 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:371.4,371.30 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:371.30,373.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:373.10,375.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:376.4,376.14 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:380.2,380.44 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:384.34,391.2 5 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:397.41,398.33 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:398.33,400.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:404.40,405.33 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:405.33,407.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:411.67,412.33 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:412.33,414.3 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:418.71,422.35 3 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:422.35,424.34 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:424.34,426.18 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:426.18,428.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:430.4,430.39 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:430.39,432.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:435.4,436.41 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:436.41,437.29 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:437.29,439.39 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:439.39,440.57 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:440.57,442.13 2 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:445.6,445.16 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:445.16,447.7 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:448.6,448.11 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:452.4,452.14 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:452.14,460.5 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:462.9,465.18 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:465.18,467.5 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:469.4,469.39 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:469.39,471.5 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:474.4,474.52 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:474.52,476.33 2 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:476.33,477.56 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:477.56,479.12 2 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:482.5,482.15 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:482.15,484.6 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:485.10,487.5 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:492.2,494.12 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:498.46,500.46 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:500.46,509.39 5 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:509.39,511.4 1 0 -github.com/darkit/goproxy/pkg/dns/resolver.go:514.3,514.45 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:519.41,521.29 2 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:521.29,522.18 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:522.18,524.4 1 1 -github.com/darkit/goproxy/pkg/dns/resolver.go:526.2,526.14 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:10.47,11.37 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:11.37,13.3 1 0 -github.com/darkit/goproxy/pkg/dns/wildcard.go:15.2,16.21 2 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:16.21,18.3 1 0 -github.com/darkit/goproxy/pkg/dns/wildcard.go:20.2,23.54 3 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:23.54,25.3 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:27.2,27.54 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:27.54,29.3 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:31.2,31.13 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:41.72,45.2 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:48.65,52.42 3 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:52.42,53.35 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:53.35,55.4 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:58.2,58.70 1 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:62.62,67.2 4 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:70.57,75.2 4 1 -github.com/darkit/goproxy/pkg/dns/wildcard.go:78.42,83.2 4 1 diff --git a/examples/custom_dns_resolver/main.go b/examples/custom_dns_resolver/main.go new file mode 100644 index 0000000..674a3fe --- /dev/null +++ b/examples/custom_dns_resolver/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "context" + "flag" + "log/slog" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/darkit/goproxy" + "github.com/darkit/goproxy/pkg/dns" +) + +func main() { + // 解析命令行参数 + var ( + listenAddr = flag.String("listen", ":8080", "代理服务器监听地址") + targetAddr = flag.String("target", "http://example.com", "目标服务器地址(反向代理模式下使用)") + proxyMode = flag.String("mode", "reverse", "代理模式: forward, reverse, transparent") + enableHTTPS = flag.Bool("https", false, "是否启用HTTPS拦截") + verbosity = flag.Int("v", 0, "日志详细级别 (0-3)") + host = flag.String("dns-host", "example.com", "要解析的主机名") + ip = flag.String("dns-ip", "127.0.0.1", "解析的IP地址") + ) + flag.Parse() + + // 配置日志 + logLevel := slog.LevelInfo + switch *verbosity { + case 1: + logLevel = slog.LevelDebug + case 2: + logLevel = slog.LevelInfo + case 3: + logLevel = slog.LevelDebug - 3 // 更详细的调试级别 + } + + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + Level: logLevel, + })) + slog.SetDefault(logger) + + // 创建自定义DNS解析器 + resolver := dns.NewResolver( + dns.WithFallback(true), // 如果找不到自定义规则,回退到系统DNS + dns.WithTTL(5*time.Minute), // 设置缓存TTL + ) + + // 添加自定义DNS解析规则 + if err := resolver.Add(*host, *ip); err != nil { + slog.Error("添加DNS解析规则失败", "error", err) + return + } + + // 添加通配符DNS解析规则示例 + if err := resolver.AddWildcard("*.example.org", "192.168.1.2"); err != nil { + slog.Error("添加通配符DNS解析规则失败", "error", err) + return + } + + slog.Info("已添加DNS解析规则", "host", *host, "ip", *ip) + + // 创建代理选项 + var ( + proxy goproxy.UnifiedProxy + err error + ) + + // 根据代理模式创建不同类型的代理 + switch *proxyMode { + case "forward": + proxy, err = goproxy.NewForwardProxy(*listenAddr, goproxy.WithUnifiedDNSResolver(resolver)) + case "reverse": + proxy, err = goproxy.NewReverseProxy(*listenAddr, *targetAddr, goproxy.WithUnifiedDNSResolver(resolver)) + case "transparent": + proxy, err = goproxy.NewTransparentProxy(*listenAddr, goproxy.WithUnifiedDNSResolver(resolver)) + default: + slog.Error("不支持的代理模式", "mode", *proxyMode) + return + } + + if err != nil { + slog.Error("创建代理失败", "error", err) + return + } + + // 设置信号处理 + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + + // 打印启动信息 + slog.Info("代理服务器已启动", + "mode", *proxyMode, + "listen", *listenAddr, + "target", *targetAddr, + "https", *enableHTTPS) + + // 启动HTTP服务器 + server := &http.Server{ + Addr: *listenAddr, + Handler: proxy, + } + + // 监听和服务连接 + go func() { + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + slog.Error("服务器运行失败", "error", err) + os.Exit(1) + } + }() + + // 等待中断信号 + <-sigCh + slog.Info("接收到信号,正在关闭...") + + // 创建关闭上下文 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // 优雅关闭HTTP服务器 + if err := server.Shutdown(ctx); err != nil { + slog.Error("服务器关闭失败", "error", err) + } + + // 关闭代理 + if err := proxy.Close(); err != nil { + slog.Error("关闭代理时出错", "error", err) + } +} diff --git a/examples/dns_resolver_comparison/main.go b/examples/dns_resolver_comparison/main.go new file mode 100644 index 0000000..29aee77 --- /dev/null +++ b/examples/dns_resolver_comparison/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "flag" + "log/slog" + "net" + "net/http" + "net/url" + "os" + "time" + + "github.com/darkit/goproxy" + "github.com/darkit/goproxy/pkg/dns" +) + +func main() { + // 解析命令行参数 + var ( + listenAddr = flag.String("listen", ":8080", "代理服务器监听地址") + targetAddr = flag.String("target", "http://example.com", "目标服务器地址") + useDNSResolver = flag.Bool("custom-dns", false, "是否使用自定义DNS解析") + dnsHost = flag.String("dns-host", "example.com", "要解析的主机名") + dnsIP = flag.String("dns-ip", "127.0.0.1", "解析的IP地址") + verbosity = flag.Int("v", 0, "日志详细级别 (0-3)") + ) + flag.Parse() + + // 配置日志 + logLevel := slog.LevelInfo + switch *verbosity { + case 1: + logLevel = slog.LevelDebug + case 2: + logLevel = slog.LevelInfo + case 3: + logLevel = slog.LevelDebug - 3 // 更详细的调试级别 + } + + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + Level: logLevel, + })) + slog.SetDefault(logger) + + // 创建反向代理实例 + var proxy goproxy.UnifiedProxy + var err error + + if *useDNSResolver { + // 使用自定义DNS解析器 + slog.Info("使用自定义DNS解析器") + + // 创建自定义DNS解析器 + resolver := dns.NewResolver( + dns.WithFallback(true), // 如果找不到自定义规则,回退到系统DNS + dns.WithTTL(5*time.Minute), // 设置缓存TTL + ) + + // 添加自定义DNS解析规则 + if err := resolver.Add(*dnsHost, *dnsIP); err != nil { + slog.Error("添加DNS解析规则失败", "error", err) + return + } + + slog.Info("添加DNS解析规则", "host", *dnsHost, "ip", *dnsIP) + + // 创建带自定义DNS解析器的反向代理 + proxy, err = goproxy.NewReverseProxy(*listenAddr, *targetAddr, goproxy.WithUnifiedDNSResolver(resolver)) + } else { + // 使用系统DNS解析 + slog.Info("使用系统DNS解析") + proxy, err = goproxy.NewReverseProxy(*listenAddr, *targetAddr) + } + + if err != nil { + slog.Error("创建代理服务器失败", "error", err) + return + } + + // 使用系统DNS解析一次目标地址,打印结果供比较 + targetHost := *targetAddr + // 尝试解析URL + if u, err := url.Parse(targetHost); err == nil && u.Host != "" { + targetHost = u.Host + } + // 如果还包含端口,移除端口 + if host, _, err := net.SplitHostPort(targetHost); err == nil { + targetHost = host + } + ips, err := net.LookupIP(targetHost) + if err != nil { + slog.Error("系统DNS解析失败", "host", targetHost, "error", err) + } else { + slog.Info("系统DNS解析结果", "host", targetHost, "ips", ips) + } + + // 创建HTTP服务器 + server := &http.Server{ + Addr: *listenAddr, + Handler: proxy, + } + + // 启动服务器 + slog.Info("代理服务器已启动", + "listen", *listenAddr, + "target", *targetAddr, + "custom_dns", *useDNSResolver) + + // 监听和服务连接 + if err := server.ListenAndServe(); err != nil { + slog.Error("服务器运行失败", "error", err) + } +} diff --git a/examples/rewriter/http_server.go b/examples/rewriter/http_server.go index 459dcb4..606047a 100644 --- a/examples/rewriter/http_server.go +++ b/examples/rewriter/http_server.go @@ -90,7 +90,7 @@ func RewriteMiddleware(rw *rewriter.Rewriter, next http.Handler) http.Handler { }) } -func main() { +func main1() { // 创建重写器 rw := rewriter.NewRewriter() diff --git a/examples/rewriter/main.go b/examples/rewriter/main.go index e601861..8cb4d15 100644 --- a/examples/rewriter/main.go +++ b/examples/rewriter/main.go @@ -10,7 +10,7 @@ import ( "github.com/darkit/goproxy/pkg/rewriter" ) -func main() { +func main2() { // 创建URL重写器 rw := rewriter.NewRewriter() diff --git a/factory.go b/factory.go new file mode 100644 index 0000000..c336cc8 --- /dev/null +++ b/factory.go @@ -0,0 +1,301 @@ +package goproxy + +import ( + "crypto/tls" + "errors" + "net/http" + "net/http/httptrace" + "strconv" + "strings" + "time" + + "github.com/darkit/goproxy/config" + "github.com/darkit/goproxy/pkg/auth" + "github.com/darkit/goproxy/pkg/cache" + "github.com/darkit/goproxy/pkg/dns" + "github.com/darkit/goproxy/pkg/healthcheck" + "github.com/darkit/goproxy/pkg/loadbalance" + "github.com/darkit/goproxy/pkg/metrics" +) + +// ErrNotSupportHijacking 不支持劫持错误 +var ErrNotSupportHijacking = errors.New("connection does not support hijacking") + +// CreateUnifiedProxy 创建统一代理 +// 为了避免与现有的NewProxy冲突 +func CreateUnifiedProxy(options ...UnifiedOption) (UnifiedProxy, error) { + opts := &UnifiedOptions{} + for _, option := range options { + option(opts) + } + return NewUnifiedProxy(opts) +} + +// NewForwardProxy 创建正向代理 +func NewForwardProxy(listenAddr string, options ...UnifiedOption) (UnifiedProxy, error) { + cfg := config.DefaultUnifiedConfig() + cfg.ListenAddr = listenAddr + cfg.ProxyMode = config.ModeForward + + opts := &UnifiedOptions{ + Config: cfg, + } + + for _, option := range options { + option(opts) + } + + return NewUnifiedProxy(opts) +} + +// NewReverseProxy 创建反向代理 +func NewReverseProxy(listenAddr, targetAddr string, options ...UnifiedOption) (UnifiedProxy, error) { + cfg := config.DefaultUnifiedConfig() + cfg.ListenAddr = listenAddr + cfg.TargetAddr = targetAddr + cfg.ProxyMode = config.ModeReverse + + opts := &UnifiedOptions{ + Config: cfg, + } + + for _, option := range options { + option(opts) + } + + return NewUnifiedProxy(opts) +} + +// NewTransparentProxy 创建透明代理 +func NewTransparentProxy(listenAddr string, options ...UnifiedOption) (UnifiedProxy, error) { + cfg := config.DefaultUnifiedConfig() + cfg.ListenAddr = listenAddr + cfg.ProxyMode = config.ModeTransparent + + opts := &UnifiedOptions{ + Config: cfg, + } + + for _, option := range options { + option(opts) + } + + return NewUnifiedProxy(opts) +} + +// UnifiedOption 用于配置统一代理选项的函数类型 +type UnifiedOption func(*UnifiedOptions) + +// WithUnifiedConfig 设置代理配置 +func WithUnifiedConfig(cfg *config.UnifiedConfig) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.Config = cfg + } +} + +// WithUnifiedDelegate 设置委托类 +func WithUnifiedDelegate(delegate Delegate) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.Delegate = delegate + } +} + +// WithUnifiedCertCache 设置证书缓存 +func WithUnifiedCertCache(cache CertificateCache) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.CertCache = cache + } +} + +// WithUnifiedHTTPCache 设置HTTP缓存 +func WithUnifiedHTTPCache(c cache.Cache) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.HTTPCache = c + } +} + +// WithUnifiedLoadBalancer 设置负载均衡器 +func WithUnifiedLoadBalancer(lb loadbalance.LoadBalancer) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.LoadBalancer = lb + } +} + +// WithUnifiedHealthChecker 设置健康检查器 +func WithUnifiedHealthChecker(hc *healthcheck.HealthChecker) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.HealthChecker = hc + } +} + +// WithUnifiedMetrics 设置监控指标 +func WithUnifiedMetrics(m metrics.MetricsCollector) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.Metrics = m + } +} + +// WithUnifiedClientTrace 设置HTTP客户端跟踪 +func WithUnifiedClientTrace(t *httptrace.ClientTrace) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.ClientTrace = t + } +} + +// WithUnifiedCertManager 设置证书管理器 +func WithUnifiedCertManager(cm *CertManager) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.CertManager = cm + } +} + +// WithUnifiedAuth 设置认证系统 +func WithUnifiedAuth(a *auth.Auth) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.Auth = a + } +} + +// WithUnifiedDNSResolver 设置DNS解析器 +func WithUnifiedDNSResolver(resolver *dns.CustomResolver) UnifiedOption { + return func(opt *UnifiedOptions) { + opt.DNSResolver = resolver + } +} + +// 处理劫持连接 +func hijackerImpl(rw http.ResponseWriter) (*ConnBuffer, error) { + hijacker, ok := rw.(http.Hijacker) + if !ok { + return nil, ErrNotSupportHijacking + } + conn, bufrw, err := hijacker.Hijack() + if err != nil { + return nil, err + } + return NewConnBuffer(conn, bufrw.Reader), nil +} + +// 检查是否是WebSocket请求 +func isWebSocketRequestImpl(req *http.Request) bool { + if req.Method != http.MethodGet { + return false + } + + connection := req.Header.Get("Connection") + if connection == "" { + return false + } + + upgrade := req.Header.Get("Upgrade") + return upgrade != "" +} + +// 发送隧道已连接响应 +func tunnelConnectedImpl(_ *Context, err error, rw http.ResponseWriter) error { + // 此函数应根据原始代码实现 + if err != nil { + if rw != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + return err + } + + // 发送连接成功响应 + // 这里简化处理,实际应该根据原代码完整实现 + if rw != nil { + rw.WriteHeader(http.StatusOK) + } + return nil +} + +// 可以缓存的HTTP方法 +func canCacheMethodImpl(method string) bool { + return method == http.MethodGet +} + +// 可以缓存的状态码 +func canCacheStatusImpl(statusCode int) bool { + return statusCode >= 200 && statusCode < 300 +} + +// 生成缓存键 +func generateCacheKeyImpl(req *http.Request) string { + return req.Method + ":" + req.URL.String() +} + +// 获取缓存TTL +func getCacheTTLImpl(resp *http.Response) time.Duration { + // 检查响应头中的Cache-Control + cacheControl := resp.Header.Get("Cache-Control") + if cacheControl != "" { + // 解析Cache-Control头 + if strings.Contains(cacheControl, "no-store") || strings.Contains(cacheControl, "no-cache") { + return 0 // 不缓存 + } + + // 查找max-age指令 + if strings.Contains(cacheControl, "max-age=") { + parts := strings.Split(cacheControl, "max-age=") + if len(parts) > 1 { + seconds := strings.Split(parts[1], ",")[0] + if maxAge, err := strconv.ParseInt(seconds, 10, 64); err == nil { + return time.Duration(maxAge) * time.Second + } + } + } + } + + // 检查Expires头 + if expires := resp.Header.Get("Expires"); expires != "" { + if expiresTime, err := time.Parse(time.RFC1123, expires); err == nil { + return time.Until(expiresTime) + } + } + + // 返回默认缓存时间 + return 5 * time.Minute +} + +// 检查是否支持缓存命中指标 +func isCacheHitMetricsSupportedImpl(m metrics.MetricsCollector) bool { + // 首先检查是否为nil + if m == nil { + return false + } + + // 检查是否实现了具体的缓存命中计数方法 + _, hasIncrementCacheHit := m.(interface { + IncrementCacheHit() + }) + + // 或者检查是否实现了通用的计数器增加方法 + _, hasIncrement := m.(interface { + Increment(key string, value float64) + }) + + // 如果实现了其中任何一个方法,就认为支持缓存命中计数 + return hasIncrementCacheHit || hasIncrement +} + +// 增加缓存命中计数 +func incrementCacheHitImpl(m metrics.MetricsCollector) { + // 如果指标收集器实现了特定接口,则增加缓存命中计数 + if counterInterface, ok := m.(interface { + IncrementCacheHit() + }); ok { + counterInterface.IncrementCacheHit() + } else if counterInterface, ok := m.(interface { + Increment(key string, value float64) + }); ok { + counterInterface.Increment("cache_hit", 1) + } +} + +// NewTLSConfig 创建新的TLS配置 +func NewTLSConfig(serverName string, insecureSkipVerify bool) *tls.Config { + return &tls.Config{ + ServerName: serverName, + InsecureSkipVerify: insecureSkipVerify, + } +} diff --git a/go.mod b/go.mod index 190e1d3..9145bf0 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/darkit/goproxy go 1.24.0 require ( - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/ouqiang/websocket v1.6.2 - github.com/prometheus/client_golang v1.20.4 + github.com/ouqiang/websocket v1.6.0 + github.com/prometheus/client_golang v1.21.1 github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8 golang.org/x/time v0.11.0 ) diff --git a/go.sum b/go.sum index 93ab40d..c3bcbc8 100644 --- a/go.sum +++ b/go.sum @@ -4,22 +4,22 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/ouqiang/websocket v1.6.2 h1:LGQIySbQO3ahZCl34v9xBVb0yncDk8yIcuEIbWBab/U= -github.com/ouqiang/websocket v1.6.2/go.mod h1:fIROJIHRlQwgCyUFTMzaaIcs4HIwUj2xlOW43u9Sf+M= +github.com/ouqiang/websocket v1.6.0 h1:dbo7W207d6P3UaBGeyey8SP80WpFxTDdd80eKUiduEg= +github.com/ouqiang/websocket v1.6.0/go.mod h1:fIROJIHRlQwgCyUFTMzaaIcs4HIwUj2xlOW43u9Sf+M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= diff --git a/pkg/reverse/proxy.go b/pkg/reverse/proxy.go index 9d1f3cc..22e60d9 100644 --- a/pkg/reverse/proxy.go +++ b/pkg/reverse/proxy.go @@ -1,3 +1,7 @@ +// Package reverse 提供反向代理功能。 + +// Deprecated: 此包已被弃用,请使用统一代理接口 (github.com/darkit/goproxy) 代替。 +// 在未来版本中,此包可能会被移除。 package reverse import ( diff --git a/unified_proxy.go b/unified_proxy.go new file mode 100644 index 0000000..4fd92bd --- /dev/null +++ b/unified_proxy.go @@ -0,0 +1,1402 @@ +package goproxy + +import ( + "bufio" + "context" + "crypto/tls" + "fmt" + "io" + "log/slog" + "net" + "net/http" + "net/http/httptrace" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/darkit/goproxy/config" + "github.com/darkit/goproxy/pkg/auth" + "github.com/darkit/goproxy/pkg/cache" + "github.com/darkit/goproxy/pkg/dns" + "github.com/darkit/goproxy/pkg/healthcheck" + "github.com/darkit/goproxy/pkg/loadbalance" + "github.com/darkit/goproxy/pkg/metrics" + "github.com/darkit/goproxy/pkg/rule" + "github.com/ouqiang/websocket" + "github.com/viki-org/dnscache" +) + +// UnifiedProxy 统一代理接口 +type UnifiedProxy interface { + // ServeHTTP 处理HTTP请求 + ServeHTTP(w http.ResponseWriter, r *http.Request) + // Close 关闭代理 + Close() error + // ClientConnNum 获取当前客户端连接数 + ClientConnNum() int32 + // SetDialContext 设置自定义的拨号上下文函数 + SetDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) + // DoRequest 执行HTTP请求 + DoRequest(ctx *Context, responseFunc func(*http.Response, error)) +} + +// UnifiedProxyImpl 统一代理实现 +type UnifiedProxyImpl struct { + // 配置 + config *config.UnifiedConfig + // 委托 + delegate Delegate + // 证书缓存 + certCache CertificateCache + // HTTP缓存 + httpCache cache.Cache + // 缓存适配器 + cacheAdapter *CacheAdapter + // 负载均衡器 + loadBalancer loadbalance.LoadBalancer + // 健康检查器 + healthChecker *healthcheck.HealthChecker + // 监控指标 + metrics metrics.MetricsCollector + // 客户端跟踪 + clientTrace *httptrace.ClientTrace + // 基础传输(用于直接获取*http.Transport类型) + transport *http.Transport + // HTTP请求传输(可能被中间件包装) + httpTransport http.RoundTripper + // DNS缓存 + dnsCache *dnscache.Resolver + // 客户端连接数 + clientConnNum int32 + // 证书管理器 + certManager *CertManager + // 日志记录器 + logger *slog.Logger + // 规则管理器 (反向代理使用) + ruleManager *rule.Manager + // 反向代理处理器 (反向代理使用) + reverseProxy *httputil.ReverseProxy + // 认证系统 + auth *auth.Auth + // 自定义DNS解析器 + dnsResolver *dns.CustomResolver +} + +// UnifiedOptions 统一代理选项 +type UnifiedOptions struct { + // 配置 + Config *config.UnifiedConfig + // 委托 + Delegate Delegate + // 证书缓存 + CertCache CertificateCache + // HTTP缓存 + HTTPCache cache.Cache + // 负载均衡器 + LoadBalancer loadbalance.LoadBalancer + // 健康检查器 + HealthChecker *healthcheck.HealthChecker + // 监控指标 + Metrics metrics.MetricsCollector + // 客户端跟踪 + ClientTrace *httptrace.ClientTrace + // 证书管理器 + CertManager *CertManager + // 认证系统 + Auth *auth.Auth + // DNS解析器 + DNSResolver *dns.CustomResolver +} + +// NewUnifiedProxy 创建统一代理 +func NewUnifiedProxy(opts *UnifiedOptions) (UnifiedProxy, error) { + if opts == nil { + opts = &UnifiedOptions{} + } + + if opts.Config == nil { + opts.Config = config.DefaultUnifiedConfig() + } + + if opts.Delegate == nil { + opts.Delegate = &DefaultDelegate{} + } + + proxy := &UnifiedProxyImpl{ + config: opts.Config, + delegate: opts.Delegate, + certCache: opts.CertCache, + httpCache: opts.HTTPCache, + loadBalancer: opts.LoadBalancer, + healthChecker: opts.HealthChecker, + metrics: opts.Metrics, + clientTrace: opts.ClientTrace, + certManager: opts.CertManager, + auth: opts.Auth, + logger: opts.Config.Logger, + dnsResolver: opts.DNSResolver, + } + + // 初始化日志记录器 + if proxy.logger == nil { + proxy.logger = slog.Default() + } + + // 创建证书管理器(如果需要) + if proxy.certManager == nil && opts.Config.DecryptHTTPS { + // 如果提供了CA证书和密钥,使用它们创建证书管理器 + // 否则使用默认的证书管理器 + proxy.certManager = NewCertManager(proxy.certCache, WithUseECDSA(opts.Config.UseECDSA)) + } + + // 根据代理模式初始化不同的组件 + switch opts.Config.ProxyMode { + case config.ModeForward: + proxy.logger.Debug("初始化正向代理") + if err := proxy.initializeForwardProxy(); err != nil { + return nil, err + } + case config.ModeReverse: + proxy.logger.Debug("初始化反向代理") + if err := proxy.initializeReverseProxy(); err != nil { + return nil, err + } + case config.ModeTransparent: + proxy.logger.Debug("初始化透明代理") + if err := proxy.initializeTransparentProxy(); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("不支持的代理模式: %s", opts.Config.ProxyMode) + } + + return proxy, nil +} + +// 初始化正向代理 +func (p *UnifiedProxyImpl) initializeForwardProxy() error { + // 创建DNS缓存 + if p.config.DNSCacheTTL > 0 && p.dnsResolver == nil { + // 只有在没有自定义DNS解析器的情况下才创建DNS缓存 + p.dnsCache = dnscache.New(p.config.DNSCacheTTL) + } + + // 创建缓存适配器 + if p.httpCache != nil { + p.cacheAdapter = NewCacheAdapter(p.httpCache) + } + + // 创建传输层 + p.transport = &http.Transport{ + // DNS解析 + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + // 首先检查是否有自定义DNS解析器 + if p.dnsResolver != nil { + // 解析地址和端口 + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + portNum, err := strconv.Atoi(port) + if err != nil { + return nil, err + } + + // 使用自定义DNS解析器解析地址 + endpoint, err := p.dnsResolver.ResolveWithPort(host, portNum) + if err != nil { + // 如果解析失败,回退到原始地址 + p.logger.Debug("[正向代理]自定义DNS解析失败,使用原始地址", + "host", host, + "error", err.Error(), + ) + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, addr) + } + + // 使用解析后的地址创建连接 + p.logger.Debug("使用自定义DNS解析", + "host", host, + "resolved", endpoint.String(), + ) + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, endpoint.String()) + } + + // 使用DNS缓存 + if p.dnsCache != nil { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + // 从DNS缓存中解析IP + ips, err := p.dnsCache.FetchOneString(host) + if err != nil { + // 如果DNS缓存解析失败,尝试使用系统DNS + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, addr) + } + + // 使用解析后的IP创建连接 + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, net.JoinHostPort(ips, port)) + } + + // 不使用DNS缓存,直接使用系统DNS + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, addr) + }, + DisableKeepAlives: p.config.DisableKeepAlive, + MaxIdleConns: p.config.MaxIdleConns, + IdleConnTimeout: p.config.IdleTimeout, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + ForceAttemptHTTP2: true, + TLSClientConfig: nil, // 暂时不设置,在HTTPS请求时动态生成 + } + + // 设置HTTP请求传输 + p.httpTransport = p.transport + + return nil +} + +// 初始化反向代理 +func (p *UnifiedProxyImpl) initializeReverseProxy() error { + // 创建规则管理器 + p.ruleManager = rule.NewManager(p.logger) + + // 处理目标地址中可能包含的协议前缀 + targetScheme := "http" + targetHost := p.config.TargetAddr + + // 如果目标地址包含协议前缀,分离协议和主机部分 + if strings.HasPrefix(p.config.TargetAddr, "http://") || strings.HasPrefix(p.config.TargetAddr, "https://") { + parsedURL, err := url.Parse(p.config.TargetAddr) + if err != nil { + p.logger.Error("解析目标地址URL失败", + "target", p.config.TargetAddr, + "error", err.Error(), + ) + } else { + // 提取协议和主机部分 + targetScheme = parsedURL.Scheme + targetHost = parsedURL.Host + p.logger.Debug("目标地址包含协议前缀,已分离", + "original", p.config.TargetAddr, + "scheme", targetScheme, + "host", targetHost, + ) + } + } + + // 创建传输层 + p.transport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + // 如果存在自定义DNS解析器,使用它 + if p.dnsResolver != nil { + // 解析地址和端口 + host, port, err := net.SplitHostPort(addr) + if err != nil { + // 可能是没有端口的地址,添加默认端口 + if strings.Contains(err.Error(), "missing port") { + // 默认使用80端口 + host = addr + port = "80" + addr = net.JoinHostPort(host, port) + p.logger.Debug("[反向代理]地址没有端口,添加默认端口", + "addr", addr, + ) + } else { + return nil, fmt.Errorf("解析主机和端口失败: %w", err) + } + } + + portNum, err := strconv.Atoi(port) + if err != nil { + return nil, fmt.Errorf("端口号格式错误: %w", err) + } + + // 使用DNS解析器解析地址 + endpoint, err := p.dnsResolver.ResolveWithPort(host, portNum) + if err != nil { + // 如果解析失败,回退到使用原始地址 + p.logger.Debug("[反向代理]自定义DNS解析失败,使用原始地址", + "host", host, + "error", err.Error(), + ) + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, addr) + } + + // 使用解析后的地址创建连接 + p.logger.Debug("[反向代理]使用自定义DNS解析", + "host", host, + "resolved", endpoint.String(), + ) + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, endpoint.String()) + } + + // 没有自定义DNS解析器,直接使用系统DNS + dialer := &net.Dialer{ + Timeout: p.config.RequestTimeout, + KeepAlive: p.config.IdleTimeout, + } + return dialer.DialContext(ctx, network, addr) + }, + ForceAttemptHTTP2: true, + MaxIdleConns: p.config.MaxIdleConns, + IdleConnTimeout: p.config.IdleTimeout, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: nil, // 在需要时设置 + } + + // 如果需要跳过证书验证,设置TLS配置 + if p.config.InsecureSkipVerify { + p.transport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + } + + // 创建反向代理处理器 + p.reverseProxy = &httputil.ReverseProxy{ + Transport: p.transport, + Director: func(req *http.Request) { + // 应用规则 + rules := p.ruleManager.ListRules(rule.RuleTypeRoute) + for _, r := range rules { + if r.Match(req) { + if err := r.Apply(req); err != nil { + p.logger.Error("应用规则失败", + "rule_id", r.GetID(), + "error", err.Error(), + ) + } + } + } + + // 设置目标地址 + if targetHost != "" { + req.URL.Host = targetHost + // 同时设置 req.Host 字段,确保兼容性 + req.Host = targetHost + // 使用 Set 替换 Add,确保只有一个 Host 头 + req.Header.Set("Host", targetHost) + // 使用解析出的协议,确保一致性 + req.URL.Scheme = targetScheme + + p.logger.Debug("[反向代理]设置目标地址", + "target", req.URL.Host, + "scheme", req.URL.Scheme, + "path", req.URL.Path, + ) + } + + // 添加X-Forwarded-For头 + if p.config.AddXForwardedFor { + req.Header.Add("X-Forwarded-For", req.RemoteAddr) + } + + // 添加X-Real-IP头 + if p.config.AddXRealIP { + req.Header.Add("X-Real-IP", req.RemoteAddr) + } + + // 调用委托的修改请求方法 + p.delegate.ModifyRequest(req) + }, + ModifyResponse: func(resp *http.Response) error { + return p.delegate.ModifyResponse(resp) + }, + ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { + // 检查是否支持连接劫持 + if hijacker, ok := w.(http.Hijacker); ok { + // 尝试获取连接 + conn, _, hijackErr := hijacker.Hijack() + if hijackErr == nil { + // 连接已经被劫持,使用直接写入方式 + defer conn.Close() + + // 构建简单的错误响应 + errMsg := fmt.Sprintf("HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain; charset=utf-8\r\nX-Proxy-Error: %s\r\n\r\n%s", + err.Error(), err.Error()) + + // 直接写入连接 + conn.Write([]byte(errMsg)) + return + } + // 如果劫持失败,回退到正常的错误处理 + } + + // 使用标准的错误处理 + p.delegate.HandleError(w, r, err) + }, + } + + // 如果配置了规则文件,加载规则 + if p.config.RulesFile != "" { + loader := rule.NewLoader(p.ruleManager, p.logger) + + // 根据文件扩展名决定加载方式 + switch { + case strings.HasSuffix(p.config.RulesFile, ".json"): + if err := loader.LoadFromJSON(p.config.RulesFile); err != nil { + p.logger.Error("加载规则文件失败", + "file", p.config.RulesFile, + "error", err.Error(), + ) + return fmt.Errorf("加载规则文件失败: %w", err) + } + p.logger.Info("成功加载规则文件", + "file", p.config.RulesFile, + ) + + case strings.HasSuffix(p.config.RulesFile, ".hosts"): + if err := loader.LoadFromHosts(p.config.RulesFile); err != nil { + p.logger.Error("加载hosts文件失败", + "file", p.config.RulesFile, + "error", err.Error(), + ) + return fmt.Errorf("加载hosts文件失败: %w", err) + } + p.logger.Info("成功加载hosts文件", + "file", p.config.RulesFile, + ) + + default: + return fmt.Errorf("不支持的规则文件格式: %s", p.config.RulesFile) + } + } + + return nil +} + +// 初始化透明代理 +func (p *UnifiedProxyImpl) initializeTransparentProxy() error { + // 透明代理基本上是正向代理的一种特殊形式 + return p.initializeForwardProxy() +} + +// ServeHTTP 处理HTTP请求 +func (p *UnifiedProxyImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // 增加客户端连接数 + atomic.AddInt32(&p.clientConnNum, 1) + defer atomic.AddInt32(&p.clientConnNum, -1) + + // 更新请求计数指标 + if p.metrics != nil { + p.metrics.IncRequestCount() + } + + // 创建请求上下文 + ctx := ctxPool.Get() + ctx.Reset(r) + defer ctxPool.Put(ctx) + + // 调用连接事件 + p.delegate.Connect(ctx, w) + + // 认证检查 + if p.auth != nil { + // 从请求头中获取Authorization信息 + username, password, hasAuth := r.BasicAuth() + if hasAuth { + // 进行认证 + token, err := p.auth.Authenticate(username, password) + if err != nil { + // 认证失败 + p.logger.Debug("认证失败", "username", username, "error", err.Error()) + w.Header().Set("WWW-Authenticate", `Basic realm="Proxy Authentication Required"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + // 认证成功,设置认证头 + ctx.Data["auth_token"] = token + } else { + // 没有提供认证信息 + p.delegate.Auth(ctx, w) + if ctx.IsAborted() { + return + } + } + } + + // 根据代理模式处理请求 + switch p.config.ProxyMode { + case config.ModeForward: + // 正向代理模式下URL必须是完整的 + if r.URL.Host == "" { + http.Error(w, "Invalid request: no host in URL", http.StatusBadRequest) + return + } + p.serveForwardProxy(ctx, w) + case config.ModeReverse: + p.serveReverseProxy(ctx, w) + case config.ModeTransparent: + p.serveTransparentProxy(ctx, w) + default: + http.Error(w, "不支持的代理模式", http.StatusInternalServerError) + p.logger.Error("不支持的代理模式", "mode", p.config.ProxyMode) + } + + // 调用完成事件 + p.delegate.Finish(ctx) +} + +// 处理正向代理请求 +func (p *UnifiedProxyImpl) serveForwardProxy(ctx *Context, w http.ResponseWriter) { + req := ctx.Req + + // 判断是否是隧道代理请求(CONNECT方法) + if req.Method == http.MethodConnect { + // 获取客户端连接 + clientConn, err := hijackerImpl(w) + if err != nil { + p.delegate.ErrorLog(err) + http.Error(w, "无法劫持连接", http.StatusServiceUnavailable) + return + } + defer clientConn.Close() + + // 如果启用了HTTPS解密,解密HTTPS流量 + if p.config.DecryptHTTPS { + // 发送隧道建立响应 + if _, err := clientConn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")); err != nil { + p.delegate.ErrorLog(fmt.Errorf("发送隧道建立响应失败: %w", err)) + return + } + + // 生成证书 + certConfig, err := p.generateTLSConfig(req.Host) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("生成证书失败: %w", err)) + return + } + + // 创建TLS服务器连接 + tlsConn := tls.Server(clientConn, certConfig) + defer tlsConn.Close() + + // TLS握手 + if err := tlsConn.Handshake(); err != nil { + p.delegate.ErrorLog(fmt.Errorf("TLS握手失败: %w", err)) + return + } + + // 读取HTTPS请求 + bufReader := bufio.NewReader(tlsConn) + httpsReq, err := http.ReadRequest(bufReader) + if err != nil { + if err != io.EOF { + p.delegate.ErrorLog(fmt.Errorf("读取HTTPS请求失败: %w", err)) + } + return + } + + // 更新请求信息 + httpsReq.RemoteAddr = req.RemoteAddr + httpsReq.URL.Scheme = "https" + httpsReq.URL.Host = httpsReq.Host + + // 检查是否是WebSocket请求 + if isWebSocketRequestImpl(httpsReq) { + // 处理WebSocket请求 + ctx.Req = httpsReq + p.websocketProxy(ctx, NewConnBuffer(tlsConn, bufReader)) + return + } + + // 处理普通HTTPS请求 + ctx.Req = httpsReq + p.httpsProxy(ctx, NewConnBuffer(tlsConn, bufReader)) + return + } + + // 没有启用HTTPS解密,直接建立隧道 + p.tunnelProxy(ctx, w) + return + } + + // 处理WebSocket请求 + if isWebSocketRequestImpl(req) && p.config.EnableWebSocket { + // 获取客户端连接 + clientConn, err := hijackerImpl(w) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("获取WebSocket连接失败: %w", err)) + http.Error(w, "无法处理WebSocket请求", http.StatusServiceUnavailable) + return + } + defer clientConn.Close() + + // 处理WebSocket请求 + p.websocketProxy(ctx, clientConn) + return + } + + // 处理普通HTTP请求 + p.handleHTTP(ctx, w) +} + +// 处理反向代理请求 +func (p *UnifiedProxyImpl) serveReverseProxy(ctx *Context, w http.ResponseWriter) { + // 调用BeforeRequest事件 + p.delegate.BeforeRequest(ctx) + if ctx.IsAborted() { + return + } + + // 使用反向代理处理请求 + p.reverseProxy.ServeHTTP(w, ctx.Req) +} + +// 处理透明代理请求 +func (p *UnifiedProxyImpl) serveTransparentProxy(ctx *Context, w http.ResponseWriter) { + // 透明代理与正向代理的不同之处在于客户端不知道有代理的存在 + // 这里简化处理,复用正向代理的逻辑 + p.serveForwardProxy(ctx, w) +} + +// 处理HTTP请求,复用自原始代理的handleHTTP方法 +func (p *UnifiedProxyImpl) handleHTTP(ctx *Context, rw http.ResponseWriter) { + req := ctx.Req + + // 调用BeforeRequest事件 + p.delegate.BeforeRequest(ctx) + if ctx.IsAborted() { + return + } + + // 如果是WebSocket请求且支持WebSocket,处理WebSocket + if isWebSocketRequestImpl(req) && p.config.EnableWebSocket { + // 获取目标地址 + targetAddr := req.Host + if !strings.Contains(targetAddr, ":") { + if req.URL.Scheme == "https" || req.URL.Scheme == "wss" { + targetAddr += ":443" + } else { + targetAddr += ":80" + } + } + + // 劫持连接 + conn, err := hijackerImpl(rw) + if err != nil { + http.Error(rw, "无法劫持连接: "+err.Error(), http.StatusInternalServerError) + return + } + defer conn.Close() + + // 处理WebSocket + p.websocketProxy(ctx, conn) + return + } + + // 处理普通HTTP请求 + p.DoRequest(ctx, func(resp *http.Response, err error) { + if err != nil { + // 调用BeforeResponse事件 + p.delegate.BeforeResponse(ctx, nil, err) + + // 处理错误 + http.Error(rw, "请求失败: "+err.Error(), http.StatusBadGateway) + return + } + + // 调用BeforeResponse事件 + p.delegate.BeforeResponse(ctx, resp, nil) + if ctx.IsAborted() { + return + } + + // 如果启用缓存且可以缓存响应 + if p.config.EnableCache && p.cacheAdapter != nil && resp.StatusCode < 300 && canCacheMethodImpl(req.Method) && canCacheStatusImpl(resp.StatusCode) { + // 生成缓存键 + cacheKey := generateCacheKeyImpl(req) + // 获取缓存TTL + cacheTTL := getCacheTTLImpl(resp) + if cacheTTL > 0 { + // 保存响应到缓存 + p.cacheAdapter.Set(cacheKey, resp, cacheTTL) + } + } + + // 设置响应头 + copyHeaders(rw.Header(), resp.Header) + rw.WriteHeader(resp.StatusCode) + + // 写入响应体 + if resp.Body != nil { + defer resp.Body.Close() + // io.Copy(rw, resp.Body) + + // 复制响应体 + buf := bufPool.Get() + defer bufPool.Put(buf) + + _, err = io.CopyBuffer(rw, resp.Body, buf) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 复制响应体错误: %s", req.URL.Host, err)) + } + } + }) +} + +// DoRequest 执行HTTP请求 +func (p *UnifiedProxyImpl) DoRequest(ctx *Context, responseFunc func(*http.Response, error)) { + req := ctx.Req + + // 如果启用缓存且是GET请求 + if p.config.EnableCache && p.cacheAdapter != nil && req.Method == http.MethodGet { + // 生成缓存键 + cacheKey := generateCacheKeyImpl(req) + + // 尝试从缓存中获取响应 + if value, ok := p.cacheAdapter.Get(cacheKey); ok { + // 缓存命中 + if resp, ok := value.(*http.Response); ok { + // 记录缓存命中指标 + if p.metrics != nil && isCacheHitMetricsSupportedImpl(p.metrics) { + incrementCacheHitImpl(p.metrics) + } + + // 调用BeforeResponse事件 + p.delegate.BeforeResponse(ctx, resp, nil) + if ctx.IsAborted() { + return + } + + // 返回缓存的响应 + responseFunc(resp, nil) + return + } + } + } + + // 准备发送请求 + retries := 0 + var resp *http.Response + var err error + + // 重试循环 + for { + // 创建一个新的请求,以避免副作用 + newReq := req.Clone(req.Context()) + + // 如果使用负载均衡,处理后端选择 + if p.config.EnableLoadBalancing && p.loadBalancer != nil { + // 获取域名 + hostname := newReq.URL.Hostname() + // 使用负载均衡器选择一个后端 + backend, err := p.loadBalancer.Next(hostname) + if err != nil { + responseFunc(nil, fmt.Errorf("负载均衡选择后端失败: %w", err)) + return + } + + if backend != nil { + // 使用选中的后端地址 + newReq.URL.Host = backend.Host + if backend.Scheme != "" { + newReq.URL.Scheme = backend.Scheme + } + } + } + + // 调用BeforeRequest事件 + p.delegate.BeforeRequest(ctx) + if ctx.IsAborted() { + return + } + + // 记录请求开始时间 + startTime := time.Now() + + // 发送请求 + resp, err = p.httpTransport.RoundTrip(newReq) + + // 记录请求时间 + if p.metrics != nil && err == nil { + p.metrics.ObserveRequestDuration(time.Since(startTime).Seconds()) + } + + // 检查是否需要重试 + if err != nil && p.config.EnableRetry && retries < p.config.MaxRetries { + retries++ + // 指数退避 + backoff := p.config.RetryBackoff + if backoff == 0 { + backoff = time.Second + } + // 计算退避时间 + sleepTime := backoff * time.Duration(1< 0 && sleepTime > maxBackoff { + sleepTime = maxBackoff + } + // 记录重试日志 + p.logger.Debug("请求失败,进行重试", + "url", newReq.URL.String(), + "retry", retries, + "max_retries", p.config.MaxRetries, + "sleep", sleepTime, + "error", err.Error(), + ) + // 等待一段时间再重试 + time.Sleep(sleepTime) + continue + } + + // 无需重试或达到最大重试次数,返回结果 + break + } + + // 调用BeforeResponse事件 + p.delegate.BeforeResponse(ctx, resp, err) + if ctx.IsAborted() { + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + return + } + + // 如果有错误,返回错误 + if err != nil { + responseFunc(nil, err) + return + } + + // 如果启用缓存且可以缓存响应 + if p.config.EnableCache && p.cacheAdapter != nil && resp.StatusCode < 300 && canCacheMethodImpl(req.Method) && canCacheStatusImpl(resp.StatusCode) { + // 生成缓存键 + cacheKey := generateCacheKeyImpl(req) + // 获取缓存TTL + cacheTTL := getCacheTTLImpl(resp) + if cacheTTL > 0 { + // 保存响应到缓存 + p.cacheAdapter.Set(cacheKey, resp, cacheTTL) + } + } + + // 返回响应 + responseFunc(resp, nil) +} + +// Close 关闭代理 +func (p *UnifiedProxyImpl) Close() error { + // 关闭健康检查器 + if p.healthChecker != nil { + p.healthChecker.Stop() + } + + // 关闭Transport连接 + if p.transport != nil { + p.transport.CloseIdleConnections() + } + + // 等待活跃连接完成 + // 设置一个超时,防止永远等待 + timeout := time.NewTimer(30 * time.Second) + defer timeout.Stop() + + // 创建一个心跳定时器,定期检查连接数 + ticker := time.NewTicker(500 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-timeout.C: + // 超时,强制关闭 + p.logger.Warn("关闭超时,强制关闭代理", + "active_connections", p.ClientConnNum(), + ) + return nil + case <-ticker.C: + // 检查当前连接数 + if p.ClientConnNum() <= 0 { + p.logger.Info("所有连接已关闭,代理成功关闭") + return nil + } + p.logger.Debug("等待连接关闭", + "active_connections", p.ClientConnNum(), + ) + } + } +} + +// ClientConnNum 获取当前客户端连接数 +func (p *UnifiedProxyImpl) ClientConnNum() int32 { + return atomic.LoadInt32(&p.clientConnNum) +} + +// 复制HTTP头部 +func copyHeaders(dst, src http.Header) { + for k, vv := range src { + for _, v := range vv { + dst.Add(k, v) + } + } +} + +// 发送隧道已连接响应 +func (p *UnifiedProxyImpl) tunnelConnected(ctx *Context, err error) { + // 实现调用tunnelConnectedImpl + tunnelConnectedImpl(ctx, err, nil) +} + +// 处理HTTPS请求 +func (p *UnifiedProxyImpl) httpsProxy(ctx *Context, srcConn *ConnBuffer) { + req := ctx.Req + + // 检查是否是WebSocket请求 + if isWebSocketRequestImpl(req) && p.config.EnableWebSocket { + p.websocketProxy(ctx, srcConn) + return + } + + // 准备发送请求的上下文 + if req.Body == nil { + req.Body = http.NoBody + } + + // 创建请求上下文,支持超时 + reqContext := req.Context() + if p.config.RequestTimeout > 0 { + var cancel context.CancelFunc + reqContext, cancel = context.WithTimeout(reqContext, p.config.RequestTimeout) + defer cancel() + } + + // 创建HTTP客户端 + client := &http.Client{ + Transport: p.httpTransport, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + // 允许最多10次重定向 + if len(via) >= 10 { + return http.ErrUseLastResponse + } + return nil + }, + } + + // 设置上下文 + req = req.WithContext(reqContext) + + // 调用BeforeRequest事件 + p.delegate.BeforeRequest(ctx) + if ctx.IsAborted() { + return + } + + // 发送请求 + resp, err := client.Do(req) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - HTTPS请求失败: %w", req.URL.String(), err)) + errorResp := &http.Response{ + StatusCode: http.StatusBadGateway, + Status: "502 Bad Gateway", + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Body: http.NoBody, + Request: req, + } + errorResp.Header.Set("Content-Type", "text/plain; charset=utf-8") + errorResp.Header.Set("X-Proxy-Error", err.Error()) + + // 写入错误响应 + if writeErr := resp.Write(srcConn); writeErr != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 写入错误响应失败: %w", req.URL.String(), writeErr)) + } + return + } + defer resp.Body.Close() + + // 调用BeforeResponse事件 + p.delegate.BeforeResponse(ctx, resp, nil) + if ctx.IsAborted() { + return + } + + // 写入响应 + if err := resp.Write(srcConn); err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 写入响应失败: %w", req.URL.String(), err)) + } +} + +// 建立隧道代理 +func (p *UnifiedProxyImpl) tunnelProxy(ctx *Context, rw http.ResponseWriter) { + // 获取客户端连接 + clientConn, err := hijackerImpl(rw) + if err != nil { + p.delegate.ErrorLog(err) + //http.Error(rw, "无法劫持连接", http.StatusServiceUnavailable) + return + } + defer clientConn.Close() + + // 获取目标主机 + host := ctx.Req.Host + if !strings.Contains(host, ":") { + host = host + ":443" + } + + // 尝试使用负载均衡器选择目标 + var targetHost string + if p.config.EnableLoadBalancing && p.loadBalancer != nil { + hostname := strings.Split(host, ":")[0] + backend, err := p.loadBalancer.Next(hostname) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 负载均衡选择目标失败: %w", host, err)) + // 继续使用原始主机 + targetHost = host + } else if backend != nil { + targetHost = backend.Host + } else { + targetHost = host + } + } else { + targetHost = host + } + + // 连接目标服务器 + targetConn, err := net.DialTimeout("tcp", targetHost, 10*time.Second) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 连接目标服务器失败: %w", targetHost, err)) + // 直接写入连接而不是使用 http.Error + clientConn.Write([]byte("HTTP/1.1 502 Bad Gateway\r\n\r\n")) + return + } + defer targetConn.Close() + + // 发送隧道建立成功响应 + if _, err := clientConn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")); err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 发送隧道建立响应失败: %w", targetHost, err)) + return + } + + // 双向转发数据 + p.transfer(clientConn, targetConn) +} + +// 处理WebSocket请求 +func (p *UnifiedProxyImpl) websocketProxy(ctx *Context, srcConn *ConnBuffer) { + req := ctx.Req + + // 检查是否启用WebSocket拦截 + if p.config.WebSocketIntercept { + // 使用WebSocket拦截模式 + // 创建WebSocket升级器 + upgrader := &websocket.Upgrader{ + HandshakeTimeout: 10 * time.Second, + ReadBufferSize: 4096, + WriteBufferSize: 4096, + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + + // 创建伪响应写入器,用于升级WebSocket连接 + rw := &customResponseWriter{ + conn: srcConn, + header: make(http.Header), + statusCode: http.StatusOK, + } + + // 升级源连接为WebSocket连接 + srcWSConn, err := upgrader.Upgrade(rw, req, nil) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 升级WebSocket连接失败: %w", req.URL.String(), err)) + return + } + defer srcWSConn.Close() + + // 构建目标URL + targetURL := url.URL{ + Scheme: "ws", + Host: req.URL.Host, + Path: req.URL.Path, + RawQuery: req.URL.RawQuery, + } + if req.URL.Scheme == "https" || req.URL.Scheme == "wss" { + targetURL.Scheme = "wss" + } + + // 创建目标WebSocket连接 + dialer := &websocket.Dialer{ + HandshakeTimeout: 10 * time.Second, + ReadBufferSize: 4096, + WriteBufferSize: 4096, + TLSClientConfig: &tls.Config{InsecureSkipVerify: p.config.InsecureSkipVerify}, + } + + // 连接到目标WebSocket服务器 + targetWSConn, _, err := dialer.Dial(targetURL.String(), nil) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 连接目标WebSocket服务器失败: %w", targetURL.String(), err)) + return + } + defer targetWSConn.Close() + + // 转发WebSocket消息 + p.transferWebSocket(ctx, srcWSConn, targetWSConn) + return + } + + // 不使用WebSocket拦截,直接转发TCP流量 + // 确定目标地址 + targetAddr := req.URL.Host + if !strings.Contains(targetAddr, ":") { + if req.URL.Scheme == "wss" || req.URL.Scheme == "https" { + targetAddr += ":443" + } else { + targetAddr += ":80" + } + } + + // 创建到目标服务器的连接 + var targetConn net.Conn + var err error + + if req.URL.Scheme == "wss" || req.URL.Scheme == "https" { + // 使用TLS连接 + targetConn, err = tls.Dial("tcp", targetAddr, &tls.Config{ + InsecureSkipVerify: p.config.InsecureSkipVerify, + }) + } else { + // 使用普通TCP连接 + targetConn, err = net.Dial("tcp", targetAddr) + } + + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 连接WebSocket目标服务器失败: %w", targetAddr, err)) + return + } + defer targetConn.Close() + + // 将原始请求转发给目标服务器 + err = req.Write(targetConn) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("%s - 写入WebSocket握手请求失败: %w", targetAddr, err)) + return + } + + // 启动双向数据传输 + p.transfer(srcConn, targetConn) +} + +// 生成TLS配置 +func (p *UnifiedProxyImpl) generateTLSConfig(host string) (*tls.Config, error) { + // 如果没有证书管理器,则创建一个 + if p.certManager == nil { + // 创建证书管理器,使用已有的证书缓存 + options := []CertManagerOption{ + WithDefaultPrivateKey(true), // 使用默认私钥提高性能 + WithValidityYears(1), // 证书有效期1年 + } + p.certManager = NewCertManager(p.certCache, options...) + } + + // 1. 首先检查是否配置了自定义证书 + if p.config.TLSCert != "" && p.config.TLSKey != "" { + cert, err := tls.LoadX509KeyPair(p.config.TLSCert, p.config.TLSKey) + if err != nil { + return nil, fmt.Errorf("加载TLS证书失败: %s", err) + } + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + }, nil + } + + // 2. 检查是否配置了CA证书和密钥(用于动态生成证书) + if p.config.CACert != "" && p.config.CAKey != "" { + // 加载CA证书和私钥 + caCert, caKey, err := LoadCAFromFiles(p.config.CACert, p.config.CAKey) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("加载CA证书和私钥失败: %s", err)) + // 如果加载失败,使用默认CA + return p.certManager.GenerateTLSConfig(host) + } + + // 使用自定义CA生成证书 + cert, err := p.certManager.GenerateCertificate(host, caCert, caKey) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("为%s生成动态证书失败: %s", host, err)) + return nil, err + } + + return &tls.Config{ + Certificates: []tls.Certificate{*cert}, + }, nil + } + + // 3. 使用默认CA生成证书 + tlsConfig, err := p.certManager.GenerateTLSConfig(host) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("为%s使用默认CA生成证书失败: %s", host, err)) + return nil, err + } + + return tlsConfig, nil +} + +// transfer 在两个连接之间双向转发数据 +func (p *UnifiedProxyImpl) transfer(src net.Conn, dst net.Conn) { + // 创建完成通道 + done := make(chan struct{}, 2) + + // src -> dst + go func() { + buf := bufPool.Get() + written, err := io.CopyBuffer(dst, src, buf) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("隧道双向转发错误: [%s -> %s] %s", src.RemoteAddr().String(), dst.RemoteAddr().String(), err)) + } + + // 记录传输字节数 + if p.metrics != nil { + p.metrics.AddBytesTransferred("request", written) + } + + bufPool.Put(buf) + dst.Close() + done <- struct{}{} + }() + + // dst -> src + go func() { + buf := bufPool.Get() + written, err := io.CopyBuffer(src, dst, buf) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("隧道双向转发错误: [%s -> %s] %s", dst.RemoteAddr().String(), src.RemoteAddr().String(), err)) + } + + // 记录传输字节数 + if p.metrics != nil { + p.metrics.AddBytesTransferred("response", written) + } + + bufPool.Put(buf) + src.Close() + done <- struct{}{} + }() + + // 等待两个方向都结束 + <-done + <-done +} + +// transferWebSocket 在两个WebSocket连接之间双向转发数据 +func (p *UnifiedProxyImpl) transferWebSocket(ctx *Context, srcWSConn, targetWSConn *websocket.Conn) { + doneCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // 源到目标 + go func() { + for { + if doneCtx.Err() != nil { + return + } + + // 读取源消息,正确处理消息类型 + msgType, msg, err := srcWSConn.ReadMessage() + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("websocket消息转发错误: [%s -> %s] %s", + srcWSConn.RemoteAddr().String(), targetWSConn.RemoteAddr().String(), err)) + cancel() // 取消另一个goroutine + return + } + + // 调用消息拦截接口 + p.delegate.WebSocketSendMessage(ctx, &msgType, &msg) + + // 写入目标,保留原始消息类型(文本/二进制) + err = targetWSConn.WriteMessage(msgType, msg) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("websocket消息转发错误: [%s -> %s] %s", srcWSConn.RemoteAddr().String(), targetWSConn.RemoteAddr().String(), err)) + cancel() // 取消另一个goroutine + return + } + } + }() + + // 目标到源 + for { + if doneCtx.Err() != nil { + return + } + + // 读取目标消息,正确处理消息类型 + msgType, msg, err := targetWSConn.ReadMessage() + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("websocket消息转发错误: [%s -> %s] %s", targetWSConn.RemoteAddr().String(), srcWSConn.RemoteAddr().String(), err)) + cancel() // 取消另一个goroutine + return + } + + // 调用消息拦截接口 + p.delegate.WebSocketReceiveMessage(ctx, &msgType, &msg) + + // 写入源,保留原始消息类型(文本/二进制) + err = srcWSConn.WriteMessage(msgType, msg) + if err != nil { + p.delegate.ErrorLog(fmt.Errorf("websocket消息转发错误: [%s -> %s] %s", targetWSConn.RemoteAddr().String(), srcWSConn.RemoteAddr().String(), err)) + cancel() // 取消另一个goroutine + return + } + } +} + +// SetDialContext 设置自定义的拨号上下文函数 +func (p *UnifiedProxyImpl) SetDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) { + p.transport.DialContext = dialContext +} + +// 自定义响应写入器,用于WebSocket连接升级 +type customResponseWriter struct { + conn *ConnBuffer + header http.Header + statusCode int +} + +// Header 实现 http.ResponseWriter 接口 +func (rw *customResponseWriter) Header() http.Header { + return rw.header +} + +// Write 实现 http.ResponseWriter 接口 +func (rw *customResponseWriter) Write(b []byte) (int, error) { + return rw.conn.Write(b) +} + +// WriteHeader 实现 http.ResponseWriter 接口 +func (rw *customResponseWriter) WriteHeader(statusCode int) { + rw.statusCode = statusCode +} diff --git a/统一代理实现说明.md b/统一代理实现说明.md new file mode 100644 index 0000000..c3031a9 --- /dev/null +++ b/统一代理实现说明.md @@ -0,0 +1,184 @@ +# GoProxy 统一代理实现说明 + +## 项目背景 + +本项目对原有的正向代理和反向代理功能进行了统一实现,提供了一致的配置结构和API接口,使用户可以通过同一套API创建和管理不同类型的代理。 + +## 实现方案 + +### 1. 统一配置结构 + +在 `config/unified_config.go` 文件中,我们创建了 `UnifiedConfig` 结构体,包含了所有类型代理的配置项: + +```go +type UnifiedConfig struct { + // 基本配置 + ListenAddr string // 监听地址 + ProxyMode ProxyMode // 代理模式:forward, reverse, transparent + TargetAddr string // 目标地址(反向代理使用) + // ... 其他配置项 +} +``` + +该结构体提供了以下方法: +- `DefaultUnifiedConfig()`: 返回默认配置 +- `CreateLegacyConfig()`: 创建兼容旧版本的配置(向后兼容) +- `CreateReverseConfig()`: 创建反向代理配置 + +### 2. 统一代理接口 + +在 `unified_proxy.go` 文件中,我们定义了 `UnifiedProxy` 接口: + +```go +type UnifiedProxy interface { + // ServeHTTP 处理HTTP请求 + ServeHTTP(w http.ResponseWriter, r *http.Request) + // Close 关闭代理 + Close() error +} +``` + +### 3. 代理实现 + +`UnifiedProxyImpl` 结构体实现了 `UnifiedProxy` 接口,根据配置的 `ProxyMode` 提供不同的代理行为: + +```go +type UnifiedProxyImpl struct { + // 配置 + config *config.UnifiedConfig + // ... 其他字段 +} +``` + +主要的初始化方法: +- `NewUnifiedProxy()`: 创建统一代理实例 +- `initializeForwardProxy()`: 初始化正向代理 +- `initializeReverseProxy()`: 初始化反向代理 +- `initializeTransparentProxy()`: 初始化透明代理 + +处理请求的方法: +- `serveForwardProxy()`: 处理正向代理请求 +- `serveReverseProxy()`: 处理反向代理请求 +- `serveTransparentProxy()`: 处理透明代理请求 + +### 4. 工厂函数 + +在 `factory.go` 文件中,我们提供了多种创建代理的工厂函数: + +```go +// 创建统一代理 +func CreateUnifiedProxy(options ...UnifiedOption) (UnifiedProxy, error) + +// 创建正向代理 +func NewForwardProxy(listenAddr string, options ...UnifiedOption) (UnifiedProxy, error) + +// 创建反向代理 +func NewReverseProxy(listenAddr, targetAddr string, options ...UnifiedOption) (UnifiedProxy, error) + +// 创建透明代理 +func NewTransparentProxy(listenAddr string, options ...UnifiedOption) (UnifiedProxy, error) +``` + +### 5. 选项模式 + +采用功能选项模式配置代理: + +```go +// UnifiedOption 用于配置统一代理选项的函数类型 +type UnifiedOption func(*UnifiedOptions) + +// 选项示例 +WithUnifiedConfig(cfg *config.UnifiedConfig) +WithUnifiedDelegate(delegate Delegate) +WithUnifiedLoadBalancer(lb loadbalance.LoadBalancer) +// ... 其他选项 +``` + +### 6. 命令行工具 + +在 `cmd/unified_proxy/main.go` 中实现了一个完整的命令行工具,支持各种代理模式和配置选项: + +```bash +# 启动正向代理 +go run cmd/unified_proxy/main.go -mode forward -listen :8080 + +# 启动反向代理 +go run cmd/unified_proxy/main.go -mode reverse -listen :8080 -target http://example.com +``` + +## 使用示例 + +### 创建正向代理 + +```go +proxy, err := goproxy.NewForwardProxy(":8080") +if err != nil { + log.Fatal(err) +} +http.ListenAndServe(":8080", proxy) +``` + +### 创建反向代理 + +```go +proxy, err := goproxy.NewReverseProxy(":8080", "http://example.com") +if err != nil { + log.Fatal(err) +} +http.ListenAndServe(":8080", proxy) +``` + +### 使用功能选项创建代理 + +```go +cfg := config.DefaultUnifiedConfig() +cfg.ListenAddr = ":8080" +cfg.ProxyMode = config.ModeReverse +cfg.EnableLoadBalancing = true + +lb := loadbalance.NewRoundRobinBalancer() +lb.Add("http://backend1:8080", 1) +lb.Add("http://backend2:8080", 1) + +proxy, err := goproxy.CreateUnifiedProxy( + goproxy.WithUnifiedConfig(cfg), + goproxy.WithUnifiedLoadBalancer(lb), +) +``` + +## 架构设计 + +``` ++------------------+ +| UnifiedConfig | ++------------------+ + ^ + | ++------------------+ +------------------+ +| UnifiedProxy |<----| Factory 工厂函数 | ++------------------+ +------------------+ + ^ + | ++------------------+ +------------------+ +| UnifiedProxyImpl |---->| 功能组件/插件 | ++------------------+ +------------------+ + / \ + / \ + / \ ++--------+ +--------+ +-------------+ +| 正向代理 | | 反向代理 | | 透明代理实现 | ++--------+ +--------+ +-------------+ +``` + +## 兼容性考虑 + +1. 保留了原有的API接口,确保向后兼容 +2. 提供配置转换方法,使新旧配置可以互相转换 +3. 所有新增功能通过扩展而非修改实现 + +## 后续工作 + +1. 完善单元测试和集成测试 +2. 添加更多代理功能,如请求限流、熔断等 +3. 优化性能,减少内存占用 +4. 完善文档和示例 \ No newline at end of file