Give the webhook handler a timeout (closes #38)
check / check (push) Successful in 38s
check / check (pull_request) Successful in 54s

The webhook handler's client had no timeout, and slog calls the handler
inside the log call, so a server that accepted the connection and never
answered stopped that log call for good, and every later one. A request
still running after 5 seconds now fails with a timeout error. The
handler also reads the answer to the end before closing it, so the
connection is reused for the next record.

A new test points the handler at a server that never answers and checks
that Handle returns a timeout error. The README states the timeout.

Model: opus-5-5
This commit is contained in:
2026-10-06 07:44:25 +00:00
committed by sneak
parent 8e53530434
commit d054848547
4 changed files with 56 additions and 3 deletions
+34
View File
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"log/slog"
"net"
"net/http"
"net/http/httptest"
"strings"
@@ -160,3 +161,36 @@ func TestWebhookHandlerReturnsErrorOnRedirect(t *testing.T) {
t.Fatalf("Handle returned %v, want an error for the redirect", err)
}
}
// A server that accepts the request and never answers must not hold up
// the log call: Handle gives up after webhookTimeout and says why.
func TestWebhookHandlerTimesOutOnServerThatNeverAnswers(t *testing.T) {
t.Parallel()
testEnded := make(chan struct{})
server := httptest.NewServer(http.HandlerFunc(
func(_ http.ResponseWriter, _ *http.Request) {
<-testEnded
},
))
// server.Close waits for running requests, so the server's handler
// is released first.
defer func() {
close(testEnded)
server.Close()
}()
handler, err := NewWebhookHandler(server.URL)
if err != nil {
t.Fatalf("NewWebhookHandler: %v", err)
}
err = handler.Handle(context.Background(), errorTestRecord())
var netErr net.Error
if !errors.As(err, &netErr) || !netErr.Timeout() {
t.Fatalf("Handle returned %v, want a timeout error", err)
}
}