forked from aegis/pyserveX
131 lines
3.1 KiB
Go
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)
|
|
}
|