read.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. package imagedata
  2. import (
  3. "io"
  4. "github.com/imgproxy/imgproxy/v3/bufpool"
  5. "github.com/imgproxy/imgproxy/v3/bufreader"
  6. "github.com/imgproxy/imgproxy/v3/config"
  7. "github.com/imgproxy/imgproxy/v3/ierrors"
  8. "github.com/imgproxy/imgproxy/v3/imagemeta"
  9. "github.com/imgproxy/imgproxy/v3/security"
  10. )
  11. var (
  12. ErrSourceFileTooBig = ierrors.New(422, "Source image file is too big", "Invalid source image")
  13. ErrSourceImageTypeNotSupported = ierrors.New(422, "Source image type not supported", "Invalid source image")
  14. )
  15. var downloadBufPool *bufpool.Pool
  16. func initRead() {
  17. downloadBufPool = bufpool.New("download", config.Concurrency, config.DownloadBufferSize)
  18. }
  19. type hardLimitReader struct {
  20. r io.Reader
  21. left int
  22. }
  23. func (lr *hardLimitReader) Read(p []byte) (n int, err error) {
  24. if lr.left <= 0 {
  25. return 0, ErrSourceFileTooBig
  26. }
  27. if len(p) > lr.left {
  28. p = p[0:lr.left]
  29. }
  30. n, err = lr.r.Read(p)
  31. lr.left -= n
  32. return
  33. }
  34. func readAndCheckImage(r io.Reader, contentLength int) (*ImageData, error) {
  35. if config.MaxSrcFileSize > 0 && contentLength > config.MaxSrcFileSize {
  36. return nil, ErrSourceFileTooBig
  37. }
  38. buf := downloadBufPool.Get(contentLength)
  39. cancel := func() { downloadBufPool.Put(buf) }
  40. if config.MaxSrcFileSize > 0 {
  41. r = &hardLimitReader{r: r, left: config.MaxSrcFileSize}
  42. }
  43. br := bufreader.New(r, buf)
  44. meta, err := imagemeta.DecodeMeta(br)
  45. if err == imagemeta.ErrFormat {
  46. return nil, ErrSourceImageTypeNotSupported
  47. }
  48. if err != nil {
  49. return nil, checkTimeoutErr(err)
  50. }
  51. if err = security.CheckDimensions(meta.Width(), meta.Height()); err != nil {
  52. return nil, err
  53. }
  54. if err = br.Flush(); err != nil {
  55. cancel()
  56. return nil, checkTimeoutErr(err)
  57. }
  58. return &ImageData{
  59. Data: buf.Bytes(),
  60. Type: meta.Format(),
  61. cancel: cancel,
  62. }, nil
  63. }