body_limit.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package middleware
  2. import (
  3. "fmt"
  4. "io"
  5. "sync"
  6. "github.com/labstack/echo"
  7. "github.com/labstack/gommon/bytes"
  8. )
  9. type (
  10. // BodyLimitConfig defines the config for BodyLimit middleware.
  11. BodyLimitConfig struct {
  12. // Skipper defines a function to skip middleware.
  13. Skipper Skipper
  14. // Maximum allowed size for a request body, it can be specified
  15. // as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P.
  16. Limit string `yaml:"limit"`
  17. limit int64
  18. }
  19. limitedReader struct {
  20. BodyLimitConfig
  21. reader io.ReadCloser
  22. read int64
  23. context echo.Context
  24. }
  25. )
  26. var (
  27. // DefaultBodyLimitConfig is the default BodyLimit middleware config.
  28. DefaultBodyLimitConfig = BodyLimitConfig{
  29. Skipper: DefaultSkipper,
  30. }
  31. )
  32. // BodyLimit returns a BodyLimit middleware.
  33. //
  34. // BodyLimit middleware sets the maximum allowed size for a request body, if the
  35. // size exceeds the configured limit, it sends "413 - Request Entity Too Large"
  36. // response. The BodyLimit is determined based on both `Content-Length` request
  37. // header and actual content read, which makes it super secure.
  38. // Limit can be specified as `4x` or `4xB`, where x is one of the multiple from K, M,
  39. // G, T or P.
  40. func BodyLimit(limit string) echo.MiddlewareFunc {
  41. c := DefaultBodyLimitConfig
  42. c.Limit = limit
  43. return BodyLimitWithConfig(c)
  44. }
  45. // BodyLimitWithConfig returns a BodyLimit middleware with config.
  46. // See: `BodyLimit()`.
  47. func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
  48. // Defaults
  49. if config.Skipper == nil {
  50. config.Skipper = DefaultBodyLimitConfig.Skipper
  51. }
  52. limit, err := bytes.Parse(config.Limit)
  53. if err != nil {
  54. panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit))
  55. }
  56. config.limit = limit
  57. pool := limitedReaderPool(config)
  58. return func(next echo.HandlerFunc) echo.HandlerFunc {
  59. return func(c echo.Context) error {
  60. if config.Skipper(c) {
  61. return next(c)
  62. }
  63. req := c.Request()
  64. // Based on content length
  65. if req.ContentLength > config.limit {
  66. return echo.ErrStatusRequestEntityTooLarge
  67. }
  68. // Based on content read
  69. r := pool.Get().(*limitedReader)
  70. r.Reset(req.Body, c)
  71. defer pool.Put(r)
  72. req.Body = r
  73. return next(c)
  74. }
  75. }
  76. }
  77. func (r *limitedReader) Read(b []byte) (n int, err error) {
  78. n, err = r.reader.Read(b)
  79. r.read += int64(n)
  80. if r.read > r.limit {
  81. return n, echo.ErrStatusRequestEntityTooLarge
  82. }
  83. return
  84. }
  85. func (r *limitedReader) Close() error {
  86. return r.reader.Close()
  87. }
  88. func (r *limitedReader) Reset(reader io.ReadCloser, context echo.Context) {
  89. r.reader = reader
  90. r.context = context
  91. r.read = 0
  92. }
  93. func limitedReaderPool(c BodyLimitConfig) sync.Pool {
  94. return sync.Pool{
  95. New: func() interface{} {
  96. return &limitedReader{BodyLimitConfig: c}
  97. },
  98. }
  99. }