93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
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(cfg *config.TLSConfig) <-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(cfg.ServerCert, cfg.ServerKey); 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
|
||
}
|