diff --git a/.gitignore b/.gitignore index c09bbeb0..a22238de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ imgproxy -config.yml +tmp/ diff --git a/Makefile b/Makefile deleted file mode 100644 index 45590972..00000000 --- a/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -current_dir := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) -vendor := $(current_dir)/_vendor -goenv := GOPATH="$(vendor):$(GOPATH)" - -all: clean vendorize build - -clean: - rm -rf bin/ - -vendorize: - cd $(current_dir) - GOPATH=$(vendor) go get -d - find $(vendor) -name ".git" -type d | xargs rm -rf - -clean-vendor: - rm -rf $(vendor) - -hard-vendorize: clean-vendor vendorize - -build: - cd $(current_dir) - $(goenv) go build -v -ldflags '-w -s' -o bin/server diff --git a/README.md b/README.md index 0080650a..039d39cd 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,30 @@ Tiny, fast and secure server for processing remote images. +Full README is on the way. + +### How to configure + +Imgproxy is 12factor-ready and can be configured with env variables: + +* IMGPROXY_BIND - TCP address to listen on. Default: :8080; +* IMGPROXY_READ_TIMEOUT - the maximum duration (seconds) for reading the entire request, including the body. Default: 10; +* IMGPROXY_WRITE_TIMEOUT - the maximum duration (seconds) for writing the response. Default: 10; +* IMGPROXY_MAX_SRC_DIMENSION - the maximum dimension of source image. Images with larger size will be rejected. Default: 4096; +* IMGPROXY_QUALITY - quality of a result image. Default: 80; +* IMGPROXY_COMPRESSION - compression of a result image. Default: 6; +* IMGPROXY_KEY - hex-encoded key +* IMGPROXY_SALT - hex-encoded salt + +You can also specify paths to a files with hex-encoded key and salt: + +``` +imgproxy -keypath /path/to/file/with/key -salt /path/to/file/with/salt +``` + ### How to generate url path -Full README is on the way. Here is a short sample which shows how to generate url -path for imgproxy. +Here is a short ruby sample which shows how to generate url path for imgproxy. ```ruby require 'openssl' diff --git a/config.go b/config.go index d09977d9..68dbe490 100644 --- a/config.go +++ b/config.go @@ -1,93 +1,125 @@ package main import ( + "bytes" "encoding/hex" "flag" "io/ioutil" "log" "os" "path/filepath" - - "gopkg.in/yaml.v2" + "strconv" ) -type config struct { - Bind string - ReadTimeout int `yaml:"read_timeout"` - WriteTimeout int `yaml:"write_timeout"` - - Key string - Salt string - KeyBin []byte - SaltBin []byte - - MaxSrcDimension int `yaml:"max_src_dimension"` - - Quality int - Compression int -} - -var conf = config{ - Bind: ":8080", - MaxSrcDimension: 4096, -} - func absPathToFile(path string) string { if filepath.IsAbs(path) { return path } - appPath, _ := filepath.Abs(filepath.Dir(os.Args[0])) + appPath, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + log.Fatalln(err) + } + return filepath.Join(appPath, path) } -func init() { - cpath := flag.String( - "config", "./config.yml", "path to configuration file", - ) - flag.Parse() - - file, err := os.Open(absPathToFile(*cpath)) - if err != nil { - log.Fatalln(err) - } - defer file.Close() - - cdata, err := ioutil.ReadAll(file) - if err != nil { - log.Fatalln(err) - } - - err = yaml.Unmarshal(cdata, &conf) - if err != nil { - log.Fatalln(err) - } - - if len(conf.Bind) == 0 { - conf.Bind = ":8080" - } - - if conf.MaxSrcDimension == 0 { - conf.MaxSrcDimension = 4096 - } - - if conf.KeyBin, err = hex.DecodeString(conf.Key); err != nil { - log.Fatalln("Invalid key. Key should be encoded to hex") - } - - if conf.SaltBin, err = hex.DecodeString(conf.Salt); err != nil { - log.Fatalln("Invalid salt. Salt should be encoded to hex") - } - - if conf.MaxSrcDimension == 0 { - conf.MaxSrcDimension = 4096 - } - - if conf.Quality == 0 { - conf.Quality = 80 - } - - if conf.Compression == 0 { - conf.Compression = 6 +func intEnvConfig(i *int, name string) { + if env, err := strconv.Atoi(os.Getenv(name)); err == nil { + *i = env + } +} + +func strEnvConfig(s *string, name string) { + if env := os.Getenv(name); len(env) > 0 { + *s = env + } +} + +func hexEnvConfig(b *[]byte, name string) { + var err error + + if env := os.Getenv(name); len(env) > 0 { + if *b, err = hex.DecodeString(env); err != nil { + log.Fatalf("%s expected to be hex-encoded string\n", name) + } + } +} + +func hexFileConfig(b *[]byte, filepath string) { + if len(filepath) == 0 { + return + } + + fullfp := absPathToFile(filepath) + f, err := os.Open(fullfp) + if err != nil { + log.Fatalf("Can't open file %s\n", fullfp) + } + + src, err := ioutil.ReadAll(f) + if err != nil { + log.Fatalln(err) + } + + src = bytes.TrimSpace(src) + + dst := make([]byte, hex.DecodedLen(len(src))) + n, err := hex.Decode(dst, src) + if err != nil { + log.Fatalf("%s expected to contain hex-encoded string\n", fullfp) + } + + *b = dst[:n] +} + +type config struct { + Bind string + ReadTimeout int + WriteTimeout int + + MaxSrcDimension int + + Quality int + Compression int + + Key []byte + Salt []byte +} + +var conf = config{ + Bind: ":8080", + ReadTimeout: 10, + WriteTimeout: 10, + MaxSrcDimension: 4096, + Quality: 80, + Compression: 6, +} + +func init() { + keypath := flag.String("keypath", "", "path of the file with hex-encoded key") + saltpath := flag.String("saltpath", "", "path of the file with hex-encoded salt") + flag.Parse() + + strEnvConfig(&conf.Bind, "IMGPROXY_BIND") + intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT") + intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT") + + intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION") + + intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY") + intEnvConfig(&conf.Compression, "IMGPROXY_COMPRESSION") + + hexEnvConfig(&conf.Key, "IMGPROXY_KEY") + hexEnvConfig(&conf.Salt, "IMGPROXY_SALT") + + hexFileConfig(&conf.Key, *keypath) + hexFileConfig(&conf.Salt, *saltpath) + + if len(conf.Key) == 0 { + log.Fatalln("Key is not defined") + } + if len(conf.Salt) == 0 { + log.Fatalln("Salt is not defined") } } diff --git a/config.yml.example b/config.yml.example deleted file mode 100644 index 96fba1e6..00000000 --- a/config.yml.example +++ /dev/null @@ -1,12 +0,0 @@ -bind: ":8080" -read_timeout: 10 -write_timeout: 10 - -# key and salt are hex-encoded -key: 943b421c9eb07c830af81030552c86009268de4e532ba2ee2eab8247c6da0881 -salt: 520f986b998545b4785e0defbc4f3c1203f22de2374a3d53cb7a7fe9fea309c5 - -max_src_dimension: 4096 - -quality: 80 -compression: 6 diff --git a/crypt.go b/crypt.go index 74704177..4f5a41c5 100644 --- a/crypt.go +++ b/crypt.go @@ -13,8 +13,8 @@ func validatePath(token, path string) error { return errors.New("Invalid token encoding") } - mac := hmac.New(sha256.New, conf.KeyBin) - mac.Write(conf.SaltBin) + mac := hmac.New(sha256.New, conf.Key) + mac.Write(conf.Salt) mac.Write([]byte(path)) expectedMAC := mac.Sum(nil) diff --git a/glide.lock b/glide.lock index d9f7bb04..b932ee32 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,8 @@ hash: de73486dba2fd32ca4e577ba7baa1552e33431fa2a88c1f34ba9b7af77e9c75b -updated: 2017-06-20T17:49:53.147746484+03:00 +updated: 2017-06-25T23:28:17.564332341+03:00 imports: - name: github.com/h2non/bimg - version: 45f8993550e71ee7b8001d40c681c6c9fa822357 + version: 32e459e416aa4abab45ba854298602cb9682256d - name: github.com/tj/go-debug version: ff4a55a20a86994118644bbddc6a216da193cc13 - name: gopkg.in/yaml.v2 diff --git a/vendor/github.com/h2non/bimg/.travis.yml b/vendor/github.com/h2non/bimg/.travis.yml index 0215050e..1e71a542 100644 --- a/vendor/github.com/h2non/bimg/.travis.yml +++ b/vendor/github.com/h2non/bimg/.travis.yml @@ -14,7 +14,7 @@ env: - LIBVIPS=8.2.3 - LIBVIPS=8.3.3 - LIBVIPS=8.4.5 - - LIBVIPS=8.5.5 + - LIBVIPS=8.5.6 - LIBVIPS=master matrix: diff --git a/vendor/github.com/h2non/bimg/History.md b/vendor/github.com/h2non/bimg/History.md index 57cdfc54..1214d718 100644 --- a/vendor/github.com/h2non/bimg/History.md +++ b/vendor/github.com/h2non/bimg/History.md @@ -1,4 +1,14 @@ +## v1.0.10 / 2017-06-25 + + * Merge pull request #164 from greut/length + * Add Image.Length() + * Merge pull request #163 from greut/libvips856 + * Run libvips 8.5.6 on Travis. + * Merge pull request #161 from henry-blip/master + * Expose vips cache memory management functions. + * feat(docs): add watermark image note in features + ## v1.0.9 / 2017-05-25 * Merge pull request #156 from Dynom/SmartCropToGravity diff --git a/vendor/github.com/h2non/bimg/README.md b/vendor/github.com/h2non/bimg/README.md index 022f6540..a1b08365 100644 --- a/vendor/github.com/h2non/bimg/README.md +++ b/vendor/github.com/h2non/bimg/README.md @@ -42,7 +42,7 @@ If you're using `gopkg.in`, you can still rely in the `v0` without worrying abou - Zoom - Thumbnail - Extract area -- Watermark (text only) +- Watermark (using text or image) - Gaussian blur effect - Custom output color space (RGB, grayscale...) - Format conversion (with additional quality/compression settings) diff --git a/vendor/github.com/h2non/bimg/image.go b/vendor/github.com/h2non/bimg/image.go index efaffcb7..7cff0791 100644 --- a/vendor/github.com/h2non/bimg/image.go +++ b/vendor/github.com/h2non/bimg/image.go @@ -217,7 +217,12 @@ func (i *Image) Size() (ImageSize, error) { return Size(i.buffer) } -// Image returns the current resultant image image buffer. +// Image returns the current resultant image buffer. func (i *Image) Image() []byte { return i.buffer } + +// Length returns the size in bytes of the image buffer. +func (i *Image) Length() int { + return len(i.buffer) +} diff --git a/vendor/github.com/h2non/bimg/image_test.go b/vendor/github.com/h2non/bimg/image_test.go index 96fc4ad9..aff3d3d3 100644 --- a/vendor/github.com/h2non/bimg/image_test.go +++ b/vendor/github.com/h2non/bimg/image_test.go @@ -475,6 +475,17 @@ func TestImageSmartCrop(t *testing.T) { Write("fixtures/test_smart_crop.jpg", buf) } +func TestImageLength(t *testing.T) { + i := initImage("test.jpg") + + actual := i.Length() + expected := 53653 + + if expected != actual { + t.Errorf("Size in Bytes of the image doesn't correspond. %d != %d", expected, actual) + } +} + func initImage(file string) *Image { buf, _ := imageBuf(file) return NewImage(buf) diff --git a/vendor/github.com/h2non/bimg/version.go b/vendor/github.com/h2non/bimg/version.go index 682eaaaf..7d5ab792 100644 --- a/vendor/github.com/h2non/bimg/version.go +++ b/vendor/github.com/h2non/bimg/version.go @@ -1,4 +1,4 @@ package bimg // Version represents the current package semantic version. -const Version = "1.0.9" +const Version = "1.0.10" diff --git a/vendor/github.com/h2non/bimg/vips.go b/vendor/github.com/h2non/bimg/vips.go index ae654c5c..221233fa 100644 --- a/vendor/github.com/h2non/bimg/vips.go +++ b/vendor/github.com/h2non/bimg/vips.go @@ -130,6 +130,22 @@ func Shutdown() { } } +// VipsCacheSetMaxMem Sets the maximum amount of tracked memory allowed before the vips operation cache +// begins to drop entries. +func VipsCacheSetMaxMem(maxCacheMem int) { + C.vips_cache_set_max_mem(C.size_t(maxCacheMem)) +} + +// VipsCacheSetMax sets the maximum number of operations to keep in the vips operation cache. +func VipsCacheSetMax(maxCacheSize int) { + C.vips_cache_set_max(C.int(maxCacheSize)) +} + +// VipsCacheDropAll drops the vips operation cache, freeing the allocated memory. +func VipsCacheDropAll() { + C.vips_cache_drop_all() +} + // VipsDebugInfo outputs to stdout libvips collected data. Useful for debugging. func VipsDebugInfo() { C.im__print_all()