Run the server until the given context is canceled. The main HTTP server (Gin), the Prometheus /metrics server, and the gRPC server (when configured) all run as separate listeners.
(ctx context.Context)
| 65 | // The main HTTP server (Gin), the Prometheus /metrics server, and the gRPC |
| 66 | // server (when configured) all run as separate listeners. |
| 67 | func (s *server) Run(ctx context.Context) error { |
| 68 | // Build the REST gateway BEFORE the router so it can be mounted at |
| 69 | // /v1/*. The gateway dials the gRPC server in-process via bufconn — |
| 70 | // no extra port hop, no TLS plumbing. |
| 71 | var gatewayCleanup func() |
| 72 | if s.Dependencies.GRPCServer != nil { |
| 73 | h, cleanup, err := gateway.Handler(ctx, s.Dependencies.GRPCServer.GRPCServer()) |
| 74 | if err != nil { |
| 75 | return err |
| 76 | } |
| 77 | s.gatewayHandler = h |
| 78 | gatewayCleanup = cleanup |
| 79 | defer gatewayCleanup() |
| 80 | } |
| 81 | |
| 82 | ginRouter := s.NewRouter() |
| 83 | httpAddr := net.JoinHostPort(s.Config.Host, strconv.Itoa(s.Config.HTTPPort)) |
| 84 | |
| 85 | // Build the main HTTP server explicitly with conservative timeouts to |
| 86 | // defend against slowloris and other slow-client DoS. ReadHeaderTimeout |
| 87 | // is the most important — it bounds time to receive request headers, |
| 88 | // which is what slowloris exploits. |
| 89 | httpSrv := &http.Server{ |
| 90 | Addr: httpAddr, |
| 91 | Handler: ginRouter, |
| 92 | ReadHeaderTimeout: 10 * time.Second, |
| 93 | ReadTimeout: 30 * time.Second, |
| 94 | WriteTimeout: 60 * time.Second, |
| 95 | IdleTimeout: 120 * time.Second, |
| 96 | MaxHeaderBytes: 1 << 20, // 1 MB |
| 97 | } |
| 98 | go func() { |
| 99 | s.Dependencies.Log.Info().Str("addr", httpAddr).Msg("Starting HTTP server") |
| 100 | if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { |
| 101 | s.Dependencies.Log.Error().Err(err).Msg("HTTP server failed") |
| 102 | } |
| 103 | }() |
| 104 | |
| 105 | mux := http.NewServeMux() |
| 106 | mux.Handle("/metrics", promhttp.Handler()) |
| 107 | metricsAddr := net.JoinHostPort(s.Config.MetricsHost, strconv.Itoa(s.Config.MetricsPort)) |
| 108 | metricsSrv := &http.Server{ |
| 109 | Addr: metricsAddr, |
| 110 | Handler: mux, |
| 111 | ReadHeaderTimeout: 10 * time.Second, |
| 112 | ReadTimeout: 30 * time.Second, |
| 113 | WriteTimeout: 60 * time.Second, |
| 114 | IdleTimeout: 120 * time.Second, |
| 115 | MaxHeaderBytes: 1 << 20, |
| 116 | } |
| 117 | go func() { |
| 118 | s.Dependencies.Log.Info().Str("addr", metricsAddr).Msg("Starting metrics server") |
| 119 | if err := metricsSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { |
| 120 | s.Dependencies.Log.Error().Err(err).Msg("Metrics server failed") |
| 121 | } |
| 122 | }() |
| 123 | |
| 124 | // gRPC listener — runs alongside HTTP and metrics. Cancelled context |