go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/web/config.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package web 9 10 import ( 11 "context" 12 "fmt" 13 "net/http" 14 "time" 15 16 "go.charczuk.com/sdk/configutil" 17 ) 18 19 // Config is an object used to set up a web app. 20 type Config struct { 21 Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` 22 BaseURL string `json:"baseURL,omitempty" yaml:"baseURL,omitempty"` 23 Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` 24 25 SkipTrailingSlashRedirects bool `json:"skipTrailingSlashRedirects,omitempty" yaml:"skipTrailingSlashRedirects,omitempty"` 26 SkipHandlingMethodOptions bool `json:"skipHandlingMethodOptions,omitempty" yaml:"skipHandlingMethodOptions,omitempty"` 27 SkipMethodNotAllowed bool `json:"skipMethodNotAllowed,omitempty" yaml:"skipMethodNotAllowed,omitempty"` 28 29 MaxHeaderBytes int `json:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty"` 30 ReadTimeout time.Duration `json:"readTimeout,omitempty" yaml:"readTimeout,omitempty"` 31 ReadHeaderTimeout time.Duration `json:"readHeaderTimeout,omitempty" yaml:"readHeaderTimeout,omitempty"` 32 WriteTimeout time.Duration `json:"writeTimeout,omitempty" yaml:"writeTimeout,omitempty"` 33 IdleTimeout time.Duration `json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty"` 34 ShutdownGracePeriod time.Duration `json:"shutdownGracePeriod,omitempty" yaml:"shutdownGracePeriod,omitempty"` 35 } 36 37 // IsZero returns if the config is unset or not. 38 func (c Config) IsZero() bool { 39 return c.Addr == "" 40 } 41 42 // Resolve resolves the config from other sources. 43 func (c *Config) Resolve(ctx context.Context) error { 44 return configutil.Resolve(ctx, 45 configutil.Set(&c.Addr, configutil.Env[string]("BIND_ADDR"), configutil.Lazy(&c.Addr), c.ResolveAddrFromPort), 46 configutil.Set(&c.BaseURL, configutil.Env[string]("BASE_URL"), configutil.Lazy(&c.BaseURL)), 47 configutil.Set(&c.MaxHeaderBytes, configutil.Env[int]("MAX_HEADER_BYTES"), configutil.Lazy(&c.MaxHeaderBytes)), 48 configutil.Set(&c.ReadTimeout, configutil.Env[time.Duration]("READ_TIMEOUT"), configutil.Lazy(&c.ReadTimeout)), 49 configutil.Set(&c.ReadHeaderTimeout, configutil.Env[time.Duration]("READ_HEADER_TIMEOUT"), configutil.Lazy(&c.ReadHeaderTimeout)), 50 configutil.Set(&c.WriteTimeout, configutil.Env[time.Duration]("WRITE_TIMEOUT"), configutil.Lazy(&c.WriteTimeout)), 51 configutil.Set(&c.IdleTimeout, configutil.Env[time.Duration]("IDLE_TIMEOUT"), configutil.Lazy(&c.IdleTimeout)), 52 configutil.Set(&c.ShutdownGracePeriod, configutil.Env[time.Duration]("SHUTDOWN_GRACE_PERIOD"), configutil.Lazy(&c.ShutdownGracePeriod)), 53 ) 54 } 55 56 // ResolveAddrFromPort resolves the `Addr` field from a `$PORT` environment variable. 57 func (c *Config) ResolveAddrFromPort(ctx context.Context) (*string, error) { 58 if port, _ := configutil.Env[string]("PORT")(ctx); port != nil && *port != "" { 59 addr := fmt.Sprintf(":%s", *port) 60 return &addr, nil 61 } 62 return nil, nil 63 } 64 65 // ApplyTo applies a given config to an app. 66 func (c *Config) ApplyTo(app *App) { 67 app.BaseURL = c.BaseURL 68 app.ShutdownGracePeriod = c.ShutdownGracePeriod 69 app.Addr = c.Addr 70 app.MaxHeaderBytes = c.MaxHeaderBytes 71 app.ReadTimeout = c.ReadTimeout 72 app.ReadHeaderTimeout = c.ReadHeaderTimeout 73 app.WriteTimeout = c.WriteTimeout 74 app.IdleTimeout = c.IdleTimeout 75 app.SkipHandlingMethodOptions = c.SkipHandlingMethodOptions 76 app.SkipMethodNotAllowed = c.SkipMethodNotAllowed 77 app.SkipTrailingSlashRedirects = c.SkipTrailingSlashRedirects 78 app.Headers = MergeHeaders(app.Headers, CopySingleHeaders(c.Headers)) 79 } 80 81 // MergeHeaders merges headers. 82 func MergeHeaders(headers ...http.Header) http.Header { 83 output := make(http.Header) 84 for _, header := range headers { 85 for key, values := range header { 86 for _, value := range values { 87 output.Add(key, value) 88 } 89 } 90 } 91 return output 92 } 93 94 // CopySingleHeaders copies headers in single value format. 95 func CopySingleHeaders(headers map[string]string) http.Header { 96 output := make(http.Header) 97 for key, value := range headers { 98 output[key] = []string{value} 99 } 100 return output 101 }