diff --git a/README.md b/README.md index 3b0cc322..33bc7699 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ You can easily generate key and salt with `openssl enc -aes-256-cbc -P -md sha25 * 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_CONCURRENCY - the maximum images to be processed simultaneously. Default: 100; #### Security diff --git a/config.go b/config.go index f057982f..95997c56 100644 --- a/config.go +++ b/config.go @@ -62,8 +62,8 @@ type config struct { Bind string ReadTimeout int WriteTimeout int - - TTL int + Concurrency int + TTL int MaxSrcDimension int @@ -80,6 +80,7 @@ var conf = config{ Bind: ":8080", ReadTimeout: 10, WriteTimeout: 10, + Concurrency: 100, TTL: 3600, MaxSrcDimension: 4096, Quality: 80, @@ -94,6 +95,7 @@ func init() { strEnvConfig(&conf.Bind, "IMGPROXY_BIND") intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT") intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT") + intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY") intEnvConfig(&conf.TTL, "IMGPROXY_TTL") @@ -129,6 +131,10 @@ func init() { log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout) } + if conf.Concurrency <= 0 { + log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency) + } + if conf.TTL <= 0 { log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL) } diff --git a/main.go b/main.go index d146f798..b937a4a3 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( func main() { s := &http.Server{ Addr: conf.Bind, - Handler: httpHandler{}, + Handler: newHttpHandler(), ReadTimeout: time.Duration(conf.ReadTimeout) * time.Second, WriteTimeout: time.Duration(conf.WriteTimeout) * time.Second, MaxHeaderBytes: 1 << 20, diff --git a/server.go b/server.go index 529b40f0..936d34f0 100644 --- a/server.go +++ b/server.go @@ -16,7 +16,13 @@ import ( "time" ) -type httpHandler struct{} +type httpHandler struct { + sem chan struct{} +} + +func newHttpHandler() httpHandler { + return httpHandler{make(chan struct{}, conf.Concurrency)} +} func parsePath(r *http.Request) (string, processingOptions, error) { var po processingOptions @@ -134,9 +140,20 @@ func checkSecret(s string) bool { return strings.HasPrefix(s, "Bearer ") && subtle.ConstantTimeCompare([]byte(strings.TrimPrefix(s, "Bearer ")), []byte(conf.Secret)) == 1 } +func (h *httpHandler) lock() { + h.sem <- struct{}{} +} + +func (h *httpHandler) unlock() { + defer func() { <-h.sem }() +} + func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { log.Printf("GET: %s\n", r.URL.RequestURI()) + h.lock() + defer h.unlock() + t := time.Now() if !checkSecret(r.Header.Get("Authorization")) {