github.com/blend/go-sdk@v1.20220411.3/web/config.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package web 9 10 import ( 11 "context" 12 "fmt" 13 "net/http" 14 "net/url" 15 "time" 16 17 "github.com/blend/go-sdk/configutil" 18 "github.com/blend/go-sdk/ref" 19 20 "github.com/blend/go-sdk/webutil" 21 ) 22 23 // Config is an object used to set up a web app. 24 type Config struct { 25 Port int32 `json:"port,omitempty" yaml:"port,omitempty" env:"PORT"` 26 BindAddr string `json:"bindAddr,omitempty" yaml:"bindAddr,omitempty" env:"BIND_ADDR"` 27 BaseURL string `json:"baseURL,omitempty" yaml:"baseURL,omitempty" env:"BASE_URL"` 28 SkipRedirectTrailingSlash bool `json:"skipRedirectTrailingSlash,omitempty" yaml:"skipRedirectTrailingSlash,omitempty"` 29 HandleOptions bool `json:"handleOptions,omitempty" yaml:"handleOptions,omitempty"` 30 HandleMethodNotAllowed bool `json:"handleMethodNotAllowed,omitempty" yaml:"handleMethodNotAllowed,omitempty"` 31 DisablePanicRecovery bool `json:"disablePanicRecovery,omitempty" yaml:"disablePanicRecovery,omitempty"` 32 SessionTimeout time.Duration `json:"sessionTimeout,omitempty" yaml:"sessionTimeout,omitempty" env:"SESSION_TIMEOUT"` 33 SessionTimeoutIsRelative bool `json:"sessionTimeoutIsRelative,omitempty" yaml:"sessionTimeoutIsRelative,omitempty"` 34 35 CookieSecure *bool `json:"cookieSecure,omitempty" yaml:"cookieSecure,omitempty" env:"COOKIE_SECURE"` 36 CookieHTTPOnly *bool `json:"cookieHTTPOnly,omitempty" yaml:"cookieHTTPOnly,omitempty" env:"COOKIE_HTTP_ONLY"` 37 CookieSameSite string `json:"cookieSameSite,omitempty" yaml:"cookieSameSite,omitempty" env:"COOKIE_SAME_SITE"` 38 CookieName string `json:"cookieName,omitempty" yaml:"cookieName,omitempty" env:"COOKIE_NAME"` 39 CookiePath string `json:"cookiePath,omitempty" yaml:"cookiePath,omitempty" env:"COOKIE_PATH"` 40 CookieDomain string `json:"cookieDomain,omitempty" yaml:"cookieDomain,omitempty" env:"COOKIE_DOMAIN"` 41 42 DefaultHeaders map[string]string `json:"defaultHeaders,omitempty" yaml:"defaultHeaders,omitempty"` 43 MaxHeaderBytes int `json:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty" env:"MAX_HEADER_BYTES"` 44 ReadTimeout time.Duration `json:"readTimeout,omitempty" yaml:"readTimeout,omitempty" env:"READ_TIMEOUT"` 45 ReadHeaderTimeout time.Duration `json:"readHeaderTimeout,omitempty" yaml:"readHeaderTimeout,omitempty" env:"READ_HEADER_TIMEOUT"` 46 WriteTimeout time.Duration `json:"writeTimeout,omitempty" yaml:"writeTimeout,omitempty" env:"WRITE_TIMEOUT"` 47 IdleTimeout time.Duration `json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty" env:"IDLE_TIMEOUT"` 48 ShutdownGracePeriod time.Duration `json:"shutdownGracePeriod,omitempty" yaml:"shutdownGracePeriod,omitempty" env:"SHUTDOWN_GRACE_PERIOD"` 49 50 KeepAlive *bool `json:"keepAlive,omitempty" yaml:"keepAlive,omitempty" env:"KEEP_ALIVE"` 51 KeepAlivePeriod time.Duration `json:"keepAlivePeriod,omitempty" yaml:"keepAlivePeriod,omitempty" env:"KEEP_ALIVE_PERIOD"` 52 UseProxyProtocol bool `json:"useProxyProtocol,omitempty" yaml:"useProxyProtocol,omitempty"` 53 54 Views ViewCacheConfig `json:"views,omitempty" yaml:"views,omitempty"` 55 } 56 57 // IsZero returns if the config is unset or not. 58 func (c Config) IsZero() bool { 59 return c.BindAddr == "" && c.Port == 0 60 } 61 62 // Resolve resolves the config from other sources. 63 func (c *Config) Resolve(ctx context.Context) error { 64 return configutil.Resolve(ctx, 65 (&c.Views).Resolve, 66 configutil.SetInt32(&c.Port, configutil.Env("PORT"), configutil.Int32(c.Port)), 67 configutil.SetString(&c.BindAddr, configutil.Env("BIND_ADDR"), configutil.String(c.BindAddr)), 68 configutil.SetString(&c.BaseURL, configutil.Env("BASE_URL"), configutil.String(c.BaseURL)), 69 configutil.SetDuration(&c.SessionTimeout, configutil.Env("SESSION_TIMEOUT"), configutil.Duration(c.SessionTimeout)), 70 configutil.SetBoolPtr(&c.CookieSecure, configutil.Env("COOKIE_SECURE"), configutil.Bool(c.CookieSecure), configutil.BoolFunc(c.ResolveCookieSecure)), 71 configutil.SetBoolPtr(&c.CookieHTTPOnly, configutil.Env("COOKIE_HTTP_ONLY"), configutil.Bool(c.CookieHTTPOnly)), 72 configutil.SetString(&c.CookieSameSite, configutil.Env("COOKIE_SAME_SITE"), configutil.String(c.CookieSameSite)), 73 configutil.SetString(&c.CookieName, configutil.Env("COOKIE_NAME"), configutil.String(c.CookieName)), 74 configutil.SetString(&c.CookiePath, configutil.Env("COOKIE_PATH"), configutil.String(c.CookiePath)), 75 configutil.SetString(&c.CookieDomain, configutil.Env("COOKIE_DOMAIN"), configutil.String(c.CookieDomain), configutil.StringFunc(c.ResolveCookieDomain)), 76 configutil.SetInt(&c.MaxHeaderBytes, configutil.Env("MAX_HEADER_BYTES"), configutil.Int(c.MaxHeaderBytes)), 77 configutil.SetDuration(&c.ReadTimeout, configutil.Env("READ_TIMEOUT"), configutil.Duration(c.ReadTimeout)), 78 configutil.SetDuration(&c.ReadHeaderTimeout, configutil.Env("READ_HEADER_TIMEOUT"), configutil.Duration(c.ReadHeaderTimeout)), 79 configutil.SetDuration(&c.WriteTimeout, configutil.Env("WRITE_TIMEOUT"), configutil.Duration(c.WriteTimeout)), 80 configutil.SetDuration(&c.IdleTimeout, configutil.Env("IDLE_TIMEOUT"), configutil.Duration(c.IdleTimeout)), 81 configutil.SetDuration(&c.ShutdownGracePeriod, configutil.Env("SHUTDOWN_GRACE_PERIOD"), configutil.Duration(c.ShutdownGracePeriod)), 82 configutil.SetBoolPtr(&c.KeepAlive, configutil.Env("KEEP_ALIVE"), configutil.Bool(c.KeepAlive)), 83 configutil.SetDuration(&c.KeepAlivePeriod, configutil.Env("KEEP_ALIVE_PERIOD"), configutil.Duration(c.KeepAlivePeriod)), 84 ) 85 } 86 87 // BindAddrOrDefault returns the bind address or a default. 88 func (c Config) BindAddrOrDefault(defaults ...string) string { 89 if len(c.BindAddr) > 0 { 90 return c.BindAddr 91 } 92 if c.Port > 0 { 93 return fmt.Sprintf(":%d", c.Port) 94 } 95 if len(defaults) > 0 { 96 return defaults[0] 97 } 98 return DefaultBindAddr 99 } 100 101 // PortOrDefault returns the int32 port for a given config. 102 // This is useful in things like kubernetes pod templates. 103 // If the config .Port is unset, it will parse the .BindAddr, 104 // or the DefaultBindAddr for the port number. 105 func (c Config) PortOrDefault() int32 { 106 if c.Port > 0 { 107 return c.Port 108 } 109 if len(c.BindAddr) > 0 { 110 return webutil.PortFromBindAddr(c.BindAddr) 111 } 112 return webutil.PortFromBindAddr(DefaultBindAddr) 113 } 114 115 // BaseURLOrDefault gets the base url for the app or a default. 116 func (c Config) BaseURLOrDefault() string { 117 return c.BaseURL 118 } 119 120 // SessionTimeoutOrDefault returns a property or a default. 121 func (c Config) SessionTimeoutOrDefault() time.Duration { 122 if c.SessionTimeout > 0 { 123 return c.SessionTimeout 124 } 125 return DefaultSessionTimeout 126 } 127 128 // CookieNameOrDefault returns a property or a default. 129 func (c Config) CookieNameOrDefault() string { 130 if c.CookieName != "" { 131 return c.CookieName 132 } 133 return DefaultCookieName 134 } 135 136 // CookiePathOrDefault returns a property or a default. 137 func (c Config) CookiePathOrDefault() string { 138 if c.CookiePath != "" { 139 return c.CookiePath 140 } 141 return DefaultCookiePath 142 } 143 144 // CookieDomainOrDefault returns a property or a default. 145 func (c Config) CookieDomainOrDefault() string { 146 if c.CookieDomain != "" { 147 return c.CookieDomain 148 } 149 return "" 150 } 151 152 // CookieSecureOrDefault returns a property or a default. 153 func (c Config) CookieSecureOrDefault() bool { 154 if c.CookieSecure != nil { 155 return *c.CookieSecure 156 } 157 if baseURL := c.BaseURLOrDefault(); baseURL != "" { 158 return webutil.SchemeIsSecure(webutil.MustParseURL(baseURL).Scheme) 159 } 160 return DefaultCookieSecure 161 } 162 163 // CookieHTTPOnlyOrDefault returns a property or a default. 164 func (c Config) CookieHTTPOnlyOrDefault() bool { 165 if c.CookieHTTPOnly != nil { 166 return *c.CookieHTTPOnly 167 } 168 return DefaultCookieHTTPOnly 169 } 170 171 // CookieSameSiteOrDefault returns a property or a default. 172 func (c Config) CookieSameSiteOrDefault() http.SameSite { 173 if c.CookieSameSite != "" { 174 return webutil.MustParseSameSite(c.CookieSameSite) 175 } 176 return DefaultCookieSameSiteMode 177 } 178 179 // MaxHeaderBytesOrDefault returns the maximum header size in bytes or a default. 180 func (c Config) MaxHeaderBytesOrDefault() int { 181 if c.MaxHeaderBytes > 0 { 182 return c.MaxHeaderBytes 183 } 184 return DefaultMaxHeaderBytes 185 } 186 187 // ReadTimeoutOrDefault gets a property. 188 func (c Config) ReadTimeoutOrDefault() time.Duration { 189 if c.ReadTimeout > 0 { 190 return c.ReadTimeout 191 } 192 return DefaultReadTimeout 193 } 194 195 // ReadHeaderTimeoutOrDefault gets a property. 196 func (c Config) ReadHeaderTimeoutOrDefault() time.Duration { 197 if c.ReadHeaderTimeout > 0 { 198 return c.ReadHeaderTimeout 199 } 200 return DefaultReadHeaderTimeout 201 } 202 203 // WriteTimeoutOrDefault gets a property. 204 func (c Config) WriteTimeoutOrDefault() time.Duration { 205 if c.WriteTimeout > 0 { 206 return c.WriteTimeout 207 } 208 return DefaultWriteTimeout 209 } 210 211 // IdleTimeoutOrDefault gets a property. 212 func (c Config) IdleTimeoutOrDefault() time.Duration { 213 if c.IdleTimeout > 0 { 214 return c.IdleTimeout 215 } 216 return DefaultIdleTimeout 217 } 218 219 // ShutdownGracePeriodOrDefault gets the shutdown grace period. 220 func (c Config) ShutdownGracePeriodOrDefault() time.Duration { 221 if c.ShutdownGracePeriod > 0 { 222 return c.ShutdownGracePeriod 223 } 224 return DefaultShutdownGracePeriod 225 } 226 227 // KeepAliveOrDefault returns if we should keep TCP connections open. 228 func (c Config) KeepAliveOrDefault() bool { 229 if c.KeepAlive != nil { 230 return *c.KeepAlive 231 } 232 return DefaultKeepAlive 233 } 234 235 // KeepAlivePeriodOrDefault returns the TCP keep alive period or a default. 236 func (c Config) KeepAlivePeriodOrDefault() time.Duration { 237 if c.KeepAlivePeriod > 0 { 238 return c.KeepAlivePeriod 239 } 240 return DefaultKeepAlivePeriod 241 } 242 243 // ResolveCookieSecure is a resolver for the `CookieSecure` field based on the BaseURL if one is provided. 244 func (c Config) ResolveCookieSecure(_ context.Context) (*bool, error) { 245 if c.BaseURL != "" { 246 baseURL, err := url.Parse(c.BaseURL) 247 if err != nil { 248 return nil, err 249 } 250 return ref.Bool(webutil.SchemeIsSecure(baseURL.Scheme)), nil 251 } 252 return nil, nil 253 } 254 255 // ResolveCookieDomain is a resolver for the `CookieDomain` field based on the BaseURL if one is provided. 256 func (c Config) ResolveCookieDomain(_ context.Context) (*string, error) { 257 if c.BaseURL != "" { 258 baseURL, err := url.Parse(c.BaseURL) 259 if err != nil { 260 return nil, err 261 } 262 return ref.String(baseURL.Hostname()), nil 263 } 264 return nil, nil 265 }