package server import ( "context" "errors" "fmt" "log/slog" "net/http" "time" "golang.org/x/net/http2" "yobble-gateway-go/internal/config" "yobble-gateway-go/internal/logger" "yobble-gateway-go/internal/middleware" "yobble-gateway-go/internal/proxy" "yobble-gateway-go/pkg/geoip" ) // Server обертка для http.Server с дополнительными полями. type Server struct { httpServer *http.Server log *slog.Logger } // NewServer создает новый экземпляр Server. func NewServer(cfg *config.Settings, geoIPService *geoip.GeoIPService) *Server { op := "server.NewServer" log := logger.NewLoggerWithOp(op) // Create the main proxy handler proxyHandler := proxy.NewProxyHandler(cfg, geoIPService) // Apply middleware chain chain := middleware.RemoveTrailingSlashMiddleware( middleware.RealIPMiddleware( proxyHandler, ), ) addr := fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port) httpServer := &http.Server{ Addr: addr, Handler: chain, // Use the chained middleware as the main handler // TODO: Add ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout from config } // Explicitly enable HTTP/2 http2.ConfigureServer(httpServer, nil) return &Server{ httpServer: httpServer, log: log, } } // Start запускает HTTP/HTTPS сервер в отдельной горутине. // Он возвращает канал, который будет закрыт, когда сервер завершит работу. func (s *Server) Start(fullchain, privkey string) <-chan error { errChan := make(chan error, 1) s.log.Info("server starting with TLS and HTTP/2", slog.String("address", s.httpServer.Addr)) go func() { defer close(errChan) if err := s.httpServer.ListenAndServeTLS(fullchain, privkey); err != nil && !errors.Is(err, http.ErrServerClosed) { s.log.Error("server failed to listen and serve", slog.Any("error", err)) errChan <- err } s.log.Info("server stopped listening") }() return errChan } // Shutdown gracefully shuts down the server. func (s *Server) Shutdown(ctx context.Context) error { s.log.Info("server is shutting down gracefully") // Give the server a grace period to finish active connections shutdownCtx, cancel := context.WithTimeout(ctx, 30*time.Second) // TODO: Make timeout configurable defer cancel() if err := s.httpServer.Shutdown(shutdownCtx); err != nil { s.log.Error("server shutdown failed", slog.Any("error", err)) return err } s.log.Info("server shutdown complete") return nil }