diff --git a/config.go b/config.go index 88c22548..19de4282 100644 --- a/config.go +++ b/config.go @@ -136,6 +136,7 @@ type config struct { Concurrency int MaxClients int TTL int + SoReuseport bool MaxSrcDimension int MaxSrcResolution int @@ -258,6 +259,8 @@ func configure() { intEnvConfig(&conf.TTL, "IMGPROXY_TTL") + boolEnvConfig(&conf.SoReuseport, "IMGPROXY_SO_REUSEPORT") + intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION") megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION") intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE") diff --git a/docs/configuration.md b/docs/configuration.md index e6ac1a75..3c45a3eb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -34,6 +34,7 @@ $ echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n') * `IMGPROXY_CONCURRENCY`: the maximum number of image requests to be processed simultaneously. Default: number of CPU cores times two; * `IMGPROXY_MAX_CLIENTS`: the maximum number of simultaneous active connections. Default: `IMGPROXY_CONCURRENCY * 10`; * `IMGPROXY_TTL`: duration (in seconds) sent in `Expires` and `Cache-Control: max-age` HTTP headers. Default: `3600` (1 hour); +* `IMGPROXY_SO_REUSEPORT`: when `true`, enables `SO_REUSEPORT` socket option (currently on linux and darwin only); * `IMGPROXY_USER_AGENT`: User-Agent header that will be sent with source image request. Default: `imgproxy/%current_version`; * `IMGPROXY_USE_ETAG`: when `true`, enables using [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) HTTP header for HTTP cache control. Default: false; diff --git a/go.mod b/go.mod index 20c4f0c9..b6aea168 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( golang.org/x/net v0.0.0-20190110200230-915654e7eabc golang.org/x/oauth2 v0.0.0-20190110195249-fd3eaa146cbb // indirect golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 - golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb // indirect + golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb google.golang.org/api v0.1.0 google.golang.org/genproto v0.0.0-20190110221437-6909d8a4a91b // indirect ) diff --git a/listen_no_reuseport.go b/listen_no_reuseport.go new file mode 100644 index 00000000..a6fdf5b9 --- /dev/null +++ b/listen_no_reuseport.go @@ -0,0 +1,15 @@ +// +build !linux,!darwin !go1.11 + +package main + +import ( + "net" +) + +func listenReuseport(network, address string) (net.Listener, error) { + if conf.SoReuseport { + logWarning("SO_REUSEPORT support is not implemented for your OS or Go version") + } + + return net.Listen(network, address) +} diff --git a/listen_reuseport.go b/listen_reuseport.go new file mode 100644 index 00000000..12ba8d4e --- /dev/null +++ b/listen_reuseport.go @@ -0,0 +1,33 @@ +// +build linux darwin +// +build go1.11 + +package main + +import ( + "context" + "net" + "syscall" + + "golang.org/x/sys/unix" +) + +func listenReuseport(network, address string) (net.Listener, error) { + if !conf.SoReuseport { + return net.Listen(network, address) + } + + lc := net.ListenConfig{ + Control: func(_, _ string, c syscall.RawConn) error { + var cerr error + err := c.Control(func(fd uintptr) { + cerr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + }) + if err != nil { + return err + } + return cerr + }, + } + + return lc.Listen(context.Background(), network, address) +} diff --git a/prometheus.go b/prometheus.go index fe1fa03b..af6eb493 100644 --- a/prometheus.go +++ b/prometheus.go @@ -100,14 +100,16 @@ func initPrometheus() { prometheusEnabled = true - s := http.Server{ - Addr: conf.PrometheusBind, - Handler: promhttp.Handler(), - } + s := http.Server{Handler: promhttp.Handler()} go func() { - logNotice("Starting Prometheus server at %s\n", s.Addr) - if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed { + l, err := listenReuseport("tcp", conf.PrometheusBind) + if err != nil { + logFatal(err.Error()) + } + + logNotice("Starting Prometheus server at %s\n", conf.PrometheusBind) + if err := s.Serve(l); err != nil && err != http.ErrServerClosed { logFatal(err.Error()) } }() diff --git a/server.go b/server.go index 8b18a48b..f9f2fd13 100644 --- a/server.go +++ b/server.go @@ -4,7 +4,6 @@ import ( "context" "crypto/subtle" "fmt" - "net" "net/http" "time" @@ -30,7 +29,7 @@ func buildRouter() *router { } func startServer() *http.Server { - l, err := net.Listen("tcp", conf.Bind) + l, err := listenReuseport("tcp", conf.Bind) if err != nil { logFatal(err.Error()) }