From 8c47c0173981b3489fc15be392964c260856e710 Mon Sep 17 00:00:00 2001 From: Name <1911860538@qq.com> Date: Sat, 28 Mar 2026 22:16:53 +0800 Subject: [PATCH] fix(rest/httpc): reject request body for HEAD method in buildRequest (#5457) Co-authored-by: 1911860538 --- rest/httpc/requests.go | 5 +- rest/httpc/requests_test.go | 103 ++++++++++++++++++++++++++++++++++++ rest/httpc/vars.go | 8 ++- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/rest/httpc/requests.go b/rest/httpc/requests.go index 3692eaae0..5dd8fb12a 100644 --- a/rest/httpc/requests.go +++ b/rest/httpc/requests.go @@ -84,8 +84,11 @@ func buildRequest(ctx context.Context, method, url string, data any) (*http.Requ var reader io.Reader jsonVars, hasJsonBody := val[jsonKey] if hasJsonBody { - if method == http.MethodGet { + switch method { + case http.MethodGet: return nil, ErrGetWithBody + case http.MethodHead: + return nil, ErrHeadWithBody } var buf bytes.Buffer diff --git a/rest/httpc/requests_test.go b/rest/httpc/requests_test.go index b30902854..de2fec7ba 100644 --- a/rest/httpc/requests_test.go +++ b/rest/httpc/requests_test.go @@ -229,3 +229,106 @@ func TestDo_WithClientHttpTrace(t *testing.T) { assert.Nil(t, err) assert.True(t, enter) } + +func TestBuildRequestWithBody(t *testing.T) { + testBody := struct { + Key string `json:"key"` + Value int `json:"value"` + }{ + Key: "foo", + Value: 10, + } + + testcases := []struct { + testName string + method string + url string + body any + wantedErr error + }{ + { + testName: "GET Request with Body", + method: http.MethodGet, + url: "/ping", + body: testBody, + wantedErr: ErrGetWithBody, + }, + { + testName: "GET Request without Body", + method: http.MethodGet, + url: "/ping", + body: nil, + wantedErr: nil, + }, + { + testName: "HEAD Request with Body", + method: http.MethodHead, + url: "/ping", + body: testBody, + wantedErr: ErrHeadWithBody, + }, + { + testName: "HEAD Request without Body", + method: http.MethodHead, + url: "/ping", + body: nil, + wantedErr: nil, + }, + { + testName: "POST Request with Body", + method: http.MethodPost, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "PUT Request with Body", + method: http.MethodPut, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "PATCH Request with Body", + method: http.MethodPatch, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "DELETE Request with Body", + method: http.MethodDelete, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "CONNECT Request with Body", + method: http.MethodConnect, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "OPTIONS Request with Body", + method: http.MethodOptions, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + { + testName: "TRACE Request with Body", + method: http.MethodTrace, + url: "/ping", + body: testBody, + wantedErr: nil, + }, + } + + for _, tc := range testcases { + t.Run(tc.testName, func(t *testing.T) { + _, err := buildRequest(context.Background(), tc.method, tc.url, tc.body) + assert.Equal(t, tc.wantedErr, err) + }) + } +} diff --git a/rest/httpc/vars.go b/rest/httpc/vars.go index 5bd5637bb..bd9b7ad94 100644 --- a/rest/httpc/vars.go +++ b/rest/httpc/vars.go @@ -11,5 +11,9 @@ const ( colon = ':' ) -// ErrGetWithBody indicates that GET request with body. -var ErrGetWithBody = errors.New("HTTP GET should not have body") +var ( + // ErrGetWithBody indicates that GET request with body. + ErrGetWithBody = errors.New("HTTP GET should not have body") + // ErrHeadWithBody indicates that HEAD request with body. + ErrHeadWithBody = errors.New("HTTP HEAD should not have body") +)