Skip to content

Commit 387b0fb

Browse files
authored
Merge branch 'main' into keyauth-use-extractors
2 parents ee2ebbc + 93fbc04 commit 387b0fb

File tree

13 files changed

+209
-92
lines changed

13 files changed

+209
-92
lines changed

ctx.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,14 +337,66 @@ func (c *DefaultCtx) IsMiddleware() bool {
337337
return c.indexHandler+1 < len(c.route.Handlers)
338338
}
339339

340-
// HasBody returns true if the request has a body or a Content-Length header greater than zero.
340+
// HasBody returns true if the request declares a body via Content-Length, Transfer-Encoding, or already buffered payload data.
341341
func (c *DefaultCtx) HasBody() bool {
342-
if c.fasthttp.Request.Header.ContentLength() > 0 {
342+
hdr := &c.fasthttp.Request.Header
343+
344+
switch cl := hdr.ContentLength(); {
345+
case cl > 0:
346+
return true
347+
case cl == -1:
348+
// fasthttp reports -1 for Transfer-Encoding: chunked bodies.
343349
return true
350+
case cl == 0:
351+
if hasTransferEncodingBody(hdr) {
352+
return true
353+
}
344354
}
355+
345356
return len(c.fasthttp.Request.Body()) > 0
346357
}
347358

359+
func hasTransferEncodingBody(hdr *fasthttp.RequestHeader) bool {
360+
teBytes := hdr.Peek(HeaderTransferEncoding)
361+
var te string
362+
363+
if len(teBytes) > 0 {
364+
te = utils.UnsafeString(teBytes)
365+
} else {
366+
for key, value := range hdr.All() {
367+
if !strings.EqualFold(utils.UnsafeString(key), HeaderTransferEncoding) {
368+
continue
369+
}
370+
te = utils.UnsafeString(value)
371+
break
372+
}
373+
}
374+
375+
if te == "" {
376+
return false
377+
}
378+
379+
hasEncoding := false
380+
for raw := range strings.SplitSeq(te, ",") {
381+
token := strings.TrimSpace(raw)
382+
if len(token) == 0 {
383+
continue
384+
}
385+
if idx := strings.IndexByte(token, ';'); idx >= 0 {
386+
token = strings.TrimSpace(token[:idx])
387+
}
388+
if token == "" {
389+
continue
390+
}
391+
if strings.EqualFold(token, "identity") {
392+
continue
393+
}
394+
hasEncoding = true
395+
}
396+
397+
return hasEncoding
398+
}
399+
348400
// IsWebSocket returns true if the request includes a WebSocket upgrade handshake.
349401
func (c *DefaultCtx) IsWebSocket() bool {
350402
conn := c.fasthttp.Request.Header.Peek(HeaderConnection)

ctx_interface_gen.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ctx_test.go

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5113,22 +5113,95 @@ func Test_Ctx_HasBody(t *testing.T) {
51135113
t.Parallel()
51145114
app := New()
51155115

5116-
ctxWithBody := app.AcquireCtx(&fasthttp.RequestCtx{})
5117-
require.NotNil(t, ctxWithBody)
5118-
t.Cleanup(func() { app.ReleaseCtx(ctxWithBody) })
5119-
ctxWithBody.Request().SetBody([]byte("test"))
5120-
require.True(t, ctxWithBody.HasBody())
5121-
5122-
ctxWithHeader := app.AcquireCtx(&fasthttp.RequestCtx{})
5123-
require.NotNil(t, ctxWithHeader)
5124-
t.Cleanup(func() { app.ReleaseCtx(ctxWithHeader) })
5125-
ctxWithHeader.Request().Header.SetContentLength(4)
5126-
require.True(t, ctxWithHeader.HasBody())
5127-
5128-
ctxWithoutBody := app.AcquireCtx(&fasthttp.RequestCtx{})
5129-
require.NotNil(t, ctxWithoutBody)
5130-
t.Cleanup(func() { app.ReleaseCtx(ctxWithoutBody) })
5131-
require.False(t, ctxWithoutBody.HasBody())
5116+
acquire := func(t *testing.T) CustomCtx {
5117+
t.Helper()
5118+
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
5119+
require.NotNil(t, ctx)
5120+
t.Cleanup(func() { app.ReleaseCtx(ctx) })
5121+
return ctx
5122+
}
5123+
5124+
setTransferEncoding := func(t *testing.T, ctx Ctx, value string) {
5125+
t.Helper()
5126+
hdr := &ctx.Request().Header
5127+
hdr.DisableSpecialHeader()
5128+
hdr.Set(HeaderTransferEncoding, value)
5129+
hdr.Set(HeaderContentLength, "0")
5130+
hdr.EnableSpecialHeader()
5131+
require.Zero(t, hdr.ContentLength())
5132+
require.Empty(t, ctx.Request().Body())
5133+
}
5134+
5135+
t.Run("body bytes", func(t *testing.T) {
5136+
t.Parallel()
5137+
ctx := acquire(t)
5138+
ctx.Request().SetBody([]byte("test"))
5139+
require.True(t, ctx.HasBody())
5140+
})
5141+
5142+
t.Run("content length header", func(t *testing.T) {
5143+
t.Parallel()
5144+
ctx := acquire(t)
5145+
ctx.Request().Header.SetContentLength(4)
5146+
require.True(t, ctx.HasBody())
5147+
})
5148+
5149+
t.Run("chunked sentinel", func(t *testing.T) {
5150+
t.Parallel()
5151+
ctx := acquire(t)
5152+
ctx.Request().Header.SetContentLength(-1)
5153+
require.Equal(t, -1, ctx.Request().Header.ContentLength())
5154+
require.Empty(t, ctx.Request().Body())
5155+
require.True(t, ctx.HasBody())
5156+
})
5157+
5158+
t.Run("transfer encoding chunked", func(t *testing.T) {
5159+
t.Parallel()
5160+
ctx := acquire(t)
5161+
setTransferEncoding(t, ctx, "chunked")
5162+
require.True(t, ctx.HasBody())
5163+
})
5164+
5165+
t.Run("transfer encoding whitespace", func(t *testing.T) {
5166+
t.Parallel()
5167+
ctx := acquire(t)
5168+
setTransferEncoding(t, ctx, " ChUnKeD ")
5169+
require.True(t, ctx.HasBody())
5170+
})
5171+
5172+
t.Run("transfer encoding parameters", func(t *testing.T) {
5173+
t.Parallel()
5174+
ctx := acquire(t)
5175+
setTransferEncoding(t, ctx, "chunked; q=1")
5176+
require.True(t, ctx.HasBody())
5177+
})
5178+
5179+
t.Run("transfer encoding multiple values", func(t *testing.T) {
5180+
t.Parallel()
5181+
ctx := acquire(t)
5182+
setTransferEncoding(t, ctx, "gzip, chunked")
5183+
require.True(t, ctx.HasBody())
5184+
})
5185+
5186+
t.Run("transfer encoding identity", func(t *testing.T) {
5187+
t.Parallel()
5188+
ctx := acquire(t)
5189+
setTransferEncoding(t, ctx, "identity")
5190+
require.False(t, ctx.HasBody())
5191+
})
5192+
5193+
t.Run("transfer encoding identity then chunked", func(t *testing.T) {
5194+
t.Parallel()
5195+
ctx := acquire(t)
5196+
setTransferEncoding(t, ctx, "identity, chunked")
5197+
require.True(t, ctx.HasBody())
5198+
})
5199+
5200+
t.Run("no body", func(t *testing.T) {
5201+
t.Parallel()
5202+
ctx := acquire(t)
5203+
require.False(t, ctx.HasBody())
5204+
})
51325205
}
51335206

51345207
func Test_Ctx_IsWebSocket(t *testing.T) {

middleware/cache/manager_msgp.go

Lines changed: 6 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/cache/manager_msgp_test.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/csrf/storage_manager_msgp.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/csrf/storage_manager_msgp_test.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/idempotency/response_msgp.go

Lines changed: 6 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/idempotency/response_msgp_test.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

middleware/limiter/manager_msgp.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)