505 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			505 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
	EnableMutualHTTPS bool
 | 
						|
	HTTPSAddr         string
 | 
						|
	HTTPSPort         int
 | 
						|
	HTTPSCertFile     string
 | 
						|
	HTTPSKeyFile      string
 | 
						|
	TrustCaFile       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
 | 
						|
	EnableStaticLogs bool   //log static files requests default: false
 | 
						|
	AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
 | 
						|
	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)
 | 
						|
	}
 | 
						|
	var filename = "app.conf"
 | 
						|
	if os.Getenv("BEEGO_RUNMODE") != "" {
 | 
						|
		filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
 | 
						|
	}
 | 
						|
	appConfigPath = filepath.Join(workPath, "conf", filename)
 | 
						|
	if !utils.FileExists(appConfigPath) {
 | 
						|
		appConfigPath = filepath.Join(AppPath, "conf", filename)
 | 
						|
		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)
 | 
						|
		}
 | 
						|
		if ctx.Output.Status != 0 {
 | 
						|
			ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
 | 
						|
		} else {
 | 
						|
			ctx.ResponseWriter.WriteHeader(500)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func newBConfig() *Config {
 | 
						|
	return &Config{
 | 
						|
		AppName:             "beego",
 | 
						|
		RunMode:             PROD,
 | 
						|
		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,
 | 
						|
			EnableStaticLogs: false,
 | 
						|
			AccessLogsFormat: "APACHE_FORMAT",
 | 
						|
			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)
 | 
						|
}
 |