processing_options.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. package main
  2. /*
  3. #cgo LDFLAGS: -s -w
  4. #include "image_types.h"
  5. */
  6. import "C"
  7. import (
  8. "context"
  9. "encoding/base64"
  10. "errors"
  11. "fmt"
  12. "net/http"
  13. "net/url"
  14. "regexp"
  15. "strconv"
  16. "strings"
  17. )
  18. type urlOptions map[string][]string
  19. type imageType int
  20. const (
  21. imageTypeUnknown = imageType(C.UNKNOWN)
  22. imageTypeJPEG = imageType(C.JPEG)
  23. imageTypePNG = imageType(C.PNG)
  24. imageTypeWEBP = imageType(C.WEBP)
  25. imageTypeGIF = imageType(C.GIF)
  26. )
  27. type processingHeaders struct {
  28. Accept string
  29. Width string
  30. ViewportWidth string
  31. }
  32. var imageTypes = map[string]imageType{
  33. "jpeg": imageTypeJPEG,
  34. "jpg": imageTypeJPEG,
  35. "png": imageTypePNG,
  36. "webp": imageTypeWEBP,
  37. "gif": imageTypeGIF,
  38. }
  39. type gravityType int
  40. const (
  41. gravityCenter gravityType = iota
  42. gravityNorth
  43. gravityEast
  44. gravitySouth
  45. gravityWest
  46. gravityNorthWest
  47. gravityNorthEast
  48. gravitySouthWest
  49. gravitySouthEast
  50. gravitySmart
  51. gravityFocusPoint
  52. )
  53. var gravityTypes = map[string]gravityType{
  54. "ce": gravityCenter,
  55. "no": gravityNorth,
  56. "ea": gravityEast,
  57. "so": gravitySouth,
  58. "we": gravityWest,
  59. "nowe": gravityNorthWest,
  60. "noea": gravityNorthEast,
  61. "sowe": gravitySouthWest,
  62. "soea": gravitySouthEast,
  63. "sm": gravitySmart,
  64. "fp": gravityFocusPoint,
  65. }
  66. type gravityOptions struct {
  67. Type gravityType
  68. X, Y float64
  69. }
  70. type resizeType int
  71. const (
  72. resizeFit resizeType = iota
  73. resizeFill
  74. resizeCrop
  75. )
  76. var resizeTypes = map[string]resizeType{
  77. "fit": resizeFit,
  78. "fill": resizeFill,
  79. "crop": resizeCrop,
  80. }
  81. type color struct{ R, G, B uint8 }
  82. var hexColorRegex = regexp.MustCompile("^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
  83. const (
  84. hexColorLongFormat = "%02x%02x%02x"
  85. hexColorShortFormat = "%1x%1x%1x"
  86. )
  87. type watermarkOptions struct {
  88. Enabled bool
  89. Opacity float64
  90. Replicate bool
  91. Gravity gravityType
  92. OffsetX int
  93. OffsetY int
  94. Scale float64
  95. }
  96. type processingOptions struct {
  97. Resize resizeType
  98. Width int
  99. Height int
  100. Gravity gravityOptions
  101. Enlarge bool
  102. Format imageType
  103. Quality int
  104. Flatten bool
  105. Background color
  106. Blur float32
  107. Sharpen float32
  108. CacheBuster string
  109. Watermark watermarkOptions
  110. UsedPresets []string
  111. }
  112. type applyOptionFunc func(po *processingOptions, args []string) error
  113. const (
  114. imageURLCtxKey = ctxKey("imageUrl")
  115. processingOptionsCtxKey = ctxKey("processingOptions")
  116. urlTokenPlain = "plain"
  117. )
  118. var (
  119. errInvalidImageURL = errors.New("Invalid image url")
  120. errInvalidURLEncoding = errors.New("Invalid url encoding")
  121. errInvalidPath = errors.New("Invalid path")
  122. errResultingImageFormatIsNotSupported = errors.New("Resulting image format is not supported")
  123. )
  124. func (it imageType) String() string {
  125. for k, v := range imageTypes {
  126. if v == it {
  127. return k
  128. }
  129. }
  130. return ""
  131. }
  132. func (gt gravityType) String() string {
  133. for k, v := range gravityTypes {
  134. if v == gt {
  135. return k
  136. }
  137. }
  138. return ""
  139. }
  140. func (rt resizeType) String() string {
  141. for k, v := range resizeTypes {
  142. if v == rt {
  143. return k
  144. }
  145. }
  146. return ""
  147. }
  148. func (po *processingOptions) isPresetUsed(name string) bool {
  149. for _, usedName := range po.UsedPresets {
  150. if usedName == name {
  151. return true
  152. }
  153. }
  154. return false
  155. }
  156. func (po *processingOptions) presetUsed(name string) {
  157. po.UsedPresets = append(po.UsedPresets, name)
  158. }
  159. func colorFromHex(hexcolor string) (color, error) {
  160. c := color{}
  161. if !hexColorRegex.MatchString(hexcolor) {
  162. return c, fmt.Errorf("Invalid hex color: %s", hexcolor)
  163. }
  164. if len(hexcolor) == 3 {
  165. fmt.Sscanf(hexcolor, hexColorShortFormat, &c.R, &c.G, &c.B)
  166. c.R *= 17
  167. c.G *= 17
  168. c.B *= 17
  169. } else {
  170. fmt.Sscanf(hexcolor, hexColorLongFormat, &c.R, &c.G, &c.B)
  171. }
  172. return c, nil
  173. }
  174. func decodeBase64URL(parts []string) (string, string, error) {
  175. var format string
  176. urlParts := strings.Split(strings.Join(parts, ""), ".")
  177. if len(urlParts) > 2 {
  178. return "", "", errInvalidURLEncoding
  179. }
  180. if len(urlParts) == 2 && len(urlParts[1]) > 0 {
  181. format = urlParts[1]
  182. }
  183. imageURL, err := base64.RawURLEncoding.DecodeString(urlParts[0])
  184. if err != nil {
  185. return "", "", errInvalidURLEncoding
  186. }
  187. fullURL := fmt.Sprintf("%s%s", conf.BaseURL, string(imageURL))
  188. if _, err := url.ParseRequestURI(fullURL); err != nil {
  189. return "", "", errInvalidImageURL
  190. }
  191. return fullURL, format, nil
  192. }
  193. func decodePlainURL(parts []string) (string, string, error) {
  194. var format string
  195. urlParts := strings.Split(strings.Join(parts, "/"), "@")
  196. if len(urlParts) > 2 {
  197. return "", "", errInvalidURLEncoding
  198. }
  199. if len(urlParts) == 2 && len(urlParts[1]) > 0 {
  200. format = urlParts[1]
  201. }
  202. fullURL := fmt.Sprintf("%s%s", conf.BaseURL, urlParts[0])
  203. if _, err := url.ParseRequestURI(fullURL); err == nil {
  204. return fullURL, format, nil
  205. }
  206. if unescaped, err := url.PathUnescape(urlParts[0]); err == nil {
  207. fullURL := fmt.Sprintf("%s%s", conf.BaseURL, unescaped)
  208. if _, err := url.ParseRequestURI(fullURL); err == nil {
  209. return fullURL, format, nil
  210. }
  211. }
  212. return "", "", errInvalidImageURL
  213. }
  214. func decodeURL(parts []string) (string, string, error) {
  215. if len(parts) == 0 {
  216. return "", "", errInvalidURLEncoding
  217. }
  218. if parts[0] == urlTokenPlain && len(parts) > 1 {
  219. return decodePlainURL(parts[1:])
  220. }
  221. return decodeBase64URL(parts)
  222. }
  223. func applyWidthOption(po *processingOptions, args []string) error {
  224. if len(args) > 1 {
  225. return fmt.Errorf("Invalid width arguments: %v", args)
  226. }
  227. if w, err := strconv.Atoi(args[0]); err == nil && w >= 0 {
  228. po.Width = w
  229. } else {
  230. return fmt.Errorf("Invalid width: %s", args[0])
  231. }
  232. return nil
  233. }
  234. func applyHeightOption(po *processingOptions, args []string) error {
  235. if len(args) > 1 {
  236. return fmt.Errorf("Invalid height arguments: %v", args)
  237. }
  238. if h, err := strconv.Atoi(args[0]); err == nil && po.Height >= 0 {
  239. po.Height = h
  240. } else {
  241. return fmt.Errorf("Invalid height: %s", args[0])
  242. }
  243. return nil
  244. }
  245. func applyEnlargeOption(po *processingOptions, args []string) error {
  246. if len(args) > 1 {
  247. return fmt.Errorf("Invalid enlarge arguments: %v", args)
  248. }
  249. po.Enlarge = args[0] != "0"
  250. return nil
  251. }
  252. func applySizeOption(po *processingOptions, args []string) (err error) {
  253. if len(args) > 3 {
  254. return fmt.Errorf("Invalid size arguments: %v", args)
  255. }
  256. if len(args) >= 1 && len(args[0]) > 0 {
  257. if err = applyWidthOption(po, args[0:1]); err != nil {
  258. return
  259. }
  260. }
  261. if len(args) >= 2 && len(args[1]) > 0 {
  262. if err = applyHeightOption(po, args[1:2]); err != nil {
  263. return
  264. }
  265. }
  266. if len(args) == 3 && len(args[2]) > 0 {
  267. if err = applyEnlargeOption(po, args[2:3]); err != nil {
  268. return
  269. }
  270. }
  271. return nil
  272. }
  273. func applyResizingTypeOption(po *processingOptions, args []string) error {
  274. if len(args) > 1 {
  275. return fmt.Errorf("Invalid resizing type arguments: %v", args)
  276. }
  277. if r, ok := resizeTypes[args[0]]; ok {
  278. po.Resize = r
  279. } else {
  280. return fmt.Errorf("Invalid resize type: %s", args[0])
  281. }
  282. return nil
  283. }
  284. func applyResizeOption(po *processingOptions, args []string) error {
  285. if len(args) > 4 {
  286. return fmt.Errorf("Invalid resize arguments: %v", args)
  287. }
  288. if len(args[0]) > 0 {
  289. if err := applyResizingTypeOption(po, args[0:1]); err != nil {
  290. return err
  291. }
  292. }
  293. if len(args) > 1 {
  294. if err := applySizeOption(po, args[1:]); err != nil {
  295. return err
  296. }
  297. }
  298. return nil
  299. }
  300. func applyGravityOption(po *processingOptions, args []string) error {
  301. if g, ok := gravityTypes[args[0]]; ok {
  302. po.Gravity.Type = g
  303. } else {
  304. return fmt.Errorf("Invalid gravity: %s", args[0])
  305. }
  306. if po.Gravity.Type == gravityFocusPoint {
  307. if len(args) != 3 {
  308. return fmt.Errorf("Invalid gravity arguments: %v", args)
  309. }
  310. if x, err := strconv.ParseFloat(args[1], 64); err == nil && x >= 0 && x <= 1 {
  311. po.Gravity.X = x
  312. } else {
  313. return fmt.Errorf("Invalid gravity X: %s", args[1])
  314. }
  315. if y, err := strconv.ParseFloat(args[2], 64); err == nil && y >= 0 && y <= 1 {
  316. po.Gravity.Y = y
  317. } else {
  318. return fmt.Errorf("Invalid gravity Y: %s", args[2])
  319. }
  320. } else if len(args) > 1 {
  321. return fmt.Errorf("Invalid gravity arguments: %v", args)
  322. }
  323. return nil
  324. }
  325. func applyQualityOption(po *processingOptions, args []string) error {
  326. if len(args) > 1 {
  327. return fmt.Errorf("Invalid quality arguments: %v", args)
  328. }
  329. if q, err := strconv.Atoi(args[0]); err == nil && q > 0 && q <= 100 {
  330. po.Quality = q
  331. } else {
  332. return fmt.Errorf("Invalid quality: %s", args[0])
  333. }
  334. return nil
  335. }
  336. func applyBackgroundOption(po *processingOptions, args []string) error {
  337. switch len(args) {
  338. case 1:
  339. if len(args[0]) == 0 {
  340. po.Flatten = false
  341. } else if c, err := colorFromHex(args[0]); err == nil {
  342. po.Flatten = true
  343. po.Background = c
  344. } else {
  345. return fmt.Errorf("Invalid background argument: %s", err)
  346. }
  347. case 3:
  348. po.Flatten = true
  349. if r, err := strconv.ParseUint(args[0], 10, 8); err == nil && r >= 0 && r <= 255 {
  350. po.Background.R = uint8(r)
  351. } else {
  352. return fmt.Errorf("Invalid background red channel: %s", args[0])
  353. }
  354. if g, err := strconv.ParseUint(args[1], 10, 8); err == nil && g >= 0 && g <= 255 {
  355. po.Background.G = uint8(g)
  356. } else {
  357. return fmt.Errorf("Invalid background green channel: %s", args[1])
  358. }
  359. if b, err := strconv.ParseUint(args[2], 10, 8); err == nil && b >= 0 && b <= 255 {
  360. po.Background.B = uint8(b)
  361. } else {
  362. return fmt.Errorf("Invalid background blue channel: %s", args[2])
  363. }
  364. default:
  365. return fmt.Errorf("Invalid background arguments: %v", args)
  366. }
  367. return nil
  368. }
  369. func applyBlurOption(po *processingOptions, args []string) error {
  370. if len(args) > 1 {
  371. return fmt.Errorf("Invalid blur arguments: %v", args)
  372. }
  373. if b, err := strconv.ParseFloat(args[0], 32); err == nil || b >= 0 {
  374. po.Blur = float32(b)
  375. } else {
  376. return fmt.Errorf("Invalid blur: %s", args[0])
  377. }
  378. return nil
  379. }
  380. func applySharpenOption(po *processingOptions, args []string) error {
  381. if len(args) > 1 {
  382. return fmt.Errorf("Invalid sharpen arguments: %v", args)
  383. }
  384. if s, err := strconv.ParseFloat(args[0], 32); err == nil || s >= 0 {
  385. po.Sharpen = float32(s)
  386. } else {
  387. return fmt.Errorf("Invalid sharpen: %s", args[0])
  388. }
  389. return nil
  390. }
  391. func applyPresetOption(po *processingOptions, args []string) error {
  392. for _, preset := range args {
  393. if p, ok := conf.Presets[preset]; ok {
  394. if po.isPresetUsed(preset) {
  395. return fmt.Errorf("Recursive preset usage is detected: %s", preset)
  396. }
  397. po.presetUsed(preset)
  398. if err := applyProcessingOptions(po, p); err != nil {
  399. return err
  400. }
  401. } else {
  402. return fmt.Errorf("Unknown asset: %s", preset)
  403. }
  404. }
  405. return nil
  406. }
  407. func applyWatermarkOption(po *processingOptions, args []string) error {
  408. if len(args) > 7 {
  409. return fmt.Errorf("Invalid watermark arguments: %v", args)
  410. }
  411. if o, err := strconv.ParseFloat(args[0], 64); err == nil && o >= 0 && o <= 1 {
  412. po.Watermark.Enabled = o > 0
  413. po.Watermark.Opacity = o
  414. } else {
  415. return fmt.Errorf("Invalid watermark opacity: %s", args[0])
  416. }
  417. if len(args) > 1 && len(args[1]) > 0 {
  418. if args[1] == "re" {
  419. po.Watermark.Replicate = true
  420. } else if g, ok := gravityTypes[args[1]]; ok && g != gravityFocusPoint && g != gravitySmart {
  421. po.Watermark.Gravity = g
  422. } else {
  423. return fmt.Errorf("Invalid watermark position: %s", args[1])
  424. }
  425. }
  426. if len(args) > 2 && len(args[2]) > 0 {
  427. if x, err := strconv.Atoi(args[2]); err == nil {
  428. po.Watermark.OffsetX = x
  429. } else {
  430. return fmt.Errorf("Invalid watermark X offset: %s", args[2])
  431. }
  432. }
  433. if len(args) > 3 && len(args[3]) > 0 {
  434. if y, err := strconv.Atoi(args[3]); err == nil {
  435. po.Watermark.OffsetY = y
  436. } else {
  437. return fmt.Errorf("Invalid watermark Y offset: %s", args[3])
  438. }
  439. }
  440. if len(args) > 4 && len(args[4]) > 0 {
  441. if s, err := strconv.ParseFloat(args[4], 64); err == nil && s >= 0 {
  442. po.Watermark.Scale = s
  443. } else {
  444. return fmt.Errorf("Invalid watermark scale: %s", args[4])
  445. }
  446. }
  447. return nil
  448. }
  449. func applyFormatOption(po *processingOptions, args []string) error {
  450. if len(args) > 1 {
  451. return fmt.Errorf("Invalid format arguments: %v", args)
  452. }
  453. if conf.EnforceWebp && po.Format == imageTypeWEBP {
  454. // Webp is enforced and already set as format
  455. return nil
  456. }
  457. if f, ok := imageTypes[args[0]]; ok {
  458. po.Format = f
  459. } else {
  460. return fmt.Errorf("Invalid image format: %s", args[0])
  461. }
  462. if !vipsTypeSupportSave[po.Format] {
  463. return errResultingImageFormatIsNotSupported
  464. }
  465. return nil
  466. }
  467. func applyCacheBusterOption(po *processingOptions, args []string) error {
  468. if len(args) > 1 {
  469. return fmt.Errorf("Invalid cache buster arguments: %v", args)
  470. }
  471. po.CacheBuster = args[0]
  472. return nil
  473. }
  474. func applyProcessingOption(po *processingOptions, name string, args []string) error {
  475. switch name {
  476. case "format", "f", "ext":
  477. if err := applyFormatOption(po, args); err != nil {
  478. return err
  479. }
  480. case "resize", "rs":
  481. if err := applyResizeOption(po, args); err != nil {
  482. return err
  483. }
  484. case "resizing_type", "rt":
  485. if err := applyResizingTypeOption(po, args); err != nil {
  486. return err
  487. }
  488. case "size", "s":
  489. if err := applySizeOption(po, args); err != nil {
  490. return err
  491. }
  492. case "width", "w":
  493. if err := applyWidthOption(po, args); err != nil {
  494. return err
  495. }
  496. case "height", "h":
  497. if err := applyHeightOption(po, args); err != nil {
  498. return err
  499. }
  500. case "enlarge", "el":
  501. if err := applyEnlargeOption(po, args); err != nil {
  502. return err
  503. }
  504. case "gravity", "g":
  505. if err := applyGravityOption(po, args); err != nil {
  506. return err
  507. }
  508. case "quality", "q":
  509. if err := applyQualityOption(po, args); err != nil {
  510. return err
  511. }
  512. case "background", "bg":
  513. if err := applyBackgroundOption(po, args); err != nil {
  514. return err
  515. }
  516. case "blur", "bl":
  517. if err := applyBlurOption(po, args); err != nil {
  518. return err
  519. }
  520. case "sharpen", "sh":
  521. if err := applySharpenOption(po, args); err != nil {
  522. return err
  523. }
  524. case "watermark", "wm":
  525. if err := applyWatermarkOption(po, args); err != nil {
  526. return err
  527. }
  528. case "preset", "pr":
  529. if err := applyPresetOption(po, args); err != nil {
  530. return err
  531. }
  532. case "cachebuster", "cb":
  533. if err := applyCacheBusterOption(po, args); err != nil {
  534. return err
  535. }
  536. default:
  537. return fmt.Errorf("Unknown processing option: %s", name)
  538. }
  539. return nil
  540. }
  541. func applyProcessingOptions(po *processingOptions, options urlOptions) error {
  542. for name, args := range options {
  543. if err := applyProcessingOption(po, name, args); err != nil {
  544. return err
  545. }
  546. }
  547. return nil
  548. }
  549. func parseURLOptions(opts []string) (urlOptions, []string) {
  550. parsed := make(urlOptions)
  551. urlStart := len(opts) + 1
  552. for i, opt := range opts {
  553. args := strings.Split(opt, ":")
  554. if len(args) == 1 {
  555. urlStart = i
  556. break
  557. }
  558. parsed[args[0]] = args[1:]
  559. }
  560. var rest []string
  561. if urlStart < len(opts) {
  562. rest = opts[urlStart:]
  563. } else {
  564. rest = []string{}
  565. }
  566. return parsed, rest
  567. }
  568. func defaultProcessingOptions(headers *processingHeaders) (*processingOptions, error) {
  569. var err error
  570. po := processingOptions{
  571. Resize: resizeFit,
  572. Width: 0,
  573. Height: 0,
  574. Gravity: gravityOptions{Type: gravityCenter},
  575. Enlarge: false,
  576. Quality: conf.Quality,
  577. Format: imageTypeJPEG,
  578. Blur: 0,
  579. Sharpen: 0,
  580. Watermark: watermarkOptions{Opacity: 1, Replicate: false, Gravity: gravityCenter},
  581. UsedPresets: make([]string, 0, len(conf.Presets)),
  582. }
  583. if (conf.EnableWebpDetection || conf.EnforceWebp) && strings.Contains(headers.Accept, "image/webp") {
  584. po.Format = imageTypeWEBP
  585. }
  586. if conf.EnableClientHints && len(headers.ViewportWidth) > 0 {
  587. if vw, err := strconv.Atoi(headers.ViewportWidth); err == nil {
  588. po.Width = vw
  589. }
  590. }
  591. if conf.EnableClientHints && len(headers.Width) > 0 {
  592. if w, err := strconv.Atoi(headers.Width); err == nil {
  593. po.Width = w
  594. }
  595. }
  596. if _, ok := conf.Presets["default"]; ok {
  597. err = applyPresetOption(&po, []string{"default"})
  598. }
  599. return &po, err
  600. }
  601. func parsePathAdvanced(parts []string, headers *processingHeaders) (string, *processingOptions, error) {
  602. po, err := defaultProcessingOptions(headers)
  603. if err != nil {
  604. return "", po, err
  605. }
  606. options, urlParts := parseURLOptions(parts)
  607. if err := applyProcessingOptions(po, options); err != nil {
  608. return "", po, err
  609. }
  610. url, extension, err := decodeURL(urlParts)
  611. if err != nil {
  612. return "", po, err
  613. }
  614. if len(extension) > 0 {
  615. if err := applyFormatOption(po, []string{extension}); err != nil {
  616. return "", po, err
  617. }
  618. }
  619. return url, po, nil
  620. }
  621. func parsePathBasic(parts []string, headers *processingHeaders) (string, *processingOptions, error) {
  622. var err error
  623. if len(parts) < 6 {
  624. return "", nil, errInvalidPath
  625. }
  626. po, err := defaultProcessingOptions(headers)
  627. if err != nil {
  628. return "", po, err
  629. }
  630. po.Resize = resizeTypes[parts[0]]
  631. if err = applyWidthOption(po, parts[1:2]); err != nil {
  632. return "", po, err
  633. }
  634. if err = applyHeightOption(po, parts[2:3]); err != nil {
  635. return "", po, err
  636. }
  637. if err = applyGravityOption(po, strings.Split(parts[3], ":")); err != nil {
  638. return "", po, err
  639. }
  640. if err = applyEnlargeOption(po, parts[4:5]); err != nil {
  641. return "", po, err
  642. }
  643. url, extension, err := decodeURL(parts[5:])
  644. if err != nil {
  645. return "", po, err
  646. }
  647. if len(extension) > 0 {
  648. if err := applyFormatOption(po, []string{extension}); err != nil {
  649. return "", po, err
  650. }
  651. }
  652. return url, po, nil
  653. }
  654. func parsePath(ctx context.Context, r *http.Request) (context.Context, error) {
  655. path := r.URL.Path
  656. parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
  657. if len(parts) < 3 {
  658. return ctx, errInvalidPath
  659. }
  660. if !conf.AllowInsecure {
  661. if err := validatePath(parts[0], strings.TrimPrefix(path, fmt.Sprintf("/%s", parts[0]))); err != nil {
  662. return ctx, err
  663. }
  664. }
  665. headers := &processingHeaders{
  666. Accept: r.Header.Get("Accept"),
  667. Width: r.Header.Get("Width"),
  668. ViewportWidth: r.Header.Get("Viewport-Width"),
  669. }
  670. var imageURL string
  671. var po *processingOptions
  672. var err error
  673. if _, ok := resizeTypes[parts[1]]; ok {
  674. imageURL, po, err = parsePathBasic(parts[1:], headers)
  675. } else {
  676. imageURL, po, err = parsePathAdvanced(parts[1:], headers)
  677. }
  678. if err != nil {
  679. return ctx, err
  680. }
  681. ctx = context.WithValue(ctx, imageURLCtxKey, imageURL)
  682. ctx = context.WithValue(ctx, processingOptionsCtxKey, po)
  683. return ctx, err
  684. }
  685. func getImageURL(ctx context.Context) string {
  686. return ctx.Value(imageURLCtxKey).(string)
  687. }
  688. func getProcessingOptions(ctx context.Context) *processingOptions {
  689. return ctx.Value(processingOptionsCtxKey).(*processingOptions)
  690. }