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  }