github.com/astaxie/beego@v1.12.3/config.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package beego 16 17 import ( 18 "crypto/tls" 19 "fmt" 20 "net/http" 21 "os" 22 "path/filepath" 23 "reflect" 24 "runtime" 25 "strings" 26 27 "github.com/astaxie/beego/config" 28 "github.com/astaxie/beego/context" 29 "github.com/astaxie/beego/logs" 30 "github.com/astaxie/beego/session" 31 "github.com/astaxie/beego/utils" 32 ) 33 34 // Config is the main struct for BConfig 35 type Config struct { 36 AppName string //Application name 37 RunMode string //Running Mode: dev | prod 38 RouterCaseSensitive bool 39 ServerName string 40 RecoverPanic bool 41 RecoverFunc func(*context.Context) 42 CopyRequestBody bool 43 EnableGzip bool 44 MaxMemory int64 45 EnableErrorsShow bool 46 EnableErrorsRender bool 47 Listen Listen 48 WebConfig WebConfig 49 Log LogConfig 50 } 51 52 // Listen holds for http and https related config 53 type Listen struct { 54 Graceful bool // Graceful means use graceful module to start the server 55 ServerTimeOut int64 56 ListenTCP4 bool 57 EnableHTTP bool 58 HTTPAddr string 59 HTTPPort int 60 AutoTLS bool 61 Domains []string 62 TLSCacheDir string 63 EnableHTTPS bool 64 EnableMutualHTTPS bool 65 HTTPSAddr string 66 HTTPSPort int 67 HTTPSCertFile string 68 HTTPSKeyFile string 69 TrustCaFile string 70 ClientAuth tls.ClientAuthType 71 EnableAdmin bool 72 AdminAddr string 73 AdminPort int 74 EnableFcgi bool 75 EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O 76 } 77 78 // WebConfig holds web related config 79 type WebConfig struct { 80 AutoRender bool 81 EnableDocs bool 82 FlashName string 83 FlashSeparator string 84 DirectoryIndex bool 85 StaticDir map[string]string 86 StaticExtensionsToGzip []string 87 StaticCacheFileSize int 88 StaticCacheFileNum int 89 TemplateLeft string 90 TemplateRight string 91 ViewsPath string 92 EnableXSRF bool 93 XSRFKey string 94 XSRFExpire int 95 Session SessionConfig 96 } 97 98 // SessionConfig holds session related config 99 type SessionConfig struct { 100 SessionOn bool 101 SessionProvider string 102 SessionName string 103 SessionGCMaxLifetime int64 104 SessionProviderConfig string 105 SessionCookieLifeTime int 106 SessionAutoSetCookie bool 107 SessionDomain string 108 SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies. 109 SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers 110 SessionNameInHTTPHeader string 111 SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params 112 SessionCookieSameSite http.SameSite 113 } 114 115 // LogConfig holds Log related config 116 type LogConfig struct { 117 AccessLogs bool 118 EnableStaticLogs bool //log static files requests default: false 119 AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string 120 FileLineNum bool 121 Outputs map[string]string // Store Adaptor : config 122 } 123 124 var ( 125 // BConfig is the default config for Application 126 BConfig *Config 127 // AppConfig is the instance of Config, store the config information from file 128 AppConfig *beegoAppConfig 129 // AppPath is the absolute path to the app 130 AppPath string 131 // GlobalSessions is the instance for the session manager 132 GlobalSessions *session.Manager 133 134 // appConfigPath is the path to the config files 135 appConfigPath string 136 // appConfigProvider is the provider for the config, default is ini 137 appConfigProvider = "ini" 138 // WorkPath is the absolute path to project root directory 139 WorkPath string 140 ) 141 142 func init() { 143 BConfig = newBConfig() 144 var err error 145 if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { 146 panic(err) 147 } 148 WorkPath, err = os.Getwd() 149 if err != nil { 150 panic(err) 151 } 152 var filename = "app.conf" 153 if os.Getenv("BEEGO_RUNMODE") != "" { 154 filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf" 155 } 156 appConfigPath = filepath.Join(WorkPath, "conf", filename) 157 if configPath := os.Getenv("BEEGO_CONFIG_PATH"); configPath != "" { 158 appConfigPath = configPath 159 } 160 if !utils.FileExists(appConfigPath) { 161 appConfigPath = filepath.Join(AppPath, "conf", filename) 162 if !utils.FileExists(appConfigPath) { 163 AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} 164 return 165 } 166 } 167 if err = parseConfig(appConfigPath); err != nil { 168 panic(err) 169 } 170 } 171 172 func recoverPanic(ctx *context.Context) { 173 if err := recover(); err != nil { 174 if err == ErrAbort { 175 return 176 } 177 if !BConfig.RecoverPanic { 178 panic(err) 179 } 180 if BConfig.EnableErrorsShow { 181 if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { 182 exception(fmt.Sprint(err), ctx) 183 return 184 } 185 } 186 var stack string 187 logs.Critical("the request url is ", ctx.Input.URL()) 188 logs.Critical("Handler crashed with error", err) 189 for i := 1; ; i++ { 190 _, file, line, ok := runtime.Caller(i) 191 if !ok { 192 break 193 } 194 logs.Critical(fmt.Sprintf("%s:%d", file, line)) 195 stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) 196 } 197 if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { 198 showErr(err, ctx, stack) 199 } 200 if ctx.Output.Status != 0 { 201 ctx.ResponseWriter.WriteHeader(ctx.Output.Status) 202 } else { 203 ctx.ResponseWriter.WriteHeader(500) 204 } 205 } 206 } 207 208 func newBConfig() *Config { 209 return &Config{ 210 AppName: "beego", 211 RunMode: PROD, 212 RouterCaseSensitive: true, 213 ServerName: "beegoServer:" + VERSION, 214 RecoverPanic: true, 215 RecoverFunc: recoverPanic, 216 CopyRequestBody: false, 217 EnableGzip: false, 218 MaxMemory: 1 << 26, //64MB 219 EnableErrorsShow: true, 220 EnableErrorsRender: true, 221 Listen: Listen{ 222 Graceful: false, 223 ServerTimeOut: 0, 224 ListenTCP4: false, 225 EnableHTTP: true, 226 AutoTLS: false, 227 Domains: []string{}, 228 TLSCacheDir: ".", 229 HTTPAddr: "", 230 HTTPPort: 8080, 231 EnableHTTPS: false, 232 HTTPSAddr: "", 233 HTTPSPort: 10443, 234 HTTPSCertFile: "", 235 HTTPSKeyFile: "", 236 EnableAdmin: false, 237 AdminAddr: "", 238 AdminPort: 8088, 239 EnableFcgi: false, 240 EnableStdIo: false, 241 ClientAuth: tls.RequireAndVerifyClientCert, 242 }, 243 WebConfig: WebConfig{ 244 AutoRender: true, 245 EnableDocs: false, 246 FlashName: "BEEGO_FLASH", 247 FlashSeparator: "BEEGOFLASH", 248 DirectoryIndex: false, 249 StaticDir: map[string]string{"/static": "static"}, 250 StaticExtensionsToGzip: []string{".css", ".js"}, 251 StaticCacheFileSize: 1024 * 100, 252 StaticCacheFileNum: 1000, 253 TemplateLeft: "{{", 254 TemplateRight: "}}", 255 ViewsPath: "views", 256 EnableXSRF: false, 257 XSRFKey: "beegoxsrf", 258 XSRFExpire: 0, 259 Session: SessionConfig{ 260 SessionOn: false, 261 SessionProvider: "memory", 262 SessionName: "beegosessionID", 263 SessionGCMaxLifetime: 3600, 264 SessionProviderConfig: "", 265 SessionDisableHTTPOnly: false, 266 SessionCookieLifeTime: 0, //set cookie default is the browser life 267 SessionAutoSetCookie: true, 268 SessionDomain: "", 269 SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers 270 SessionNameInHTTPHeader: "Beegosessionid", 271 SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params 272 SessionCookieSameSite: http.SameSiteDefaultMode, 273 }, 274 }, 275 Log: LogConfig{ 276 AccessLogs: false, 277 EnableStaticLogs: false, 278 AccessLogsFormat: "APACHE_FORMAT", 279 FileLineNum: true, 280 Outputs: map[string]string{"console": ""}, 281 }, 282 } 283 } 284 285 // now only support ini, next will support json. 286 func parseConfig(appConfigPath string) (err error) { 287 AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) 288 if err != nil { 289 return err 290 } 291 return assignConfig(AppConfig) 292 } 293 294 func assignConfig(ac config.Configer) error { 295 for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { 296 assignSingleConfig(i, ac) 297 } 298 // set the run mode first 299 if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { 300 BConfig.RunMode = envRunMode 301 } else if runMode := ac.String("RunMode"); runMode != "" { 302 BConfig.RunMode = runMode 303 } 304 305 if sd := ac.String("StaticDir"); sd != "" { 306 BConfig.WebConfig.StaticDir = map[string]string{} 307 sds := strings.Fields(sd) 308 for _, v := range sds { 309 if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { 310 BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] 311 } else { 312 BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] 313 } 314 } 315 } 316 317 if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" { 318 extensions := strings.Split(sgz, ",") 319 fileExts := []string{} 320 for _, ext := range extensions { 321 ext = strings.TrimSpace(ext) 322 if ext == "" { 323 continue 324 } 325 if !strings.HasPrefix(ext, ".") { 326 ext = "." + ext 327 } 328 fileExts = append(fileExts, ext) 329 } 330 if len(fileExts) > 0 { 331 BConfig.WebConfig.StaticExtensionsToGzip = fileExts 332 } 333 } 334 335 if sfs, err := ac.Int("StaticCacheFileSize"); err == nil { 336 BConfig.WebConfig.StaticCacheFileSize = sfs 337 } 338 339 if sfn, err := ac.Int("StaticCacheFileNum"); err == nil { 340 BConfig.WebConfig.StaticCacheFileNum = sfn 341 } 342 343 if lo := ac.String("LogOutputs"); lo != "" { 344 // if lo is not nil or empty 345 // means user has set his own LogOutputs 346 // clear the default setting to BConfig.Log.Outputs 347 BConfig.Log.Outputs = make(map[string]string) 348 los := strings.Split(lo, ";") 349 for _, v := range los { 350 if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { 351 BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1] 352 } else { 353 continue 354 } 355 } 356 } 357 358 //init log 359 logs.Reset() 360 for adaptor, config := range BConfig.Log.Outputs { 361 err := logs.SetLogger(adaptor, config) 362 if err != nil { 363 fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error())) 364 } 365 } 366 logs.SetLogFuncCall(BConfig.Log.FileLineNum) 367 368 return nil 369 } 370 371 func assignSingleConfig(p interface{}, ac config.Configer) { 372 pt := reflect.TypeOf(p) 373 if pt.Kind() != reflect.Ptr { 374 return 375 } 376 pt = pt.Elem() 377 if pt.Kind() != reflect.Struct { 378 return 379 } 380 pv := reflect.ValueOf(p).Elem() 381 382 for i := 0; i < pt.NumField(); i++ { 383 pf := pv.Field(i) 384 if !pf.CanSet() { 385 continue 386 } 387 name := pt.Field(i).Name 388 switch pf.Kind() { 389 case reflect.String: 390 pf.SetString(ac.DefaultString(name, pf.String())) 391 case reflect.Int, reflect.Int64: 392 pf.SetInt(ac.DefaultInt64(name, pf.Int())) 393 case reflect.Bool: 394 pf.SetBool(ac.DefaultBool(name, pf.Bool())) 395 case reflect.Struct: 396 default: 397 //do nothing here 398 } 399 } 400 401 } 402 403 // LoadAppConfig allow developer to apply a config file 404 func LoadAppConfig(adapterName, configPath string) error { 405 absConfigPath, err := filepath.Abs(configPath) 406 if err != nil { 407 return err 408 } 409 410 if !utils.FileExists(absConfigPath) { 411 return fmt.Errorf("the target config file: %s don't exist", configPath) 412 } 413 414 appConfigPath = absConfigPath 415 appConfigProvider = adapterName 416 417 return parseConfig(appConfigPath) 418 } 419 420 type beegoAppConfig struct { 421 innerConfig config.Configer 422 } 423 424 func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) { 425 ac, err := config.NewConfig(appConfigProvider, appConfigPath) 426 if err != nil { 427 return nil, err 428 } 429 return &beegoAppConfig{ac}, nil 430 } 431 432 func (b *beegoAppConfig) Set(key, val string) error { 433 if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil { 434 return b.innerConfig.Set(key, val) 435 } 436 return nil 437 } 438 439 func (b *beegoAppConfig) String(key string) string { 440 if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" { 441 return v 442 } 443 return b.innerConfig.String(key) 444 } 445 446 func (b *beegoAppConfig) Strings(key string) []string { 447 if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 { 448 return v 449 } 450 return b.innerConfig.Strings(key) 451 } 452 453 func (b *beegoAppConfig) Int(key string) (int, error) { 454 if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil { 455 return v, nil 456 } 457 return b.innerConfig.Int(key) 458 } 459 460 func (b *beegoAppConfig) Int64(key string) (int64, error) { 461 if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil { 462 return v, nil 463 } 464 return b.innerConfig.Int64(key) 465 } 466 467 func (b *beegoAppConfig) Bool(key string) (bool, error) { 468 if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil { 469 return v, nil 470 } 471 return b.innerConfig.Bool(key) 472 } 473 474 func (b *beegoAppConfig) Float(key string) (float64, error) { 475 if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil { 476 return v, nil 477 } 478 return b.innerConfig.Float(key) 479 } 480 481 func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { 482 if v := b.String(key); v != "" { 483 return v 484 } 485 return defaultVal 486 } 487 488 func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { 489 if v := b.Strings(key); len(v) != 0 { 490 return v 491 } 492 return defaultVal 493 } 494 495 func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { 496 if v, err := b.Int(key); err == nil { 497 return v 498 } 499 return defaultVal 500 } 501 502 func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { 503 if v, err := b.Int64(key); err == nil { 504 return v 505 } 506 return defaultVal 507 } 508 509 func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { 510 if v, err := b.Bool(key); err == nil { 511 return v 512 } 513 return defaultVal 514 } 515 516 func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { 517 if v, err := b.Float(key); err == nil { 518 return v 519 } 520 return defaultVal 521 } 522 523 func (b *beegoAppConfig) DIY(key string) (interface{}, error) { 524 return b.innerConfig.DIY(key) 525 } 526 527 func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { 528 return b.innerConfig.GetSection(section) 529 } 530 531 func (b *beegoAppConfig) SaveConfigFile(filename string) error { 532 return b.innerConfig.SaveConfigFile(filename) 533 }