processing_handler_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. package integration
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "net/http/httptest"
  7. "os"
  8. "regexp"
  9. "testing"
  10. "time"
  11. "github.com/imgproxy/imgproxy/v3"
  12. "github.com/imgproxy/imgproxy/v3/config"
  13. "github.com/imgproxy/imgproxy/v3/config/configurators"
  14. "github.com/imgproxy/imgproxy/v3/fetcher"
  15. "github.com/imgproxy/imgproxy/v3/httpheaders"
  16. "github.com/imgproxy/imgproxy/v3/imagedata"
  17. "github.com/imgproxy/imgproxy/v3/imagetype"
  18. "github.com/imgproxy/imgproxy/v3/svg"
  19. "github.com/imgproxy/imgproxy/v3/testutil"
  20. "github.com/imgproxy/imgproxy/v3/vips"
  21. "github.com/sirupsen/logrus"
  22. "github.com/stretchr/testify/suite"
  23. )
  24. // ProcessingHandlerTestSuite is a test suite for testing image processing handler
  25. type ProcessingHandlerTestSuite struct {
  26. Suite
  27. testData *testutil.TestDataProvider
  28. // NOTE: lazy obj is required here because in the specific tests we sometimes
  29. // change the config values in config.go. Config instantiation should
  30. // happen afterwards. It is done via lazy obj. When all config values will be moved
  31. // to imgproxy.Config struct, this can be removed.
  32. config testutil.LazyObj[*imgproxy.Config]
  33. server testutil.LazyObj[*TestServer]
  34. }
  35. func (s *ProcessingHandlerTestSuite) SetupSuite() {
  36. // Silence all the logs
  37. logrus.SetOutput(io.Discard)
  38. // Initialize test data provider (local test files)
  39. s.testData = testutil.NewTestDataProvider(s.T)
  40. s.config, _ = testutil.NewLazySuiteObj(s, func() (*imgproxy.Config, error) {
  41. c, err := imgproxy.LoadConfigFromEnv(nil)
  42. s.Require().NoError(err)
  43. c.Fetcher.Transport.Local.Root = s.testData.Root()
  44. c.Fetcher.Transport.HTTP.ClientKeepAliveTimeout = 0
  45. return c, nil
  46. })
  47. s.server, _ = testutil.NewLazySuiteObj(
  48. s,
  49. func() (*TestServer, error) {
  50. return s.StartImgproxy(s.config()), nil
  51. },
  52. func(s *TestServer) error {
  53. s.Shutdown()
  54. return nil
  55. },
  56. )
  57. }
  58. func (s *ProcessingHandlerTestSuite) TearDownSuite() {
  59. logrus.SetOutput(os.Stdout)
  60. }
  61. func (s *ProcessingHandlerTestSuite) SetupTest() {
  62. config.Reset() // We reset config only at the start of each test
  63. // NOTE: This must be moved to security config
  64. config.AllowLoopbackSourceAddresses = true
  65. // NOTE: end note
  66. }
  67. func (s *ProcessingHandlerTestSuite) SetupSubTest() {
  68. // We use t.Run() a lot, so we need to reset lazy objects at the beginning of each subtest
  69. s.ResetLazyObjects()
  70. }
  71. // GET performs a GET request to the imageproxy real server
  72. // NOTE: Do not forget to move this to Suite in case of need in other future test suites
  73. func (s *ProcessingHandlerTestSuite) GET(path string, header ...http.Header) *http.Response {
  74. url := fmt.Sprintf("http://%s%s", s.server().Addr, path)
  75. // Perform GET request to an url
  76. req, _ := http.NewRequest("GET", url, nil)
  77. for h := range header {
  78. for k, v := range header[h] {
  79. req.Header.Set(k, v[0]) // only first value will go to the request
  80. }
  81. }
  82. // Do the request
  83. resp, err := http.DefaultClient.Do(req)
  84. s.Require().NoError(err)
  85. return resp
  86. }
  87. func (s *ProcessingHandlerTestSuite) TestSignatureValidationFailure() {
  88. config.Keys = [][]byte{[]byte("test-key")}
  89. config.Salts = [][]byte{[]byte("test-salt")}
  90. tt := []struct {
  91. name string
  92. url string
  93. statusCode int
  94. }{
  95. {
  96. name: "NoSignature",
  97. url: "/unsafe/rs:fill:4:4/plain/local:///test1.png",
  98. statusCode: http.StatusForbidden,
  99. },
  100. {
  101. name: "BadSignature",
  102. url: "/bad-signature/rs:fill:4:4/plain/local:///test1.png",
  103. statusCode: http.StatusForbidden,
  104. },
  105. {
  106. name: "ValidSignature",
  107. url: "/My9d3xq_PYpVHsPrCyww0Kh1w5KZeZhIlWhsa4az1TI/rs:fill:4:4/plain/local:///test1.png",
  108. statusCode: http.StatusOK,
  109. },
  110. }
  111. for _, tc := range tt {
  112. s.Run(tc.name, func() {
  113. res := s.GET(tc.url)
  114. s.Require().Equal(tc.statusCode, res.StatusCode)
  115. })
  116. }
  117. }
  118. func (s *ProcessingHandlerTestSuite) TestSourceValidation() {
  119. imagedata.RedirectAllRequestsTo("local:///test1.png")
  120. defer imagedata.StopRedirectingRequests()
  121. tt := []struct {
  122. name string
  123. allowedSources []string
  124. requestPath string
  125. expectedError bool
  126. }{
  127. {
  128. name: "match http URL without wildcard",
  129. allowedSources: []string{"local://", "http://images.dev/"},
  130. requestPath: "/unsafe/plain/http://images.dev/lorem/ipsum.jpg",
  131. },
  132. {
  133. name: "match http URL with wildcard in hostname single level",
  134. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  135. requestPath: "/unsafe/plain/http://a-1.mycdn.dev/lorem/ipsum.jpg",
  136. },
  137. {
  138. name: "match http URL with wildcard in hostname multiple levels",
  139. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  140. requestPath: "/unsafe/plain/http://a-1.b-2.mycdn.dev/lorem/ipsum.jpg",
  141. },
  142. {
  143. name: "no match s3 URL with allowed local and http URLs",
  144. allowedSources: []string{"local://", "http://images.dev/"},
  145. requestPath: "/unsafe/plain/s3://images/lorem/ipsum.jpg",
  146. expectedError: true,
  147. },
  148. {
  149. name: "no match http URL with wildcard in hostname including slash",
  150. allowedSources: []string{"local://", "http://*.mycdn.dev/"},
  151. requestPath: "/unsafe/plain/http://other.dev/.mycdn.dev/lorem/ipsum.jpg",
  152. expectedError: true,
  153. },
  154. }
  155. for _, tc := range tt {
  156. s.Run(tc.name, func() {
  157. config.AllowedSources = make([]*regexp.Regexp, len(tc.allowedSources))
  158. for i, pattern := range tc.allowedSources {
  159. config.AllowedSources[i] = configurators.RegexpFromPattern(pattern)
  160. }
  161. res := s.GET(tc.requestPath)
  162. if tc.expectedError {
  163. s.Require().Equal(http.StatusNotFound, res.StatusCode)
  164. } else {
  165. s.Require().Equal(http.StatusOK, res.StatusCode)
  166. }
  167. })
  168. }
  169. }
  170. func (s *ProcessingHandlerTestSuite) TestSourceNetworkValidation() {
  171. data := s.testData.Read("test1.png")
  172. server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  173. rw.WriteHeader(200)
  174. rw.Write(data)
  175. }))
  176. defer server.Close()
  177. url := fmt.Sprintf("/unsafe/rs:fill:4:4/plain/%s/test1.png", server.URL)
  178. // We wrap this in a subtest to reset s.router()
  179. s.Run("AllowLoopbackSourceAddressesTrue", func() {
  180. config.AllowLoopbackSourceAddresses = true
  181. res := s.GET(url)
  182. s.Require().Equal(http.StatusOK, res.StatusCode)
  183. })
  184. s.Run("AllowLoopbackSourceAddressesFalse", func() {
  185. config.AllowLoopbackSourceAddresses = false
  186. res := s.GET(url)
  187. s.Require().Equal(http.StatusNotFound, res.StatusCode)
  188. })
  189. }
  190. func (s *ProcessingHandlerTestSuite) TestSourceFormatNotSupported() {
  191. vips.DisableLoadSupport(imagetype.PNG)
  192. defer vips.ResetLoadSupport()
  193. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  194. s.Require().Equal(http.StatusUnprocessableEntity, res.StatusCode)
  195. }
  196. func (s *ProcessingHandlerTestSuite) TestResultingFormatNotSupported() {
  197. vips.DisableSaveSupport(imagetype.PNG)
  198. defer vips.ResetSaveSupport()
  199. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png@png")
  200. s.Require().Equal(http.StatusUnprocessableEntity, res.StatusCode)
  201. }
  202. func (s *ProcessingHandlerTestSuite) TestSkipProcessingConfig() {
  203. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  204. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  205. s.Require().Equal(http.StatusOK, res.StatusCode)
  206. s.Require().True(s.testData.FileEqualsToReader("test1.png", res.Body))
  207. }
  208. func (s *ProcessingHandlerTestSuite) TestSkipProcessingPO() {
  209. res := s.GET("/unsafe/rs:fill:4:4/skp:png/plain/local:///test1.png")
  210. s.Require().Equal(http.StatusOK, res.StatusCode)
  211. s.Require().True(s.testData.FileEqualsToReader("test1.png", res.Body))
  212. }
  213. func (s *ProcessingHandlerTestSuite) TestSkipProcessingSameFormat() {
  214. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  215. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png@png")
  216. s.Require().Equal(http.StatusOK, res.StatusCode)
  217. s.Require().True(s.testData.FileEqualsToReader("test1.png", res.Body))
  218. }
  219. func (s *ProcessingHandlerTestSuite) TestSkipProcessingDifferentFormat() {
  220. config.SkipProcessingFormats = []imagetype.Type{imagetype.PNG}
  221. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png@jpg")
  222. s.Require().Equal(http.StatusOK, res.StatusCode)
  223. s.Require().False(s.testData.FileEqualsToReader("test1.png", res.Body))
  224. }
  225. func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
  226. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.svg")
  227. s.Require().Equal(http.StatusOK, res.StatusCode)
  228. c := fetcher.NewDefaultConfig()
  229. f, err := fetcher.New(&c)
  230. s.Require().NoError(err)
  231. idf := imagedata.NewFactory(f)
  232. data, err := idf.NewFromBytes(s.testData.Read("test1.svg"))
  233. s.Require().NoError(err)
  234. expected, err := svg.Sanitize(data)
  235. s.Require().NoError(err)
  236. s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))
  237. }
  238. func (s *ProcessingHandlerTestSuite) TestNotSkipProcessingSVGToJPG() {
  239. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.svg@jpg")
  240. s.Require().Equal(http.StatusOK, res.StatusCode)
  241. s.Require().False(s.testData.FileEqualsToReader("test1.svg", res.Body))
  242. }
  243. func (s *ProcessingHandlerTestSuite) TestErrorSavingToSVG() {
  244. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png@svg")
  245. s.Require().Equal(http.StatusUnprocessableEntity, res.StatusCode)
  246. }
  247. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughCacheControl() {
  248. s.config().HeaderWriter.CacheControlPassthrough = true
  249. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  250. rw.Header().Set(httpheaders.CacheControl, "max-age=1234, public")
  251. rw.Header().Set(httpheaders.Expires, time.Now().Add(time.Hour).UTC().Format(http.TimeFormat))
  252. rw.WriteHeader(200)
  253. rw.Write(s.testData.Read("test1.png"))
  254. }))
  255. defer ts.Close()
  256. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  257. s.Require().Equal(http.StatusOK, res.StatusCode)
  258. s.Require().Equal("max-age=1234, public", res.Header.Get(httpheaders.CacheControl))
  259. s.Require().Empty(res.Header.Get(httpheaders.Expires))
  260. }
  261. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughExpires() {
  262. config.CacheControlPassthrough = true
  263. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  264. rw.Header().Set(httpheaders.Expires, time.Now().Add(1239*time.Second).UTC().Format(http.TimeFormat))
  265. rw.WriteHeader(200)
  266. rw.Write(s.testData.Read("test1.png"))
  267. }))
  268. defer ts.Close()
  269. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  270. // Use regex to allow some delay
  271. s.Require().Regexp("max-age=123[0-9], public", res.Header.Get(httpheaders.CacheControl))
  272. s.Require().Empty(res.Header.Get(httpheaders.Expires))
  273. }
  274. func (s *ProcessingHandlerTestSuite) TestCacheControlPassthroughDisabled() {
  275. config.CacheControlPassthrough = false
  276. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  277. rw.Header().Set(httpheaders.CacheControl, "max-age=1234, public")
  278. rw.Header().Set(httpheaders.Expires, time.Now().Add(time.Hour).UTC().Format(http.TimeFormat))
  279. rw.WriteHeader(200)
  280. rw.Write(s.testData.Read("test1.png"))
  281. }))
  282. defer ts.Close()
  283. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  284. s.Require().NotEqual("max-age=1234, public", res.Header.Get(httpheaders.CacheControl))
  285. s.Require().Empty(res.Header.Get(httpheaders.Expires))
  286. }
  287. func (s *ProcessingHandlerTestSuite) TestETagDisabled() {
  288. config.ETagEnabled = false
  289. res := s.GET("/unsafe/rs:fill:4:4/plain/local:///test1.png")
  290. s.Require().Equal(200, res.StatusCode)
  291. s.Require().Empty(res.Header.Get(httpheaders.Etag))
  292. }
  293. func (s *ProcessingHandlerTestSuite) TestETagDataMatch() {
  294. config.ETagEnabled = true
  295. etag := `"loremipsumdolor"`
  296. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  297. s.NotEmpty(r.Header.Get(httpheaders.IfNoneMatch))
  298. rw.Header().Set(httpheaders.Etag, etag)
  299. rw.WriteHeader(http.StatusNotModified)
  300. }))
  301. defer ts.Close()
  302. header := make(http.Header)
  303. header.Set(httpheaders.IfNoneMatch, etag)
  304. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  305. s.Require().Equal(304, res.StatusCode)
  306. s.Require().Equal(etag, res.Header.Get(httpheaders.Etag))
  307. }
  308. func (s *ProcessingHandlerTestSuite) TestLastModifiedEnabled() {
  309. config.LastModifiedEnabled = true
  310. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  311. rw.Header().Set(httpheaders.LastModified, "Wed, 21 Oct 2015 07:28:00 GMT")
  312. rw.WriteHeader(200)
  313. rw.Write(s.testData.Read("test1.png"))
  314. }))
  315. defer ts.Close()
  316. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  317. s.Require().Equal("Wed, 21 Oct 2015 07:28:00 GMT", res.Header.Get(httpheaders.LastModified))
  318. }
  319. func (s *ProcessingHandlerTestSuite) TestLastModifiedDisabled() {
  320. config.LastModifiedEnabled = false
  321. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  322. rw.Header().Set(httpheaders.LastModified, "Wed, 21 Oct 2015 07:28:00 GMT")
  323. rw.WriteHeader(200)
  324. rw.Write(s.testData.Read("test1.png"))
  325. }))
  326. defer ts.Close()
  327. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  328. s.Require().Empty(res.Header.Get(httpheaders.LastModified))
  329. }
  330. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedDisabled() {
  331. config.LastModifiedEnabled = false
  332. data := s.testData.Read("test1.png")
  333. lastModified := "Wed, 21 Oct 2015 07:28:00 GMT"
  334. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  335. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  336. s.Empty(modifiedSince)
  337. rw.WriteHeader(200)
  338. rw.Write(data)
  339. }))
  340. defer ts.Close()
  341. header := make(http.Header)
  342. header.Set(httpheaders.IfModifiedSince, lastModified)
  343. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  344. s.Require().Equal(200, res.StatusCode)
  345. }
  346. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedEnabled() {
  347. config.LastModifiedEnabled = true
  348. lastModified := "Wed, 21 Oct 2015 07:28:00 GMT"
  349. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  350. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  351. s.Equal(lastModified, modifiedSince)
  352. rw.WriteHeader(304)
  353. }))
  354. defer ts.Close()
  355. header := make(http.Header)
  356. header.Set(httpheaders.IfModifiedSince, lastModified)
  357. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  358. s.Require().Equal(304, res.StatusCode)
  359. }
  360. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastModifiedDisabled() {
  361. data := s.testData.Read("test1.png")
  362. config.LastModifiedEnabled = false
  363. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  364. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  365. s.Empty(modifiedSince)
  366. rw.WriteHeader(200)
  367. rw.Write(data)
  368. }))
  369. defer ts.Close()
  370. recentTimestamp := "Thu, 25 Feb 2021 01:45:00 GMT"
  371. header := make(http.Header)
  372. header.Set(httpheaders.IfModifiedSince, recentTimestamp)
  373. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  374. s.Require().Equal(200, res.StatusCode)
  375. }
  376. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastModifiedEnabled() {
  377. config.LastModifiedEnabled = true
  378. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  379. fileLastModified, _ := time.Parse(http.TimeFormat, "Wed, 21 Oct 2015 07:28:00 GMT")
  380. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  381. parsedModifiedSince, err := time.Parse(http.TimeFormat, modifiedSince)
  382. s.NoError(err)
  383. s.True(fileLastModified.Before(parsedModifiedSince))
  384. rw.WriteHeader(304)
  385. }))
  386. defer ts.Close()
  387. recentTimestamp := "Thu, 25 Feb 2021 01:45:00 GMT"
  388. header := make(http.Header)
  389. header.Set(httpheaders.IfModifiedSince, recentTimestamp)
  390. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  391. s.Require().Equal(304, res.StatusCode)
  392. }
  393. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedDisabled() {
  394. s.config().ProcessingHandler.LastModifiedEnabled = false
  395. data := s.testData.Read("test1.png")
  396. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  397. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  398. s.Empty(modifiedSince)
  399. rw.WriteHeader(200)
  400. rw.Write(data)
  401. }))
  402. defer ts.Close()
  403. oldTimestamp := "Tue, 01 Oct 2013 17:31:00 GMT"
  404. header := make(http.Header)
  405. header.Set(httpheaders.IfModifiedSince, oldTimestamp)
  406. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  407. s.Require().Equal(200, res.StatusCode)
  408. }
  409. func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedEnabled() {
  410. config.LastModifiedEnabled = true
  411. data := s.testData.Read("test1.png")
  412. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  413. fileLastModified, _ := time.Parse(http.TimeFormat, "Wed, 21 Oct 2015 07:28:00 GMT")
  414. modifiedSince := r.Header.Get(httpheaders.IfModifiedSince)
  415. parsedModifiedSince, err := time.Parse(http.TimeFormat, modifiedSince)
  416. s.NoError(err)
  417. s.True(fileLastModified.After(parsedModifiedSince))
  418. rw.WriteHeader(200)
  419. rw.Write(data)
  420. }))
  421. defer ts.Close()
  422. oldTimestamp := "Tue, 01 Oct 2013 17:31:00 GMT"
  423. header := make(http.Header)
  424. header.Set(httpheaders.IfModifiedSince, oldTimestamp)
  425. res := s.GET(fmt.Sprintf("/unsafe/plain/%s", ts.URL), header)
  426. s.Require().Equal(200, res.StatusCode)
  427. }
  428. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvg() {
  429. config.AlwaysRasterizeSvg = true
  430. res := s.GET("/unsafe/rs:fill:40:40/plain/local:///test1.svg")
  431. s.Require().Equal(200, res.StatusCode)
  432. s.Require().Equal("image/png", res.Header.Get(httpheaders.ContentType))
  433. }
  434. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithEnforceAvif() {
  435. config.AlwaysRasterizeSvg = true
  436. config.EnforceWebp = true
  437. res := s.GET("/unsafe/plain/local:///test1.svg", http.Header{"Accept": []string{"image/webp"}})
  438. s.Require().Equal(200, res.StatusCode)
  439. s.Require().Equal("image/webp", res.Header.Get(httpheaders.ContentType))
  440. }
  441. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgDisabled() {
  442. config.AlwaysRasterizeSvg = false
  443. config.EnforceWebp = true
  444. res := s.GET("/unsafe/plain/local:///test1.svg")
  445. s.Require().Equal(200, res.StatusCode)
  446. s.Require().Equal("image/svg+xml", res.Header.Get(httpheaders.ContentType))
  447. }
  448. func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithFormat() {
  449. config.AlwaysRasterizeSvg = true
  450. config.SkipProcessingFormats = []imagetype.Type{imagetype.SVG}
  451. res := s.GET("/unsafe/plain/local:///test1.svg@svg")
  452. s.Require().Equal(200, res.StatusCode)
  453. s.Require().Equal("image/svg+xml", res.Header.Get(httpheaders.ContentType))
  454. }
  455. func (s *ProcessingHandlerTestSuite) TestMaxSrcFileSizeGlobal() {
  456. config.MaxSrcFileSize = 1
  457. ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  458. rw.WriteHeader(200)
  459. rw.Write(s.testData.Read("test1.png"))
  460. }))
  461. defer ts.Close()
  462. res := s.GET("/unsafe/rs:fill:4:4/plain/" + ts.URL)
  463. s.Require().Equal(422, res.StatusCode)
  464. }
  465. func TestProcessingHandler(t *testing.T) {
  466. suite.Run(t, new(ProcessingHandlerTestSuite))
  467. }