Workhorse: Handle http-abort panic in actions.go
What does this MR do and why?
When a Duo Workflow agent action triggers an HTTP request via runHTTPActionHandler.Execute, it calls a.backend.ServeHTTP(nrw, req) directly from an agent goroutine. This routes through the full workhorse middleware stack including httputil.ReverseProxy, which panics with http.ErrAbortHandler when the request context is canceled (e.g. client disconnect or server shutdown).
In a normal HTTP handler chain, this panic is safe — Go's net/http server catches it at the top of every connection goroutine. However, Execute is called from a manually spawned goroutine inside runner.Execute, which is outside net/http's managed context. As a result, the panic propagates uncaught and crashes the workhorse process.
This was observed in production logs where a workhorse restart (config reload) canceled in-flight contexts, triggering the panic and causing an immediate process crash.
Related logs:
- https://log.gprd.gitlab.net/app/r/s/sLqMK (the 2nd page)
- The error seems to happen quite often: https://log.gprd.gitlab.net/app/r/s/SCF4U
Changes
- Add serveHTTPSafe: a thin wrapper around h.ServeHTTP that recovers http.ErrAbortHandler and converts it to a normal error return. Any other panic value is re-panicked so genuine bugs still surface.
- Extract buildClientEvent: refactored inline pb.ClientEvent construction into a method on *runHTTPActionHandler, consistent with the same pattern used in mcp.go.
- Tests: added TestServeHTTPSafe covering the three recovery cases (abort, unexpected panic, normal completion), and TestRunHttpActionHandler_Execute_ContextCancelled which directly reproduces the crash scenario.