@@ -25,6 +25,7 @@ const (
2525 defaultListenHost = "0.0.0.0"
2626 defaultListenPort = 8080
2727 defaultLogFormat = "text"
28+ defaultEnvPrefix = "HTTPBIN_ENV_"
2829
2930 // Reasonable defaults for our http server
3031 srvReadTimeout = 5 * time .Second
@@ -35,13 +36,13 @@ const (
3536// Main is the main entrypoint for the go-httpbin binary. See loadConfig() for
3637// command line argument parsing.
3738func Main () int {
38- return mainImpl (os .Args [1 :], os .Getenv , os .Hostname , os .Stderr )
39+ return mainImpl (os .Args [1 :], os .Getenv , os .Environ , os . Hostname , os .Stderr )
3940}
4041
4142// mainImpl is the real implementation of Main(), extracted for better
4243// testability.
43- func mainImpl (args []string , getEnv func (string ) string , getHostname func () (string , error ), out io.Writer ) int {
44- cfg , err := loadConfig (args , getEnv , getHostname )
44+ func mainImpl (args []string , getEnvVal func (string ) string , getEnviron func () [] string , getHostname func () (string , error ), out io.Writer ) int {
45+ cfg , err := loadConfig (args , getEnvVal , getEnviron , getHostname )
4546 if err != nil {
4647 if cfgErr , ok := err .(ConfigError ); ok {
4748 // for -h/-help, just print usage and exit without error
@@ -75,6 +76,7 @@ func mainImpl(args []string, getEnv func(string) string, getHostname func() (str
7576 }
7677
7778 opts := []httpbin.OptionFunc {
79+ httpbin .WithEnv (cfg .Env ),
7880 httpbin .WithMaxBodySize (cfg .MaxBodySize ),
7981 httpbin .WithMaxDuration (cfg .MaxDuration ),
8082 httpbin .WithObserver (httpbin .StdLogObserver (logger )),
@@ -110,6 +112,7 @@ func mainImpl(args []string, getEnv func(string) string, getHostname func() (str
110112// config holds the configuration needed to initialize and run go-httpbin as a
111113// standalone server.
112114type config struct {
115+ Env map [string ]string
113116 AllowedRedirectDomains []string
114117 ListenHost string
115118 ExcludeHeaders string
@@ -144,7 +147,7 @@ func (e ConfigError) Error() string {
144147
145148// loadConfig parses command line arguments and env vars into a fully resolved
146149// Config struct. Command line arguments take precedence over env vars.
147- func loadConfig (args []string , getEnv func (string ) string , getHostname func () (string , error )) (* config , error ) {
150+ func loadConfig (args []string , getEnvVal func (string ) string , getEnviron func () [] string , getHostname func () (string , error )) (* config , error ) {
148151 cfg := & config {}
149152
150153 fs := flag .NewFlagSet ("go-httpbin" , flag .ContinueOnError )
@@ -192,24 +195,24 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
192195 // Command line flags take precedence over environment vars, so we only
193196 // check for environment vars if we have default values for our command
194197 // line flags.
195- if cfg .MaxBodySize == httpbin .DefaultMaxBodySize && getEnv ("MAX_BODY_SIZE" ) != "" {
196- cfg .MaxBodySize , err = strconv .ParseInt (getEnv ("MAX_BODY_SIZE" ), 10 , 64 )
198+ if cfg .MaxBodySize == httpbin .DefaultMaxBodySize && getEnvVal ("MAX_BODY_SIZE" ) != "" {
199+ cfg .MaxBodySize , err = strconv .ParseInt (getEnvVal ("MAX_BODY_SIZE" ), 10 , 64 )
197200 if err != nil {
198- return nil , configErr ("invalid value %#v for env var MAX_BODY_SIZE: parse error" , getEnv ("MAX_BODY_SIZE" ))
201+ return nil , configErr ("invalid value %#v for env var MAX_BODY_SIZE: parse error" , getEnvVal ("MAX_BODY_SIZE" ))
199202 }
200203 }
201204
202- if cfg .MaxDuration == httpbin .DefaultMaxDuration && getEnv ("MAX_DURATION" ) != "" {
203- cfg .MaxDuration , err = time .ParseDuration (getEnv ("MAX_DURATION" ))
205+ if cfg .MaxDuration == httpbin .DefaultMaxDuration && getEnvVal ("MAX_DURATION" ) != "" {
206+ cfg .MaxDuration , err = time .ParseDuration (getEnvVal ("MAX_DURATION" ))
204207 if err != nil {
205- return nil , configErr ("invalid value %#v for env var MAX_DURATION: parse error" , getEnv ("MAX_DURATION" ))
208+ return nil , configErr ("invalid value %#v for env var MAX_DURATION: parse error" , getEnvVal ("MAX_DURATION" ))
206209 }
207210 }
208- if cfg .ListenHost == defaultListenHost && getEnv ("HOST" ) != "" {
209- cfg .ListenHost = getEnv ("HOST" )
211+ if cfg .ListenHost == defaultListenHost && getEnvVal ("HOST" ) != "" {
212+ cfg .ListenHost = getEnvVal ("HOST" )
210213 }
211214 if cfg .Prefix == "" {
212- if prefix := getEnv ("PREFIX" ); prefix != "" {
215+ if prefix := getEnvVal ("PREFIX" ); prefix != "" {
213216 cfg .Prefix = prefix
214217 }
215218 }
@@ -221,29 +224,29 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
221224 return nil , configErr ("Prefix %#v must not end with a slash" , cfg .Prefix )
222225 }
223226 }
224- if cfg .ExcludeHeaders == "" && getEnv ("EXCLUDE_HEADERS" ) != "" {
225- cfg .ExcludeHeaders = getEnv ("EXCLUDE_HEADERS" )
227+ if cfg .ExcludeHeaders == "" && getEnvVal ("EXCLUDE_HEADERS" ) != "" {
228+ cfg .ExcludeHeaders = getEnvVal ("EXCLUDE_HEADERS" )
226229 }
227- if cfg .ListenPort == defaultListenPort && getEnv ("PORT" ) != "" {
228- cfg .ListenPort , err = strconv .Atoi (getEnv ("PORT" ))
230+ if cfg .ListenPort == defaultListenPort && getEnvVal ("PORT" ) != "" {
231+ cfg .ListenPort , err = strconv .Atoi (getEnvVal ("PORT" ))
229232 if err != nil {
230- return nil , configErr ("invalid value %#v for env var PORT: parse error" , getEnv ("PORT" ))
233+ return nil , configErr ("invalid value %#v for env var PORT: parse error" , getEnvVal ("PORT" ))
231234 }
232235 }
233236
234- if cfg .TLSCertFile == "" && getEnv ("HTTPS_CERT_FILE" ) != "" {
235- cfg .TLSCertFile = getEnv ("HTTPS_CERT_FILE" )
237+ if cfg .TLSCertFile == "" && getEnvVal ("HTTPS_CERT_FILE" ) != "" {
238+ cfg .TLSCertFile = getEnvVal ("HTTPS_CERT_FILE" )
236239 }
237- if cfg .TLSKeyFile == "" && getEnv ("HTTPS_KEY_FILE" ) != "" {
238- cfg .TLSKeyFile = getEnv ("HTTPS_KEY_FILE" )
240+ if cfg .TLSKeyFile == "" && getEnvVal ("HTTPS_KEY_FILE" ) != "" {
241+ cfg .TLSKeyFile = getEnvVal ("HTTPS_KEY_FILE" )
239242 }
240243 if cfg .TLSCertFile != "" || cfg .TLSKeyFile != "" {
241244 if cfg .TLSCertFile == "" || cfg .TLSKeyFile == "" {
242245 return nil , configErr ("https cert and key must both be provided" )
243246 }
244247 }
245- if cfg .LogFormat == defaultLogFormat && getEnv ("LOG_FORMAT" ) != "" {
246- cfg .LogFormat = getEnv ("LOG_FORMAT" )
248+ if cfg .LogFormat == defaultLogFormat && getEnvVal ("LOG_FORMAT" ) != "" {
249+ cfg .LogFormat = getEnvVal ("LOG_FORMAT" )
247250 }
248251 if cfg .LogFormat != "text" && cfg .LogFormat != "json" {
249252 return nil , configErr (`invalid log format %q, must be "text" or "json"` , cfg .LogFormat )
@@ -252,7 +255,7 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
252255 // useRealHostname will be true if either the `-use-real-hostname`
253256 // arg is given on the command line or if the USE_REAL_HOSTNAME env var
254257 // is one of "1" or "true".
255- if useRealHostnameEnv := getEnv ("USE_REAL_HOSTNAME" ); useRealHostnameEnv == "1" || useRealHostnameEnv == "true" {
258+ if useRealHostnameEnv := getEnvVal ("USE_REAL_HOSTNAME" ); useRealHostnameEnv == "1" || useRealHostnameEnv == "true" {
256259 cfg .rawUseRealHostname = true
257260 }
258261 if cfg .rawUseRealHostname {
@@ -263,8 +266,8 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
263266 }
264267
265268 // split comma-separated list of domains into a slice, if given
266- if cfg .rawAllowedRedirectDomains == "" && getEnv ("ALLOWED_REDIRECT_DOMAINS" ) != "" {
267- cfg .rawAllowedRedirectDomains = getEnv ("ALLOWED_REDIRECT_DOMAINS" )
269+ if cfg .rawAllowedRedirectDomains == "" && getEnvVal ("ALLOWED_REDIRECT_DOMAINS" ) != "" {
270+ cfg .rawAllowedRedirectDomains = getEnvVal ("ALLOWED_REDIRECT_DOMAINS" )
268271 }
269272 for _ , domain := range strings .Split (cfg .rawAllowedRedirectDomains , "," ) {
270273 if strings .TrimSpace (domain ) != "" {
@@ -275,6 +278,18 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
275278 // reset temporary fields to their zero values
276279 cfg .rawAllowedRedirectDomains = ""
277280 cfg .rawUseRealHostname = false
281+
282+ for _ , envVar := range getEnviron () {
283+ name , value , _ := strings .Cut (envVar , "=" )
284+ if ! strings .HasPrefix (name , defaultEnvPrefix ) {
285+ continue
286+ }
287+ if cfg .Env == nil {
288+ cfg .Env = make (map [string ]string )
289+ }
290+ cfg .Env [name ] = value
291+ }
292+
278293 return cfg , nil
279294}
280295
0 commit comments