lolly/internal/app/app_windows.go
xfy c18ce613b3 style: 格式化代码
- 使用 Go 1.13+ 八进制字面量格式 (0o644)
- 修复文件末尾缺少换行符
- 对齐结构体字段

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 10:20:18 +08:00

290 lines
7.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build windows
// Windows lacks POSIX signals (SIGUSR1, SIGUSR2, SIGHUP, SIGQUIT);
// this file provides stub implementations for those Unix-specific signals.
package app
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
"time"
"rua.plus/lolly/internal/config"
"rua.plus/lolly/internal/http2"
"rua.plus/lolly/internal/http3"
"rua.plus/lolly/internal/logging"
"rua.plus/lolly/internal/resolver"
"rua.plus/lolly/internal/server"
"rua.plus/lolly/internal/stream"
"rua.plus/lolly/internal/variable"
)
// App manages the server lifecycle (Windows version).
type App struct {
resv resolver.Resolver
cfg *config.Config
srv *server.Server
http3Srv *http3.Server
http2Srv *http2.Server
streamSrv *stream.Server
logger *logging.AppLogger
cfgPath string
pidFile string
logFile string
listeners []net.Listener
upgradeMgr *server.UpgradeManager
}
func NewApp(cfgPath string) *App {
return &App{
cfgPath: cfgPath,
}
}
func (a *App) SetPidFile(path string) {
a.pidFile = path
}
func (a *App) SetLogFile(path string) {
a.logFile = path
}
// Run starts the application: loads config, creates servers, and handles signals (Windows version).
func (a *App) Run() int {
cfg, err := config.Load(a.cfgPath)
if err != nil {
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
return 1
}
a.cfg = cfg
a.logger = logging.NewAppLogger(&cfg.Logging)
variable.SetGlobalVariables(cfg.Variables.Set)
if len(cfg.Variables.Set) > 0 {
a.logger.LogStartup("全局变量已加载", map[string]string{
"count": fmt.Sprintf("%d", len(cfg.Variables.Set)),
})
}
a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath})
mode := a.cfg.GetMode()
if mode == config.ServerModeMultiServer {
for i, srv := range a.cfg.Servers {
a.logger.LogStartup("监听地址", map[string]string{
"index": fmt.Sprintf("[%d]", i),
"listen": srv.Listen,
"name": srv.Name,
})
}
} else {
a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen})
}
if a.cfg.Resolver.Enabled {
a.resv = resolver.New(&a.cfg.Resolver)
a.logger.LogStartup("DNS 解析器已启用", map[string]string{
"addresses": fmt.Sprintf("%v", a.cfg.Resolver.Addresses),
"ttl": a.cfg.Resolver.TTL().String(),
})
}
a.srv = server.New(a.cfg)
if a.resv != nil {
a.srv.SetResolver(a.resv)
}
if len(a.cfg.Stream) > 0 {
a.streamSrv = stream.NewServer()
for _, sc := range a.cfg.Stream {
targets := make([]stream.TargetSpec, len(sc.Upstream.Targets))
for i, t := range sc.Upstream.Targets {
targets[i] = stream.TargetSpec{
Addr: t.Addr,
Weight: t.Weight,
}
}
if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil {
a.logger.Error().Err(err).Msg("添加 Stream 上游失败")
}
if sc.Protocol == "udp" {
if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil {
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败")
}
} else {
if err := a.streamSrv.ListenTCP(sc.Listen); err != nil {
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 TCP 失败")
}
}
}
go func() {
a.logger.LogStartup("Stream 服务器启动中", nil)
if err := a.streamSrv.Start(); err != nil {
a.logger.Error().Err(err).Msg("Stream 服务器启动失败")
}
}()
}
if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
tlsConfig, err := a.srv.GetTLSConfig()
if err != nil {
a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/3")
} else {
a.http3Srv, err = http3.NewServer(&a.cfg.HTTP3, a.srv.GetHandler(), tlsConfig)
if err != nil {
a.logger.Error().Err(err).Msg("创建 HTTP/3 服务器失败")
} else {
go func() {
a.logger.LogStartup("HTTP/3 服务器启动中", map[string]string{"listen": a.cfg.HTTP3.Listen})
if err := a.http3Srv.Start(); err != nil {
a.logger.Error().Err(err).Msg("HTTP/3 服务器启动失败")
}
}()
}
}
}
if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
tlsConfig, err := a.srv.GetTLSConfig()
if err != nil {
a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/2")
} else {
a.http2Srv, err = http2.NewServer(&a.cfg.Servers[0].SSL.HTTP2, a.srv.GetHandler(), tlsConfig)
if err != nil {
a.logger.Error().Err(err).Msg("创建 HTTP/2 服务器失败")
} else {
go func() {
a.logger.LogStartup("HTTP/2 服务器启动中", map[string]string{
"listen": a.cfg.Servers[0].Listen,
"max_concurrent_streams": fmt.Sprintf("%d", a.cfg.Servers[0].SSL.HTTP2.MaxConcurrentStreams),
"push_enabled": fmt.Sprintf("%t", a.cfg.Servers[0].SSL.HTTP2.PushEnabled),
})
listeners := a.srv.GetListeners()
if len(listeners) > 0 {
if err := a.http2Srv.Serve(listeners[0]); err != nil {
a.logger.Error().Err(err).Msg("HTTP/2 服务器启动失败")
}
} else {
a.logger.Error().Msg("HTTP/2 服务器启动失败: 无可用监听器")
}
}()
}
}
}
a.upgradeMgr = server.NewUpgradeManager(a.srv)
if a.pidFile != "" {
a.upgradeMgr.SetPidFile(a.pidFile)
_ = a.upgradeMgr.WritePid()
}
sigChan := make(chan os.Signal, 1)
a.setupSignalHandlers(sigChan)
errChan := make(chan error, 1)
go func() {
a.logger.LogStartup("HTTP 服务器启动中", nil)
if err := a.srv.Start(); err != nil {
errChan <- err
}
}()
sigintCount := 0
for {
select {
case err := <-errChan:
a.logger.Error().Err(err).Msg("服务器启动失败")
return 1
case sig := <-sigChan:
if sig == syscall.SIGINT {
sigintCount++
if sigintCount >= 3 {
a.logger.LogShutdown("收到 3 次 SIGINT强制退出")
return 1
}
}
if !a.handleSignal(sig) {
a.logger.LogShutdown("服务器已停止")
return 0
}
}
}
}
func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
signal.Notify(sigChan,
syscall.SIGTERM,
syscall.SIGINT,
)
}
// handleSignal returns false to indicate the app should exit (Windows version).
func (a *App) handleSignal(sig os.Signal) bool {
switch sig {
case syscall.SIGTERM, syscall.SIGINT:
timeout := a.cfg.Shutdown.FastTimeout
if timeout <= 0 {
timeout = 5 * time.Second
}
a.logger.LogSignal(sigName(sig.(syscall.Signal)), "停止服务器")
a.shutdownHTTP2()
a.shutdownHTTP3()
_ = a.srv.StopWithTimeout(timeout)
return false
default:
a.logger.Info().Str("signal", sig.String()).Msg("收到信号Windows 忽略)")
return true
}
}
func (a *App) shutdownHTTP3() {
if a.http3Srv != nil {
if err := a.http3Srv.Stop(); err != nil {
a.logger.Error().Err(err).Msg("HTTP/3 服务器关闭失败")
}
}
}
func (a *App) shutdownHTTP2() {
if a.http2Srv != nil {
if err := a.http2Srv.Stop(); err != nil {
a.logger.Error().Err(err).Msg("HTTP/2 服务器关闭失败")
}
}
}
func (a *App) reloadConfig() {
// Windows stub - functionality limited
}
func (a *App) reopenLogs() {
if a.cfg != nil {
logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
a.logger = logging.NewAppLogger(&a.cfg.Logging)
}
a.logger.LogStartup("日志已重新打开", nil)
}
func (a *App) gracefulUpgrade() {
a.logger.Info().Msg("Windows 不支持热升级")
}
func sigName(sig syscall.Signal) string {
switch sig {
case syscall.SIGTERM:
return "SIGTERM"
case syscall.SIGINT:
return "SIGINT"
default:
return fmt.Sprintf("Signal(%d)", sig)
}
}