config.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "flag"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "runtime"
  11. "strconv"
  12. )
  13. func intEnvConfig(i *int, name string) {
  14. if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
  15. *i = env
  16. }
  17. }
  18. func megaIntEnvConfig(f *int, name string) {
  19. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  20. *f = int(env * 1000000)
  21. }
  22. }
  23. func strEnvConfig(s *string, name string) {
  24. if env := os.Getenv(name); len(env) > 0 {
  25. *s = env
  26. }
  27. }
  28. func boolEnvConfig(b *bool, name string) {
  29. *b = false
  30. if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
  31. *b = env
  32. }
  33. }
  34. func hexEnvConfig(b *[]byte, name string) {
  35. var err error
  36. if env := os.Getenv(name); len(env) > 0 {
  37. if *b, err = hex.DecodeString(env); err != nil {
  38. log.Fatalf("%s expected to be hex-encoded string\n", name)
  39. }
  40. }
  41. }
  42. func hexFileConfig(b *[]byte, filepath string) {
  43. if len(filepath) == 0 {
  44. return
  45. }
  46. f, err := os.Open(filepath)
  47. if err != nil {
  48. log.Fatalf("Can't open file %s\n", filepath)
  49. }
  50. src, err := ioutil.ReadAll(f)
  51. if err != nil {
  52. log.Fatalln(err)
  53. }
  54. src = bytes.TrimSpace(src)
  55. dst := make([]byte, hex.DecodedLen(len(src)))
  56. n, err := hex.Decode(dst, src)
  57. if err != nil {
  58. log.Fatalf("%s expected to contain hex-encoded string\n", filepath)
  59. }
  60. *b = dst[:n]
  61. }
  62. type config struct {
  63. Bind string
  64. ReadTimeout int
  65. WaitTimeout int
  66. WriteTimeout int
  67. DownloadTimeout int
  68. Concurrency int
  69. MaxClients int
  70. TTL int
  71. MaxSrcDimension int
  72. MaxSrcResolution int
  73. JpegProgressive bool
  74. PngInterlaced bool
  75. Quality int
  76. GZipCompression int
  77. Key []byte
  78. Salt []byte
  79. Secret string
  80. AllowOrigin string
  81. IgnoreSslVerification bool
  82. LocalFileSystemRoot string
  83. ETagEnabled bool
  84. BaseURL string
  85. }
  86. var conf = config{
  87. Bind: ":8080",
  88. ReadTimeout: 10,
  89. WriteTimeout: 10,
  90. DownloadTimeout: 5,
  91. Concurrency: runtime.NumCPU() * 2,
  92. TTL: 3600,
  93. IgnoreSslVerification: false,
  94. MaxSrcDimension: 8192,
  95. MaxSrcResolution: 16800000,
  96. Quality: 80,
  97. GZipCompression: 5,
  98. ETagEnabled: false,
  99. }
  100. func init() {
  101. keypath := flag.String("keypath", "", "path of the file with hex-encoded key")
  102. saltpath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
  103. showVersion := flag.Bool("v", false, "show version")
  104. flag.Parse()
  105. if *showVersion {
  106. fmt.Println(version)
  107. os.Exit(0)
  108. }
  109. if port := os.Getenv("PORT"); len(port) > 0 {
  110. conf.Bind = fmt.Sprintf(":%s", port)
  111. }
  112. strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
  113. intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
  114. intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
  115. intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
  116. intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
  117. intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
  118. intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
  119. intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
  120. megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
  121. boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
  122. boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
  123. intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
  124. intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
  125. hexEnvConfig(&conf.Key, "IMGPROXY_KEY")
  126. hexEnvConfig(&conf.Salt, "IMGPROXY_SALT")
  127. hexFileConfig(&conf.Key, *keypath)
  128. hexFileConfig(&conf.Salt, *saltpath)
  129. strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
  130. strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
  131. boolEnvConfig(&conf.IgnoreSslVerification, "IMGPROXY_IGNORE_SSL_VERIFICATION")
  132. strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
  133. boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
  134. strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
  135. if len(conf.Key) == 0 {
  136. log.Fatalln("Key is not defined")
  137. }
  138. if len(conf.Salt) == 0 {
  139. log.Fatalln("Salt is not defined")
  140. }
  141. if len(conf.Bind) == 0 {
  142. log.Fatalln("Bind address is not defined")
  143. }
  144. if conf.ReadTimeout <= 0 {
  145. log.Fatalf("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
  146. }
  147. if conf.WriteTimeout <= 0 {
  148. log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
  149. }
  150. if conf.DownloadTimeout <= 0 {
  151. log.Fatalf("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
  152. }
  153. if conf.Concurrency <= 0 {
  154. log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
  155. }
  156. if conf.MaxClients <= 0 {
  157. conf.MaxClients = conf.Concurrency * 10
  158. }
  159. if conf.TTL <= 0 {
  160. log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL)
  161. }
  162. if conf.MaxSrcDimension <= 0 {
  163. log.Fatalf("Max src dimension should be greater than 0, now - %d\n", conf.MaxSrcDimension)
  164. }
  165. if conf.MaxSrcResolution <= 0 {
  166. log.Fatalf("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
  167. }
  168. if conf.Quality <= 0 {
  169. log.Fatalf("Quality should be greater than 0, now - %d\n", conf.Quality)
  170. } else if conf.Quality > 100 {
  171. log.Fatalf("Quality can't be greater than 100, now - %d\n", conf.Quality)
  172. }
  173. if conf.GZipCompression < 0 {
  174. log.Fatalf("GZip compression should be greater than or quual to 0, now - %d\n", conf.GZipCompression)
  175. } else if conf.GZipCompression > 9 {
  176. log.Fatalf("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
  177. }
  178. if conf.IgnoreSslVerification {
  179. log.Println("Ignoring SSL verification is very unsafe. Hope you know what you're doing")
  180. }
  181. if conf.LocalFileSystemRoot != "" {
  182. stat, err := os.Stat(conf.LocalFileSystemRoot)
  183. if err != nil {
  184. log.Fatalf("Cannot use local directory: %s", err)
  185. } else {
  186. if !stat.IsDir() {
  187. log.Fatalf("Cannot use local directory: not a directory")
  188. }
  189. }
  190. if conf.LocalFileSystemRoot == "/" {
  191. log.Print("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
  192. }
  193. }
  194. initVips()
  195. initDownloading()
  196. }