processing_options.go 18 KB

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