From 14464bf62dcdd3463c1df47e913e5d6e5c2b184d Mon Sep 17 00:00:00 2001 From: Masahiro Furudate <178inaba.git@gmail.com> Date: Wed, 11 Dec 2024 02:41:52 +0900 Subject: [PATCH 1/2] Add conditions to ensure Bind succeeds with `Transfer-Encoding: chunked`. --- bind.go | 9 ++++++++- bind_test.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bind.go b/bind.go index acc346506..bd9d40749 100644 --- a/bind.go +++ b/bind.go @@ -67,7 +67,14 @@ func (b *DefaultBinder) BindQueryParams(c Context, i interface{}) error { // See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { req := c.Request() - if req.ContentLength <= 0 { + var isChunked bool + for _, enc := range req.TransferEncoding { + if enc == "chunked" { + isChunked = true + break + } + } + if req.ContentLength <= 0 && !isChunked { return } diff --git a/bind_test.go b/bind_test.go index a7e8dbb3a..17d41c058 100644 --- a/bind_test.go +++ b/bind_test.go @@ -13,6 +13,7 @@ import ( "mime/multipart" "net/http" "net/http/httptest" + "net/http/httputil" "net/url" "reflect" "strconv" @@ -941,6 +942,7 @@ func TestDefaultBinder_BindBody(t *testing.T) { givenMethod string givenContentType string whenNoPathParams bool + whenChunkedBody bool whenBindTarget interface{} expect interface{} expectError string @@ -1068,6 +1070,15 @@ func TestDefaultBinder_BindBody(t *testing.T) { givenContent: http.NoBody, expect: &Node{ID: 0, Node: ""}, }, + { + name: "ok, JSON POST bind to struct with: path + query + chunked body", + givenURL: "/api/real_node/endpoint?node=xxx", + givenMethod: http.MethodPost, + givenContentType: MIMEApplicationJSON, + givenContent: httputil.NewChunkedReader(strings.NewReader("18\r\n" + `{"id": 1, "node": "zzz"}` + "\r\n0\r\n")), + whenChunkedBody: true, + expect: &Node{ID: 1, Node: "zzz"}, + }, } for _, tc := range testCases { @@ -1083,6 +1094,10 @@ func TestDefaultBinder_BindBody(t *testing.T) { case MIMEApplicationJSON: req.Header.Set(HeaderContentType, MIMEApplicationJSON) } + if tc.whenChunkedBody { + req.ContentLength = -1 + req.TransferEncoding = append(req.TransferEncoding, "chunked") + } rec := httptest.NewRecorder() c := e.NewContext(req, rec) From 7b331eea324993d722a40fefaf3931593f59c8af Mon Sep 17 00:00:00 2001 From: Masahiro Furudate <178inaba.git@gmail.com> Date: Wed, 11 Dec 2024 15:09:51 +0900 Subject: [PATCH 2/2] Revert the ContentLength conditions for BindBody --- bind.go | 9 +-------- bind_test.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bind.go b/bind.go index bd9d40749..ed7ca3249 100644 --- a/bind.go +++ b/bind.go @@ -67,14 +67,7 @@ func (b *DefaultBinder) BindQueryParams(c Context, i interface{}) error { // See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { req := c.Request() - var isChunked bool - for _, enc := range req.TransferEncoding { - if enc == "chunked" { - isChunked = true - break - } - } - if req.ContentLength <= 0 && !isChunked { + if req.ContentLength == 0 { return } diff --git a/bind_test.go b/bind_test.go index 17d41c058..303c8854a 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1063,12 +1063,21 @@ func TestDefaultBinder_BindBody(t *testing.T) { expectError: "code=415, message=Unsupported Media Type", }, { - name: "ok, JSON POST bind to struct with: path + query + http.NoBody", + name: "nok, JSON POST with http.NoBody", givenURL: "/api/real_node/endpoint?node=xxx", givenMethod: http.MethodPost, givenContentType: MIMEApplicationJSON, givenContent: http.NoBody, expect: &Node{ID: 0, Node: ""}, + expectError: "code=400, message=EOF, internal=EOF", + }, + { + name: "ok, JSON POST with empty body", + givenURL: "/api/real_node/endpoint?node=xxx", + givenMethod: http.MethodPost, + givenContentType: MIMEApplicationJSON, + givenContent: strings.NewReader(""), + expect: &Node{ID: 0, Node: ""}, }, { name: "ok, JSON POST bind to struct with: path + query + chunked body",