From 526d17ff4a74a02376a7c4c792bbd03ffa316ca6 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 15 Jul 2026 09:23:04 +0200 Subject: [PATCH] Fix DoJSON error on empty-body 2xx response (#31801) --- util/request/helper.go | 9 +++++- util/request/helper_test.go | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 util/request/helper_test.go diff --git a/util/request/helper.go b/util/request/helper.go index bf3f047f9..d96ba4ce6 100644 --- a/util/request/helper.go +++ b/util/request/helper.go @@ -3,6 +3,8 @@ package request import ( "encoding/json" "encoding/xml" + "errors" + "io" "net/http" "time" @@ -60,7 +62,12 @@ func decodeJSON(resp *http.Response, res any) error { return err } - return json.NewDecoder(resp.Body).Decode(&res) + // swallow io.EOF to allow empty response + if err := json.NewDecoder(resp.Body).Decode(&res); err != nil && !errors.Is(err, io.EOF) { + return err + } + + return nil } // decodeXML reads HTTP response and decodes XML body if error is nil diff --git a/util/request/helper_test.go b/util/request/helper_test.go new file mode 100644 index 000000000..e0a4922d6 --- /dev/null +++ b/util/request/helper_test.go @@ -0,0 +1,55 @@ +package request + +import ( + "io" + "net/http" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type roundTripFunc func(req *http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func testHelper(status int, body string) *Helper { + return &Helper{ + Client: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: status, + Body: io.NopCloser(strings.NewReader(body)), + Header: make(http.Header), + }, nil + }), + }, + } +} + +func TestDoJSON(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, "http://example.com", nil) + require.NoError(t, err) + + t.Run("empty body is not an error", func(t *testing.T) { + var res struct{ Code int } + err := testHelper(http.StatusOK, "").DoJSON(req, &res) + assert.NoError(t, err) + }) + + t.Run("valid body is decoded", func(t *testing.T) { + var res struct{ Code int } + err := testHelper(http.StatusOK, `{"Code":528}`).DoJSON(req, &res) + assert.NoError(t, err) + assert.Equal(t, 528, res.Code) + }) + + t.Run("truncated body is an error", func(t *testing.T) { + var res struct{ Code int } + err := testHelper(http.StatusOK, `{"Code":52`).DoJSON(req, &res) + assert.Error(t, err) + }) +}