diff --git a/internal/bridge/client.go b/internal/bridge/client.go index 172aa996d2..196b062b0f 100644 --- a/internal/bridge/client.go +++ b/internal/bridge/client.go @@ -93,7 +93,7 @@ func (c *Client) doWithBackoff( headers.Set("User-Agent", "IVO/"+c.Version) url := c.BaseURL.JoinPath(path).String() - err := wait.ExponentialBackoff(c.Backoff, func() (bool, error) { + err := wait.ExponentialBackoffWithContext(ctx, c.Backoff, func() (bool, error) { // NOTE: The [net/http] package treats an empty [bytes.Reader] the same as nil. request, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(body)) diff --git a/internal/bridge/client_test.go b/internal/bridge/client_test.go index 964240af6c..daca48a4d0 100644 --- a/internal/bridge/client_test.go +++ b/internal/bridge/client_test.go @@ -181,6 +181,27 @@ func TestClientDoWithBackoff(t *testing.T) { assert.Assert(t, requests > 0, "expected multiple requests") }) + t.Run("CancellationDuringBackoff", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusServiceUnavailable) + })) + t.Cleanup(server.Close) + + client := NewClient(server.URL, "") + client.Backoff.Duration = 3 * time.Second // nolint:staticcheck + client.Backoff.Jitter = 0 // nolint:staticcheck + client.Backoff.Steps = 2 // nolint:staticcheck + + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + t.Cleanup(cancel) + + started := time.Now() + _, err := client.doWithBackoff(ctx, "POST", "/any", nil, nil) //nolint:bodyclose + assert.ErrorIs(t, err, context.DeadlineExceeded) + assert.Assert(t, time.Since(started) < time.Second, + "cancellation must interrupt the backoff delay") + }) + t.Run("Cancellation", func(t *testing.T) { requests := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {