package main import ( "context" "io" "log/slog" "net/http" "os" "os/signal" "strings" "syscall" "time" "github.com/gin-gonic/gin" "github.com/lmittmann/tint" "anyproxy/internal/config" "anyproxy/internal/middleware" "anyproxy/internal/proxy" "anyproxy/internal/version" ) func main() { cfg := config.Parse() // 日志初始化设置 (支持显式日志等级) levelVar := new(slog.LevelVar) lvlStr := strings.ToLower(cfg.LogLevel) switch lvlStr { case "debug": levelVar.Set(slog.LevelDebug) case "info": levelVar.Set(slog.LevelInfo) case "warn", "warning": levelVar.Set(slog.LevelWarn) case "error", "err": levelVar.Set(slog.LevelError) default: levelVar.Set(slog.LevelWarn) // 回退到默认 warn } var writer io.Writer = os.Stderr if cfg.LogFile != "" { f, err := os.OpenFile(cfg.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { panic(err) } writer = io.MultiWriter(os.Stderr, f) } h := tint.NewHandler(writer, &tint.Options{AddSource: true, Level: levelVar, TimeFormat: "2006-01-02 15:04:05"}) logger := slog.New(h) slog.SetDefault(logger) if cfg.Debug || lvlStr == "debug" { gin.SetMode(gin.DebugMode) } else { gin.SetMode(gin.ReleaseMode) } // 可复用的 HTTP 客户端(保持连接复用) transport := &http.Transport{Proxy: http.ProxyFromEnvironment, DisableCompression: true} client := &http.Client{Transport: transport} if cfg.RequestTimeout > 0 { client.Timeout = time.Duration(cfg.RequestTimeout) * time.Second } p := proxy.New(client, logger) r := gin.New() r.Use(middleware.Recovery(logger), middleware.RequestID(), middleware.Logger(logger)) r.GET("/", proxy.HelloPage) // 欢迎页面 r.Any("/proxy/*proxyPath", p.HandleProxyPath) // 处理 /proxy/*path 形式的请求 r.Any(":protocol/*remainder", p.HandleProtocol) // 处理 /:protocol/*remainder 形式的请求 logger.Info("服务器启动", "addr", cfg.Addr(), "debug", cfg.Debug, "log_level", lvlStr, "version", version.Version, "commit", version.GitCommit) // 优雅停机设置:监听系统信号,执行平滑关闭 srv := &http.Server{Addr: cfg.Addr(), Handler: r} go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Error("服务器监听错误", "error", err) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) <-stop logger.Info("开始关闭 (收到退出信号)") ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.ShutdownGrace)*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { logger.Error("关闭出错", "error", err) } else { logger.Info("关闭完成") } }