mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 07:41:02 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
cbbb6bfb08 | |||
b2a96234ab | |||
db3defa76a | |||
1dffa20435 | |||
8e37fe3b78 | |||
91e18996bd | |||
b8c1e133bf | |||
d66321fe4e | |||
0b7ece44cf | |||
6ffbc0a2b8 | |||
26208a53e6 | |||
f6519b29a8 | |||
91410be722 | |||
ff53e12191 | |||
60bb057783 | |||
8736ffaf6f | |||
f946a35acd | |||
9bd3a27e80 |
@ -8,6 +8,15 @@ It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific feature
|
||||
|
||||
## Quick Start
|
||||
|
||||
#### Create `hello` directory, cd `hello` directory
|
||||
|
||||
mkdir hello
|
||||
cd hello
|
||||
|
||||
#### Init module
|
||||
|
||||
go mod init
|
||||
|
||||
#### Download and install
|
||||
|
||||
go get github.com/astaxie/beego
|
||||
|
62
admin.go
62
admin.go
@ -21,6 +21,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@ -71,7 +72,7 @@ func init() {
|
||||
// AdminIndex is the default http.Handler for admin module.
|
||||
// it matches url pattern "/".
|
||||
func adminIndex(rw http.ResponseWriter, _ *http.Request) {
|
||||
execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
|
||||
writeTemplate(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
|
||||
}
|
||||
|
||||
// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter.
|
||||
@ -91,7 +92,7 @@ func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
execTpl(rw, data, qpsTpl, defaultScriptsTpl)
|
||||
writeTemplate(rw, data, qpsTpl, defaultScriptsTpl)
|
||||
}
|
||||
|
||||
// ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
|
||||
@ -128,7 +129,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
data["Content"] = content
|
||||
data["Title"] = "Routers"
|
||||
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||
case "filter":
|
||||
var (
|
||||
content = M{
|
||||
@ -171,7 +172,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
data["Content"] = content
|
||||
data["Title"] = "Filters"
|
||||
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||
default:
|
||||
rw.Write([]byte("command not support"))
|
||||
}
|
||||
@ -279,9 +280,7 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Write(dataJSON)
|
||||
writeJSON(rw, dataJSON)
|
||||
return
|
||||
}
|
||||
|
||||
@ -290,12 +289,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
if command == "gc summary" {
|
||||
defaultTpl = gcAjaxTpl
|
||||
}
|
||||
execTpl(rw, data, profillingTpl, defaultTpl)
|
||||
writeTemplate(rw, data, profillingTpl, defaultTpl)
|
||||
}
|
||||
|
||||
// Healthcheck is a http.Handler calling health checking and showing the result.
|
||||
// it's in "/healthcheck" pattern in admin module.
|
||||
func healthcheck(rw http.ResponseWriter, _ *http.Request) {
|
||||
func healthcheck(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
result []string
|
||||
data = make(map[interface{}]interface{})
|
||||
@ -322,10 +321,49 @@ func healthcheck(rw http.ResponseWriter, _ *http.Request) {
|
||||
*resultList = append(*resultList, result)
|
||||
}
|
||||
|
||||
queryParams := r.URL.Query()
|
||||
jsonFlag := queryParams.Get("json")
|
||||
shouldReturnJSON, _ := strconv.ParseBool(jsonFlag)
|
||||
|
||||
if shouldReturnJSON {
|
||||
response := buildHealthCheckResponseList(resultList)
|
||||
jsonResponse, err := json.Marshal(response)
|
||||
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
} else {
|
||||
writeJSON(rw, jsonResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
content["Data"] = resultList
|
||||
data["Content"] = content
|
||||
data["Title"] = "Health Check"
|
||||
execTpl(rw, data, healthCheckTpl, defaultScriptsTpl)
|
||||
|
||||
writeTemplate(rw, data, healthCheckTpl, defaultScriptsTpl)
|
||||
}
|
||||
|
||||
func buildHealthCheckResponseList(healthCheckResults *[][]string) []map[string]interface{} {
|
||||
response := make([]map[string]interface{}, len(*healthCheckResults))
|
||||
|
||||
for i, healthCheckResult := range *healthCheckResults {
|
||||
currentResultMap := make(map[string]interface{})
|
||||
|
||||
currentResultMap["name"] = healthCheckResult[0]
|
||||
currentResultMap["message"] = healthCheckResult[1]
|
||||
currentResultMap["status"] = healthCheckResult[2]
|
||||
|
||||
response[i] = currentResultMap
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
}
|
||||
|
||||
func writeJSON(rw http.ResponseWriter, jsonData []byte) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Write(jsonData)
|
||||
}
|
||||
|
||||
// TaskStatus is a http.Handler with running task status (task name, status and the last execution).
|
||||
@ -371,10 +409,10 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
content["Data"] = resultList
|
||||
data["Content"] = content
|
||||
data["Title"] = "Tasks"
|
||||
execTpl(rw, data, tasksTpl, defaultScriptsTpl)
|
||||
writeTemplate(rw, data, tasksTpl, defaultScriptsTpl)
|
||||
}
|
||||
|
||||
func execTpl(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
|
||||
func writeTemplate(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
|
||||
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||
for _, tpl := range tpls {
|
||||
tmpl = template.Must(tmpl.Parse(tpl))
|
||||
|
162
admin_test.go
162
admin_test.go
@ -1,10 +1,32 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/toolbox"
|
||||
)
|
||||
|
||||
type SampleDatabaseCheck struct {
|
||||
}
|
||||
|
||||
type SampleCacheCheck struct {
|
||||
}
|
||||
|
||||
func (dc *SampleDatabaseCheck) Check() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc *SampleCacheCheck) Check() error {
|
||||
return errors.New("no cache detected")
|
||||
}
|
||||
|
||||
func TestList_01(t *testing.T) {
|
||||
m := make(M)
|
||||
list("BConfig", BConfig, m)
|
||||
@ -75,3 +97,143 @@ func oldMap() M {
|
||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||
return m
|
||||
}
|
||||
|
||||
func TestWriteJSON(t *testing.T) {
|
||||
t.Log("Testing the adding of JSON to the response")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
originalBody := []int{1, 2, 3}
|
||||
|
||||
res, _ := json.Marshal(originalBody)
|
||||
|
||||
writeJSON(w, res)
|
||||
|
||||
decodedBody := []int{}
|
||||
err := json.NewDecoder(w.Body).Decode(&decodedBody)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Could not decode response body into slice.")
|
||||
}
|
||||
|
||||
for i := range decodedBody {
|
||||
if decodedBody[i] != originalBody[i] {
|
||||
t.Fatalf("Expected %d but got %d in decoded body slice", originalBody[i], decodedBody[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthCheckHandlerDefault(t *testing.T) {
|
||||
endpointPath := "/healthcheck"
|
||||
|
||||
toolbox.AddHealthCheck("database", &SampleDatabaseCheck{})
|
||||
toolbox.AddHealthCheck("cache", &SampleCacheCheck{})
|
||||
|
||||
req, err := http.NewRequest("GET", endpointPath, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := http.HandlerFunc(healthcheck)
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(w.Body.String(), "database") {
|
||||
t.Errorf("Expected 'database' in generated template.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuildHealthCheckResponseList(t *testing.T) {
|
||||
healthCheckResults := [][]string{
|
||||
[]string{
|
||||
"error",
|
||||
"Database",
|
||||
"Error occurred while starting the db",
|
||||
},
|
||||
[]string{
|
||||
"success",
|
||||
"Cache",
|
||||
"Cache started successfully",
|
||||
},
|
||||
}
|
||||
|
||||
responseList := buildHealthCheckResponseList(&healthCheckResults)
|
||||
|
||||
if len(responseList) != len(healthCheckResults) {
|
||||
t.Errorf("invalid response map length: got %d want %d",
|
||||
len(responseList), len(healthCheckResults))
|
||||
}
|
||||
|
||||
responseFields := []string{"name", "message", "status"}
|
||||
|
||||
for _, response := range responseList {
|
||||
for _, field := range responseFields {
|
||||
_, ok := response[field]
|
||||
if !ok {
|
||||
t.Errorf("expected %s to be in the response %v", field, response)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHealthCheckHandlerReturnsJSON(t *testing.T) {
|
||||
|
||||
toolbox.AddHealthCheck("database", &SampleDatabaseCheck{})
|
||||
toolbox.AddHealthCheck("cache", &SampleCacheCheck{})
|
||||
|
||||
req, err := http.NewRequest("GET", "/healthcheck?json=true", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := http.HandlerFunc(healthcheck)
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
if status := w.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
decodedResponseBody := []map[string]interface{}{}
|
||||
expectedResponseBody := []map[string]interface{}{}
|
||||
|
||||
expectedJSONString := []byte(`
|
||||
[
|
||||
{
|
||||
"message":"database",
|
||||
"name":"success",
|
||||
"status":"OK"
|
||||
},
|
||||
{
|
||||
"message":"cache",
|
||||
"name":"error",
|
||||
"status":"no cache detected"
|
||||
}
|
||||
]
|
||||
`)
|
||||
|
||||
json.Unmarshal(expectedJSONString, &expectedResponseBody)
|
||||
|
||||
json.Unmarshal(w.Body.Bytes(), &decodedResponseBody)
|
||||
|
||||
if len(expectedResponseBody) != len(decodedResponseBody) {
|
||||
t.Errorf("invalid response map length: got %d want %d",
|
||||
len(decodedResponseBody), len(expectedResponseBody))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(decodedResponseBody, expectedResponseBody) {
|
||||
t.Errorf("handler returned unexpected body: got %v want %v",
|
||||
decodedResponseBody, expectedResponseBody)
|
||||
}
|
||||
|
||||
}
|
||||
|
2
app.go
2
app.go
@ -197,7 +197,7 @@ func (app *App) Run(mws ...MiddleWare) {
|
||||
pool.AppendCertsFromPEM(data)
|
||||
app.Server.TLSConfig = &tls.Config{
|
||||
ClientCAs: pool,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
ClientAuth: BConfig.Listen.ClientAuth,
|
||||
}
|
||||
}
|
||||
if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||
|
2
beego.go
2
beego.go
@ -23,7 +23,7 @@ import (
|
||||
|
||||
const (
|
||||
// VERSION represent beego web framework version.
|
||||
VERSION = "1.12.2"
|
||||
VERSION = "1.12.3"
|
||||
|
||||
// DEV is for develop
|
||||
DEV = "dev"
|
||||
|
@ -15,11 +15,11 @@
|
||||
package beego
|
||||
|
||||
var (
|
||||
BuildVersion string
|
||||
BuildVersion string
|
||||
BuildGitRevision string
|
||||
BuildStatus string
|
||||
BuildTag string
|
||||
BuildTime string
|
||||
BuildStatus string
|
||||
BuildTag string
|
||||
BuildTime string
|
||||
|
||||
GoVersion string
|
||||
|
||||
|
5
cache/redis/redis.go
vendored
5
cache/redis/redis.go
vendored
@ -38,8 +38,9 @@ import (
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -57,7 +58,7 @@ type Cache struct {
|
||||
maxIdle int
|
||||
|
||||
//the timeout to a value less than the redis server's timeout.
|
||||
timeout time.Duration
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewRedisCache create new redis cache with default collection name.
|
||||
|
@ -15,7 +15,9 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -65,6 +67,7 @@ type Listen struct {
|
||||
HTTPSCertFile string
|
||||
HTTPSKeyFile string
|
||||
TrustCaFile string
|
||||
ClientAuth tls.ClientAuthType
|
||||
EnableAdmin bool
|
||||
AdminAddr string
|
||||
AdminPort int
|
||||
@ -106,6 +109,7 @@ type SessionConfig struct {
|
||||
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHTTPHeader string
|
||||
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
||||
SessionCookieSameSite http.SameSite
|
||||
}
|
||||
|
||||
// LogConfig holds Log related config
|
||||
@ -150,6 +154,9 @@ func init() {
|
||||
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
|
||||
}
|
||||
appConfigPath = filepath.Join(WorkPath, "conf", filename)
|
||||
if configPath := os.Getenv("BEEGO_CONFIG_PATH"); configPath != "" {
|
||||
appConfigPath = configPath
|
||||
}
|
||||
if !utils.FileExists(appConfigPath) {
|
||||
appConfigPath = filepath.Join(AppPath, "conf", filename)
|
||||
if !utils.FileExists(appConfigPath) {
|
||||
@ -231,6 +238,7 @@ func newBConfig() *Config {
|
||||
AdminPort: 8088,
|
||||
EnableFcgi: false,
|
||||
EnableStdIo: false,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
},
|
||||
WebConfig: WebConfig{
|
||||
AutoRender: true,
|
||||
@ -261,6 +269,7 @@ func newBConfig() *Config {
|
||||
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHTTPHeader: "Beegosessionid",
|
||||
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
||||
SessionCookieSameSite: http.SameSiteDefaultMode,
|
||||
},
|
||||
},
|
||||
Log: LogConfig{
|
||||
|
@ -145,7 +145,7 @@ httpport = 8080
|
||||
# enable db
|
||||
[dbinfo]
|
||||
# db type name
|
||||
# suport mysql,sqlserver
|
||||
# support mysql,sqlserver
|
||||
name = mysql
|
||||
`
|
||||
|
||||
@ -161,7 +161,7 @@ httpport=8080
|
||||
# enable db
|
||||
[dbinfo]
|
||||
# db type name
|
||||
# suport mysql,sqlserver
|
||||
# support mysql,sqlserver
|
||||
name=mysql
|
||||
`
|
||||
)
|
||||
|
@ -296,7 +296,7 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
|
||||
case map[string]interface{}:
|
||||
{
|
||||
tmpData = v.(map[string]interface{})
|
||||
if idx == len(keys) - 1 {
|
||||
if idx == len(keys)-1 {
|
||||
return tmpData, nil
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ func (ctx *Context) XSRFToken(key string, expire int64) string {
|
||||
token, ok := ctx.GetSecureCookie(key, "_xsrf")
|
||||
if !ok {
|
||||
token = string(utils.RandomCreateBytes(32))
|
||||
ctx.SetSecureCookie(key, "_xsrf", token, expire)
|
||||
ctx.SetSecureCookie(key, "_xsrf", token, expire, "", "", true, true)
|
||||
}
|
||||
ctx._xsrfToken = token
|
||||
}
|
||||
|
@ -17,7 +17,10 @@ package context
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestXsrfReset_01(t *testing.T) {
|
||||
@ -44,4 +47,8 @@ func TestXsrfReset_01(t *testing.T) {
|
||||
if token == c._xsrfToken {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
ck := c.ResponseWriter.Header().Get("Set-Cookie")
|
||||
assert.True(t, strings.Contains(ck, "Secure"))
|
||||
assert.True(t, strings.Contains(ck, "HttpOnly"))
|
||||
}
|
||||
|
@ -333,8 +333,14 @@ func (input *BeegoInput) Query(key string) string {
|
||||
return val
|
||||
}
|
||||
if input.Context.Request.Form == nil {
|
||||
input.Context.Request.ParseForm()
|
||||
input.dataLock.Lock()
|
||||
if input.Context.Request.Form == nil {
|
||||
input.Context.Request.ParseForm()
|
||||
}
|
||||
input.dataLock.Unlock()
|
||||
}
|
||||
input.dataLock.RLock()
|
||||
defer input.dataLock.RUnlock()
|
||||
return input.Context.Request.Form.Get(key)
|
||||
}
|
||||
|
||||
|
@ -205,3 +205,13 @@ func TestParams(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
func BenchmarkQuery(b *testing.B) {
|
||||
beegoInput := NewInput()
|
||||
beegoInput.Context = NewContext()
|
||||
beegoInput.Context.Request, _ = http.NewRequest("POST", "http://www.example.com/?q=foo", nil)
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
beegoInput.Query("q")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package param
|
||||
|
||||
import "testing"
|
||||
import "reflect"
|
||||
import "time"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testDefinition struct {
|
||||
strValue string
|
||||
|
@ -19,9 +19,10 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
)
|
||||
|
||||
func TestGetInt(t *testing.T) {
|
||||
@ -125,8 +126,8 @@ func TestGetUint64(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAdditionalViewPaths(t *testing.T) {
|
||||
dir1 := "_beeTmp"
|
||||
dir2 := "_beeTmp2"
|
||||
dir1 := tmpDir("TestAdditionalViewPaths1")
|
||||
dir2 := tmpDir("TestAdditionalViewPaths2")
|
||||
defer os.RemoveAll(dir1)
|
||||
defer os.RemoveAll(dir2)
|
||||
|
||||
|
16
error.go
16
error.go
@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
errorTypeHandler = iota
|
||||
errorTypeHandler = iota
|
||||
errorTypeController
|
||||
)
|
||||
|
||||
@ -359,6 +359,20 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
}
|
||||
|
||||
// show 413 Payload Too Large
|
||||
func payloadTooLarge(rw http.ResponseWriter, r *http.Request) {
|
||||
responseError(rw, r,
|
||||
413,
|
||||
`<br>The page you have requested is unavailable.
|
||||
<br>Perhaps you are here because:<br><br>
|
||||
<ul>
|
||||
<br>The request entity is larger than limits defined by server.
|
||||
<br>Please change the request entity and try again.
|
||||
</ul>
|
||||
`,
|
||||
)
|
||||
}
|
||||
|
||||
func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := M{
|
||||
|
3
go.mod
3
go.mod
@ -22,6 +22,7 @@ require (
|
||||
github.com/lib/pq v1.0.0
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/pelletier/go-toml v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.7.0
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec
|
||||
@ -29,7 +30,7 @@ require (
|
||||
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect
|
||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
||||
|
11
go.sum
11
go.sum
@ -53,7 +53,6 @@ github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d h1:xy93KVe+KrIIwWDEAfQBdIfsiHJkepbYsDr+VY3g9/o=
|
||||
github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@ -114,11 +113,10 @@ github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pingcap/tidb v2.0.11+incompatible/go.mod h1:I8C6jrPINP2rrVunTRd7C9fRRhQrtR43S1/CL5ix/yQ=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -164,9 +162,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -175,7 +171,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -189,8 +184,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
|
2
hooks.go
2
hooks.go
@ -34,6 +34,7 @@ func registerDefaultErrorHandler() error {
|
||||
"504": gatewayTimeout,
|
||||
"417": invalidxsrf,
|
||||
"422": missingxsrf,
|
||||
"413": payloadTooLarge,
|
||||
}
|
||||
for e, h := range m {
|
||||
if _, ok := ErrorMaps[e]; !ok {
|
||||
@ -60,6 +61,7 @@ func registerSession() error {
|
||||
conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
|
||||
conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
||||
conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
||||
conf.CookieSameSite = BConfig.WebConfig.Session.SessionCookieSameSite
|
||||
} else {
|
||||
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
|
||||
return err
|
||||
|
@ -144,6 +144,7 @@ type BeegoHTTPSettings struct {
|
||||
Gzip bool
|
||||
DumpBody bool
|
||||
Retries int // if set to -1 means will retry forever
|
||||
RetryDelay time.Duration
|
||||
}
|
||||
|
||||
// BeegoHTTPRequest provides more useful methods for requesting one url than http.Request.
|
||||
@ -202,6 +203,11 @@ func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest {
|
||||
b.setting.RetryDelay = delay
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpBody setting whether need to Dump the Body.
|
||||
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
||||
b.setting.DumpBody = isdump
|
||||
@ -512,11 +518,13 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
|
||||
// retries default value is 0, it will run once.
|
||||
// retries equal to -1, it will run forever until success
|
||||
// retries is setted, it will retries fixed times.
|
||||
// Sleeps for a 400ms in between calls to reduce spam
|
||||
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
||||
resp, err = client.Do(b.req)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(b.setting.RetryDelay)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -33,6 +34,34 @@ func TestResponse(t *testing.T) {
|
||||
t.Log(resp)
|
||||
}
|
||||
|
||||
func TestDoRequest(t *testing.T) {
|
||||
req := Get("https://goolnk.com/33BD2j")
|
||||
retryAmount := 1
|
||||
req.Retries(1)
|
||||
req.RetryDelay(1400 * time.Millisecond)
|
||||
retryDelay := 1400 * time.Millisecond
|
||||
|
||||
req.setting.CheckRedirect = func(redirectReq *http.Request, redirectVia []*http.Request) error {
|
||||
return errors.New("Redirect triggered")
|
||||
}
|
||||
|
||||
startTime := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
|
||||
_, err := req.Response()
|
||||
if err == nil {
|
||||
t.Fatal("Response should have yielded an error")
|
||||
}
|
||||
|
||||
endTime := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
elapsedTime := endTime - startTime
|
||||
delayedTime := int64(retryAmount) * retryDelay.Milliseconds()
|
||||
|
||||
if elapsedTime < delayedTime {
|
||||
t.Errorf("Not enough retries. Took %dms. Delay was meant to take %dms", elapsedTime, delayedTime)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
req := Get("http://httpbin.org/get")
|
||||
b, err := req.Bytes()
|
||||
|
@ -16,9 +16,9 @@ package logs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -63,7 +63,10 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
defer c.innerWriter.Close()
|
||||
}
|
||||
|
||||
c.lg.writeln(when, msg)
|
||||
_, err := c.lg.writeln(when, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -101,7 +104,6 @@ func (c *connWriter) connect() error {
|
||||
|
||||
func (c *connWriter) needToConnectOnMsg() bool {
|
||||
if c.Reconnect {
|
||||
c.Reconnect = false
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,65 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ConnTCPListener takes a TCP listener and accepts n TCP connections
|
||||
// Returns connections using connChan
|
||||
func connTCPListener(t *testing.T, n int, ln net.Listener, connChan chan<- net.Conn) {
|
||||
|
||||
// Listen and accept n incoming connections
|
||||
for i := 0; i < n; i++ {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Log("Error accepting connection: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Send accepted connection to channel
|
||||
connChan <- conn
|
||||
}
|
||||
ln.Close()
|
||||
close(connChan)
|
||||
}
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
log := NewLogger(1000)
|
||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||
log.Informational("informational")
|
||||
}
|
||||
|
||||
func TestReconnect(t *testing.T) {
|
||||
// Setup connection listener
|
||||
newConns := make(chan net.Conn)
|
||||
connNum := 2
|
||||
ln, err := net.Listen("tcp", ":6002")
|
||||
if err != nil {
|
||||
t.Log("Error listening:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
go connTCPListener(t, connNum, ln, newConns)
|
||||
|
||||
// Setup logger
|
||||
log := NewLogger(1000)
|
||||
log.SetPrefix("test")
|
||||
log.SetLogger(AdapterConn, `{"net":"tcp","reconnect":true,"level":6,"addr":":6002"}`)
|
||||
log.Informational("informational 1")
|
||||
|
||||
// Refuse first connection
|
||||
first := <-newConns
|
||||
first.Close()
|
||||
|
||||
// Send another log after conn closed
|
||||
log.Informational("informational 2")
|
||||
|
||||
// Check if there was a second connection attempt
|
||||
select {
|
||||
case second := <-newConns:
|
||||
second.Close()
|
||||
default:
|
||||
t.Error("Did not reconnect")
|
||||
}
|
||||
}
|
||||
|
30
logs/file.go
30
logs/file.go
@ -373,21 +373,21 @@ func (w *fileLogWriter) deleteOldLog() {
|
||||
if info == nil {
|
||||
return
|
||||
}
|
||||
if w.Hourly {
|
||||
if !info.IsDir() && info.ModTime().Add(1 * time.Hour * time.Duration(w.MaxHours)).Before(time.Now()) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
} else if w.Daily {
|
||||
if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
if w.Hourly {
|
||||
if !info.IsDir() && info.ModTime().Add(1*time.Hour*time.Duration(w.MaxHours)).Before(time.Now()) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
} else if w.Daily {
|
||||
if !info.IsDir() && info.ModTime().Add(24*time.Hour*time.Duration(w.MaxDays)).Before(time.Now()) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ func TestFileDailyRotate_06(t *testing.T) { //test file mode
|
||||
|
||||
func TestFileHourlyRotate_01(t *testing.T) {
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test3.log","hourly":true,"maxlines":4}`)
|
||||
log.SetLogger("file", `{"filename":"test3.log","hourly":true,"maxlines":4}`)
|
||||
log.Debug("debug")
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
@ -237,7 +237,7 @@ func TestFileHourlyRotate_05(t *testing.T) {
|
||||
|
||||
func TestFileHourlyRotate_06(t *testing.T) { //test file mode
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test3.log", "hourly":true, "maxlines":4}`)
|
||||
log.SetLogger("file", `{"filename":"test3.log", "hourly":true, "maxlines":4}`)
|
||||
log.Debug("debug")
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
@ -269,19 +269,19 @@ func testFileRotate(t *testing.T, fn1, fn2 string, daily, hourly bool) {
|
||||
RotatePerm: "0440",
|
||||
}
|
||||
|
||||
if daily {
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
||||
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
||||
fw.dailyOpenDate = fw.dailyOpenTime.Day()
|
||||
}
|
||||
if daily {
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
||||
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
||||
fw.dailyOpenDate = fw.dailyOpenTime.Day()
|
||||
}
|
||||
|
||||
if hourly {
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
|
||||
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
|
||||
fw.hourlyOpenDate = fw.hourlyOpenTime.Day()
|
||||
}
|
||||
if hourly {
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
|
||||
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
|
||||
fw.hourlyOpenDate = fw.hourlyOpenTime.Day()
|
||||
}
|
||||
|
||||
fw.WriteMsg(time.Now(), "this is a msg for test", LevelDebug)
|
||||
fw.WriteMsg(time.Now(), "this is a msg for test", LevelDebug)
|
||||
|
||||
for _, file := range []string{fn1, fn2} {
|
||||
_, err := os.Stat(file)
|
||||
@ -328,8 +328,8 @@ func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
|
||||
|
||||
func testFileHourlyRotate(t *testing.T, fn1, fn2 string) {
|
||||
fw := &fileLogWriter{
|
||||
Hourly: true,
|
||||
MaxHours: 168,
|
||||
Hourly: true,
|
||||
MaxHours: 168,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
|
@ -30,11 +30,12 @@ func newLogWriter(wr io.Writer) *logWriter {
|
||||
return &logWriter{writer: wr}
|
||||
}
|
||||
|
||||
func (lg *logWriter) writeln(when time.Time, msg string) {
|
||||
func (lg *logWriter) writeln(when time.Time, msg string) (int, error) {
|
||||
lg.Lock()
|
||||
h, _, _ := formatTimeHeader(when)
|
||||
lg.writer.Write(append(append(h, msg...), '\n'))
|
||||
n, err := lg.writer.Write(append(append(h, msg...), '\n'))
|
||||
lg.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -57,15 +57,15 @@ func registerBuildInfo() {
|
||||
Subsystem: "build_info",
|
||||
Help: "The building information",
|
||||
ConstLabels: map[string]string{
|
||||
"appname": beego.BConfig.AppName,
|
||||
"appname": beego.BConfig.AppName,
|
||||
"build_version": beego.BuildVersion,
|
||||
"build_revision": beego.BuildGitRevision,
|
||||
"build_status": beego.BuildStatus,
|
||||
"build_tag": beego.BuildTag,
|
||||
"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
|
||||
"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
|
||||
"go_version": beego.GoVersion,
|
||||
"git_branch": beego.GitBranch,
|
||||
"start_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
"start_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
}, []string{})
|
||||
|
||||
|
@ -197,9 +197,9 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
if strings.Contains(column, "%COL%") {
|
||||
column = strings.Replace(column, "%COL%", fi.column, -1)
|
||||
}
|
||||
|
||||
if fi.description != "" && al.Driver!=DRSqlite {
|
||||
column += " " + fmt.Sprintf("COMMENT '%s'",fi.description)
|
||||
|
||||
if fi.description != "" && al.Driver != DRSqlite {
|
||||
column += " " + fmt.Sprintf("COMMENT '%s'", fi.description)
|
||||
}
|
||||
|
||||
columns = append(columns, column)
|
||||
|
18
orm/db.go
18
orm/db.go
@ -36,10 +36,11 @@ var (
|
||||
|
||||
var (
|
||||
operators = map[string]bool{
|
||||
"exact": true,
|
||||
"iexact": true,
|
||||
"contains": true,
|
||||
"icontains": true,
|
||||
"exact": true,
|
||||
"iexact": true,
|
||||
"strictexact": true,
|
||||
"contains": true,
|
||||
"icontains": true,
|
||||
// "regex": true,
|
||||
// "iregex": true,
|
||||
"gt": true,
|
||||
@ -702,6 +703,13 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
|
||||
return 0, err
|
||||
}
|
||||
if num > 0 {
|
||||
if mi.fields.pk.auto {
|
||||
if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
|
||||
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0)
|
||||
} else {
|
||||
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0)
|
||||
}
|
||||
}
|
||||
err := d.deleteRels(q, mi, args, tz)
|
||||
if err != nil {
|
||||
return num, err
|
||||
@ -1195,7 +1203,7 @@ func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator stri
|
||||
}
|
||||
sql = d.ins.OperatorSQL(operator)
|
||||
switch operator {
|
||||
case "exact":
|
||||
case "exact", "strictexact":
|
||||
if arg == nil {
|
||||
params[0] = "IS NULL"
|
||||
}
|
||||
|
@ -18,10 +18,11 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// DriverType database driver constant int.
|
||||
@ -424,8 +425,7 @@ func GetDB(aliasNames ...string) (*sql.DB, error) {
|
||||
}
|
||||
|
||||
type stmtDecorator struct {
|
||||
wg sync.WaitGroup
|
||||
lastUse int64
|
||||
wg sync.WaitGroup
|
||||
stmt *sql.Stmt
|
||||
}
|
||||
|
||||
@ -433,9 +433,12 @@ func (s *stmtDecorator) getStmt() *sql.Stmt {
|
||||
return s.stmt
|
||||
}
|
||||
|
||||
// acquire will add one
|
||||
// since this method will be used inside read lock scope,
|
||||
// so we can not do more things here
|
||||
// we should think about refactor this
|
||||
func (s *stmtDecorator) acquire() {
|
||||
s.wg.Add(1)
|
||||
s.lastUse = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (s *stmtDecorator) release() {
|
||||
@ -453,12 +456,13 @@ func (s *stmtDecorator) destroy() {
|
||||
func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator {
|
||||
return &stmtDecorator{
|
||||
stmt: sqlStmt,
|
||||
lastUse: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func newStmtDecoratorLruWithEvict() *lru.Cache {
|
||||
cache, _ := lru.NewWithEvict(1000, func(key interface{}, value interface{}) {
|
||||
// temporarily solution
|
||||
// we fixed this problem in v2.x
|
||||
cache, _ := lru.NewWithEvict(50, func(key interface{}, value interface{}) {
|
||||
value.(*stmtDecorator).destroy()
|
||||
})
|
||||
return cache
|
||||
|
@ -22,10 +22,11 @@ import (
|
||||
|
||||
// mysql operators.
|
||||
var mysqlOperators = map[string]string{
|
||||
"exact": "= ?",
|
||||
"iexact": "LIKE ?",
|
||||
"contains": "LIKE BINARY ?",
|
||||
"icontains": "LIKE ?",
|
||||
"exact": "= ?",
|
||||
"iexact": "LIKE ?",
|
||||
"strictexact": "= BINARY ?",
|
||||
"contains": "LIKE BINARY ?",
|
||||
"icontains": "LIKE ?",
|
||||
// "regex": "REGEXP BINARY ?",
|
||||
// "iregex": "REGEXP ?",
|
||||
"gt": "> ?",
|
||||
|
@ -53,18 +53,24 @@ func (e *SliceStringField) FieldType() int {
|
||||
}
|
||||
|
||||
func (e *SliceStringField) SetRaw(value interface{}) error {
|
||||
switch d := value.(type) {
|
||||
case []string:
|
||||
e.Set(d)
|
||||
case string:
|
||||
if len(d) > 0 {
|
||||
parts := strings.Split(d, ",")
|
||||
f := func(str string) {
|
||||
if len(str) > 0 {
|
||||
parts := strings.Split(str, ",")
|
||||
v := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
v = append(v, strings.TrimSpace(p))
|
||||
}
|
||||
e.Set(v)
|
||||
}
|
||||
}
|
||||
|
||||
switch d := value.(type) {
|
||||
case []string:
|
||||
e.Set(d)
|
||||
case string:
|
||||
f(d)
|
||||
case []byte:
|
||||
f(string(d))
|
||||
default:
|
||||
return fmt.Errorf("<SliceStringField.SetRaw> unknown value `%v`", value)
|
||||
}
|
||||
@ -96,6 +102,8 @@ func (e *JSONFieldTest) SetRaw(value interface{}) error {
|
||||
switch d := value.(type) {
|
||||
case string:
|
||||
return json.Unmarshal([]byte(d), e)
|
||||
case []byte:
|
||||
return json.Unmarshal(d, e)
|
||||
default:
|
||||
return fmt.Errorf("<JSONField.SetRaw> unknown value `%v`", value)
|
||||
}
|
||||
|
@ -242,7 +242,13 @@ func (o *orm) Update(md interface{}, cols ...string) (int64, error) {
|
||||
func (o *orm) Delete(md interface{}, cols ...string) (int64, error) {
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols)
|
||||
return num, err
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
if num > 0 {
|
||||
o.setPk(mi, ind, 0)
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// create a models to models queryer
|
||||
|
@ -61,7 +61,7 @@ func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error
|
||||
con += " - " + err.Error()
|
||||
}
|
||||
logMap["sql"] = fmt.Sprintf("%s-`%s`", query, strings.Join(cons, "`, `"))
|
||||
if LogFunc != nil{
|
||||
if LogFunc != nil {
|
||||
LogFunc(logMap)
|
||||
}
|
||||
DebugLog.Println(con)
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// raw sql string prepared statement
|
||||
@ -368,7 +370,15 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
|
||||
field.Set(mf)
|
||||
field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex)
|
||||
}
|
||||
o.setFieldValue(field, value)
|
||||
if fi.isFielder {
|
||||
fd := field.Addr().Interface().(Fielder)
|
||||
err := fd.SetRaw(value)
|
||||
if err != nil {
|
||||
return errors.Errorf("set raw error:%s", err)
|
||||
}
|
||||
} else {
|
||||
o.setFieldValue(field, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -509,7 +519,15 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
||||
field.Set(mf)
|
||||
field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex)
|
||||
}
|
||||
o.setFieldValue(field, value)
|
||||
if fi.isFielder {
|
||||
fd := field.Addr().Interface().(Fielder)
|
||||
err := fd.SetRaw(value)
|
||||
if err != nil {
|
||||
return 0, errors.Errorf("set raw error:%s", err)
|
||||
}
|
||||
} else {
|
||||
o.setFieldValue(field, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -769,6 +769,20 @@ func TestCustomField(t *testing.T) {
|
||||
|
||||
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
||||
throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
|
||||
|
||||
var users []User
|
||||
Q := dDbBaser.TableQuote()
|
||||
n, err := dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRows(&users)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(n, 1))
|
||||
throwFailNow(t, AssertIs(users[0].Extra.Name, "beego"))
|
||||
throwFailNow(t, AssertIs(users[0].Extra.Data, "orm"))
|
||||
|
||||
user = User{}
|
||||
err = dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRow(&user)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
||||
throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
|
||||
}
|
||||
|
||||
func TestExpr(t *testing.T) {
|
||||
@ -808,6 +822,17 @@ func TestOperators(t *testing.T) {
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
|
||||
if IsMysql {
|
||||
// Now only mysql support `strictexact`
|
||||
num, err = qs.Filter("user_name__strictexact", "Slene").Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 0))
|
||||
|
||||
num, err = qs.Filter("user_name__strictexact", "slene").Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
}
|
||||
|
||||
num, err = qs.Filter("user_name__contains", "e").Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 2))
|
||||
|
@ -40,10 +40,11 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/casbin/casbin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NewAuthorizer returns the authorizer.
|
||||
|
@ -15,13 +15,14 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/plugins/auth"
|
||||
"github.com/casbin/casbin"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, path string, method string, code int) {
|
||||
|
@ -742,6 +742,12 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
if BConfig.CopyRequestBody && !context.Input.IsUpload() {
|
||||
// connection will close if the incoming data are larger (RFC 7231, 6.5.11)
|
||||
if r.ContentLength > BConfig.MaxMemory {
|
||||
logs.Error(errors.New("payload too large"))
|
||||
exception("413", context)
|
||||
goto Admin
|
||||
}
|
||||
context.Input.CopyBody(BConfig.MaxMemory)
|
||||
}
|
||||
context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
|
||||
|
@ -15,6 +15,7 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@ -71,7 +72,6 @@ func (tc *TestController) GetEmptyBody() {
|
||||
tc.Ctx.Output.Body(res)
|
||||
}
|
||||
|
||||
|
||||
type JSONController struct {
|
||||
Controller
|
||||
}
|
||||
@ -656,17 +656,14 @@ func beegoBeforeRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeRouter1")
|
||||
}
|
||||
|
||||
|
||||
func beegoBeforeExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeExec1")
|
||||
}
|
||||
|
||||
|
||||
func beegoAfterExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|AfterExec1")
|
||||
}
|
||||
|
||||
|
||||
func beegoFinishRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|FinishRouter1")
|
||||
}
|
||||
@ -709,3 +706,27 @@ func TestYAMLPrepare(t *testing.T) {
|
||||
t.Errorf(w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterEntityTooLargeCopyBody(t *testing.T) {
|
||||
_MaxMemory := BConfig.MaxMemory
|
||||
_CopyRequestBody := BConfig.CopyRequestBody
|
||||
BConfig.CopyRequestBody = true
|
||||
BConfig.MaxMemory = 20
|
||||
|
||||
b := bytes.NewBuffer([]byte("barbarbarbarbarbarbarbarbarbar"))
|
||||
r, _ := http.NewRequest("POST", "/user/123", b)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := NewControllerRegister()
|
||||
handler.Post("/user/:id", func(ctx *context.Context) {
|
||||
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
|
||||
})
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
BConfig.CopyRequestBody = _CopyRequestBody
|
||||
BConfig.MaxMemory = _MaxMemory
|
||||
|
||||
if w.Code != http.StatusRequestEntityTooLarge {
|
||||
t.Errorf("TestRouterRequestEntityTooLarge can't run")
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,16 @@
|
||||
//
|
||||
// more docs: http://beego.me/docs/module/session.md
|
||||
package redis_cluster
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/session"
|
||||
rediss "github.com/go-redis/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
var redispder = &Provider{}
|
||||
@ -101,7 +103,7 @@ func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
|
||||
return
|
||||
}
|
||||
c := rs.p
|
||||
c.Set(rs.sid, string(b), time.Duration(rs.maxlifetime) * time.Second)
|
||||
c.Set(rs.sid, string(b), time.Duration(rs.maxlifetime)*time.Second)
|
||||
}
|
||||
|
||||
// Provider redis_cluster session provider
|
||||
@ -146,10 +148,10 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
} else {
|
||||
rp.dbNum = 0
|
||||
}
|
||||
|
||||
|
||||
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
|
||||
Addrs: strings.Split(rp.savePath, ";"),
|
||||
Password: rp.password,
|
||||
Password: rp.password,
|
||||
PoolSize: rp.poolsize,
|
||||
})
|
||||
return rp.poollist.Ping().Err()
|
||||
@ -186,15 +188,15 @@ func (rp *Provider) SessionExist(sid string) bool {
|
||||
// SessionRegenerate generate new sid for redis_cluster session
|
||||
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
|
||||
c := rp.poollist
|
||||
|
||||
|
||||
if existed, err := c.Exists(oldsid).Result(); err != nil || existed == 0 {
|
||||
// oldsid doesn't exists, set the new sid directly
|
||||
// ignore error here, since if it return error
|
||||
// the existed value will be 0
|
||||
c.Set(sid, "", time.Duration(rp.maxlifetime) * time.Second)
|
||||
c.Set(sid, "", time.Duration(rp.maxlifetime)*time.Second)
|
||||
} else {
|
||||
c.Rename(oldsid, sid)
|
||||
c.Expire(sid, time.Duration(rp.maxlifetime) * time.Second)
|
||||
c.Expire(sid, time.Duration(rp.maxlifetime)*time.Second)
|
||||
}
|
||||
return rp.SessionRead(sid)
|
||||
}
|
||||
|
@ -33,13 +33,14 @@
|
||||
package redis_sentinel
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/session"
|
||||
"github.com/go-redis/redis"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/session"
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
var redispder = &Provider{}
|
||||
|
@ -15,11 +15,11 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"errors"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -180,6 +180,11 @@ func (fp *FileProvider) SessionExist(sid string) bool {
|
||||
filepder.lock.Lock()
|
||||
defer filepder.lock.Unlock()
|
||||
|
||||
if len(sid) < 2 {
|
||||
SLogger.Println("min length of session id is 2", sid)
|
||||
return false
|
||||
}
|
||||
|
||||
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||
return err == nil
|
||||
}
|
||||
|
386
session/sess_file_test.go
Normal file
386
session/sess_file_test.go
Normal file
@ -0,0 +1,386 @@
|
||||
// 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 session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const sid = "Session_id"
|
||||
const sidNew = "Session_id_new"
|
||||
const sessionPath = "./_session_runtime"
|
||||
|
||||
var (
|
||||
mutex sync.Mutex
|
||||
)
|
||||
|
||||
func TestFileProvider_SessionInit(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
if fp.maxlifetime != 180 {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
if fp.savePath != sessionPath {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionExist(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
if fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
_, err := fp.SessionRead(sid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionExist2(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
if fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
if fp.SessionExist("") {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
if fp.SessionExist("1") {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionRead(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
s, err := fp.SessionRead(sid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_ = s.Set("sessionValue", 18975)
|
||||
v := s.Get("sessionValue")
|
||||
|
||||
if v.(int) != 18975 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionRead1(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
_, err := fp.SessionRead("")
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = fp.SessionRead("1")
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionAll(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
sessionCount := 546
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
_, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if fp.SessionAll() != sessionCount {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionRegenerate(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
_, err := fp.SessionRead(sid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
_, err = fp.SessionRegenerate(sid, sidNew)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
if !fp.SessionExist(sidNew) {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionDestroy(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
_, err := fp.SessionRead(sid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
err = fp.SessionDestroy(sid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if fp.SessionExist(sid) {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProvider_SessionGC(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(1, sessionPath)
|
||||
|
||||
sessionCount := 412
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
_, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
fp.SessionGC()
|
||||
if fp.SessionAll() != 0 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_Set(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
sessionCount := 100
|
||||
s, _ := fp.SessionRead(sid)
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
err := s.Set(i, i)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_Get(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
sessionCount := 100
|
||||
s, _ := fp.SessionRead(sid)
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
_ = s.Set(i, i)
|
||||
|
||||
v := s.Get(i)
|
||||
if v.(int) != i {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_Delete(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
s, _ := fp.SessionRead(sid)
|
||||
s.Set("1", 1)
|
||||
|
||||
if s.Get("1") == nil {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
s.Delete("1")
|
||||
|
||||
if s.Get("1") != nil {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_Flush(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
sessionCount := 100
|
||||
s, _ := fp.SessionRead(sid)
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
_ = s.Set(i, i)
|
||||
}
|
||||
|
||||
_ = s.Flush()
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
if s.Get(i) != nil {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_SessionID(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
|
||||
sessionCount := 85
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
s, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if s.SessionID() != fmt.Sprintf("%s_%d", sid, i) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSessionStore_SessionRelease(t *testing.T) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
os.RemoveAll(sessionPath)
|
||||
defer os.RemoveAll(sessionPath)
|
||||
fp := &FileProvider{}
|
||||
|
||||
_ = fp.SessionInit(180, sessionPath)
|
||||
filepder.savePath = sessionPath
|
||||
sessionCount := 85
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
s, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
s.Set(i, i)
|
||||
s.SessionRelease(nil)
|
||||
}
|
||||
|
||||
for i := 1; i <= sessionCount; i++ {
|
||||
s, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if s.Get(i).(int) != i {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
}
|
@ -92,20 +92,21 @@ func GetProvider(name string) (Provider, error) {
|
||||
|
||||
// ManagerConfig define the session config
|
||||
type ManagerConfig struct {
|
||||
CookieName string `json:"cookieName"`
|
||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||
Gclifetime int64 `json:"gclifetime"`
|
||||
Maxlifetime int64 `json:"maxLifetime"`
|
||||
DisableHTTPOnly bool `json:"disableHTTPOnly"`
|
||||
Secure bool `json:"secure"`
|
||||
CookieLifeTime int `json:"cookieLifeTime"`
|
||||
ProviderConfig string `json:"providerConfig"`
|
||||
Domain string `json:"domain"`
|
||||
SessionIDLength int64 `json:"sessionIDLength"`
|
||||
EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
|
||||
SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
|
||||
EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
|
||||
SessionIDPrefix string `json:"sessionIDPrefix"`
|
||||
CookieName string `json:"cookieName"`
|
||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||
Gclifetime int64 `json:"gclifetime"`
|
||||
Maxlifetime int64 `json:"maxLifetime"`
|
||||
DisableHTTPOnly bool `json:"disableHTTPOnly"`
|
||||
Secure bool `json:"secure"`
|
||||
CookieLifeTime int `json:"cookieLifeTime"`
|
||||
ProviderConfig string `json:"providerConfig"`
|
||||
Domain string `json:"domain"`
|
||||
SessionIDLength int64 `json:"sessionIDLength"`
|
||||
EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
|
||||
SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
|
||||
EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
|
||||
SessionIDPrefix string `json:"sessionIDPrefix"`
|
||||
CookieSameSite http.SameSite `json:"cookieSameSite"`
|
||||
}
|
||||
|
||||
// Manager contains Provider and its configuration.
|
||||
@ -232,6 +233,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Secure: manager.isSecure(r),
|
||||
Domain: manager.config.Domain,
|
||||
SameSite: manager.config.CookieSameSite,
|
||||
}
|
||||
if manager.config.CookieLifeTime > 0 {
|
||||
cookie.MaxAge = manager.config.CookieLifeTime
|
||||
@ -271,7 +273,9 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Expires: expiration,
|
||||
MaxAge: -1,
|
||||
Domain: manager.config.Domain}
|
||||
Domain: manager.config.Domain,
|
||||
SameSite: manager.config.CookieSameSite,
|
||||
}
|
||||
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
@ -306,6 +310,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Secure: manager.isSecure(r),
|
||||
Domain: manager.config.Domain,
|
||||
SameSite: manager.config.CookieSameSite,
|
||||
}
|
||||
} else {
|
||||
oldsid, _ := url.QueryUnescape(cookie.Value)
|
||||
|
@ -202,7 +202,7 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
|
||||
if !strings.Contains(requestPath, prefix) {
|
||||
continue
|
||||
}
|
||||
if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
|
||||
if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
|
||||
continue
|
||||
}
|
||||
filePath := path.Join(staticDir, requestPath[len(prefix):])
|
||||
|
@ -16,12 +16,13 @@ package beego
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/astaxie/beego/testdata"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/testdata"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
)
|
||||
|
||||
var header = `{{define "header"}}
|
||||
@ -45,8 +46,12 @@ var block = `{{define "block"}}
|
||||
<h1>Hello, blocks!</h1>
|
||||
{{end}}`
|
||||
|
||||
func tmpDir(s string) string {
|
||||
return filepath.Join(os.TempDir(), s)
|
||||
}
|
||||
|
||||
func TestTemplate(t *testing.T) {
|
||||
dir := "_beeTmp"
|
||||
dir := tmpDir("TestTemplate")
|
||||
files := []string{
|
||||
"header.tpl",
|
||||
"index.tpl",
|
||||
@ -107,7 +112,7 @@ var user = `<!DOCTYPE html>
|
||||
`
|
||||
|
||||
func TestRelativeTemplate(t *testing.T) {
|
||||
dir := "_beeTmp"
|
||||
dir := tmpDir("TestRelativeTemplate")
|
||||
|
||||
//Just add dir to known viewPaths
|
||||
if err := AddViewPath(dir); err != nil {
|
||||
@ -218,7 +223,7 @@ var output = `<!DOCTYPE html>
|
||||
`
|
||||
|
||||
func TestTemplateLayout(t *testing.T) {
|
||||
dir := "_beeTmp"
|
||||
dir := tmpDir("TestTemplateLayout")
|
||||
files := []string{
|
||||
"add.tpl",
|
||||
"layout_blog.tpl",
|
||||
|
@ -362,7 +362,7 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e
|
||||
value = value[:25]
|
||||
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
||||
} else if strings.HasSuffix(strings.ToUpper(value), "Z") {
|
||||
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
||||
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
||||
} else if len(value) >= 19 {
|
||||
if strings.Contains(value, "T") {
|
||||
value = value[:19]
|
||||
|
14
test.sh
Normal file
14
test.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker-compose -f test_docker_compose.yaml up -d
|
||||
|
||||
export ORM_DRIVER=mysql
|
||||
export TZ=UTC
|
||||
export ORM_SOURCE="beego:test@tcp(localhost:13306)/orm_test?charset=utf8"
|
||||
|
||||
go test ./...
|
||||
|
||||
# clear all container
|
||||
docker-compose -f test_docker_compose.yaml down
|
||||
|
||||
|
39
test_docker_compose.yaml
Normal file
39
test_docker_compose.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
redis:
|
||||
container_name: "beego-redis"
|
||||
image: redis
|
||||
environment:
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
mysql:
|
||||
container_name: "beego-mysql"
|
||||
image: mysql:5.7.30
|
||||
ports:
|
||||
- "13306:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=1q2w3e
|
||||
- MYSQL_DATABASE=orm_test
|
||||
- MYSQL_USER=beego
|
||||
- MYSQL_PASSWORD=test
|
||||
|
||||
postgresql:
|
||||
container_name: "beego-postgresql"
|
||||
image: bitnami/postgresql:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
ssdb:
|
||||
container_name: "beego-ssdb"
|
||||
image: wendal/ssdb
|
||||
ports:
|
||||
- "8888:8888"
|
||||
memcache:
|
||||
container_name: "beego-memcache"
|
||||
image: memcached
|
||||
ports:
|
||||
- "11211:11211"
|
||||
|
3
testdata/bindata.go
vendored
3
testdata/bindata.go
vendored
@ -11,13 +11,14 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
|
@ -213,7 +213,7 @@ func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
tParams, err := trim(name, key+"."+ name + "." + label, params)
|
||||
tParams, err := trim(name, key+"."+name+"."+label, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -16,13 +16,14 @@ package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty
|
||||
|
Reference in New Issue
Block a user