diff --git a/context.go b/context.go index 1dc730e3..3c5f6c04 100644 --- a/context.go +++ b/context.go @@ -940,6 +940,7 @@ func (c *Context) ShouldBindUri(obj any) error { // ShouldBindWith binds the passed struct pointer using the specified binding engine. // See the binding package. func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { + c.limitRequestBody() return b.Bind(c.Request, obj) } @@ -949,6 +950,7 @@ func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { // NOTE: This method reads the body before binding. So you should use // ShouldBindWith for better performance if you need to call only once. func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) { + c.limitRequestBody() var body []byte if cb, ok := c.Get(BodyBytesKey); ok { if cbb, ok := cb.([]byte); ok { @@ -965,6 +967,19 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error return bb.BindBody(body, obj) } +// limitRequestBody wraps c.Request.Body in place so that reading more than +// c.engine.MaxRequestBodyBytes from it fails with *http.MaxBytesError, which +// MustBindWith already maps to a 413 response. +func (c *Context) limitRequestBody() { + if c.engine == nil || c.engine.MaxRequestBodyBytes <= 0 { + return + } + if c.Request == nil || c.Request.Body == nil { + return + } + c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, c.engine.MaxRequestBodyBytes) +} + // ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). func (c *Context) ShouldBindBodyWithJSON(obj any) error { return c.ShouldBindBodyWith(obj, binding.JSON) diff --git a/gin.go b/gin.go index 2e033bf3..b0922b9a 100644 --- a/gin.go +++ b/gin.go @@ -23,10 +23,11 @@ import ( ) const ( - defaultMultipartMemory = 32 << 20 // 32 MB - escapedColon = "\\:" - colon = ":" - backslash = "\\" + defaultMultipartMemory = 32 << 20 // 32 MB + defaultMaxRequestBodyBytes = 128 << 20 // 128 MiB + escapedColon = "\\:" + colon = ":" + backslash = "\\" ) var ( @@ -166,6 +167,13 @@ type Engine struct { // method call. MaxMultipartMemory int64 + // MaxRequestBodyBytes limits how many bytes Context's Bind*/ShouldBind*/ + // ShouldBindBodyWith* methods will read from a request body before failing + // with a 413. It protects against a client sending an oversized or + // malformed body that would otherwise be buffered into memory in full + // before being rejected. Set to <= 0 to disable and read bodies unbounded. + MaxRequestBodyBytes int64 + // UseH2C enable h2c support. UseH2C bool @@ -219,6 +227,7 @@ func New(opts ...OptionFunc) *Engine { RemoveExtraSlash: false, UnescapePathValues: true, MaxMultipartMemory: defaultMultipartMemory, + MaxRequestBodyBytes: defaultMaxRequestBodyBytes, trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJSONPrefix: "while(1);",