@@ -25,6 +25,7 @@ const (
25
25
defaultListenHost = "0.0.0.0"
26
26
defaultListenPort = 8080
27
27
defaultLogFormat = "text"
28
+ defaultEnvPrefix = "HTTPBIN_ENV_"
28
29
29
30
// Reasonable defaults for our http server
30
31
srvReadTimeout = 5 * time .Second
@@ -35,13 +36,13 @@ const (
35
36
// Main is the main entrypoint for the go-httpbin binary. See loadConfig() for
36
37
// command line argument parsing.
37
38
func 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 )
39
40
}
40
41
41
42
// mainImpl is the real implementation of Main(), extracted for better
42
43
// 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 )
45
46
if err != nil {
46
47
if cfgErr , ok := err .(ConfigError ); ok {
47
48
// 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
75
76
}
76
77
77
78
opts := []httpbin.OptionFunc {
79
+ httpbin .WithEnv (cfg .Env ),
78
80
httpbin .WithMaxBodySize (cfg .MaxBodySize ),
79
81
httpbin .WithMaxDuration (cfg .MaxDuration ),
80
82
httpbin .WithObserver (httpbin .StdLogObserver (logger )),
@@ -110,6 +112,7 @@ func mainImpl(args []string, getEnv func(string) string, getHostname func() (str
110
112
// config holds the configuration needed to initialize and run go-httpbin as a
111
113
// standalone server.
112
114
type config struct {
115
+ Env map [string ]string
113
116
AllowedRedirectDomains []string
114
117
ListenHost string
115
118
ExcludeHeaders string
@@ -144,7 +147,7 @@ func (e ConfigError) Error() string {
144
147
145
148
// loadConfig parses command line arguments and env vars into a fully resolved
146
149
// 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 ) {
148
151
cfg := & config {}
149
152
150
153
fs := flag .NewFlagSet ("go-httpbin" , flag .ContinueOnError )
@@ -192,24 +195,24 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
192
195
// Command line flags take precedence over environment vars, so we only
193
196
// check for environment vars if we have default values for our command
194
197
// 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 )
197
200
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" ))
199
202
}
200
203
}
201
204
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" ))
204
207
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" ))
206
209
}
207
210
}
208
- if cfg .ListenHost == defaultListenHost && getEnv ("HOST" ) != "" {
209
- cfg .ListenHost = getEnv ("HOST" )
211
+ if cfg .ListenHost == defaultListenHost && getEnvVal ("HOST" ) != "" {
212
+ cfg .ListenHost = getEnvVal ("HOST" )
210
213
}
211
214
if cfg .Prefix == "" {
212
- if prefix := getEnv ("PREFIX" ); prefix != "" {
215
+ if prefix := getEnvVal ("PREFIX" ); prefix != "" {
213
216
cfg .Prefix = prefix
214
217
}
215
218
}
@@ -221,29 +224,29 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
221
224
return nil , configErr ("Prefix %#v must not end with a slash" , cfg .Prefix )
222
225
}
223
226
}
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" )
226
229
}
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" ))
229
232
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" ))
231
234
}
232
235
}
233
236
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" )
236
239
}
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" )
239
242
}
240
243
if cfg .TLSCertFile != "" || cfg .TLSKeyFile != "" {
241
244
if cfg .TLSCertFile == "" || cfg .TLSKeyFile == "" {
242
245
return nil , configErr ("https cert and key must both be provided" )
243
246
}
244
247
}
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" )
247
250
}
248
251
if cfg .LogFormat != "text" && cfg .LogFormat != "json" {
249
252
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
252
255
// useRealHostname will be true if either the `-use-real-hostname`
253
256
// arg is given on the command line or if the USE_REAL_HOSTNAME env var
254
257
// 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" {
256
259
cfg .rawUseRealHostname = true
257
260
}
258
261
if cfg .rawUseRealHostname {
@@ -263,8 +266,8 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
263
266
}
264
267
265
268
// 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" )
268
271
}
269
272
for _ , domain := range strings .Split (cfg .rawAllowedRedirectDomains , "," ) {
270
273
if strings .TrimSpace (domain ) != "" {
@@ -275,6 +278,18 @@ func loadConfig(args []string, getEnv func(string) string, getHostname func() (s
275
278
// reset temporary fields to their zero values
276
279
cfg .rawAllowedRedirectDomains = ""
277
280
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
+
278
293
return cfg , nil
279
294
}
280
295
0 commit comments