Ver código fonte

control: harden HTTP server timeouts

tags/v0.9.0
Jan Svabenik 1 mês atrás
pai
commit
002bb0a96e
4 arquivos alterados com 68 adições e 5 exclusões
  1. +6
    -5
      cmd/fmrtx/main.go
  2. +2
    -0
      docs/README.md
  3. +27
    -0
      internal/control/server.go
  4. +33
    -0
      internal/control/server_test.go

+ 6
- 5
cmd/fmrtx/main.go Ver arquivo

@@ -5,7 +5,6 @@ import (
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
@@ -109,8 +108,9 @@ func main() {

// --- default: HTTP only ---
srv := ctrlpkg.NewServer(cfg)
log.Printf("fm-rds-tx listening on %s (TX default: off, use --tx for hardware)", cfg.Control.ListenAddress)
log.Fatal(http.ListenAndServe(cfg.Control.ListenAddress, srv.Handler()))
server := ctrlpkg.NewHTTPServer(cfg, srv.Handler())
log.Printf("fm-rds-tx listening on %s (TX default: off, use --tx for hardware)", server.Addr)
log.Fatal(server.ListenAndServe())
}

// selectDriver picks the best available driver based on config and build tags.
@@ -228,9 +228,10 @@ func runTXMode(cfg cfgpkg.Config, driver platform.SoapyDriver, autoStart bool, a
log.Println("TX ready (idle) — POST /tx/start to begin")
}

ctrlServer := ctrlpkg.NewHTTPServer(cfg, srv.Handler())
go func() {
log.Printf("control plane on %s", cfg.Control.ListenAddress)
if err := http.ListenAndServe(cfg.Control.ListenAddress, srv.Handler()); err != nil {
log.Printf("control plane on %s (read=%s write=%s idle=%s)", ctrlServer.Addr, ctrlServer.ReadTimeout, ctrlServer.WriteTimeout, ctrlServer.IdleTimeout)
if err := ctrlServer.ListenAndServe(); err != nil {
log.Printf("http: %v", err)
}
}()


+ 2
- 0
docs/README.md Ver arquivo

@@ -87,6 +87,8 @@ All major TX parameters are hot-reloadable via `POST /config` during live transm

Available endpoints: `/healthz`, `/status`, `/runtime`, `/config` (GET/POST), `/dry-run`, `/tx/start`, `/tx/stop`

Control-plane HTTP server is configured with 5s read, 10s write, and 60s idle timeouts plus a 1 MiB header limit to reduce slow-client abuse.

### Internal DSP module
- `cd internal`
- `go test ./...`


+ 27
- 0
internal/control/server.go Ver arquivo

@@ -0,0 +1,27 @@
package control

import (
"net/http"
"time"

"github.com/jan/fm-rds-tx/internal/config"
)

const (
defaultReadTimeout = 5 * time.Second
defaultWriteTimeout = 10 * time.Second
defaultIdleTimeout = 60 * time.Second
defaultMaxHeaderBytes = 1 << 20 // 1 MiB
)

// NewHTTPServer returns a configured HTTP server for the control plane.
func NewHTTPServer(cfg config.Config, handler http.Handler) *http.Server {
return &http.Server{
Addr: cfg.Control.ListenAddress,
Handler: handler,
ReadTimeout: defaultReadTimeout,
WriteTimeout: defaultWriteTimeout,
IdleTimeout: defaultIdleTimeout,
MaxHeaderBytes: defaultMaxHeaderBytes,
}
}

+ 33
- 0
internal/control/server_test.go Ver arquivo

@@ -0,0 +1,33 @@
package control

import (
"net/http"
"testing"

cfgpkg "github.com/jan/fm-rds-tx/internal/config"
)

func TestNewHTTPServerConfig(t *testing.T) {
cfg := cfgpkg.Default()
handler := http.NewServeMux()
srv := NewHTTPServer(cfg, handler)

if srv.Addr != cfg.Control.ListenAddress {
t.Fatalf("expected server address %q, got %q", cfg.Control.ListenAddress, srv.Addr)
}
if srv.Handler != handler {
t.Fatalf("expected handler to be preserved")
}
if srv.ReadTimeout != defaultReadTimeout {
t.Fatalf("expected read timeout %s, got %s", defaultReadTimeout, srv.ReadTimeout)
}
if srv.WriteTimeout != defaultWriteTimeout {
t.Fatalf("expected write timeout %s, got %s", defaultWriteTimeout, srv.WriteTimeout)
}
if srv.IdleTimeout != defaultIdleTimeout {
t.Fatalf("expected idle timeout %s, got %s", defaultIdleTimeout, srv.IdleTimeout)
}
if srv.MaxHeaderBytes != defaultMaxHeaderBytes {
t.Fatalf("expected max header bytes %d, got %d", defaultMaxHeaderBytes, srv.MaxHeaderBytes)
}
}

Carregando…
Cancelar
Salvar