Илья Глазунов 8f5b9a5cd1 go implementation
2025-12-11 16:52:13 +03:00

131 lines
3.1 KiB
Go

// Package server provides the HTTP server implementation
package server
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/konduktor/konduktor/internal/config"
"github.com/konduktor/konduktor/internal/logging"
"github.com/konduktor/konduktor/internal/middleware"
"github.com/konduktor/konduktor/internal/routing"
)
const Version = "0.1.0"
// Server represents the Konduktor HTTP server
type Server struct {
config *config.Config
httpServer *http.Server
router *routing.Router
logger *logging.Logger
}
// New creates a new server instance
func New(cfg *config.Config) (*Server, error) {
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("invalid configuration: %w", err)
}
logger, err := logging.NewFromConfig(cfg.Logging)
if err != nil {
return nil, fmt.Errorf("failed to create logger: %w", err)
}
router := routing.New(cfg, logger)
srv := &Server{
config: cfg,
router: router,
logger: logger,
}
return srv, nil
}
// Run starts the server and blocks until shutdown
func (s *Server) Run() error {
// Build handler chain with middleware
handler := s.buildHandler()
// Create HTTP server
addr := fmt.Sprintf("%s:%d", s.config.Server.Host, s.config.Server.Port)
s.httpServer = &http.Server{
Addr: addr,
Handler: handler,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
// Start server in goroutine
errChan := make(chan error, 1)
go func() {
s.logger.Info("Server starting", "addr", addr, "version", Version)
var err error
if s.config.SSL.Enabled {
err = s.httpServer.ListenAndServeTLS(s.config.SSL.CertFile, s.config.SSL.KeyFile)
} else {
err = s.httpServer.ListenAndServe()
}
if err != nil && err != http.ErrServerClosed {
errChan <- err
}
}()
// Wait for shutdown signal
return s.waitForShutdown(errChan)
}
// buildHandler builds the HTTP handler chain
func (s *Server) buildHandler() http.Handler {
var handler http.Handler = s.router
// Add middleware
handler = middleware.AccessLog(handler, s.logger)
handler = middleware.ServerHeader(handler, Version)
handler = middleware.Recovery(handler, s.logger)
return handler
}
// waitForShutdown waits for shutdown signal and gracefully stops the server
func (s *Server) waitForShutdown(errChan <-chan error) error {
// Listen for shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
select {
case err := <-errChan:
return err
case sig := <-sigChan:
s.logger.Info("Shutdown signal received", "signal", sig.String())
}
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
s.logger.Info("Shutting down server...")
if err := s.httpServer.Shutdown(ctx); err != nil {
s.logger.Error("Error during shutdown", "error", err)
return err
}
s.logger.Info("Server stopped gracefully")
return nil
}
// Shutdown gracefully shuts down the server
func (s *Server) Shutdown(ctx context.Context) error {
return s.httpServer.Shutdown(ctx)
}