Merge branch 'master' of github.com:beego/beego into dev

This commit is contained in:
Deng Ming 2022-01-28 00:12:47 +08:00
commit 9081c40eaa
9 changed files with 50 additions and 46 deletions

View File

@ -1,4 +1,6 @@
# developing # developing
- [https://github.com/beego/beego/pull/4845](https://github.com/beego/beego/pull/4845)
# v2.0.2-beta.1
- Add a custom option for whether to escape HTML special characters when processing http request parameters. [4701](https://github.com/beego/beego/pull/4701) - Add a custom option for whether to escape HTML special characters when processing http request parameters. [4701](https://github.com/beego/beego/pull/4701)
- Always set the response status in the CustomAbort function. [4686](https://github.com/beego/beego/pull/4686) - Always set the response status in the CustomAbort function. [4686](https://github.com/beego/beego/pull/4686)
@ -67,6 +69,10 @@
- Fix 4698: Prompt error when config format is incorrect. [4757](https://github.com/beego/beego/pull/4757) - Fix 4698: Prompt error when config format is incorrect. [4757](https://github.com/beego/beego/pull/4757)
- Fix 4674: Tx Orm missing debug log [4756](https://github.com/beego/beego/pull/4756) - Fix 4674: Tx Orm missing debug log [4756](https://github.com/beego/beego/pull/4756)
- Fix 4759: fix numeric notation of permissions [4759](https://github.com/beego/beego/pull/4759) - Fix 4759: fix numeric notation of permissions [4759](https://github.com/beego/beego/pull/4759)
- set default rate and capacity for ratelimit filter [4796](https://github.com/beego/beego/pull/4796)
- Fix 4782: must set status before rendering error page [4797](https://github.com/beego/beego/pull/4797)
- Fix 4791: delay to format parameter in log module [4804](https://github.com/beego/beego/pull/4804)
## Fix Sonar ## Fix Sonar
- [4677](https://github.com/beego/beego/pull/4677) - [4677](https://github.com/beego/beego/pull/4677)

View File

@ -46,7 +46,7 @@ func newEtcdConfiger(client *clientv3.Client, prefix string) *EtcdConfiger {
return res return res
} }
// reader is an general implementation that read config from etcd. // reader is a general implementation that read config from etcd.
func (e *EtcdConfiger) reader(ctx context.Context, key string) (string, error) { func (e *EtcdConfiger) reader(ctx context.Context, key string) (string, error) {
resp, err := get(e.client, e.prefix+key) resp, err := get(e.client, e.prefix+key)
if err != nil { if err != nil {
@ -188,5 +188,5 @@ func get(client *clientv3.Client, key string) (*clientv3.GetResponse, error) {
} }
func init() { func init() {
config.Register("json", &EtcdConfigerProvider{}) config.Register("etcd", &EtcdConfigerProvider{})
} }

View File

@ -60,7 +60,7 @@ func GetFormatter(name string) (LogFormatter, bool) {
return res, ok return res, ok
} }
// 'w' when, 'm' msg,'f' filename'F' full path'n' line number // ToString 'w' when, 'm' msg,'f' filename'F' full path'n' line number
// 'l' level number, 't' prefix of level type, 'T' full name of level type // 'l' level number, 't' prefix of level type, 'T' full name of level type
func (p *PatternLogFormatter) ToString(lm *LogMsg) string { func (p *PatternLogFormatter) ToString(lm *LogMsg) string {
s := []rune(p.Pattern) s := []rune(p.Pattern)

View File

@ -705,61 +705,61 @@ func SetLogger(adapter string, config ...string) error {
// Emergency logs a message at emergency level. // Emergency logs a message at emergency level.
func Emergency(f interface{}, v ...interface{}) { func Emergency(f interface{}, v ...interface{}) {
beeLogger.Emergency(formatLog(f, v...)) beeLogger.Emergency(formatPattern(f, v...), v...)
} }
// Alert logs a message at alert level. // Alert logs a message at alert level.
func Alert(f interface{}, v ...interface{}) { func Alert(f interface{}, v ...interface{}) {
beeLogger.Alert(formatLog(f, v...)) beeLogger.Alert(formatPattern(f, v...), v...)
} }
// Critical logs a message at critical level. // Critical logs a message at critical level.
func Critical(f interface{}, v ...interface{}) { func Critical(f interface{}, v ...interface{}) {
beeLogger.Critical(formatLog(f, v...)) beeLogger.Critical(formatPattern(f, v...), v...)
} }
// Error logs a message at error level. // Error logs a message at error level.
func Error(f interface{}, v ...interface{}) { func Error(f interface{}, v ...interface{}) {
beeLogger.Error(formatLog(f, v...)) beeLogger.Error(formatPattern(f, v...), v...)
} }
// Warning logs a message at warning level. // Warning logs a message at warning level.
func Warning(f interface{}, v ...interface{}) { func Warning(f interface{}, v ...interface{}) {
beeLogger.Warn(formatLog(f, v...)) beeLogger.Warn(formatPattern(f, v...), v...)
} }
// Warn compatibility alias for Warning() // Warn compatibility alias for Warning()
func Warn(f interface{}, v ...interface{}) { func Warn(f interface{}, v ...interface{}) {
beeLogger.Warn(formatLog(f, v...)) beeLogger.Warn(formatPattern(f, v...), v...)
} }
// Notice logs a message at notice level. // Notice logs a message at notice level.
func Notice(f interface{}, v ...interface{}) { func Notice(f interface{}, v ...interface{}) {
beeLogger.Notice(formatLog(f, v...)) beeLogger.Notice(formatPattern(f, v...), v...)
} }
// Informational logs a message at info level. // Informational logs a message at info level.
func Informational(f interface{}, v ...interface{}) { func Informational(f interface{}, v ...interface{}) {
beeLogger.Info(formatLog(f, v...)) beeLogger.Info(formatPattern(f, v...), v...)
} }
// Info compatibility alias for Warning() // Info compatibility alias for Warning()
func Info(f interface{}, v ...interface{}) { func Info(f interface{}, v ...interface{}) {
beeLogger.Info(formatLog(f, v...)) beeLogger.Info(formatPattern(f, v...), v...)
} }
// Debug logs a message at debug level. // Debug logs a message at debug level.
func Debug(f interface{}, v ...interface{}) { func Debug(f interface{}, v ...interface{}) {
beeLogger.Debug(formatLog(f, v...)) beeLogger.Debug(formatPattern(f, v...), v...)
} }
// Trace logs a message at trace level. // Trace logs a message at trace level.
// compatibility alias for Warning() // compatibility alias for Warning()
func Trace(f interface{}, v ...interface{}) { func Trace(f interface{}, v ...interface{}) {
beeLogger.Trace(formatLog(f, v...)) beeLogger.Trace(formatPattern(f, v...), v...)
} }
func formatLog(f interface{}, v ...interface{}) string { func formatPattern(f interface{}, v ...interface{}) string {
var msg string var msg string
switch f.(type) { switch f.(type) {
case string: case string:
@ -778,5 +778,5 @@ func formatLog(f interface{}, v ...interface{}) string {
} }
msg += strings.Repeat(" %v", len(v)) msg += strings.Repeat(" %v", len(v))
} }
return fmt.Sprintf(msg, v...) return msg
} }

View File

@ -507,14 +507,16 @@ func defaultRecoverPanic(ctx *context.Context, cfg *Config) {
logs.Critical(fmt.Sprintf("%s:%d", file, line)) logs.Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
} }
if cfg.RunMode == DEV && cfg.EnableErrorsRender {
showErr(err, ctx, stack)
}
if ctx.Output.Status != 0 { if ctx.Output.Status != 0 {
ctx.ResponseWriter.WriteHeader(ctx.Output.Status) ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
} else { } else {
ctx.ResponseWriter.WriteHeader(500) ctx.ResponseWriter.WriteHeader(500)
} }
if cfg.RunMode == DEV && cfg.EnableErrorsRender {
showErr(err, ctx, stack)
}
} }
} }

View File

@ -15,7 +15,6 @@
package ratelimit package ratelimit
import ( import (
"net/http"
"sync" "sync"
"time" "time"
@ -23,11 +22,6 @@ import (
"github.com/beego/beego/v2/server/web/context" "github.com/beego/beego/v2/server/web/context"
) )
// Limiter is an interface used to ratelimit
type Limiter interface {
take(amount uint, r *http.Request) bool
}
// limiterOption is constructor option // limiterOption is constructor option
type limiterOption func(l *limiter) type limiterOption func(l *limiter)
@ -37,7 +31,7 @@ type limiter struct {
rate time.Duration rate time.Duration
buckets map[string]bucket buckets map[string]bucket
bucketFactory func(opts ...bucketOption) bucket bucketFactory func(opts ...bucketOption) bucket
sessionKey func(r *http.Request) string sessionKey func(ctx *context.Context) string
resp RejectionResponse resp RejectionResponse
} }
@ -60,10 +54,10 @@ var defaultRejectionResponse = RejectionResponse{
func NewLimiter(opts ...limiterOption) web.FilterFunc { func NewLimiter(opts ...limiterOption) web.FilterFunc {
l := &limiter{ l := &limiter{
buckets: make(map[string]bucket), buckets: make(map[string]bucket),
sessionKey: func(r *http.Request) string { sessionKey: defaultSessionKey,
return defaultSessionKey(r) rate: time.Millisecond * 10,
}, capacity: 100,
bucketFactory: NewTokenBucket, bucketFactory: newTokenBucket,
resp: defaultRejectionResponse, resp: defaultRejectionResponse,
} }
for _, o := range opts { for _, o := range opts {
@ -71,7 +65,7 @@ func NewLimiter(opts ...limiterOption) web.FilterFunc {
} }
return func(ctx *context.Context) { return func(ctx *context.Context) {
if !l.take(perRequestConsumedAmount, ctx.Request) { if !l.take(perRequestConsumedAmount, ctx) {
ctx.ResponseWriter.WriteHeader(l.resp.code) ctx.ResponseWriter.WriteHeader(l.resp.code)
ctx.WriteString(l.resp.body) ctx.WriteString(l.resp.body)
} }
@ -79,8 +73,8 @@ func NewLimiter(opts ...limiterOption) web.FilterFunc {
} }
// WithSessionKey return limiterOption. WithSessionKey config func // WithSessionKey return limiterOption. WithSessionKey config func
// which defines the request characteristic againstthe limit is applied // which defines the request characteristic against the limit is applied
func WithSessionKey(f func(r *http.Request) string) limiterOption { func WithSessionKey(f func(ctx *context.Context) string) limiterOption {
return func(l *limiter) { return func(l *limiter) {
l.sessionKey = f l.sessionKey = f
} }
@ -119,16 +113,16 @@ func WithRejectionResponse(resp RejectionResponse) limiterOption {
} }
} }
func (l *limiter) take(amount uint, r *http.Request) bool { func (l *limiter) take(amount uint, ctx *context.Context) bool {
bucket := l.getBucket(r) bucket := l.getBucket(ctx)
if bucket == nil { if bucket == nil {
return true return true
} }
return bucket.take(amount) return bucket.take(amount)
} }
func (l *limiter) getBucket(r *http.Request) bucket { func (l *limiter) getBucket(ctx *context.Context) bucket {
key := l.sessionKey(r) key := l.sessionKey(ctx)
l.RLock() l.RLock()
b, ok := l.buckets[key] b, ok := l.buckets[key]
l.RUnlock() l.RUnlock()
@ -152,11 +146,12 @@ func (l *limiter) createBucket(key string) bucket {
return b return b
} }
func defaultSessionKey(r *http.Request) string { func defaultSessionKey(ctx *context.Context) string {
return "" return "BEEGO_ALL"
} }
func RemoteIPSessionKey(r *http.Request) string { func RemoteIPSessionKey(ctx *context.Context) string {
r := ctx.Request
IPAddress := r.Header.Get("X-Real-Ip") IPAddress := r.Header.Get("X-Real-Ip")
if IPAddress == "" { if IPAddress == "" {
IPAddress = r.Header.Get("X-Forwarded-For") IPAddress = r.Header.Get("X-Forwarded-For")

View File

@ -13,8 +13,8 @@ type tokenBucket struct {
rate time.Duration rate time.Duration
} }
// NewTokenBucket return an bucket that implements token bucket // newTokenBucket return an bucket that implements token bucket
func NewTokenBucket(opts ...bucketOption) bucket { func newTokenBucket(opts ...bucketOption) bucket {
b := &tokenBucket{lastCheckAt: time.Now()} b := &tokenBucket{lastCheckAt: time.Now()}
for _, o := range opts { for _, o := range opts {
o(b) o(b)

View File

@ -8,24 +8,24 @@ import (
) )
func TestGetRate(t *testing.T) { func TestGetRate(t *testing.T) {
b := NewTokenBucket(withRate(1 * time.Second)).(*tokenBucket) b := newTokenBucket(withRate(1 * time.Second)).(*tokenBucket)
assert.Equal(t, b.getRate(), 1*time.Second) assert.Equal(t, b.getRate(), 1*time.Second)
} }
func TestGetRemainingAndCapacity(t *testing.T) { func TestGetRemainingAndCapacity(t *testing.T) {
b := NewTokenBucket(withCapacity(10)) b := newTokenBucket(withCapacity(10))
assert.Equal(t, b.getRemaining(), uint(10)) assert.Equal(t, b.getRemaining(), uint(10))
assert.Equal(t, b.getCapacity(), uint(10)) assert.Equal(t, b.getCapacity(), uint(10))
} }
func TestTake(t *testing.T) { func TestTake(t *testing.T) {
b := NewTokenBucket(withCapacity(10), withRate(10*time.Millisecond)).(*tokenBucket) b := newTokenBucket(withCapacity(10), withRate(10*time.Millisecond)).(*tokenBucket)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
assert.True(t, b.take(1)) assert.True(t, b.take(1))
} }
assert.False(t, b.take(1)) assert.False(t, b.take(1))
assert.Equal(t, b.getRemaining(), uint(0)) assert.Equal(t, b.getRemaining(), uint(0))
b = NewTokenBucket(withCapacity(1), withRate(1*time.Millisecond)).(*tokenBucket) b = newTokenBucket(withCapacity(1), withRate(1*time.Millisecond)).(*tokenBucket)
assert.True(t, b.take(1)) assert.True(t, b.take(1))
time.Sleep(2 * time.Millisecond) time.Sleep(2 * time.Millisecond)
assert.True(t, b.take(1)) assert.True(t, b.take(1))

View File

@ -11,6 +11,7 @@ import (
// Session maintain session for web service // Session maintain session for web service
// Session new a session storage and store it into webContext.Context // Session new a session storage and store it into webContext.Context
// experimental feature, we may change this in the future
func Session(providerType session.ProviderType, options ...session.ManagerConfigOpt) web.FilterChain { func Session(providerType session.ProviderType, options ...session.ManagerConfigOpt) web.FilterChain {
sessionConfig := session.NewManagerConfig(options...) sessionConfig := session.NewManagerConfig(options...)
sessionManager, _ := session.NewManager(string(providerType), sessionConfig) sessionManager, _ := session.NewManager(string(providerType), sessionConfig)