processing_options.go 19 KB

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