processing_options_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. package options
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "regexp"
  8. "strings"
  9. "testing"
  10. "github.com/stretchr/testify/suite"
  11. "github.com/imgproxy/imgproxy/v3/config"
  12. "github.com/imgproxy/imgproxy/v3/imagetype"
  13. )
  14. type ProcessingOptionsTestSuite struct{ suite.Suite }
  15. func (s *ProcessingOptionsTestSuite) SetupTest() {
  16. config.Reset()
  17. // Reset presets
  18. presets = make(map[string]urlOptions)
  19. }
  20. func (s *ProcessingOptionsTestSuite) TestParseBase64URL() {
  21. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  22. path := fmt.Sprintf("/size:100:100/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  23. po, imageURL, err := ParsePath(path, make(http.Header))
  24. s.Require().NoError(err)
  25. s.Require().Equal(originURL, imageURL)
  26. s.Require().Equal(imagetype.PNG, po.Format)
  27. }
  28. func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithFilename() {
  29. config.Base64URLIncludesFilename = true
  30. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  31. path := fmt.Sprintf("/size:100:100/%s.png/puppy.jpg", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  32. po, imageURL, err := ParsePath(path, make(http.Header))
  33. s.Require().NoError(err)
  34. s.Require().Equal(originURL, imageURL)
  35. s.Require().Equal(imagetype.PNG, po.Format)
  36. }
  37. func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithoutExtension() {
  38. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  39. path := fmt.Sprintf("/size:100:100/%s", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  40. po, imageURL, err := ParsePath(path, make(http.Header))
  41. s.Require().NoError(err)
  42. s.Require().Equal(originURL, imageURL)
  43. s.Require().Equal(imagetype.Unknown, po.Format)
  44. }
  45. func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithBase() {
  46. config.BaseURL = "http://images.dev/"
  47. originURL := "lorem/ipsum.jpg?param=value"
  48. path := fmt.Sprintf("/size:100:100/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  49. po, imageURL, err := ParsePath(path, make(http.Header))
  50. s.Require().NoError(err)
  51. s.Require().Equal(fmt.Sprintf("%s%s", config.BaseURL, originURL), imageURL)
  52. s.Require().Equal(imagetype.PNG, po.Format)
  53. }
  54. func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithReplacement() {
  55. config.URLReplacements = []config.URLReplacement{
  56. {Regexp: regexp.MustCompile("^test://([^/]*)/"), Replacement: "test2://images.dev/${1}/dolor/"},
  57. {Regexp: regexp.MustCompile("^test2://"), Replacement: "http://"},
  58. }
  59. originURL := "test://lorem/ipsum.jpg?param=value"
  60. path := fmt.Sprintf("/size:100:100/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  61. po, imageURL, err := ParsePath(path, make(http.Header))
  62. s.Require().NoError(err)
  63. s.Require().Equal("http://images.dev/lorem/dolor/ipsum.jpg?param=value", imageURL)
  64. s.Require().Equal(imagetype.PNG, po.Format)
  65. }
  66. func (s *ProcessingOptionsTestSuite) TestParsePlainURL() {
  67. originURL := "http://images.dev/lorem/ipsum.jpg"
  68. path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
  69. po, imageURL, err := ParsePath(path, make(http.Header))
  70. s.Require().NoError(err)
  71. s.Require().Equal(originURL, imageURL)
  72. s.Require().Equal(imagetype.PNG, po.Format)
  73. }
  74. func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithoutExtension() {
  75. originURL := "http://images.dev/lorem/ipsum.jpg"
  76. path := fmt.Sprintf("/size:100:100/plain/%s", originURL)
  77. po, imageURL, err := ParsePath(path, make(http.Header))
  78. s.Require().NoError(err)
  79. s.Require().Equal(originURL, imageURL)
  80. s.Require().Equal(imagetype.Unknown, po.Format)
  81. }
  82. func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscaped() {
  83. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  84. path := fmt.Sprintf("/size:100:100/plain/%s@png", url.PathEscape(originURL))
  85. po, imageURL, err := ParsePath(path, make(http.Header))
  86. s.Require().NoError(err)
  87. s.Require().Equal(originURL, imageURL)
  88. s.Require().Equal(imagetype.PNG, po.Format)
  89. }
  90. func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithBase() {
  91. config.BaseURL = "http://images.dev/"
  92. originURL := "lorem/ipsum.jpg"
  93. path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
  94. po, imageURL, err := ParsePath(path, make(http.Header))
  95. s.Require().NoError(err)
  96. s.Require().Equal(fmt.Sprintf("%s%s", config.BaseURL, originURL), imageURL)
  97. s.Require().Equal(imagetype.PNG, po.Format)
  98. }
  99. func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithReplacement() {
  100. config.URLReplacements = []config.URLReplacement{
  101. {Regexp: regexp.MustCompile("^test://([^/]*)/"), Replacement: "test2://images.dev/${1}/dolor/"},
  102. {Regexp: regexp.MustCompile("^test2://"), Replacement: "http://"},
  103. }
  104. originURL := "test://lorem/ipsum.jpg"
  105. path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
  106. po, imageURL, err := ParsePath(path, make(http.Header))
  107. s.Require().NoError(err)
  108. s.Require().Equal("http://images.dev/lorem/dolor/ipsum.jpg", imageURL)
  109. s.Require().Equal(imagetype.PNG, po.Format)
  110. }
  111. func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscapedWithBase() {
  112. config.BaseURL = "http://images.dev/"
  113. originURL := "lorem/ipsum.jpg?param=value"
  114. path := fmt.Sprintf("/size:100:100/plain/%s@png", url.PathEscape(originURL))
  115. po, imageURL, err := ParsePath(path, make(http.Header))
  116. s.Require().NoError(err)
  117. s.Require().Equal(fmt.Sprintf("%s%s", config.BaseURL, originURL), imageURL)
  118. s.Require().Equal(imagetype.PNG, po.Format)
  119. }
  120. func (s *ProcessingOptionsTestSuite) TestParseWithArgumentsSeparator() {
  121. config.ArgumentsSeparator = ","
  122. path := "/size,100,100,1/plain/http://images.dev/lorem/ipsum.jpg"
  123. po, _, err := ParsePath(path, make(http.Header))
  124. s.Require().NoError(err)
  125. s.Require().Equal(100, po.Width)
  126. s.Require().Equal(100, po.Height)
  127. s.Require().True(po.Enlarge)
  128. }
  129. // func (s *ProcessingOptionsTestSuite) TestParseURLAllowedSource() {
  130. // config.AllowedSources = []string{"local://", "http://images.dev/"}
  131. // path := "/plain/http://images.dev/lorem/ipsum.jpg"
  132. // _, _, err := ParsePath(path, make(http.Header))
  133. // s.Require().NoError(err)
  134. // }
  135. // func (s *ProcessingOptionsTestSuite) TestParseURLNotAllowedSource() {
  136. // config.AllowedSources = []string{"local://", "http://images.dev/"}
  137. // path := "/plain/s3://images/lorem/ipsum.jpg"
  138. // _, _, err := ParsePath(path, make(http.Header))
  139. // s.Require().Error(err)
  140. // }
  141. func (s *ProcessingOptionsTestSuite) TestParsePathFormat() {
  142. path := "/format:webp/plain/http://images.dev/lorem/ipsum.jpg"
  143. po, _, err := ParsePath(path, make(http.Header))
  144. s.Require().NoError(err)
  145. s.Require().Equal(imagetype.WEBP, po.Format)
  146. }
  147. func (s *ProcessingOptionsTestSuite) TestParsePathResize() {
  148. path := "/resize:fill:100:200:1/plain/http://images.dev/lorem/ipsum.jpg"
  149. po, _, err := ParsePath(path, make(http.Header))
  150. s.Require().NoError(err)
  151. s.Require().Equal(ResizeFill, po.ResizingType)
  152. s.Require().Equal(100, po.Width)
  153. s.Require().Equal(200, po.Height)
  154. s.Require().True(po.Enlarge)
  155. }
  156. func (s *ProcessingOptionsTestSuite) TestParsePathResizingType() {
  157. path := "/resizing_type:fill/plain/http://images.dev/lorem/ipsum.jpg"
  158. po, _, err := ParsePath(path, make(http.Header))
  159. s.Require().NoError(err)
  160. s.Require().Equal(ResizeFill, po.ResizingType)
  161. }
  162. func (s *ProcessingOptionsTestSuite) TestParsePathSize() {
  163. path := "/size:100:200:1/plain/http://images.dev/lorem/ipsum.jpg"
  164. po, _, err := ParsePath(path, make(http.Header))
  165. s.Require().NoError(err)
  166. s.Require().Equal(100, po.Width)
  167. s.Require().Equal(200, po.Height)
  168. s.Require().True(po.Enlarge)
  169. }
  170. func (s *ProcessingOptionsTestSuite) TestParsePathWidth() {
  171. path := "/width:100/plain/http://images.dev/lorem/ipsum.jpg"
  172. po, _, err := ParsePath(path, make(http.Header))
  173. s.Require().NoError(err)
  174. s.Require().Equal(100, po.Width)
  175. }
  176. func (s *ProcessingOptionsTestSuite) TestParsePathHeight() {
  177. path := "/height:100/plain/http://images.dev/lorem/ipsum.jpg"
  178. po, _, err := ParsePath(path, make(http.Header))
  179. s.Require().NoError(err)
  180. s.Require().Equal(100, po.Height)
  181. }
  182. func (s *ProcessingOptionsTestSuite) TestParsePathEnlarge() {
  183. path := "/enlarge:1/plain/http://images.dev/lorem/ipsum.jpg"
  184. po, _, err := ParsePath(path, make(http.Header))
  185. s.Require().NoError(err)
  186. s.Require().True(po.Enlarge)
  187. }
  188. func (s *ProcessingOptionsTestSuite) TestParsePathExtend() {
  189. path := "/extend:1:so:10:20/plain/http://images.dev/lorem/ipsum.jpg"
  190. po, _, err := ParsePath(path, make(http.Header))
  191. s.Require().NoError(err)
  192. s.Require().True(po.Extend.Enabled)
  193. s.Require().Equal(GravitySouth, po.Extend.Gravity.Type)
  194. s.Require().InDelta(10.0, po.Extend.Gravity.X, 0.0001)
  195. s.Require().InDelta(20.0, po.Extend.Gravity.Y, 0.0001)
  196. }
  197. func (s *ProcessingOptionsTestSuite) TestParsePathExtendSmartGravity() {
  198. path := "/extend:1:sm/plain/http://images.dev/lorem/ipsum.jpg"
  199. _, _, err := ParsePath(path, make(http.Header))
  200. s.Require().Error(err)
  201. }
  202. func (s *ProcessingOptionsTestSuite) TestParsePathExtendReplicateGravity() {
  203. path := "/extend:1:re/plain/http://images.dev/lorem/ipsum.jpg"
  204. _, _, err := ParsePath(path, make(http.Header))
  205. s.Require().Error(err)
  206. }
  207. func (s *ProcessingOptionsTestSuite) TestParsePathGravity() {
  208. path := "/gravity:soea/plain/http://images.dev/lorem/ipsum.jpg"
  209. po, _, err := ParsePath(path, make(http.Header))
  210. s.Require().NoError(err)
  211. s.Require().Equal(GravitySouthEast, po.Gravity.Type)
  212. }
  213. func (s *ProcessingOptionsTestSuite) TestParsePathGravityFocusPoint() {
  214. path := "/gravity:fp:0.5:0.75/plain/http://images.dev/lorem/ipsum.jpg"
  215. po, _, err := ParsePath(path, make(http.Header))
  216. s.Require().NoError(err)
  217. s.Require().Equal(GravityFocusPoint, po.Gravity.Type)
  218. s.Require().InDelta(0.5, po.Gravity.X, 0.0001)
  219. s.Require().InDelta(0.75, po.Gravity.Y, 0.0001)
  220. }
  221. func (s *ProcessingOptionsTestSuite) TestParsePathGravityReplicate() {
  222. path := "/gravity:re/plain/http://images.dev/lorem/ipsum.jpg"
  223. _, _, err := ParsePath(path, make(http.Header))
  224. s.Require().Error(err)
  225. }
  226. func (s *ProcessingOptionsTestSuite) TestParsePathCrop() {
  227. path := "/crop:100:200/plain/http://images.dev/lorem/ipsum.jpg"
  228. po, _, err := ParsePath(path, make(http.Header))
  229. s.Require().NoError(err)
  230. s.Require().InDelta(100.0, po.Crop.Width, 0.0001)
  231. s.Require().InDelta(200.0, po.Crop.Height, 0.0001)
  232. s.Require().Equal(GravityUnknown, po.Crop.Gravity.Type)
  233. s.Require().InDelta(0.0, po.Crop.Gravity.X, 0.0001)
  234. s.Require().InDelta(0.0, po.Crop.Gravity.Y, 0.0001)
  235. }
  236. func (s *ProcessingOptionsTestSuite) TestParsePathCropGravity() {
  237. path := "/crop:100:200:nowe:10:20/plain/http://images.dev/lorem/ipsum.jpg"
  238. po, _, err := ParsePath(path, make(http.Header))
  239. s.Require().NoError(err)
  240. s.Require().InDelta(100.0, po.Crop.Width, 0.0001)
  241. s.Require().InDelta(200.0, po.Crop.Height, 0.0001)
  242. s.Require().Equal(GravityNorthWest, po.Crop.Gravity.Type)
  243. s.Require().InDelta(10.0, po.Crop.Gravity.X, 0.0001)
  244. s.Require().InDelta(20.0, po.Crop.Gravity.Y, 0.0001)
  245. }
  246. func (s *ProcessingOptionsTestSuite) TestParsePathCropGravityReplicate() {
  247. path := "/crop:100:200:re/plain/http://images.dev/lorem/ipsum.jpg"
  248. _, _, err := ParsePath(path, make(http.Header))
  249. s.Require().Error(err)
  250. }
  251. func (s *ProcessingOptionsTestSuite) TestParsePathQuality() {
  252. path := "/quality:55/plain/http://images.dev/lorem/ipsum.jpg"
  253. po, _, err := ParsePath(path, make(http.Header))
  254. s.Require().NoError(err)
  255. s.Require().Equal(55, po.Quality)
  256. }
  257. func (s *ProcessingOptionsTestSuite) TestParsePathBackground() {
  258. path := "/background:128:129:130/plain/http://images.dev/lorem/ipsum.jpg"
  259. po, _, err := ParsePath(path, make(http.Header))
  260. s.Require().NoError(err)
  261. s.Require().True(po.Flatten)
  262. s.Require().Equal(uint8(128), po.Background.R)
  263. s.Require().Equal(uint8(129), po.Background.G)
  264. s.Require().Equal(uint8(130), po.Background.B)
  265. }
  266. func (s *ProcessingOptionsTestSuite) TestParsePathBackgroundHex() {
  267. path := "/background:ffddee/plain/http://images.dev/lorem/ipsum.jpg"
  268. po, _, err := ParsePath(path, make(http.Header))
  269. s.Require().NoError(err)
  270. s.Require().True(po.Flatten)
  271. s.Require().Equal(uint8(0xff), po.Background.R)
  272. s.Require().Equal(uint8(0xdd), po.Background.G)
  273. s.Require().Equal(uint8(0xee), po.Background.B)
  274. }
  275. func (s *ProcessingOptionsTestSuite) TestParsePathBackgroundDisable() {
  276. path := "/background:fff/background:/plain/http://images.dev/lorem/ipsum.jpg"
  277. po, _, err := ParsePath(path, make(http.Header))
  278. s.Require().NoError(err)
  279. s.Require().False(po.Flatten)
  280. }
  281. func (s *ProcessingOptionsTestSuite) TestParsePathBlur() {
  282. path := "/blur:0.2/plain/http://images.dev/lorem/ipsum.jpg"
  283. po, _, err := ParsePath(path, make(http.Header))
  284. s.Require().NoError(err)
  285. s.Require().InDelta(float32(0.2), po.Blur, 0.0001)
  286. }
  287. func (s *ProcessingOptionsTestSuite) TestParsePathSharpen() {
  288. path := "/sharpen:0.2/plain/http://images.dev/lorem/ipsum.jpg"
  289. po, _, err := ParsePath(path, make(http.Header))
  290. s.Require().NoError(err)
  291. s.Require().InDelta(float32(0.2), po.Sharpen, 0.0001)
  292. }
  293. func (s *ProcessingOptionsTestSuite) TestParsePathDpr() {
  294. path := "/dpr:2/plain/http://images.dev/lorem/ipsum.jpg"
  295. po, _, err := ParsePath(path, make(http.Header))
  296. s.Require().NoError(err)
  297. s.Require().InDelta(2.0, po.Dpr, 0.0001)
  298. }
  299. func (s *ProcessingOptionsTestSuite) TestParsePathWatermark() {
  300. path := "/watermark:0.5:soea:10:20:0.6/plain/http://images.dev/lorem/ipsum.jpg"
  301. po, _, err := ParsePath(path, make(http.Header))
  302. s.Require().NoError(err)
  303. s.Require().True(po.Watermark.Enabled)
  304. s.Require().Equal(GravitySouthEast, po.Watermark.Position.Type)
  305. s.Require().InDelta(10.0, po.Watermark.Position.X, 0.0001)
  306. s.Require().InDelta(20.0, po.Watermark.Position.Y, 0.0001)
  307. s.Require().InDelta(0.6, po.Watermark.Scale, 0.0001)
  308. }
  309. func (s *ProcessingOptionsTestSuite) TestParsePathPreset() {
  310. presets["test1"] = urlOptions{
  311. urlOption{Name: "resizing_type", Args: []string{"fill"}},
  312. }
  313. presets["test2"] = urlOptions{
  314. urlOption{Name: "blur", Args: []string{"0.2"}},
  315. urlOption{Name: "quality", Args: []string{"50"}},
  316. }
  317. path := "/preset:test1:test2/plain/http://images.dev/lorem/ipsum.jpg"
  318. po, _, err := ParsePath(path, make(http.Header))
  319. s.Require().NoError(err)
  320. s.Require().Equal(ResizeFill, po.ResizingType)
  321. s.Require().InDelta(float32(0.2), po.Blur, 0.0001)
  322. s.Require().Equal(50, po.Quality)
  323. }
  324. func (s *ProcessingOptionsTestSuite) TestParsePathPresetDefault() {
  325. presets["default"] = urlOptions{
  326. urlOption{Name: "resizing_type", Args: []string{"fill"}},
  327. urlOption{Name: "blur", Args: []string{"0.2"}},
  328. urlOption{Name: "quality", Args: []string{"50"}},
  329. }
  330. path := "/quality:70/plain/http://images.dev/lorem/ipsum.jpg"
  331. po, _, err := ParsePath(path, make(http.Header))
  332. s.Require().NoError(err)
  333. s.Require().Equal(ResizeFill, po.ResizingType)
  334. s.Require().InDelta(float32(0.2), po.Blur, 0.0001)
  335. s.Require().Equal(70, po.Quality)
  336. }
  337. func (s *ProcessingOptionsTestSuite) TestParsePathPresetLoopDetection() {
  338. presets["test1"] = urlOptions{
  339. urlOption{Name: "resizing_type", Args: []string{"fill"}},
  340. urlOption{Name: "preset", Args: []string{"test2"}},
  341. }
  342. presets["test2"] = urlOptions{
  343. urlOption{Name: "blur", Args: []string{"0.2"}},
  344. urlOption{Name: "preset", Args: []string{"test1"}},
  345. }
  346. path := "/preset:test1/plain/http://images.dev/lorem/ipsum.jpg"
  347. po, _, err := ParsePath(path, make(http.Header))
  348. s.Require().NoError(err)
  349. s.Require().ElementsMatch(po.UsedPresets, []string{"test1", "test2"})
  350. }
  351. func (s *ProcessingOptionsTestSuite) TestParsePathCachebuster() {
  352. path := "/cachebuster:123/plain/http://images.dev/lorem/ipsum.jpg"
  353. po, _, err := ParsePath(path, make(http.Header))
  354. s.Require().NoError(err)
  355. s.Require().Equal("123", po.CacheBuster)
  356. }
  357. func (s *ProcessingOptionsTestSuite) TestParsePathStripMetadata() {
  358. path := "/strip_metadata:true/plain/http://images.dev/lorem/ipsum.jpg"
  359. po, _, err := ParsePath(path, make(http.Header))
  360. s.Require().NoError(err)
  361. s.Require().True(po.StripMetadata)
  362. }
  363. func (s *ProcessingOptionsTestSuite) TestParsePathWebpDetection() {
  364. config.AutoWebp = true
  365. path := "/plain/http://images.dev/lorem/ipsum.jpg"
  366. headers := http.Header{"Accept": []string{"image/webp"}}
  367. po, _, err := ParsePath(path, headers)
  368. s.Require().NoError(err)
  369. s.Require().True(po.PreferWebP)
  370. s.Require().False(po.EnforceWebP)
  371. }
  372. func (s *ProcessingOptionsTestSuite) TestParsePathWebpEnforce() {
  373. config.EnforceWebp = true
  374. path := "/plain/http://images.dev/lorem/ipsum.jpg@png"
  375. headers := http.Header{"Accept": []string{"image/webp"}}
  376. po, _, err := ParsePath(path, headers)
  377. s.Require().NoError(err)
  378. s.Require().True(po.PreferWebP)
  379. s.Require().True(po.EnforceWebP)
  380. }
  381. func (s *ProcessingOptionsTestSuite) TestParsePathWidthHeader() {
  382. config.EnableClientHints = true
  383. path := "/plain/http://images.dev/lorem/ipsum.jpg@png"
  384. headers := http.Header{"Width": []string{"100"}}
  385. po, _, err := ParsePath(path, headers)
  386. s.Require().NoError(err)
  387. s.Require().Equal(100, po.Width)
  388. }
  389. func (s *ProcessingOptionsTestSuite) TestParsePathWidthHeaderDisabled() {
  390. path := "/plain/http://images.dev/lorem/ipsum.jpg@png"
  391. headers := http.Header{"Width": []string{"100"}}
  392. po, _, err := ParsePath(path, headers)
  393. s.Require().NoError(err)
  394. s.Require().Equal(0, po.Width)
  395. }
  396. func (s *ProcessingOptionsTestSuite) TestParsePathWidthHeaderRedefine() {
  397. config.EnableClientHints = true
  398. path := "/width:150/plain/http://images.dev/lorem/ipsum.jpg@png"
  399. headers := http.Header{"Width": []string{"100"}}
  400. po, _, err := ParsePath(path, headers)
  401. s.Require().NoError(err)
  402. s.Require().Equal(150, po.Width)
  403. }
  404. func (s *ProcessingOptionsTestSuite) TestParsePathDprHeader() {
  405. config.EnableClientHints = true
  406. path := "/plain/http://images.dev/lorem/ipsum.jpg@png"
  407. headers := http.Header{"Dpr": []string{"2"}}
  408. po, _, err := ParsePath(path, headers)
  409. s.Require().NoError(err)
  410. s.Require().InDelta(2.0, po.Dpr, 0.0001)
  411. }
  412. func (s *ProcessingOptionsTestSuite) TestParsePathDprHeaderDisabled() {
  413. path := "/plain/http://images.dev/lorem/ipsum.jpg@png"
  414. headers := http.Header{"Dpr": []string{"2"}}
  415. po, _, err := ParsePath(path, headers)
  416. s.Require().NoError(err)
  417. s.Require().InDelta(1.0, po.Dpr, 0.0001)
  418. }
  419. func (s *ProcessingOptionsTestSuite) TestParseSkipProcessing() {
  420. path := "/skp:jpg:png/plain/http://images.dev/lorem/ipsum.jpg"
  421. po, _, err := ParsePath(path, make(http.Header))
  422. s.Require().NoError(err)
  423. s.Require().Equal([]imagetype.Type{imagetype.JPEG, imagetype.PNG}, po.SkipProcessingFormats)
  424. }
  425. func (s *ProcessingOptionsTestSuite) TestParseSkipProcessingInvalid() {
  426. path := "/skp:jpg:png:bad_format/plain/http://images.dev/lorem/ipsum.jpg"
  427. _, _, err := ParsePath(path, make(http.Header))
  428. s.Require().Error(err)
  429. s.Require().Equal("Invalid image format in skip processing: bad_format", err.Error())
  430. }
  431. func (s *ProcessingOptionsTestSuite) TestParseExpires() {
  432. path := "/exp:32503669200/plain/http://images.dev/lorem/ipsum.jpg"
  433. _, _, err := ParsePath(path, make(http.Header))
  434. s.Require().NoError(err)
  435. }
  436. func (s *ProcessingOptionsTestSuite) TestParseExpiresExpired() {
  437. path := "/exp:1609448400/plain/http://images.dev/lorem/ipsum.jpg"
  438. _, _, err := ParsePath(path, make(http.Header))
  439. s.Require().Error(err, "Expired URL")
  440. }
  441. func (s *ProcessingOptionsTestSuite) TestParsePathOnlyPresets() {
  442. config.OnlyPresets = true
  443. presets["test1"] = urlOptions{
  444. urlOption{Name: "blur", Args: []string{"0.2"}},
  445. }
  446. presets["test2"] = urlOptions{
  447. urlOption{Name: "quality", Args: []string{"50"}},
  448. }
  449. path := "/test1:test2/plain/http://images.dev/lorem/ipsum.jpg"
  450. po, _, err := ParsePath(path, make(http.Header))
  451. s.Require().NoError(err)
  452. s.Require().InDelta(float32(0.2), po.Blur, 0.0001)
  453. s.Require().Equal(50, po.Quality)
  454. }
  455. func (s *ProcessingOptionsTestSuite) TestParseBase64URLOnlyPresets() {
  456. config.OnlyPresets = true
  457. presets["test1"] = urlOptions{
  458. urlOption{Name: "blur", Args: []string{"0.2"}},
  459. }
  460. presets["test2"] = urlOptions{
  461. urlOption{Name: "quality", Args: []string{"50"}},
  462. }
  463. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  464. path := fmt.Sprintf("/test1:test2/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  465. po, imageURL, err := ParsePath(path, make(http.Header))
  466. s.Require().NoError(err)
  467. s.Require().InDelta(float32(0.2), po.Blur, 0.0001)
  468. s.Require().Equal(50, po.Quality)
  469. s.Require().Equal(originURL, imageURL)
  470. }
  471. func (s *ProcessingOptionsTestSuite) TestParseAllowedOptions() {
  472. config.AllowedProcessiongOptions = []string{"w", "h", "pr"}
  473. presets["test1"] = urlOptions{
  474. urlOption{Name: "blur", Args: []string{"0.2"}},
  475. }
  476. originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
  477. testCases := []struct {
  478. options string
  479. expectedError string
  480. }{
  481. {options: "w:100/h:200", expectedError: ""},
  482. {options: "w:100/h:200/blur:10", expectedError: "Forbidden processing option blur"},
  483. {options: "w:100/h:200/pr:test1", expectedError: ""},
  484. {options: "w:100/h:200/pr:test1/blur:10", expectedError: "Forbidden processing option blur"},
  485. }
  486. for _, tc := range testCases {
  487. s.Run(strings.ReplaceAll(tc.options, "/", "_"), func() {
  488. path := fmt.Sprintf("/%s/%s.png", tc.options, base64.RawURLEncoding.EncodeToString([]byte(originURL)))
  489. _, _, err := ParsePath(path, make(http.Header))
  490. if len(tc.expectedError) > 0 {
  491. s.Require().Error(err)
  492. s.Require().Equal(tc.expectedError, err.Error())
  493. } else {
  494. s.Require().NoError(err)
  495. }
  496. })
  497. }
  498. }
  499. func TestProcessingOptions(t *testing.T) {
  500. suite.Run(t, new(ProcessingOptionsTestSuite))
  501. }