Stream beets imports over HTTP.

fix(server): enforce HTTP read limits and per-write SSE flush deadlines #26

Merged glerp.sullen.net
targeting main from glerp.sullen.net/goosefoot:fix/http-sse-timeouts
Merged opened by and assigned to glerp.sullen.net
targeting main from glerp.sullen.net/goosefoot:fix/http-sse-timeouts

Summary #

Enforces bounded HTTP header/body read limits on http.Server and per-write flush deadlines on SSE streams so slow request bodies and silently stalled TCP clients cannot pin connections or wedge the serialized import queue.

Changes #

  • HTTP request header & body limits (internal/server/server.go, internal/server/import.go, cmd/goosefoot/main.go):
    • Set ReadTimeout (DefaultReadTimeout = 15s), ReadHeaderTimeout: min(5s, ReadTimeout), and MaxHeaderBytes (DefaultMaxHeaderBytes = 16 KiB) on http.Server.
    • Consume request.Body to EOF via io.ReadAll + json.Unmarshal in parseImportRequest while ReadTimeout is armed, ensuring trailing unread body bytes cannot block post-handler request drain before clearing the connection read deadline (rc.SetReadDeadline(time.Time{})) for long-lived SSE streaming.
  • Per-write SSE flush deadlines & stalled-reader detachment (internal/server/sse.go):
    • Arm rc.SetWriteDeadline(time.Now().Add(writeTimeout)) (DefaultSSEWriteTimeout = 10s) before initial header flush and every sendRaw write/flush.
    • Mark the stream detached under sseStream.mu on write/flush failure (or request context cancellation) and stop the heartbeat goroutine, treating silently stalled TCP sockets identically to clean client disconnects so queued/active imports complete in the background and release s.queue and admission slots.
  • Tests (internal/server/server_test.go):
    • Behavioral ListenAndServe tests covering unauthenticated slow-body 401 socket closure, authenticated stalled-body 400 rejection, 431 oversized headers, SSE streams outliving ReadTimeout, and stalled TCP receive buffers detaching mid-import without wedging subsequent imports or post-import hooks.
Commits 0