// Copyright 2014 beego Author. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package beego import ( "fmt" "os" "path/filepath" "reflect" "runtime" "strings" "github.com/astaxie/beego/config" "github.com/astaxie/beego/context" "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" "github.com/astaxie/beego/utils" ) // Config is the main struct for BConfig type Config struct { AppName string //Application name RunMode string //Running Mode: dev | prod RouterCaseSensitive bool ServerName string RecoverPanic bool RecoverFunc func(*context.Context) CopyRequestBody bool EnableGzip bool MaxMemory int64 EnableErrorsShow bool EnableErrorsRender bool Listen Listen WebConfig WebConfig Log LogConfig } // Listen holds for http and https related config type Listen struct { Graceful bool // Graceful means use graceful module to start the server ServerTimeOut int64 ListenTCP4 bool EnableHTTP bool HTTPAddr string HTTPPort int EnableHTTPS bool HTTPSAddr string HTTPSPort int HTTPSCertFile string HTTPSKeyFile string EnableAdmin bool AdminAddr string AdminPort int EnableFcgi bool EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O } // WebConfig holds web related config type WebConfig struct { AutoRender bool EnableDocs bool FlashName string FlashSeparator string DirectoryIndex bool StaticDir map[string]string StaticExtensionsToGzip []string TemplateLeft string TemplateRight string ViewsPath string EnableXSRF bool XSRFKey string XSRFExpire int Session SessionConfig } // SessionConfig holds session related config type SessionConfig struct { SessionOn bool SessionProvider string SessionName string SessionGCMaxLifetime int64 SessionProviderConfig string SessionCookieLifeTime int SessionAutoSetCookie bool SessionDomain string SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies. SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers SessionNameInHTTPHeader string SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params } // LogConfig holds Log related config type LogConfig struct { AccessLogs bool FileLineNum bool Outputs map[string]string // Store Adaptor : config } var ( // BConfig is the default config for Application BConfig *Config // AppConfig is the instance of Config, store the config information from file AppConfig *beegoAppConfig // AppPath is the absolute path to the app AppPath string // GlobalSessions is the instance for the session manager GlobalSessions *session.Manager // appConfigPath is the path to the config files appConfigPath string // appConfigProvider is the provider for the config, default is ini appConfigProvider = "ini" ) func init() { BConfig = newBConfig() var err error if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { panic(err) } workPath, err := os.Getwd() if err != nil { panic(err) } appConfigPath = filepath.Join(workPath, "conf", "app.conf") if !utils.FileExists(appConfigPath) { appConfigPath = filepath.Join(AppPath, "conf", "app.conf") if !utils.FileExists(appConfigPath) { AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} return } } if err = parseConfig(appConfigPath); err != nil { panic(err) } } func recoverPanic(ctx *context.Context) { if err := recover(); err != nil { if err == ErrAbort { return } if !BConfig.RecoverPanic { panic(err) } if BConfig.EnableErrorsShow { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { exception(fmt.Sprint(err), ctx) return } } var stack string logs.Critical("the request url is ", ctx.Input.URL()) logs.Critical("Handler crashed with error", err) for i := 1; ; i++ { _, file, line, ok := runtime.Caller(i) if !ok { break } logs.Critical(fmt.Sprintf("%s:%d", file, line)) stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) } if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { showErr(err, ctx, stack) } } } func newBConfig() *Config { return &Config{ AppName: "beego", RunMode: DEV, RouterCaseSensitive: true, ServerName: "beegoServer:" + VERSION, RecoverPanic: true, RecoverFunc: recoverPanic, CopyRequestBody: false, EnableGzip: false, MaxMemory: 1 << 26, //64MB EnableErrorsShow: true, EnableErrorsRender: true, Listen: Listen{ Graceful: false, ServerTimeOut: 0, ListenTCP4: false, EnableHTTP: true, HTTPAddr: "", HTTPPort: 8080, EnableHTTPS: false, HTTPSAddr: "", HTTPSPort: 10443, HTTPSCertFile: "", HTTPSKeyFile: "", EnableAdmin: false, AdminAddr: "", AdminPort: 8088, EnableFcgi: false, EnableStdIo: false, }, WebConfig: WebConfig{ AutoRender: true, EnableDocs: false, FlashName: "BEEGO_FLASH", FlashSeparator: "BEEGOFLASH", DirectoryIndex: false, StaticDir: map[string]string{"/static": "static"}, StaticExtensionsToGzip: []string{".css", ".js"}, TemplateLeft: "{{", TemplateRight: "}}", ViewsPath: "views", EnableXSRF: false, XSRFKey: "beegoxsrf", XSRFExpire: 0, Session: SessionConfig{ SessionOn: false, SessionProvider: "memory", SessionName: "beegosessionID", SessionGCMaxLifetime: 3600, SessionProviderConfig: "", SessionDisableHTTPOnly: false, SessionCookieLifeTime: 0, //set cookie default is the browser life SessionAutoSetCookie: true, SessionDomain: "", SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers SessionNameInHTTPHeader: "Beegosessionid", SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params }, }, Log: LogConfig{ AccessLogs: false, FileLineNum: true, Outputs: map[string]string{"console": ""}, }, } } // now only support ini, next will support json. func parseConfig(appConfigPath string) (err error) { AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) if err != nil { return err } return assignConfig(AppConfig) } func assignConfig(ac config.Configer) error { for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { assignSingleConfig(i, ac) } // set the run mode first if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { BConfig.RunMode = envRunMode } else if runMode := ac.String("RunMode"); runMode != "" { BConfig.RunMode = runMode } if sd := ac.String("StaticDir"); sd != "" { BConfig.WebConfig.StaticDir = map[string]string{} sds := strings.Fields(sd) for _, v := range sds { if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] } else { BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] } } } if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" { extensions := strings.Split(sgz, ",") fileExts := []string{} for _, ext := range extensions { ext = strings.TrimSpace(ext) if ext == "" { continue } if !strings.HasPrefix(ext, ".") { ext = "." + ext } fileExts = append(fileExts, ext) } if len(fileExts) > 0 { BConfig.WebConfig.StaticExtensionsToGzip = fileExts } } if lo := ac.String("LogOutputs"); lo != "" { // if lo is not nil or empty // means user has set his own LogOutputs // clear the default setting to BConfig.Log.Outputs BConfig.Log.Outputs = make(map[string]string) los := strings.Split(lo, ";") for _, v := range los { if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1] } else { continue } } } //init log logs.Reset() for adaptor, config := range BConfig.Log.Outputs { err := logs.SetLogger(adaptor, config) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error())) } } logs.SetLogFuncCall(BConfig.Log.FileLineNum) return nil } func assignSingleConfig(p interface{}, ac config.Configer) { pt := reflect.TypeOf(p) if pt.Kind() != reflect.Ptr { return } pt = pt.Elem() if pt.Kind() != reflect.Struct { return } pv := reflect.ValueOf(p).Elem() for i := 0; i < pt.NumField(); i++ { pf := pv.Field(i) if !pf.CanSet() { continue } name := pt.Field(i).Name switch pf.Kind() { case reflect.String: pf.SetString(ac.DefaultString(name, pf.String())) case reflect.Int, reflect.Int64: pf.SetInt(ac.DefaultInt64(name, pf.Int())) case reflect.Bool: pf.SetBool(ac.DefaultBool(name, pf.Bool())) case reflect.Struct: default: //do nothing here } } } // LoadAppConfig allow developer to apply a config file func LoadAppConfig(adapterName, configPath string) error { absConfigPath, err := filepath.Abs(configPath) if err != nil { return err } if !utils.FileExists(absConfigPath) { return fmt.Errorf("the target config file: %s don't exist", configPath) } appConfigPath = absConfigPath appConfigProvider = adapterName return parseConfig(appConfigPath) } type beegoAppConfig struct { innerConfig config.Configer } func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) { ac, err := config.NewConfig(appConfigProvider, appConfigPath) if err != nil { return nil, err } return &beegoAppConfig{ac}, nil } func (b *beegoAppConfig) Set(key, val string) error { if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil { return err } return b.innerConfig.Set(key, val) } func (b *beegoAppConfig) String(key string) string { if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" { return v } return b.innerConfig.String(key) } func (b *beegoAppConfig) Strings(key string) []string { if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 { return v } return b.innerConfig.Strings(key) } func (b *beegoAppConfig) Int(key string) (int, error) { if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil { return v, nil } return b.innerConfig.Int(key) } func (b *beegoAppConfig) Int64(key string) (int64, error) { if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil { return v, nil } return b.innerConfig.Int64(key) } func (b *beegoAppConfig) Bool(key string) (bool, error) { if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil { return v, nil } return b.innerConfig.Bool(key) } func (b *beegoAppConfig) Float(key string) (float64, error) { if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil { return v, nil } return b.innerConfig.Float(key) } func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { if v := b.String(key); v != "" { return v } return defaultVal } func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { if v := b.Strings(key); len(v) != 0 { return v } return defaultVal } func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { if v, err := b.Int(key); err == nil { return v } return defaultVal } func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { if v, err := b.Int64(key); err == nil { return v } return defaultVal } func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { if v, err := b.Bool(key); err == nil { return v } return defaultVal } func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { if v, err := b.Float(key); err == nil { return v } return defaultVal } func (b *beegoAppConfig) DIY(key string) (interface{}, error) { return b.innerConfig.DIY(key) } func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { return b.innerConfig.GetSection(section) } func (b *beegoAppConfig) SaveConfigFile(filename string) error { return b.innerConfig.SaveConfigFile(filename) }