image_data_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. package imagedatanew
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "encoding/base64"
  6. "fmt"
  7. "io"
  8. "net"
  9. "net/http"
  10. "net/http/httptest"
  11. "os"
  12. "strconv"
  13. "testing"
  14. "github.com/stretchr/testify/suite"
  15. "github.com/imgproxy/imgproxy/v3/config"
  16. "github.com/imgproxy/imgproxy/v3/ierrors"
  17. "github.com/imgproxy/imgproxy/v3/imagefetcher"
  18. "github.com/imgproxy/imgproxy/v3/imagetype"
  19. "github.com/imgproxy/imgproxy/v3/security"
  20. "github.com/imgproxy/imgproxy/v3/transport"
  21. )
  22. type ImageDataTestSuite struct {
  23. suite.Suite
  24. server *httptest.Server
  25. fetcher *imagefetcher.Fetcher
  26. status int
  27. data []byte
  28. header http.Header
  29. check func(*http.Request)
  30. defaultData []byte
  31. }
  32. func (s *ImageDataTestSuite) SetupSuite() {
  33. config.Reset()
  34. config.ClientKeepAliveTimeout = 0
  35. f, err := os.Open("../testdata/test1.jpg")
  36. s.Require().NoError(err)
  37. defer f.Close()
  38. data, err := io.ReadAll(f)
  39. s.Require().NoError(err)
  40. s.defaultData = data
  41. tr, err := transport.NewTransport()
  42. s.Require().NoError(err)
  43. s.fetcher, err = imagefetcher.NewFetcher(tr, imagefetcher.NewConfigFromEnv())
  44. s.Require().NoError(err)
  45. s.server = httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  46. if s.check != nil {
  47. s.check(r)
  48. }
  49. for k, vv := range s.header {
  50. for _, v := range vv {
  51. rw.Header().Add(k, v)
  52. }
  53. }
  54. data := s.data
  55. if data == nil {
  56. data = s.defaultData
  57. }
  58. rw.Header().Set("Content-Length", strconv.Itoa(len(data)))
  59. rw.WriteHeader(s.status)
  60. rw.Write(data)
  61. }))
  62. }
  63. func (s *ImageDataTestSuite) TearDownSuite() {
  64. s.server.Close()
  65. }
  66. func (s *ImageDataTestSuite) SetupTest() {
  67. config.Reset()
  68. config.AllowLoopbackSourceAddresses = true
  69. s.status = http.StatusOK
  70. s.data = nil
  71. s.check = nil
  72. s.header = http.Header{}
  73. s.header.Set("Content-Type", "image/jpeg")
  74. }
  75. func (s *ImageDataTestSuite) readData(i ImageData) []byte {
  76. d, err := io.ReadAll(i.Reader())
  77. s.Require().NoError(err)
  78. return d
  79. }
  80. // download simulates downloading an image from the given URL and returns ImageData.
  81. func (s *ImageDataTestSuite) download(url string, secopts security.Options) (ImageData, error) {
  82. req, err := s.fetcher.BuildRequest(s.T().Context(), url, make(http.Header), nil)
  83. if err != nil {
  84. defer req.Cancel()
  85. return nil, err
  86. }
  87. res, err := req.FetchImage()
  88. if err != nil {
  89. if res != nil {
  90. res.Body.Close()
  91. }
  92. return nil, err
  93. }
  94. imgdata, err := NewFromResponse(res, secopts)
  95. if err != nil {
  96. if res != nil {
  97. res.Body.Close()
  98. }
  99. return nil, err
  100. }
  101. return imgdata, nil
  102. }
  103. func (s *ImageDataTestSuite) TestDownloadStatusOK() {
  104. imgdata, err := s.download(s.server.URL, security.DefaultOptions()) // Ensure the server is running
  105. s.Require().NoError(err)
  106. defer imgdata.Close()
  107. s.Require().NotNil(imgdata)
  108. s.Require().Equal(s.defaultData, s.readData(imgdata))
  109. s.Require().Equal(imagetype.JPEG, imgdata.Format())
  110. }
  111. func (s *ImageDataTestSuite) TestDownloadStatusPartialContent() {
  112. s.status = http.StatusPartialContent
  113. testCases := []struct {
  114. name string
  115. contentRange string
  116. expectErr bool
  117. }{
  118. {
  119. name: "Full Content-Range",
  120. contentRange: fmt.Sprintf("bytes 0-%d/%d", len(s.defaultData)-1, len(s.defaultData)),
  121. expectErr: false,
  122. },
  123. {
  124. name: "Partial Content-Range, early end",
  125. contentRange: fmt.Sprintf("bytes 0-%d/%d", len(s.defaultData)-2, len(s.defaultData)),
  126. expectErr: true,
  127. },
  128. {
  129. name: "Partial Content-Range, late start",
  130. contentRange: fmt.Sprintf("bytes 1-%d/%d", len(s.defaultData)-1, len(s.defaultData)),
  131. expectErr: true,
  132. },
  133. {
  134. name: "Zero Content-Range",
  135. contentRange: "bytes 0-0/0",
  136. expectErr: true,
  137. },
  138. {
  139. name: "Invalid Content-Range",
  140. contentRange: "invalid",
  141. expectErr: true,
  142. },
  143. {
  144. name: "Unknown Content-Range range",
  145. contentRange: fmt.Sprintf("bytes */%d", len(s.defaultData)),
  146. expectErr: true,
  147. },
  148. {
  149. name: "Unknown Content-Range size, full range",
  150. contentRange: fmt.Sprintf("bytes 0-%d/*", len(s.defaultData)-1),
  151. expectErr: false,
  152. },
  153. {
  154. name: "Unknown Content-Range size, early end",
  155. contentRange: fmt.Sprintf("bytes 0-%d/*", len(s.defaultData)-2),
  156. expectErr: true,
  157. },
  158. {
  159. name: "Unknown Content-Range size, late start",
  160. contentRange: fmt.Sprintf("bytes 1-%d/*", len(s.defaultData)-1),
  161. expectErr: true,
  162. },
  163. }
  164. for _, tc := range testCases {
  165. s.Run(tc.name, func() {
  166. s.header.Set("Content-Range", tc.contentRange)
  167. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  168. if tc.expectErr {
  169. s.Require().Error(err)
  170. s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
  171. } else {
  172. s.Require().NoError(err)
  173. defer imgdata.Close()
  174. s.Require().NotNil(imgdata)
  175. s.Require().Equal(s.defaultData, s.readData(imgdata))
  176. s.Require().Equal(imagetype.JPEG, imgdata.Format())
  177. }
  178. })
  179. }
  180. }
  181. func (s *ImageDataTestSuite) TestDownloadStatusNotFound() {
  182. s.status = http.StatusNotFound
  183. s.data = []byte("Not Found")
  184. s.header.Set("Content-Type", "text/plain")
  185. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  186. s.Require().Error(err)
  187. s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
  188. s.Require().Nil(imgdata)
  189. }
  190. func (s *ImageDataTestSuite) TestDownloadStatusForbidden() {
  191. s.status = http.StatusForbidden
  192. s.data = []byte("Forbidden")
  193. s.header.Set("Content-Type", "text/plain")
  194. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  195. s.Require().Error(err)
  196. s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
  197. s.Require().Nil(imgdata)
  198. }
  199. func (s *ImageDataTestSuite) TestDownloadStatusInternalServerError() {
  200. s.status = http.StatusInternalServerError
  201. s.data = []byte("Internal Server Error")
  202. s.header.Set("Content-Type", "text/plain")
  203. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  204. s.Require().Error(err)
  205. s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode())
  206. s.Require().Nil(imgdata)
  207. }
  208. func (s *ImageDataTestSuite) TestDownloadUnreachable() {
  209. l, err := net.Listen("tcp", "127.0.0.1:0")
  210. s.Require().NoError(err)
  211. l.Close()
  212. serverURL := fmt.Sprintf("http://%s", l.Addr().String())
  213. imgdata, err := s.download(serverURL, security.DefaultOptions())
  214. s.Require().Error(err)
  215. s.Require().Equal(500, ierrors.Wrap(err, 0).StatusCode())
  216. s.Require().Nil(imgdata)
  217. }
  218. func (s *ImageDataTestSuite) TestDownloadInvalidImage() {
  219. s.data = []byte("invalid")
  220. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  221. s.Require().Error(err)
  222. s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
  223. s.Require().Nil(imgdata)
  224. }
  225. func (s *ImageDataTestSuite) TestDownloadSourceAddressNotAllowed() {
  226. config.AllowLoopbackSourceAddresses = false
  227. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  228. s.Require().Error(err)
  229. s.Require().Equal(404, ierrors.Wrap(err, 0).StatusCode())
  230. s.Require().Nil(imgdata)
  231. }
  232. func (s *ImageDataTestSuite) TestDownloadImageTooLarge() {
  233. config.MaxSrcResolution = 1
  234. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  235. s.Require().Error(err)
  236. s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
  237. s.Require().Nil(imgdata)
  238. }
  239. func (s *ImageDataTestSuite) TestDownloadImageFileTooLarge() {
  240. config.MaxSrcFileSize = 1
  241. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  242. s.Require().Error(err)
  243. s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode())
  244. s.Require().Nil(imgdata)
  245. }
  246. func (s *ImageDataTestSuite) TestDownloadGzip() {
  247. buf := new(bytes.Buffer)
  248. enc := gzip.NewWriter(buf)
  249. _, err := enc.Write(s.defaultData)
  250. s.Require().NoError(err)
  251. err = enc.Close()
  252. s.Require().NoError(err)
  253. s.data = buf.Bytes()
  254. s.header.Set("Content-Encoding", "gzip")
  255. imgdata, err := s.download(s.server.URL, security.DefaultOptions())
  256. s.Require().NoError(err)
  257. defer imgdata.Close()
  258. s.Require().NotNil(imgdata)
  259. s.Require().Equal(s.defaultData, s.readData(imgdata))
  260. s.Require().Equal(imagetype.JPEG, imgdata.Format())
  261. }
  262. func (s *ImageDataTestSuite) TestFromFile() {
  263. imgdata, err := NewFromFile("../testdata/test1.jpg", http.Header{}, security.DefaultOptions())
  264. s.Require().NoError(err)
  265. s.Require().NotNil(imgdata)
  266. s.Require().Equal(s.defaultData, s.readData(imgdata))
  267. s.Require().Equal(imagetype.JPEG, imgdata.Format())
  268. }
  269. func (s *ImageDataTestSuite) TestFromBase64() {
  270. b64 := base64.StdEncoding.EncodeToString(s.defaultData)
  271. imgdata, err := NewFromBase64(b64, http.Header{}, security.DefaultOptions())
  272. s.Require().NoError(err)
  273. s.Require().NotNil(imgdata)
  274. s.Require().Equal(s.defaultData, s.readData(imgdata))
  275. s.Require().Equal(imagetype.JPEG, imgdata.Format())
  276. }
  277. func TestImageData(t *testing.T) {
  278. suite.Run(t, new(ImageDataTestSuite))
  279. }