diff --git a/v2/protocol/http/protocol_retry_test.go b/v2/protocol/http/protocol_retry_test.go index 45612eed9..3e967a3b6 100644 --- a/v2/protocol/http/protocol_retry_test.go +++ b/v2/protocol/http/protocol_retry_test.go @@ -9,6 +9,7 @@ import ( "context" "net/http" "net/http/httptest" + "sync" "testing" "time" @@ -215,6 +216,48 @@ func TestRequestWithRetries_linear(t *testing.T) { } } +func TestSendWithRetriesPreservesResponseBody(t *testing.T) { + var ( + mu sync.Mutex + requestCount int + ) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + mu.Lock() + defer mu.Unlock() + + requestCount++ + switch requestCount { + case 1: + w.WriteHeader(http.StatusTooEarly) + _, _ = w.Write([]byte("retry this")) + default: + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte("final failure")) + } + })) + defer srv.Close() + + p, err := New(WithClient(http.Client{Timeout: time.Second})) + require.NoError(t, err) + + ctx := cecontext.WithTarget(context.Background(), srv.URL) + ctx = cecontext.WithRetriesLinearBackoff(ctx, time.Nanosecond, 2) + + evt := newEvent(t, event.ApplicationJSON, map[string]string{"hello": "world"}) + err = p.Send(ctx, binding.ToMessage(&evt)) + require.Error(t, err) + + mu.Lock() + assert.Equal(t, 2, requestCount) + mu.Unlock() + + var result *Result + require.True(t, protocol.ResultAs(err, &result)) + assert.Equal(t, http.StatusBadRequest, result.StatusCode) + assert.Contains(t, err.Error(), "final failure") +} + func newEvent(t *testing.T, encoding string, body interface{}) event.Event { e := event.New() if body != nil { diff --git a/v2/protocol/http/retries_result.go b/v2/protocol/http/retries_result.go index f4046d522..21e49b113 100644 --- a/v2/protocol/http/retries_result.go +++ b/v2/protocol/http/retries_result.go @@ -57,3 +57,11 @@ func (e *RetriesResult) Error() string { } return fmt.Sprintf("%s (%dx)", e.Result.Error(), e.Retries) } + +// Unwrap returns the wrapped result to preserve standard error traversal. +func (e *RetriesResult) Unwrap() error { + if e == nil { + return nil + } + return e.Result +} diff --git a/v2/protocol/http/retries_result_test.go b/v2/protocol/http/retries_result_test.go index bf844eded..167ebfeea 100644 --- a/v2/protocol/http/retries_result_test.go +++ b/v2/protocol/http/retries_result_test.go @@ -71,6 +71,19 @@ func TestRetriesNew_As(t *testing.T) { } } +func TestRetriesNewHTTPResult_As(t *testing.T) { + err := NewRetriesResult(NewResult(404, "this is an example error, %s", "yep"), 5, time.Now(), nil) + + var result *Result + if !protocol.ResultAs(err, &result) { + t.Errorf("Expected error to unwrap to an HTTP Result, is not") + } + + if result.StatusCode != 404 { + t.Errorf("Mismatched status code") + } +} + func TestRetriesNil_As(t *testing.T) { var err error