diff --git a/config/config.go b/config/config.go index 59e02080..18f41dd5 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "net/http" "strings" "time" @@ -95,6 +96,7 @@ func setConfigDefaults(v *viper.Viper) { v.SetDefault("request_limits.max_size_bytes", utils.REQUEST_MAX_SIZE_BYTES) v.SetDefault("request_limits.max_num_values", utils.REQUEST_MAX_NUM_VALUES) v.SetDefault("request_limits.max_ttl_seconds", utils.REQUEST_MAX_TTL_SECONDS) + v.SetDefault("request_limits.max_header_size_bytes", http.DefaultMaxHeaderBytes) v.SetDefault("routes.allow_public_write", true) } @@ -179,6 +181,7 @@ type RequestLimits struct { MaxNumValues int `mapstructure:"max_num_values"` MaxTTLSeconds int `mapstructure:"max_ttl_seconds"` AllowSettingKeys bool `mapstructure:"allow_setting_keys"` + MaxHeaderSize int `mapstructure:"max_header_size_bytes"` } func (cfg *RequestLimits) validateAndLog() { @@ -201,6 +204,12 @@ func (cfg *RequestLimits) validateAndLog() { } else { log.Fatalf("invalid config.request_limits.max_num_values: %d. Value cannot be negative.", cfg.MaxNumValues) } + + if cfg.MaxHeaderSize >= 0 { + log.Infof("config.request_limits.max_header_size_bytes: %d", cfg.MaxHeaderSize) + } else { + log.Fatalf("invalid config.request_limits.max_header_size_bytes: %d. Value cannot be negative.", cfg.MaxHeaderSize) + } } type Compression struct { diff --git a/config/config_test.go b/config/config_test.go index 8ab6da94..1c12cdb1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -867,6 +867,18 @@ func TestRequestLimitsValidateAndLog(t *testing.T) { }, expectFatal: true, }, + { + description: "Negative max_header_size_bytes, expect fatal level log and early exit", + inRequestLimitsCfg: &RequestLimits{MaxHeaderSize: -1}, + expectedLogInfo: []logComponents{ + {msg: `config.request_limits.allow_setting_keys: false`, lvl: logrus.InfoLevel}, + {msg: `config.request_limits.max_ttl_seconds: 0`, lvl: logrus.InfoLevel}, + {msg: `config.request_limits.max_size_bytes: 0`, lvl: logrus.InfoLevel}, + {msg: `config.request_limits.max_num_values: 0`, lvl: logrus.InfoLevel}, + {msg: `invalid config.request_limits.max_header_size_bytes: -1. Value cannot be negative.`, lvl: logrus.FatalLevel}, + }, + expectFatal: true, + }, } //substitute logger exit function so execution doesn't get interrupted @@ -1078,6 +1090,7 @@ func TestConfigurationValidateAndLog(t *testing.T) { {msg: fmt.Sprintf("config.request_limits.max_ttl_seconds: %d", expectedConfig.RequestLimits.MaxTTLSeconds), lvl: logrus.InfoLevel}, {msg: fmt.Sprintf("config.request_limits.max_size_bytes: %d", expectedConfig.RequestLimits.MaxSize), lvl: logrus.InfoLevel}, {msg: fmt.Sprintf("config.request_limits.max_num_values: %d", expectedConfig.RequestLimits.MaxNumValues), lvl: logrus.InfoLevel}, + {msg: fmt.Sprintf("config.request_limits.max_header_size_bytes: %d", expectedConfig.RequestLimits.MaxHeaderSize), lvl: logrus.InfoLevel}, {msg: fmt.Sprintf("config.backend.type: %s", expectedConfig.Backend.Type), lvl: logrus.InfoLevel}, {msg: fmt.Sprintf("config.compression.type: %s", expectedConfig.Compression.Type), lvl: logrus.InfoLevel}, {msg: fmt.Sprintf("Prebid Cache will run without metrics"), lvl: logrus.InfoLevel}, @@ -1219,6 +1232,7 @@ func getExpectedDefaultConfig() Configuration { MaxSize: 10240, MaxNumValues: 10, MaxTTLSeconds: 3600, + MaxHeaderSize: 1048576, }, Routes: Routes{ AllowPublicWrite: true, @@ -1244,6 +1258,7 @@ func getExpectedFullConfigForTestFile() Configuration { MaxNumValues: 10, MaxTTLSeconds: 5000, AllowSettingKeys: true, + MaxHeaderSize: 16384, //16KiB }, Backend: Backend{ Type: BackendMemory, diff --git a/config/configtest/sample_full_config.yaml b/config/configtest/sample_full_config.yaml index 3c40fbb7..23854c9f 100644 --- a/config/configtest/sample_full_config.yaml +++ b/config/configtest/sample_full_config.yaml @@ -11,6 +11,7 @@ request_limits: max_num_values: 10 max_ttl_seconds: 5000 allow_setting_keys: true + max_header_size_bytes: 16384 backend: type: "memory" aerospike: diff --git a/server/server.go b/server/server.go index 4f75d5a7..6f103e2c 100644 --- a/server/server.go +++ b/server/server.go @@ -74,20 +74,37 @@ func Listen(cfg config.Configuration, publicHandler http.Handler, adminHandler h return } +// newAdminServer returns an http.Server with the AdminPort and RequestLimits.MaxHeaderBytes +// from Prebid Cache's config files or environment variables. If RequestLimits.MaxHeaderBytes +// is zero or was not specified, the http library's DefaultMaxHeaderBytes value of 1 MB +// is set instead. func newAdminServer(cfg config.Configuration, handler http.Handler) *http.Server { - return &http.Server{ + server := &http.Server{ Addr: ":" + strconv.Itoa(cfg.AdminPort), Handler: handler, } + if cfg.RequestLimits.MaxHeaderSize > 0 { + server.MaxHeaderBytes = cfg.RequestLimits.MaxHeaderSize + } + return server } +// newMainServer returns an http.Server with the configured Port and +// RequestLimits.MaxHeaderBytes values specified in Prebid Cache's config files +// or environment variables. If RequestLimits.MaxHeaderBytes is zero or was not +// specified, 1 MB, which is the value of the http library's DefaultMaxHeaderBytes, +// is set instead. func newMainServer(cfg config.Configuration, handler http.Handler) *http.Server { - return &http.Server{ + server := &http.Server{ Addr: ":" + strconv.Itoa(cfg.Port), Handler: handler, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } + if cfg.RequestLimits.MaxHeaderSize > 0 { + server.MaxHeaderBytes = cfg.RequestLimits.MaxHeaderSize + } + return server } func runServer(server *http.Server, name string, listener net.Listener) {