processing_handler_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. package main
  2. // NOTE: this test is the integration test for the processing handler. We can't extract and
  3. // move it to handlers package yet because it depends on the global routes, methods and
  4. // initialization functions. Once those would we wrapped into structures, we'll be able to move this test
  5. // to where it belongs.
  6. import (
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/http/httptest"
  11. "os"
  12. "path/filepath"
  13. "regexp"
  14. "testing"
  15. "time"
  16. "github.com/sirupsen/logrus"
  17. "github.com/stretchr/testify/suite"
  18. "github.com/imgproxy/imgproxy/v3/config"
  19. "github.com/imgproxy/imgproxy/v3/config/configurators"
  20. "github.com/imgproxy/imgproxy/v3/fetcher"
  21. "github.com/imgproxy/imgproxy/v3/httpheaders"
  22. "github.com/imgproxy/imgproxy/v3/imagedata"
  23. "github.com/imgproxy/imgproxy/v3/imagetype"
  24. "github.com/imgproxy/imgproxy/v3/server"
  25. "github.com/imgproxy/imgproxy/v3/svg"
  26. "github.com/imgproxy/imgproxy/v3/testutil"
  27. "github.com/imgproxy/imgproxy/v3/transport"
  28. "github.com/imgproxy/imgproxy/v3/vips"
  29. )
  30. type ProcessingHandlerTestSuite struct {
  31. suite.Suite
  32. router *server.Router
  33. }
  34. func (s *ProcessingHandlerTestSuite) SetupSuite() {
  35. config.Reset()
  36. wd, err := os.Getwd()
  37. s.Require().NoError(err)
  38. s.T().Setenv("IMGPROXY_LOCAL_FILESYSTEM_ROOT", filepath.Join(wd, "/testdata"))
  39. s.T().Setenv("IMGPROXY_CLIENT_KEEP_ALIVE_TIMEOUT", "0")
  40. err = initialize()
  41. s.Require().NoError(err)
  42. logrus.SetOutput(io.Discard)
  43. cfg := server.NewDefaultConfig()
  44. r, err := server.NewRouter(cfg)
  45. s.Require().NoError(err)
  46. s.router = buildRouter(r)
  47. }
  48. func (s *ProcessingHandlerTestSuite) TeardownSuite() {
  49. shutdown()
  50. logrus.SetOutput(os.Stdout)
  51. }
  52. func (s *ProcessingHandlerTestSuite) SetupTest() {
  53. wd, err := os.Getwd()
  54. s.Require().NoError(err)
  55. config.Reset()
  56. config.AllowLoopbackSourceAddresses = true
  57. config.LocalFileSystemRoot = filepath.Join(wd, "/testdata")
  58. config.ClientKeepAliveTimeout = 0
  59. }
  60. func (s *ProcessingHandlerTestSuite) send(path string, header ...http.Header) *httptest.ResponseRecorder {
  61. req := httptest.NewRequest(http.MethodGet, path, nil)
  62. rw := httptest.NewRecorder()
  63. if len(header) > 0 {
  64. req.Header = header[0]
  65. }
  66. s.router.ServeHTTP(rw, req)
  67. return rw
  68. }
  69. func (s *ProcessingHandlerTestSuite) readTestFile(name string) []byte {
  70. wd, err := os.Getwd()
  71. s.Require().NoError(err)
  72. data, err := os.ReadFile(filepath.Join(wd, "testdata", name))
  73. s.Require().NoError(err)
  74. return data
  75. }
  76. func (s *ProcessingHandlerTestSuite) readTestImageData(name string) imagedata.ImageData {
  77. wd, err := os.Getwd()
  78. s.Require().NoError(err)
  79. data, err := os.ReadFile(filepath.Join(wd, "testdata", name))
  80. s.Require().NoError(err)
  81. // NOTE: Temporary workaround. We create imagedata.Factory here
  82. // because currently configuration is changed via env vars
  83. // or config. We need to pick up those config changes.
  84. // This will be addressed in the next PR
  85. trc, err := transport.LoadFromEnv(transport.NewDefaultConfig())
  86. s.Require().NoError(err)
  87. tr, err := transport.New(trc)
  88. s.Require().NoError(err)
  89. fc, err := fetcher.LoadFromEnv(fetcher.NewDefaultConfig())
  90. s.Require().NoError(err)
  91. f, err := fetcher.New(tr, fc)
  92. s.Require().NoError(err)
  93. idf := imagedata.NewFactory(f)
  94. // end of workaround
  95. imgdata, err := idf.NewFromBytes(data)
  96. s.Require().NoError(err)
  97. return imgdata
  98. }
  99. func (s *ProcessingHandlerTestSuite) TestRequest() {
  100. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  101. res := rw.Result()
  102. s.Require().Equal(200, res.StatusCode)
  103. s.Require().Equal("image/png", res.Header.Get("Content-Type"))
  104. format, err := imagetype.Detect(res.Body)
  105. s.Require().NoError(err)
  106. s.Require().Equal(imagetype.PNG, format)
  107. }
  108. func (s *ProcessingHandlerTestSuite) TestSignatureValidationFailure() {
  109. config.Keys = [][]byte{[]byte("test-key")}
  110. config.Salts = [][]byte{[]byte("test-salt")}
  111. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  112. res := rw.Result()
  113. s.Require().Equal(403, res.StatusCode)
  114. }
  115. func (s *ProcessingHandlerTestSuite) TestSignatureValidationSuccess() {
  116. config.Keys = [][]byte{[]byte("test-key")}
  117. config.Salts = [][]byte{[]byte("test-salt")}
  118. rw := s.send("/My9d3xq_PYpVHsPrCyww0Kh1w5KZeZhIlWhsa4az1TI/rs:fill:4:4/plain/local:///test1.png")
  119. res := rw.Result()
  120. s.Require().Equal(200, res.StatusCode)
  121. }
  122. func (s *ProcessingHandlerTestSuite) TestSourceValidation() {
  123. imagedata.RedirectAllRequestsTo("local:///test1.png")
  124. defer imagedata.StopRedirectingRequests()
  125. tt := []struct {
  126. name string
  127. allowedSources []string
  128. requestPath string
  129. expectedError bool
  130. }{
  131. {
  132. name: "match http URL without wildcard",
  133. allowedSources: []string{"local://", "http://images.dev/"},
  134. requestPath: "/unsafe/plain/http://images.dev/lorem/ipsum.jpg",
  135. expectedError: false,
  136. },
  137. {
  138. name: "match http URL with wildcard in hostname single level",
  139. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  140. requestPath: "/unsafe/plain/http://a-1.mycdn.dev/lorem/ipsum.jpg",
  141. expectedError: false,
  142. },
  143. {
  144. name: "match http URL with wildcard in hostname multiple levels",
  145. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  146. requestPath: "/unsafe/plain/http://a-1.b-2.mycdn.dev/lorem/ipsum.jpg",
  147. expectedError: false,
  148. },
  149. {
  150. name: "no match s3 URL with allowed local and http URLs",
  151. allowedSources: []string{"local://", "http://images.dev/"},
  152. requestPath: "/unsafe/plain/s3://images/lorem/ipsum.jpg",
  153. expectedError: true,
  154. },
  155. {
  156. name: "no match http URL with wildcard in hostname including slash",
  157. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  158. requestPath: "/unsafe/plain/http://other.dev/.mycdn.dev/lorem/ipsum.jpg",
  159. expectedError: true,
  160. },
  161. }
  162. for _, tc := range tt {
  163. s.Run(tc.name, func() {
  164. exps := make([]*regexp.Regexp, len(tc.allowedSources))
  165. for i, pattern := range tc.allowedSources {
  166. exps[i] = configurators.RegexpFromPattern(pattern)
  167. }
  168. config.AllowedSources = exps
  169. rw := s.send(tc.requestPath)
  170. res := rw.Result()
  171. if tc.expectedError {
  172. s.Require().Equal(404, res.StatusCode)
  173. } else {
  174. s.Require().Equal(200, res.StatusCode)
  175. }
  176. })
  177. }
  178. }
  179. func (s *ProcessingHandlerTestSuite) TestSourceNetworkValidation() {
  180. data := s.readTestFile("test1.png")
  181. server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  182. rw.WriteHeader(200)
  183. rw.Write(data)
  184. }))
  185. defer server.Close()
  186. var rw *httptest.ResponseRecorder
  187. u := fmt.Sprintf("/unsafe/rs:fill:4:4/plain/%s/test1.png", server.URL)
  188. rw = s.send(u)
  189. s.Require().Equal(200, rw.Result().StatusCode)
  190. config.AllowLoopbackSourceAddresses = false
  191. rw = s.send(u)
  192. s.Require().Equal(404, rw.Result().StatusCode)
  193. }
  194. func (s *ProcessingHandlerTestSuite) TestSourceFormatNotSupported() {
  195. vips.DisableLoadSupport(imagetype.PNG)
  196. defer vips.ResetLoadSupport()
  197. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  198. res := rw.Result()
  199. s.Require().Equal(422, res.StatusCode)
  200. }
  201. func (s *ProcessingHandlerTestSuite) TestResultingFormatNotSupported() {
  202. vips.DisableSaveSupport(imagetype.PNG)
  203. defer vips.ResetSaveSupport()
  204. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png@png")
  205. res := rw.Result()
  206. s.Require().Equal(422, res.StatusCode)
  207. }
  208. func (s *ProcessingHandlerTestSuite) TestSkipProcessingConfig() {
  209. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  210. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  211. res := rw.Result()
  212. s.Require().Equal(200, res.StatusCode)
  213. expected := s.readTestImageData("test1.png")
  214. s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  215. }
  216. func (s *ProcessingHandlerTestSuite) TestSkipProcessingPO() {
  217. rw := s.send("/unsafe/rs:fill:4:4/skp:png/plain/local:///test1.png")
  218. res := rw.Result()
  219. s.Require().Equal(200, res.StatusCode)
  220. expected := s.readTestImageData("test1.png")
  221. s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  222. }
  223. func (s *ProcessingHandlerTestSuite) TestSkipProcessingSameFormat() {
  224. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  225. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png@png")
  226. res := rw.Result()
  227. s.Require().Equal(200, res.StatusCode)
  228. expected := s.readTestImageData("test1.png")
  229. s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  230. }
  231. func (s *ProcessingHandlerTestSuite) TestSkipProcessingDifferentFormat() {
  232. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  233. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png@jpg")
  234. res := rw.Result()
  235. s.Require().Equal(200, res.StatusCode)
  236. expected := s.readTestImageData("test1.png")
  237. s.Require().False(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  238. }
  239. func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
  240. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg")
  241. res := rw.Result()
  242. s.Require().Equal(200, res.StatusCode)
  243. expected, err := svg.Sanitize(s.readTestImageData("test1.svg"))
  244. s.Require().NoError(err)
  245. s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  246. }
  247. func (s *ProcessingHandlerTestSuite) TestNotSkipProcessingSVGToJPG() {
  248. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg@jpg")
  249. res := rw.Result()
  250. s.Require().Equal(200, res.StatusCode)
  251. expected := s.readTestImageData("test1.svg")
  252. s.Require().False(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  253. }
  254. func (s *ProcessingHandlerTestSuite) TestErrorSavingToSVG() {
  255. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png@svg")
  256. res := rw.Result()
  257. s.Require().Equal(422, res.StatusCode)
  258. }
  259. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughCacheControl() {
  260. config.CacheControlPassthrough = true
  261. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  262. rw.Header().Set("Cache-Control", "max-age=1234, public")
  263. rw.Header().Set("Expires", time.Now().Add(time.Hour).UTC().Format(http.TimeFormat))
  264. rw.WriteHeader(200)
  265. rw.Write(s.readTestFile("test1.png"))
  266. }))
  267. defer ts.Close()
  268. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  269. res := rw.Result()
  270. s.Require().Equal("max-age=1234, public", res.Header.Get("Cache-Control"))
  271. s.Require().Empty(res.Header.Get("Expires"))
  272. }
  273. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughExpires() {
  274. config.CacheControlPassthrough = true
  275. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  276. rw.Header().Set("Expires", time.Now().Add(1239*time.Second).UTC().Format(http.TimeFormat))
  277. rw.WriteHeader(200)
  278. rw.Write(s.readTestFile("test1.png"))
  279. }))
  280. defer ts.Close()
  281. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  282. res := rw.Result()
  283. // Use regex to allow some delay
  284. s.Require().Regexp("max-age=123[0-9], public", res.Header.Get("Cache-Control"))
  285. s.Require().Empty(res.Header.Get("Expires"))
  286. }
  287. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughDisabled() {
  288. config.CacheControlPassthrough = false
  289. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  290. rw.Header().Set("Cache-Control", "max-age=1234, public")
  291. rw.Header().Set("Expires", time.Now().Add(time.Hour).UTC().Format(http.TimeFormat))
  292. rw.WriteHeader(200)
  293. rw.Write(s.readTestFile("test1.png"))
  294. }))
  295. defer ts.Close()
  296. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  297. res := rw.Result()
  298. s.Require().NotEqual("max-age=1234, public", res.Header.Get("Cache-Control"))
  299. s.Require().Empty(res.Header.Get("Expires"))
  300. }
  301. func (s *ProcessingHandlerTestSuite) TestETagDisabled() {
  302. config.ETagEnabled = false
  303. rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  304. res := rw.Result()
  305. s.Require().Equal(200, res.StatusCode)
  306. s.Require().Empty(res.Header.Get("ETag"))
  307. }
  308. func (s *ProcessingHandlerTestSuite) TestETagDataMatch() {
  309. config.ETagEnabled = true
  310. etag := `"loremipsumdolor"`
  311. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  312. s.NotEmpty(r.Header.Get(httpheaders.IfNoneMatch))
  313. rw.Header().Set(httpheaders.Etag, etag)
  314. rw.WriteHeader(http.StatusNotModified)
  315. }))
  316. defer ts.Close()
  317. header := make(http.Header)
  318. header.Set(httpheaders.IfNoneMatch, etag)
  319. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  320. res := rw.Result()
  321. s.Require().Equal(304, res.StatusCode)
  322. s.Require().Equal(etag, res.Header.Get(httpheaders.Etag))
  323. }
  324. func (s *ProcessingHandlerTestSuite) TestLastModifiedEnabled() {
  325. config.LastModifiedEnabled = true
  326. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  327. rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT")
  328. rw.WriteHeader(200)
  329. rw.Write(s.readTestFile("test1.png"))
  330. }))
  331. defer ts.Close()
  332. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  333. res := rw.Result()
  334. s.Require().Equal("Wed, 21 Oct 2015 07:28:00 GMT", res.Header.Get("Last-Modified"))
  335. }
  336. func (s *ProcessingHandlerTestSuite) TestLastModifiedDisabled() {
  337. config.LastModifiedEnabled = false
  338. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  339. rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT")
  340. rw.WriteHeader(200)
  341. rw.Write(s.readTestFile("test1.png"))
  342. }))
  343. defer ts.Close()
  344. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  345. res := rw.Result()
  346. s.Require().Empty(res.Header.Get("Last-Modified"))
  347. }
  348. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedDisabled() {
  349. config.LastModifiedEnabled = false
  350. data := s.readTestFile("test1.png")
  351. lastModified := "Wed, 21 Oct 2015 07:28:00 GMT"
  352. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  353. modifiedSince := r.Header.Get("If-Modified-Since")
  354. s.Empty(modifiedSince)
  355. rw.WriteHeader(200)
  356. rw.Write(data)
  357. }))
  358. defer ts.Close()
  359. header := make(http.Header)
  360. header.Set("If-Modified-Since", lastModified)
  361. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  362. res := rw.Result()
  363. s.Require().Equal(200, res.StatusCode)
  364. }
  365. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedEnabled() {
  366. config.LastModifiedEnabled = true
  367. lastModified := "Wed, 21 Oct 2015 07:28:00 GMT"
  368. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  369. modifiedSince := r.Header.Get("If-Modified-Since")
  370. s.Equal(lastModified, modifiedSince)
  371. rw.WriteHeader(304)
  372. }))
  373. defer ts.Close()
  374. header := make(http.Header)
  375. header.Set("If-Modified-Since", lastModified)
  376. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  377. res := rw.Result()
  378. s.Require().Equal(304, res.StatusCode)
  379. }
  380. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastModifiedDisabled() {
  381. data := s.readTestFile("test1.png")
  382. config.LastModifiedEnabled = false
  383. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  384. modifiedSince := r.Header.Get("If-Modified-Since")
  385. s.Empty(modifiedSince)
  386. rw.WriteHeader(200)
  387. rw.Write(data)
  388. }))
  389. defer ts.Close()
  390. recentTimestamp := "Thu, 25 Feb 2021 01:45:00 GMT"
  391. header := make(http.Header)
  392. header.Set("If-Modified-Since", recentTimestamp)
  393. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  394. res := rw.Result()
  395. s.Require().Equal(200, res.StatusCode)
  396. }
  397. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastModifiedEnabled() {
  398. config.LastModifiedEnabled = true
  399. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  400. fileLastModified, _ := time.Parse(http.TimeFormat, "Wed, 21 Oct 2015 07:28:00 GMT")
  401. modifiedSince := r.Header.Get("If-Modified-Since")
  402. parsedModifiedSince, err := time.Parse(http.TimeFormat, modifiedSince)
  403. s.NoError(err)
  404. s.True(fileLastModified.Before(parsedModifiedSince))
  405. rw.WriteHeader(304)
  406. }))
  407. defer ts.Close()
  408. recentTimestamp := "Thu, 25 Feb 2021 01:45:00 GMT"
  409. header := make(http.Header)
  410. header.Set("If-Modified-Since", recentTimestamp)
  411. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  412. res := rw.Result()
  413. s.Require().Equal(304, res.StatusCode)
  414. }
  415. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedDisabled() {
  416. config.LastModifiedEnabled = false
  417. data := s.readTestFile("test1.png")
  418. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  419. modifiedSince := r.Header.Get("If-Modified-Since")
  420. s.Empty(modifiedSince)
  421. rw.WriteHeader(200)
  422. rw.Write(data)
  423. }))
  424. defer ts.Close()
  425. oldTimestamp := "Tue, 01 Oct 2013 17:31:00 GMT"
  426. header := make(http.Header)
  427. header.Set("If-Modified-Since", oldTimestamp)
  428. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  429. res := rw.Result()
  430. s.Require().Equal(200, res.StatusCode)
  431. }
  432. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedEnabled() {
  433. config.LastModifiedEnabled = true
  434. data := s.readTestFile("test1.png")
  435. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  436. fileLastModified, _ := time.Parse(http.TimeFormat, "Wed, 21 Oct 2015 07:28:00 GMT")
  437. modifiedSince := r.Header.Get("If-Modified-Since")
  438. parsedModifiedSince, err := time.Parse(http.TimeFormat, modifiedSince)
  439. s.NoError(err)
  440. s.True(fileLastModified.After(parsedModifiedSince))
  441. rw.WriteHeader(200)
  442. rw.Write(data)
  443. }))
  444. defer ts.Close()
  445. oldTimestamp := "Tue, 01 Oct 2013 17:31:00 GMT"
  446. header := make(http.Header)
  447. header.Set("If-Modified-Since", oldTimestamp)
  448. rw := s.send(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  449. res := rw.Result()
  450. s.Require().Equal(200, res.StatusCode)
  451. }
  452. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvg() {
  453. config.AlwaysRasterizeSvg = true
  454. rw := s.send("/unsafe/rs:fill:40:40/plain/local:///test1.svg")
  455. res := rw.Result()
  456. s.Require().Equal(200, res.StatusCode)
  457. s.Require().Equal("image/png", res.Header.Get("Content-Type"))
  458. }
  459. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithEnforceAvif() {
  460. config.AlwaysRasterizeSvg = true
  461. config.EnforceWebp = true
  462. rw := s.send("/unsafe/plain/local:///test1.svg", http.Header{"Accept": []string{"image/webp"}})
  463. res := rw.Result()
  464. s.Require().Equal(200, res.StatusCode)
  465. s.Require().Equal("image/webp", res.Header.Get("Content-Type"))
  466. }
  467. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgDisabled() {
  468. config.AlwaysRasterizeSvg = false
  469. config.EnforceWebp = true
  470. rw := s.send("/unsafe/plain/local:///test1.svg")
  471. res := rw.Result()
  472. s.Require().Equal(200, res.StatusCode)
  473. s.Require().Equal("image/svg+xml", res.Header.Get("Content-Type"))
  474. }
  475. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithFormat() {
  476. config.AlwaysRasterizeSvg = true
  477. config.SkipProcessingFormats = []imagetype.Type{imagetype.SVG}
  478. rw := s.send("/unsafe/plain/local:///test1.svg@svg")
  479. res := rw.Result()
  480. s.Require().Equal(200, res.StatusCode)
  481. s.Require().Equal("image/svg+xml", res.Header.Get("Content-Type"))
  482. }
  483. func (s *ProcessingHandlerTestSuite) TestMaxSrcFileSizeGlobal() {
  484. config.MaxSrcFileSize = 1
  485. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  486. rw.WriteHeader(200)
  487. rw.Write(s.readTestFile("test1.png"))
  488. }))
  489. defer ts.Close()
  490. rw := s.send("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  491. res := rw.Result()
  492. s.Require().Equal(422, res.StatusCode)
  493. }
  494. func TestProcessingHandler(t *testing.T) {
  495. suite.Run(t, new(ProcessingHandlerTestSuite))
  496. }