mirror of
https://github.com/astaxie/beego.git
synced 2024-11-25 21:41:29 +00:00
Merge branch 'develop' of https://github.com/astaxie/beego into develop
This commit is contained in:
commit
0d0d87f600
@ -67,6 +67,7 @@ func oldMap() map[string]interface{} {
|
|||||||
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
|
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
|
||||||
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
||||||
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
|
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
|
||||||
|
m["BConfig.Log.EnableStaticLogs"] = BConfig.Log.EnableStaticLogs
|
||||||
m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
|
m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
|
||||||
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
||||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||||
|
@ -106,6 +106,7 @@ type SessionConfig struct {
|
|||||||
// LogConfig holds Log related config
|
// LogConfig holds Log related config
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
AccessLogs bool
|
AccessLogs bool
|
||||||
|
EnableStaticLogs bool //log static files requests default: false
|
||||||
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
||||||
FileLineNum bool
|
FileLineNum bool
|
||||||
Outputs map[string]string // Store Adaptor : config
|
Outputs map[string]string // Store Adaptor : config
|
||||||
@ -182,6 +183,11 @@ func recoverPanic(ctx *context.Context) {
|
|||||||
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
||||||
showErr(err, ctx, stack)
|
showErr(err, ctx, stack)
|
||||||
}
|
}
|
||||||
|
if ctx.Output.Status != 0 {
|
||||||
|
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
||||||
|
} else {
|
||||||
|
ctx.ResponseWriter.WriteHeader(500)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +253,7 @@ func newBConfig() *Config {
|
|||||||
},
|
},
|
||||||
Log: LogConfig{
|
Log: LogConfig{
|
||||||
AccessLogs: false,
|
AccessLogs: false,
|
||||||
|
EnableStaticLogs: false,
|
||||||
AccessLogsFormat: "APACHE_FORMAT",
|
AccessLogsFormat: "APACHE_FORMAT",
|
||||||
FileLineNum: true,
|
FileLineNum: true,
|
||||||
Outputs: map[string]string{"console": ""},
|
Outputs: map[string]string{"console": ""},
|
||||||
|
@ -272,7 +272,22 @@ func (c *Controller) viewPath() string {
|
|||||||
|
|
||||||
// Redirect sends the redirection response to url with status code.
|
// Redirect sends the redirection response to url with status code.
|
||||||
func (c *Controller) Redirect(url string, code int) {
|
func (c *Controller) Redirect(url string, code int) {
|
||||||
|
logAccess(c.Ctx, nil, code)
|
||||||
c.Ctx.Redirect(code, url)
|
c.Ctx.Redirect(code, url)
|
||||||
|
panic(ErrAbort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the data depending on the accepted
|
||||||
|
func (c *Controller) SetData(data interface{}) {
|
||||||
|
accept := c.Ctx.Input.Header("Accept")
|
||||||
|
switch accept {
|
||||||
|
case applicationJSON:
|
||||||
|
c.Data["json"] = data
|
||||||
|
case applicationXML, textXML:
|
||||||
|
c.Data["xml"] = data
|
||||||
|
default:
|
||||||
|
c.Data["json"] = data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
|
// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
|
||||||
|
10
error.go
10
error.go
@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errorTypeHandler = iota
|
errorTypeHandler = iota
|
||||||
errorTypeController
|
errorTypeController
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -93,11 +93,6 @@ func showErr(err interface{}, ctx *context.Context, stack string) {
|
|||||||
"BeegoVersion": VERSION,
|
"BeegoVersion": VERSION,
|
||||||
"GoVersion": runtime.Version(),
|
"GoVersion": runtime.Version(),
|
||||||
}
|
}
|
||||||
if ctx.Output.Status != 0 {
|
|
||||||
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
|
||||||
} else {
|
|
||||||
ctx.ResponseWriter.WriteHeader(500)
|
|
||||||
}
|
|
||||||
t.Execute(ctx.ResponseWriter, data)
|
t.Execute(ctx.ResponseWriter, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +434,9 @@ func exception(errCode string, ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
||||||
|
//make sure to log the error in the access log
|
||||||
|
logAccess(ctx, nil, code)
|
||||||
|
|
||||||
if err.errorType == errorTypeHandler {
|
if err.errorType == errorTypeHandler {
|
||||||
ctx.ResponseWriter.WriteHeader(code)
|
ctx.ResponseWriter.WriteHeader(code)
|
||||||
err.handler(ctx.ResponseWriter, ctx.Request)
|
err.handler(ctx.ResponseWriter, ctx.Request)
|
||||||
|
@ -16,48 +16,57 @@ As of now this logs support console, file,smtp and conn.
|
|||||||
|
|
||||||
First you must import it
|
First you must import it
|
||||||
|
|
||||||
import (
|
```golang
|
||||||
"github.com/astaxie/beego/logs"
|
import (
|
||||||
)
|
"github.com/astaxie/beego/logs"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
Then init a Log (example with console adapter)
|
Then init a Log (example with console adapter)
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("console", "")
|
log := logs.NewLogger(10000)
|
||||||
|
log.SetLogger("console", "")
|
||||||
|
```
|
||||||
|
|
||||||
> the first params stand for how many channel
|
> the first params stand for how many channel
|
||||||
|
|
||||||
Use it like this:
|
Use it like this:
|
||||||
|
|
||||||
log.Trace("trace")
|
|
||||||
log.Info("info")
|
|
||||||
log.Warn("warning")
|
|
||||||
log.Debug("debug")
|
|
||||||
log.Critical("critical")
|
|
||||||
|
|
||||||
|
```golang
|
||||||
|
log.Trace("trace")
|
||||||
|
log.Info("info")
|
||||||
|
log.Warn("warning")
|
||||||
|
log.Debug("debug")
|
||||||
|
log.Critical("critical")
|
||||||
|
```
|
||||||
|
|
||||||
## File adapter
|
## File adapter
|
||||||
|
|
||||||
Configure file adapter like this:
|
Configure file adapter like this:
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("file", `{"filename":"test.log"}`)
|
log := NewLogger(10000)
|
||||||
|
log.SetLogger("file", `{"filename":"test.log"}`)
|
||||||
|
```
|
||||||
|
|
||||||
## Conn adapter
|
## Conn adapter
|
||||||
|
|
||||||
Configure like this:
|
Configure like this:
|
||||||
|
|
||||||
log := NewLogger(1000)
|
```golang
|
||||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
log := NewLogger(1000)
|
||||||
log.Info("info")
|
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||||
|
log.Info("info")
|
||||||
|
```
|
||||||
|
|
||||||
## Smtp adapter
|
## Smtp adapter
|
||||||
|
|
||||||
Configure like this:
|
Configure like this:
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
|
log := NewLogger(10000)
|
||||||
log.Critical("sendmail critical")
|
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
|
||||||
time.Sleep(time.Second * 30)
|
log.Critical("sendmail critical")
|
||||||
|
time.Sleep(time.Second * 30)
|
||||||
|
```
|
||||||
|
@ -16,6 +16,7 @@ package logs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"strings"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
@ -63,9 +64,7 @@ func disableEscapeHTML(i interface{}) {
|
|||||||
// AccessLog - Format and print access log.
|
// AccessLog - Format and print access log.
|
||||||
func AccessLog(r *AccessLogRecord, format string) {
|
func AccessLog(r *AccessLogRecord, format string) {
|
||||||
var msg string
|
var msg string
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
|
|
||||||
case apacheFormat:
|
case apacheFormat:
|
||||||
timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
|
timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
|
||||||
msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
|
msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
|
||||||
@ -80,6 +79,5 @@ func AccessLog(r *AccessLogRecord, format string) {
|
|||||||
msg = string(jsonData)
|
msg = string(jsonData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
beeLogger.writeMsg(levelLoggerImpl, strings.TrimSpace(msg))
|
||||||
beeLogger.Debug(msg)
|
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,11 @@ func (o *querySet) PrepareInsert() (Inserter, error) {
|
|||||||
// query all data and map to containers.
|
// query all data and map to containers.
|
||||||
// cols means the columns when querying.
|
// cols means the columns when querying.
|
||||||
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
|
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
|
||||||
return o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
||||||
|
if num == 0 {
|
||||||
|
return 0, ErrNoRows
|
||||||
|
}
|
||||||
|
return num, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// query one row data and map to containers.
|
// query one row data and map to containers.
|
||||||
|
92
router.go
92
router.go
@ -43,7 +43,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
routerTypeBeego = iota
|
routerTypeBeego = iota
|
||||||
routerTypeRESTFul
|
routerTypeRESTFul
|
||||||
routerTypeHandler
|
routerTypeHandler
|
||||||
)
|
)
|
||||||
@ -877,13 +877,15 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Admin:
|
Admin:
|
||||||
//admin module record QPS
|
//admin module record QPS
|
||||||
|
|
||||||
statusCode := context.ResponseWriter.Status
|
statusCode := context.ResponseWriter.Status
|
||||||
if statusCode == 0 {
|
if statusCode == 0 {
|
||||||
statusCode = 200
|
statusCode = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logAccess(context, &startTime, statusCode)
|
||||||
|
|
||||||
if BConfig.Listen.EnableAdmin {
|
if BConfig.Listen.EnableAdmin {
|
||||||
timeDur := time.Since(startTime)
|
timeDur := time.Since(startTime)
|
||||||
pattern := ""
|
pattern := ""
|
||||||
@ -900,49 +902,30 @@ Admin:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if BConfig.RunMode == DEV || BConfig.Log.AccessLogs {
|
if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
|
||||||
timeDur := time.Since(startTime)
|
|
||||||
var devInfo string
|
var devInfo string
|
||||||
|
timeDur := time.Since(startTime)
|
||||||
iswin := (runtime.GOOS == "windows")
|
iswin := (runtime.GOOS == "windows")
|
||||||
statusColor := logs.ColorByStatus(iswin, statusCode)
|
statusColor := logs.ColorByStatus(iswin, statusCode)
|
||||||
methodColor := logs.ColorByMethod(iswin, r.Method)
|
methodColor := logs.ColorByMethod(iswin, r.Method)
|
||||||
resetColor := logs.ColorByMethod(iswin, "")
|
resetColor := logs.ColorByMethod(iswin, "")
|
||||||
if BConfig.Log.AccessLogsFormat != "" {
|
if findRouter {
|
||||||
record := &logs.AccessLogRecord{
|
if routerInfo != nil {
|
||||||
RemoteAddr: context.Input.IP(),
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
||||||
RequestTime: startTime,
|
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
||||||
RequestMethod: r.Method,
|
routerInfo.pattern)
|
||||||
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
|
||||||
ServerProtocol: r.Proto,
|
|
||||||
Host: r.Host,
|
|
||||||
Status: statusCode,
|
|
||||||
ElapsedTime: timeDur,
|
|
||||||
HTTPReferrer: r.Header.Get("Referer"),
|
|
||||||
HTTPUserAgent: r.Header.Get("User-Agent"),
|
|
||||||
RemoteUser: r.Header.Get("Remote-User"),
|
|
||||||
BodyBytesSent: 0, //@todo this one is missing!
|
|
||||||
}
|
|
||||||
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
|
||||||
} else {
|
|
||||||
if findRouter {
|
|
||||||
if routerInfo != nil {
|
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
|
||||||
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
|
||||||
routerInfo.pattern)
|
|
||||||
} else {
|
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
|
||||||
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
}
|
|
||||||
if iswin {
|
|
||||||
logs.W32Debug(devInfo)
|
|
||||||
} else {
|
|
||||||
logs.Debug(devInfo)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
|
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
|
}
|
||||||
|
if iswin {
|
||||||
|
logs.W32Debug(devInfo)
|
||||||
|
} else {
|
||||||
|
logs.Debug(devInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Call WriteHeader if status code has been set changed
|
// Call WriteHeader if status code has been set changed
|
||||||
@ -991,3 +974,38 @@ func toURL(params map[string]string) string {
|
|||||||
}
|
}
|
||||||
return strings.TrimRight(u, "&")
|
return strings.TrimRight(u, "&")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
|
||||||
|
//Skip logging if AccessLogs config is false
|
||||||
|
if !BConfig.Log.AccessLogs {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Skip logging static requests unless EnableStaticLogs config is true
|
||||||
|
if !BConfig.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
requestTime time.Time
|
||||||
|
elapsedTime time.Duration
|
||||||
|
r = ctx.Request
|
||||||
|
)
|
||||||
|
if startTime != nil {
|
||||||
|
requestTime = *startTime
|
||||||
|
elapsedTime = time.Since(*startTime)
|
||||||
|
}
|
||||||
|
record := &logs.AccessLogRecord{
|
||||||
|
RemoteAddr: ctx.Input.IP(),
|
||||||
|
RequestTime: requestTime,
|
||||||
|
RequestMethod: r.Method,
|
||||||
|
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
||||||
|
ServerProtocol: r.Proto,
|
||||||
|
Host: r.Host,
|
||||||
|
Status: statusCode,
|
||||||
|
ElapsedTime: elapsedTime,
|
||||||
|
HTTPReferrer: r.Header.Get("Referer"),
|
||||||
|
HTTPUserAgent: r.Header.Get("User-Agent"),
|
||||||
|
RemoteUser: r.Header.Get("Remote-User"),
|
||||||
|
BodyBytesSent: 0, //@todo this one is missing!
|
||||||
|
}
|
||||||
|
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
||||||
|
}
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
|
|
||||||
@ -118,8 +119,8 @@ type Provider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SessionInit init redis session
|
// SessionInit init redis session
|
||||||
// savepath like redis server addr,pool size,password,dbnum
|
// savepath like redis server addr,pool size,password,dbnum,IdleTimeout second
|
||||||
// e.g. 127.0.0.1:6379,100,astaxie,0
|
// e.g. 127.0.0.1:6379,100,astaxie,0,30
|
||||||
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
rp.maxlifetime = maxlifetime
|
rp.maxlifetime = maxlifetime
|
||||||
configs := strings.Split(savePath, ",")
|
configs := strings.Split(savePath, ",")
|
||||||
@ -149,6 +150,13 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
} else {
|
} else {
|
||||||
rp.dbNum = 0
|
rp.dbNum = 0
|
||||||
}
|
}
|
||||||
|
var idleTimeout time.Duration = 0
|
||||||
|
if len(configs) > 4 {
|
||||||
|
timeout, err := strconv.Atoi(configs[4])
|
||||||
|
if err == nil && timeout > 0 {
|
||||||
|
idleTimeout = time.Duration(timeout) * time.Second
|
||||||
|
}
|
||||||
|
}
|
||||||
rp.poollist = &redis.Pool{
|
rp.poollist = &redis.Pool{
|
||||||
Dial: func() (redis.Conn, error) {
|
Dial: func() (redis.Conn, error) {
|
||||||
c, err := redis.Dial("tcp", rp.savePath)
|
c, err := redis.Dial("tcp", rp.savePath)
|
||||||
@ -171,9 +179,11 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
}
|
}
|
||||||
return c, err
|
return c, err
|
||||||
},
|
},
|
||||||
MaxIdle: rp.poolsize,
|
MaxIdle: rp.poolsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rp.poollist.IdleTimeout = idleTimeout
|
||||||
|
|
||||||
return rp.poollist.Get().Err()
|
return rp.poollist.Get().Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ func serverStaticRouter(ctx *context.Context) {
|
|||||||
if enableCompress {
|
if enableCompress {
|
||||||
acceptEncoding = context.ParseEncoding(ctx.Request)
|
acceptEncoding = context.ParseEncoding(ctx.Request)
|
||||||
}
|
}
|
||||||
b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding)
|
b, n, sch, reader, err := openFile(filePath, fileInfo, acceptEncoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if BConfig.RunMode == DEV {
|
if BConfig.RunMode == DEV {
|
||||||
logs.Warn("Can't compress the file:", filePath, err)
|
logs.Warn("Can't compress the file:", filePath, err)
|
||||||
@ -89,47 +89,53 @@ func serverStaticRouter(ctx *context.Context) {
|
|||||||
ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
|
ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
|
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
type serveContentHolder struct {
|
type serveContentHolder struct {
|
||||||
*bytes.Reader
|
data []byte
|
||||||
modTime time.Time
|
modTime time.Time
|
||||||
size int64
|
size int64
|
||||||
encoding string
|
encoding string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serveContentReader struct {
|
||||||
|
*bytes.Reader
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
staticFileMap = make(map[string]*serveContentHolder)
|
staticFileMap = make(map[string]*serveContentHolder)
|
||||||
mapLock sync.RWMutex
|
mapLock sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) {
|
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, *serveContentReader, error) {
|
||||||
mapKey := acceptEncoding + ":" + filePath
|
mapKey := acceptEncoding + ":" + filePath
|
||||||
mapLock.RLock()
|
mapLock.RLock()
|
||||||
mapFile := staticFileMap[mapKey]
|
mapFile := staticFileMap[mapKey]
|
||||||
mapLock.RUnlock()
|
mapLock.RUnlock()
|
||||||
if isOk(mapFile, fi) {
|
if isOk(mapFile, fi) {
|
||||||
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, reader, nil
|
||||||
}
|
}
|
||||||
mapLock.Lock()
|
mapLock.Lock()
|
||||||
defer mapLock.Unlock()
|
defer mapLock.Unlock()
|
||||||
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
var bufferWriter bytes.Buffer
|
var bufferWriter bytes.Buffer
|
||||||
_, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
|
_, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, nil, err
|
||||||
}
|
}
|
||||||
mapFile = &serveContentHolder{Reader: bytes.NewReader(bufferWriter.Bytes()), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
|
mapFile = &serveContentHolder{data: bufferWriter.Bytes(), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
|
||||||
staticFileMap[mapKey] = mapFile
|
staticFileMap[mapKey] = mapFile
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
|
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
|
||||||
|
@ -16,7 +16,7 @@ var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
|
|||||||
|
|
||||||
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
||||||
fi, _ := os.Stat(licenseFile)
|
fi, _ := os.Stat(licenseFile)
|
||||||
b, n, sch, err := openFile(licenseFile, fi, encoding)
|
b, n, sch, reader, err := openFile(licenseFile, fi, encoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@ -24,7 +24,7 @@ func testOpenFile(encoding string, content []byte, t *testing.T) {
|
|||||||
|
|
||||||
t.Log("open static file encoding "+n, b)
|
t.Log("open static file encoding "+n, b)
|
||||||
|
|
||||||
assetOpenFileAndContent(sch, content, t)
|
assetOpenFileAndContent(sch, reader, content, t)
|
||||||
}
|
}
|
||||||
func TestOpenStaticFile_1(t *testing.T) {
|
func TestOpenStaticFile_1(t *testing.T) {
|
||||||
file, _ := os.Open(licenseFile)
|
file, _ := os.Open(licenseFile)
|
||||||
@ -53,13 +53,13 @@ func TestOpenStaticFileDeflate_1(t *testing.T) {
|
|||||||
testOpenFile("deflate", content, t)
|
testOpenFile("deflate", content, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assetOpenFileAndContent(sch *serveContentHolder, content []byte, t *testing.T) {
|
func assetOpenFileAndContent(sch *serveContentHolder, reader *serveContentReader, content []byte, t *testing.T) {
|
||||||
t.Log(sch.size, len(content))
|
t.Log(sch.size, len(content))
|
||||||
if sch.size != int64(len(content)) {
|
if sch.size != int64(len(content)) {
|
||||||
t.Log("static content file size not same")
|
t.Log("static content file size not same")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
bs, _ := ioutil.ReadAll(sch)
|
bs, _ := ioutil.ReadAll(reader)
|
||||||
for i, v := range content {
|
for i, v := range content {
|
||||||
if v != bs[i] {
|
if v != bs[i] {
|
||||||
t.Log("content not same")
|
t.Log("content not same")
|
||||||
|
@ -365,10 +365,10 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasReuired bool
|
var hasRequired bool
|
||||||
for _, vf := range vfs {
|
for _, vf := range vfs {
|
||||||
if vf.Name == "Required" {
|
if vf.Name == "Required" {
|
||||||
hasReuired = true
|
hasRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
currentField := objV.Field(i).Interface()
|
currentField := objV.Field(i).Interface()
|
||||||
@ -382,7 +382,7 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) {
|
|||||||
|
|
||||||
|
|
||||||
chk := Required{""}.IsSatisfied(currentField)
|
chk := Required{""}.IsSatisfied(currentField)
|
||||||
if !hasReuired && v.RequiredFirst && !chk {
|
if !hasRequired && v.RequiredFirst && !chk {
|
||||||
if _, ok := CanSkipFuncs[vf.Name]; ok {
|
if _, ok := CanSkipFuncs[vf.Name]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user