config.go 14.2 KB
Newer Older
A
astaxie 已提交
1
// Copyright 2014 beego Author. All Rights Reserved.
A
astaxie 已提交
2
//
A
astaxie 已提交
3 4 5
// 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
A
astaxie 已提交
6
//
A
astaxie 已提交
7
//      http://www.apache.org/licenses/LICENSE-2.0
A
astaxie 已提交
8
//
A
astaxie 已提交
9 10 11 12 13 14
// 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.

15 16 17
package beego

import (
Y
youngsterxyf 已提交
18
	"fmt"
19
	"os"
20
	"path/filepath"
J
JessonChan 已提交
21
	"reflect"
A
astaxie 已提交
22
	"runtime"
23
	"strings"
P
Pengfei Xue 已提交
24 25

	"github.com/astaxie/beego/config"
A
astaxie 已提交
26
	"github.com/astaxie/beego/context"
27
	"github.com/astaxie/beego/logs"
P
Pengfei Xue 已提交
28
	"github.com/astaxie/beego/session"
29
	"github.com/astaxie/beego/utils"
30 31
)

A
astaxie 已提交
32
// Config is the main struct for BConfig
A
astaxie 已提交
33
type Config struct {
A
astaxie 已提交
34 35
	AppName             string //Application name
	RunMode             string //Running Mode: dev | prod
36
	RouterCaseSensitive bool
A
astaxie 已提交
37 38
	ServerName          string
	RecoverPanic        bool
A
astaxie 已提交
39
	RecoverFunc         func(*context.Context)
A
astaxie 已提交
40 41 42 43
	CopyRequestBody     bool
	EnableGzip          bool
	MaxMemory           int64
	EnableErrorsShow    bool
X
xhzhang 已提交
44
	EnableErrorsRender  bool
A
astaxie 已提交
45 46 47
	Listen              Listen
	WebConfig           WebConfig
	Log                 LogConfig
A
astaxie 已提交
48 49
}

A
astaxie 已提交
50
// Listen holds for http and https related config
A
astaxie 已提交
51
type Listen struct {
L
lotus 已提交
52 53 54 55 56 57
	Graceful          bool // Graceful means use graceful module to start the server
	ServerTimeOut     int64
	ListenTCP4        bool
	EnableHTTP        bool
	HTTPAddr          string
	HTTPPort          int
R
Ruben Cid 已提交
58 59 60
	AutoTLS           bool
	Domains           []string
	TLSCacheDir       string
L
lotus 已提交
61 62 63 64 65 66 67 68 69 70 71 72
	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
A
astaxie 已提交
73 74
}

A
astaxie 已提交
75
// WebConfig holds web related config
A
astaxie 已提交
76 77 78 79
type WebConfig struct {
	AutoRender             bool
	EnableDocs             bool
	FlashName              string
J
JessonChan 已提交
80
	FlashSeparator         string
A
astaxie 已提交
81 82 83
	DirectoryIndex         bool
	StaticDir              map[string]string
	StaticExtensionsToGzip []string
84 85
	StaticCacheFileSize    int
	StaticCacheFileNum     int
A
astaxie 已提交
86 87 88 89
	TemplateLeft           string
	TemplateRight          string
	ViewsPath              string
	EnableXSRF             bool
J
JessonChan 已提交
90
	XSRFKey                string
A
astaxie 已提交
91 92
	XSRFExpire             int
	Session                SessionConfig
A
astaxie 已提交
93 94
}

A
astaxie 已提交
95
// SessionConfig holds session related config
A
astaxie 已提交
96
type SessionConfig struct {
G
GrimTheReaper 已提交
97 98 99 100 101 102 103 104 105
	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.
T
takeo-lvgs 已提交
106
	SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
G
GrimTheReaper 已提交
107
	SessionNameInHTTPHeader      string
T
takeo-lvgs 已提交
108
	SessionEnableSidInURLQuery   bool // enable get the sessionId from Url Query params
A
astaxie 已提交
109 110
}

A
astaxie 已提交
111
// LogConfig holds Log related config
A
astaxie 已提交
112
type LogConfig struct {
A
astaxie 已提交
113
	AccessLogs       bool
114
	EnableStaticLogs bool   //log static files requests default: false
A
astaxie 已提交
115 116 117
	AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
	FileLineNum      bool
	Outputs          map[string]string // Store Adaptor : config
A
astaxie 已提交
118 119
}

A
astaxie 已提交
120 121
var (
	// BConfig is the default config for Application
A
astaxie 已提交
122
	BConfig *Config
A
astaxie 已提交
123 124
	// AppConfig is the instance of Config, store the config information from file
	AppConfig *beegoAppConfig
C
coseyo 已提交
125 126
	// AppPath is the absolute path to the app
	AppPath string
A
astaxie 已提交
127 128
	// GlobalSessions is the instance for the session manager
	GlobalSessions *session.Manager
Y
youngsterxyf 已提交
129

130 131 132 133
	// appConfigPath is the path to the config files
	appConfigPath string
	// appConfigProvider is the provider for the config, default is ini
	appConfigProvider = "ini"
A
astaxie 已提交
134
)
A
astaxie 已提交
135

A
astaxie 已提交
136
func init() {
J
JessonChan 已提交
137 138 139 140 141 142 143 144 145
	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)
	}
A
Abel 已提交
146
	var filename = "app.conf"
T
takeo-lvgs 已提交
147 148
	if os.Getenv("BEEGO_RUNMODE") != "" {
		filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
A
Abel 已提交
149
	}
A
Abel 已提交
150
	appConfigPath = filepath.Join(workPath, "conf", filename)
J
JessonChan 已提交
151
	if !utils.FileExists(appConfigPath) {
A
Abel 已提交
152
		appConfigPath = filepath.Join(AppPath, "conf", filename)
J
JessonChan 已提交
153 154 155 156 157 158 159 160 161
		if !utils.FileExists(appConfigPath) {
			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
			return
		}
	}
	if err = parseConfig(appConfigPath); err != nil {
		panic(err)
	}
}
Y
youngsterxyf 已提交
162

A
astaxie 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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))
		}
X
xhzhang 已提交
188
		if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
A
astaxie 已提交
189 190
			showErr(err, ctx, stack)
		}
A
Amit Ashkenazi 已提交
191 192 193 194 195
		if ctx.Output.Status != 0 {
			ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
		} else {
			ctx.ResponseWriter.WriteHeader(500)
		}
A
astaxie 已提交
196 197 198
	}
}

J
JessonChan 已提交
199 200
func newBConfig() *Config {
	return &Config{
A
astaxie 已提交
201
		AppName:             "beego",
202
		RunMode:             PROD,
A
astaxie 已提交
203 204 205
		RouterCaseSensitive: true,
		ServerName:          "beegoServer:" + VERSION,
		RecoverPanic:        true,
A
astaxie 已提交
206
		RecoverFunc:         recoverPanic,
A
astaxie 已提交
207 208 209 210
		CopyRequestBody:     false,
		EnableGzip:          false,
		MaxMemory:           1 << 26, //64MB
		EnableErrorsShow:    true,
X
xhzhang 已提交
211
		EnableErrorsRender:  true,
A
astaxie 已提交
212 213 214 215
		Listen: Listen{
			Graceful:      false,
			ServerTimeOut: 0,
			ListenTCP4:    false,
A
astaxie 已提交
216
			EnableHTTP:    true,
R
Ruben Cid 已提交
217 218 219
			AutoTLS:       false,
			Domains:       []string{},
			TLSCacheDir:   ".",
A
astaxie 已提交
220 221
			HTTPAddr:      "",
			HTTPPort:      8080,
A
astaxie 已提交
222
			EnableHTTPS:   false,
A
astaxie 已提交
223 224 225 226
			HTTPSAddr:     "",
			HTTPSPort:     10443,
			HTTPSCertFile: "",
			HTTPSKeyFile:  "",
A
astaxie 已提交
227
			EnableAdmin:   false,
A
astaxie 已提交
228 229 230 231 232 233 234 235 236
			AdminAddr:     "",
			AdminPort:     8088,
			EnableFcgi:    false,
			EnableStdIo:   false,
		},
		WebConfig: WebConfig{
			AutoRender:             true,
			EnableDocs:             false,
			FlashName:              "BEEGO_FLASH",
J
JessonChan 已提交
237
			FlashSeparator:         "BEEGOFLASH",
A
astaxie 已提交
238 239 240
			DirectoryIndex:         false,
			StaticDir:              map[string]string{"/static": "static"},
			StaticExtensionsToGzip: []string{".css", ".js"},
241 242
			StaticCacheFileSize:    1024 * 100,
			StaticCacheFileNum:     1000,
A
astaxie 已提交
243 244 245 246
			TemplateLeft:           "{{",
			TemplateRight:          "}}",
			ViewsPath:              "views",
			EnableXSRF:             false,
J
JessonChan 已提交
247
			XSRFKey:                "beegoxsrf",
A
astaxie 已提交
248 249
			XSRFExpire:             0,
			Session: SessionConfig{
G
GrimTheReaper 已提交
250 251 252 253 254 255 256 257 258
				SessionOn:                    false,
				SessionProvider:              "memory",
				SessionName:                  "beegosessionID",
				SessionGCMaxLifetime:         3600,
				SessionProviderConfig:        "",
				SessionDisableHTTPOnly:       false,
				SessionCookieLifeTime:        0, //set cookie default is the browser life
				SessionAutoSetCookie:         true,
				SessionDomain:                "",
T
takeo-lvgs 已提交
259
				SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
G
GrimTheReaper 已提交
260
				SessionNameInHTTPHeader:      "Beegosessionid",
T
takeo-lvgs 已提交
261
				SessionEnableSidInURLQuery:   false, // enable get the sessionId from Url Query params
A
astaxie 已提交
262 263 264
			},
		},
		Log: LogConfig{
A
astaxie 已提交
265
			AccessLogs:       false,
266
			EnableStaticLogs: false,
A
astaxie 已提交
267 268 269
			AccessLogsFormat: "APACHE_FORMAT",
			FileLineNum:      true,
			Outputs:          map[string]string{"console": ""},
A
astaxie 已提交
270
		},
A
astaxie 已提交
271
	}
Y
youngsterxyf 已提交
272 273
}

A
astaxie 已提交
274
// now only support ini, next will support json.
Y
youngsterxyf 已提交
275
func parseConfig(appConfigPath string) (err error) {
276
	AppConfig, err = newAppConfig(appConfigProvider, appConfigPath)
A
astaxie 已提交
277 278 279
	if err != nil {
		return err
	}
J
JessonChan 已提交
280 281 282 283
	return assignConfig(AppConfig)
}

func assignConfig(ac config.Configer) error {
A
astaxie 已提交
284
	for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
A
astaxie 已提交
285 286
		assignSingleConfig(i, ac)
	}
287
	// set the run mode first
F
fuxiaohei 已提交
288
	if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" {
A
astaxie 已提交
289
		BConfig.RunMode = envRunMode
J
JessonChan 已提交
290
	} else if runMode := ac.String("RunMode"); runMode != "" {
291
		BConfig.RunMode = runMode
A
astaxie 已提交
292
	}
293

J
JessonChan 已提交
294 295
	if sd := ac.String("StaticDir"); sd != "" {
		BConfig.WebConfig.StaticDir = map[string]string{}
A
astaxie 已提交
296 297 298
		sds := strings.Fields(sd)
		for _, v := range sds {
			if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
299
				BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1]
A
astaxie 已提交
300
			} else {
301
				BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0]
302 303
			}
		}
A
astaxie 已提交
304
	}
305

J
JessonChan 已提交
306
	if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" {
A
astaxie 已提交
307
		extensions := strings.Split(sgz, ",")
J
JessonChan 已提交
308
		fileExts := []string{}
J
JessonChan 已提交
309 310 311 312
		for _, ext := range extensions {
			ext = strings.TrimSpace(ext)
			if ext == "" {
				continue
F
Francois 已提交
313
			}
J
JessonChan 已提交
314 315 316
			if !strings.HasPrefix(ext, ".") {
				ext = "." + ext
			}
J
JessonChan 已提交
317 318 319
			fileExts = append(fileExts, ext)
		}
		if len(fileExts) > 0 {
A
astaxie 已提交
320
			BConfig.WebConfig.StaticExtensionsToGzip = fileExts
F
Francois 已提交
321
		}
A
astaxie 已提交
322
	}
Y
youngsterxyf 已提交
323

324 325 326 327 328 329 330 331
	if sfs, err := ac.Int("StaticCacheFileSize"); err == nil {
		BConfig.WebConfig.StaticCacheFileSize = sfs
	}

	if sfn, err := ac.Int("StaticCacheFileNum"); err == nil {
		BConfig.WebConfig.StaticCacheFileNum = sfn
	}

J
JessonChan 已提交
332
	if lo := ac.String("LogOutputs"); lo != "" {
B
Back 已提交
333 334 335 336
		// 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)
Y
youngsterxyf 已提交
337 338 339 340 341 342 343 344 345 346 347
		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
348
	logs.Reset()
Y
youngsterxyf 已提交
349
	for adaptor, config := range BConfig.Log.Outputs {
J
JessonChan 已提交
350
		err := logs.SetLogger(adaptor, config)
Y
youngsterxyf 已提交
351
		if err != nil {
Y
ysqi 已提交
352
			fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error()))
Y
youngsterxyf 已提交
353 354
		}
	}
355
	logs.SetLogFuncCall(BConfig.Log.FileLineNum)
Y
youngsterxyf 已提交
356

A
astaxie 已提交
357
	return nil
358
}
A
astaxie 已提交
359

J
JessonChan 已提交
360
func assignSingleConfig(p interface{}, ac config.Configer) {
J
JessonChan 已提交
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
	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:
S
Sergey Lanzman 已提交
381
			pf.SetInt(ac.DefaultInt64(name, pf.Int()))
J
JessonChan 已提交
382 383 384 385
		case reflect.Bool:
			pf.SetBool(ac.DefaultBool(name, pf.Bool()))
		case reflect.Struct:
		default:
J
JessonChan 已提交
386
			//do nothing here
J
JessonChan 已提交
387 388 389 390 391
		}
	}

}

Y
youngsterxyf 已提交
392
// LoadAppConfig allow developer to apply a config file
393
func LoadAppConfig(adapterName, configPath string) error {
Y
youngsterxyf 已提交
394 395 396 397
	absConfigPath, err := filepath.Abs(configPath)
	if err != nil {
		return err
	}
Y
youngsterxyf 已提交
398 399

	if !utils.FileExists(absConfigPath) {
A
astaxie 已提交
400
		return fmt.Errorf("the target config file: %s don't exist", configPath)
Y
youngsterxyf 已提交
401 402
	}

403 404
	appConfigPath = absConfigPath
	appConfigProvider = adapterName
Y
youngsterxyf 已提交
405

406
	return parseConfig(appConfigPath)
Y
youngsterxyf 已提交
407 408
}

A
astaxie 已提交
409 410 411 412
type beegoAppConfig struct {
	innerConfig config.Configer
}

413 414
func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) {
	ac, err := config.NewConfig(appConfigProvider, appConfigPath)
A
astaxie 已提交
415 416 417
	if err != nil {
		return nil, err
	}
F
fuxiaohei 已提交
418
	return &beegoAppConfig{ac}, nil
A
astaxie 已提交
419 420 421
}

func (b *beegoAppConfig) Set(key, val string) error {
F
fuxiaohei 已提交
422
	if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil {
423
		return b.innerConfig.Set(key, val)
A
astaxie 已提交
424
	}
425
	return nil
A
astaxie 已提交
426 427 428
}

func (b *beegoAppConfig) String(key string) string {
F
fuxiaohei 已提交
429 430
	if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" {
		return v
A
astaxie 已提交
431
	}
F
fuxiaohei 已提交
432
	return b.innerConfig.String(key)
A
astaxie 已提交
433 434 435
}

func (b *beegoAppConfig) Strings(key string) []string {
J
JessonChan 已提交
436
	if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 {
F
fud 已提交
437
		return v
A
astaxie 已提交
438
	}
F
fuxiaohei 已提交
439
	return b.innerConfig.Strings(key)
A
astaxie 已提交
440 441 442
}

func (b *beegoAppConfig) Int(key string) (int, error) {
F
fuxiaohei 已提交
443 444
	if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil {
		return v, nil
A
astaxie 已提交
445
	}
F
fuxiaohei 已提交
446
	return b.innerConfig.Int(key)
A
astaxie 已提交
447 448 449
}

func (b *beegoAppConfig) Int64(key string) (int64, error) {
F
fuxiaohei 已提交
450 451
	if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil {
		return v, nil
A
astaxie 已提交
452
	}
F
fuxiaohei 已提交
453
	return b.innerConfig.Int64(key)
A
astaxie 已提交
454 455 456
}

func (b *beegoAppConfig) Bool(key string) (bool, error) {
F
fuxiaohei 已提交
457 458
	if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil {
		return v, nil
A
astaxie 已提交
459
	}
F
fuxiaohei 已提交
460
	return b.innerConfig.Bool(key)
A
astaxie 已提交
461 462 463
}

func (b *beegoAppConfig) Float(key string) (float64, error) {
F
fuxiaohei 已提交
464 465
	if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil {
		return v, nil
A
astaxie 已提交
466
	}
F
fuxiaohei 已提交
467
	return b.innerConfig.Float(key)
A
astaxie 已提交
468 469
}

470
func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string {
F
fuxiaohei 已提交
471
	if v := b.String(key); v != "" {
A
astaxie 已提交
472 473
		return v
	}
474
	return defaultVal
A
astaxie 已提交
475 476
}

477
func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string {
F
fuxiaohei 已提交
478
	if v := b.Strings(key); len(v) != 0 {
A
astaxie 已提交
479 480
		return v
	}
481
	return defaultVal
A
astaxie 已提交
482 483
}

484
func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int {
F
fuxiaohei 已提交
485
	if v, err := b.Int(key); err == nil {
A
astaxie 已提交
486 487
		return v
	}
488
	return defaultVal
A
astaxie 已提交
489 490
}

491
func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 {
F
fuxiaohei 已提交
492
	if v, err := b.Int64(key); err == nil {
A
astaxie 已提交
493 494
		return v
	}
495
	return defaultVal
A
astaxie 已提交
496 497
}

498
func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool {
F
fuxiaohei 已提交
499
	if v, err := b.Bool(key); err == nil {
A
astaxie 已提交
500 501
		return v
	}
502
	return defaultVal
A
astaxie 已提交
503 504
}

505
func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 {
F
fuxiaohei 已提交
506
	if v, err := b.Float(key); err == nil {
A
astaxie 已提交
507 508
		return v
	}
509
	return defaultVal
A
astaxie 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522
}

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)
}