a73x

internal/server/boot/httpserver_test.go

Ref:   Size: 2.1 KiB   History

package boot

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

// TestHTTPServerBoundsSlowClientsWithoutCappingStreams pins the timeout posture
// of the plane's HTTP server: the header phase and idle keep-alives are bounded
// (Slowloris defense, fd reclamation), while ReadTimeout and WriteTimeout stay
// off so the SSE event stream and the serial-console WebSocket — both long-lived
// by design — are never cut at a deadline.
//
// The two bounded values are asserted against literals, not against the
// constants that set them: a deadline compared to itself passes at zero.
func TestHTTPServerBoundsSlowClientsWithoutCappingStreams(t *testing.T) {
	srv := httpServer(":0", http.NewServeMux())

	assert.GreaterOrEqual(t, srv.ReadHeaderTimeout, time.Second,
		"ReadHeaderTimeout must bound how long a client may dribble request headers: at 0 there is no deadline at all, "+
			"a single host holds a goroutine and an fd open forever, and a few hundred of them exhaust the listener. "+
			"This is the Slowloris guard and it is the only one — ReadTimeout is deliberately left 0 so SSE and console "+
			"WebSocket streams survive, so nothing else bounds the header phase")
	assert.LessOrEqual(t, srv.ReadHeaderTimeout, 30*time.Second,
		"a header phase measured in minutes is as good as unbounded: the attack is cheap precisely because an unfinished "+
			"request costs the client nothing to hold")

	assert.GreaterOrEqual(t, srv.IdleTimeout, 10*time.Second,
		"IdleTimeout must reclaim a keep-alive connection that has gone quiet between requests: at 0 net/http never closes "+
			"an idle connection, so every probe that opens one and walks away costs an fd until the process restarts. "+
			"It applies only while idle, never mid-request, so a live stream is untouched")
	assert.LessOrEqual(t, srv.IdleTimeout, 10*time.Minute,
		"an idle window measured in hours reclaims nothing on the timescale a flood works at")

	assert.Zero(t, srv.ReadTimeout, "a whole-request read deadline would cancel the SSE stream")
	assert.Zero(t, srv.WriteTimeout, "a whole-request write deadline would sever a live console")
}