mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 09:21:02 +00:00
Compare commits
27 Commits
master
...
develop-1.
Author | SHA1 | Date | |
---|---|---|---|
3b6d634f55 | |||
d0847e866e | |||
19e6ba8e7c | |||
5fddd26069 | |||
89d4c9a3df | |||
db6af39bcd | |||
9418b243d0 | |||
3d93903f9e | |||
2781f88173 | |||
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
|
## Quick Start
|
||||||
|
|
||||||
|
#### Create `hello` directory, cd `hello` directory
|
||||||
|
|
||||||
|
mkdir hello
|
||||||
|
cd hello
|
||||||
|
|
||||||
|
#### Init module
|
||||||
|
|
||||||
|
go mod init
|
||||||
|
|
||||||
#### Download and install
|
#### Download and install
|
||||||
|
|
||||||
go get github.com/astaxie/beego
|
go get github.com/astaxie/beego
|
||||||
|
62
admin.go
62
admin.go
@ -21,6 +21,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ func init() {
|
|||||||
// AdminIndex is the default http.Handler for admin module.
|
// AdminIndex is the default http.Handler for admin module.
|
||||||
// it matches url pattern "/".
|
// it matches url pattern "/".
|
||||||
func adminIndex(rw http.ResponseWriter, _ *http.Request) {
|
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.
|
// 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.
|
// 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["Content"] = content
|
||||||
data["Title"] = "Routers"
|
data["Title"] = "Routers"
|
||||||
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
case "filter":
|
case "filter":
|
||||||
var (
|
var (
|
||||||
content = M{
|
content = M{
|
||||||
@ -171,7 +172,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
data["Content"] = content
|
data["Content"] = content
|
||||||
data["Title"] = "Filters"
|
data["Title"] = "Filters"
|
||||||
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
default:
|
default:
|
||||||
rw.Write([]byte("command not support"))
|
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)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
writeJSON(rw, dataJSON)
|
||||||
rw.Header().Set("Content-Type", "application/json")
|
|
||||||
rw.Write(dataJSON)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,12 +289,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
|
|||||||
if command == "gc summary" {
|
if command == "gc summary" {
|
||||||
defaultTpl = gcAjaxTpl
|
defaultTpl = gcAjaxTpl
|
||||||
}
|
}
|
||||||
execTpl(rw, data, profillingTpl, defaultTpl)
|
writeTemplate(rw, data, profillingTpl, defaultTpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Healthcheck is a http.Handler calling health checking and showing the result.
|
// Healthcheck is a http.Handler calling health checking and showing the result.
|
||||||
// it's in "/healthcheck" pattern in admin module.
|
// it's in "/healthcheck" pattern in admin module.
|
||||||
func healthcheck(rw http.ResponseWriter, _ *http.Request) {
|
func healthcheck(rw http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
result []string
|
result []string
|
||||||
data = make(map[interface{}]interface{})
|
data = make(map[interface{}]interface{})
|
||||||
@ -322,10 +321,49 @@ func healthcheck(rw http.ResponseWriter, _ *http.Request) {
|
|||||||
*resultList = append(*resultList, result)
|
*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
|
content["Data"] = resultList
|
||||||
data["Content"] = content
|
data["Content"] = content
|
||||||
data["Title"] = "Health Check"
|
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).
|
// 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
|
content["Data"] = resultList
|
||||||
data["Content"] = content
|
data["Content"] = content
|
||||||
data["Title"] = "Tasks"
|
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))
|
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||||
for _, tpl := range tpls {
|
for _, tpl := range tpls {
|
||||||
tmpl = template.Must(tmpl.Parse(tpl))
|
tmpl = template.Must(tmpl.Parse(tpl))
|
||||||
|
162
admin_test.go
162
admin_test.go
@ -1,10 +1,32 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"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) {
|
func TestList_01(t *testing.T) {
|
||||||
m := make(M)
|
m := make(M)
|
||||||
list("BConfig", BConfig, m)
|
list("BConfig", BConfig, m)
|
||||||
@ -75,3 +97,143 @@ func oldMap() M {
|
|||||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||||
return m
|
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)
|
pool.AppendCertsFromPEM(data)
|
||||||
app.Server.TLSConfig = &tls.Config{
|
app.Server.TLSConfig = &tls.Config{
|
||||||
ClientCAs: pool,
|
ClientCAs: pool,
|
||||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
ClientAuth: BConfig.Listen.ClientAuth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
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 (
|
const (
|
||||||
// VERSION represent beego web framework version.
|
// VERSION represent beego web framework version.
|
||||||
VERSION = "1.12.2"
|
VERSION = "1.12.3"
|
||||||
|
|
||||||
// DEV is for develop
|
// DEV is for develop
|
||||||
DEV = "dev"
|
DEV = "dev"
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BuildVersion string
|
BuildVersion string
|
||||||
BuildGitRevision string
|
BuildGitRevision string
|
||||||
BuildStatus string
|
BuildStatus string
|
||||||
BuildTag string
|
BuildTag string
|
||||||
BuildTime string
|
BuildTime string
|
||||||
|
|
||||||
GoVersion 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/gomodule/redigo/redis"
|
||||||
|
|
||||||
"github.com/astaxie/beego/cache"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -57,7 +58,7 @@ type Cache struct {
|
|||||||
maxIdle int
|
maxIdle int
|
||||||
|
|
||||||
//the timeout to a value less than the redis server's timeout.
|
//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.
|
// NewRedisCache create new redis cache with default collection name.
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -65,6 +67,7 @@ type Listen struct {
|
|||||||
HTTPSCertFile string
|
HTTPSCertFile string
|
||||||
HTTPSKeyFile string
|
HTTPSKeyFile string
|
||||||
TrustCaFile string
|
TrustCaFile string
|
||||||
|
ClientAuth tls.ClientAuthType
|
||||||
EnableAdmin bool
|
EnableAdmin bool
|
||||||
AdminAddr string
|
AdminAddr string
|
||||||
AdminPort int
|
AdminPort int
|
||||||
@ -106,6 +109,7 @@ type SessionConfig struct {
|
|||||||
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
||||||
SessionNameInHTTPHeader string
|
SessionNameInHTTPHeader string
|
||||||
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
||||||
|
SessionCookieSameSite http.SameSite
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogConfig holds Log related config
|
// LogConfig holds Log related config
|
||||||
@ -150,6 +154,9 @@ func init() {
|
|||||||
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
|
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
|
||||||
}
|
}
|
||||||
appConfigPath = filepath.Join(WorkPath, "conf", filename)
|
appConfigPath = filepath.Join(WorkPath, "conf", filename)
|
||||||
|
if configPath := os.Getenv("BEEGO_CONFIG_PATH"); configPath != "" {
|
||||||
|
appConfigPath = configPath
|
||||||
|
}
|
||||||
if !utils.FileExists(appConfigPath) {
|
if !utils.FileExists(appConfigPath) {
|
||||||
appConfigPath = filepath.Join(AppPath, "conf", filename)
|
appConfigPath = filepath.Join(AppPath, "conf", filename)
|
||||||
if !utils.FileExists(appConfigPath) {
|
if !utils.FileExists(appConfigPath) {
|
||||||
@ -231,6 +238,7 @@ func newBConfig() *Config {
|
|||||||
AdminPort: 8088,
|
AdminPort: 8088,
|
||||||
EnableFcgi: false,
|
EnableFcgi: false,
|
||||||
EnableStdIo: false,
|
EnableStdIo: false,
|
||||||
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
},
|
},
|
||||||
WebConfig: WebConfig{
|
WebConfig: WebConfig{
|
||||||
AutoRender: true,
|
AutoRender: true,
|
||||||
@ -261,6 +269,7 @@ func newBConfig() *Config {
|
|||||||
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
||||||
SessionNameInHTTPHeader: "Beegosessionid",
|
SessionNameInHTTPHeader: "Beegosessionid",
|
||||||
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
||||||
|
SessionCookieSameSite: http.SameSiteDefaultMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Log: LogConfig{
|
Log: LogConfig{
|
||||||
|
@ -145,7 +145,7 @@ httpport = 8080
|
|||||||
# enable db
|
# enable db
|
||||||
[dbinfo]
|
[dbinfo]
|
||||||
# db type name
|
# db type name
|
||||||
# suport mysql,sqlserver
|
# support mysql,sqlserver
|
||||||
name = mysql
|
name = mysql
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ httpport=8080
|
|||||||
# enable db
|
# enable db
|
||||||
[dbinfo]
|
[dbinfo]
|
||||||
# db type name
|
# db type name
|
||||||
# suport mysql,sqlserver
|
# support mysql,sqlserver
|
||||||
name=mysql
|
name=mysql
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
@ -296,7 +296,7 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
|
|||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
{
|
{
|
||||||
tmpData = v.(map[string]interface{})
|
tmpData = v.(map[string]interface{})
|
||||||
if idx == len(keys) - 1 {
|
if idx == len(keys)-1 {
|
||||||
return tmpData, nil
|
return tmpData, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ func (ctx *Context) XSRFToken(key string, expire int64) string {
|
|||||||
token, ok := ctx.GetSecureCookie(key, "_xsrf")
|
token, ok := ctx.GetSecureCookie(key, "_xsrf")
|
||||||
if !ok {
|
if !ok {
|
||||||
token = string(utils.RandomCreateBytes(32))
|
token = string(utils.RandomCreateBytes(32))
|
||||||
ctx.SetSecureCookie(key, "_xsrf", token, expire)
|
ctx.SetSecureCookie(key, "_xsrf", token, expire, "", "", true, true)
|
||||||
}
|
}
|
||||||
ctx._xsrfToken = token
|
ctx._xsrfToken = token
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@ package context
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestXsrfReset_01(t *testing.T) {
|
func TestXsrfReset_01(t *testing.T) {
|
||||||
@ -44,4 +47,8 @@ func TestXsrfReset_01(t *testing.T) {
|
|||||||
if token == c._xsrfToken {
|
if token == c._xsrfToken {
|
||||||
t.FailNow()
|
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
|
return val
|
||||||
}
|
}
|
||||||
if input.Context.Request.Form == nil {
|
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)
|
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
|
package param
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
import "reflect"
|
"reflect"
|
||||||
import "time"
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type testDefinition struct {
|
type testDefinition struct {
|
||||||
strValue string
|
strValue string
|
||||||
|
@ -255,7 +255,7 @@ func (c *Controller) RenderString() (string, error) {
|
|||||||
// RenderBytes returns the bytes of rendered template string. Do not send out response.
|
// RenderBytes returns the bytes of rendered template string. Do not send out response.
|
||||||
func (c *Controller) RenderBytes() ([]byte, error) {
|
func (c *Controller) RenderBytes() ([]byte, error) {
|
||||||
buf, err := c.renderTemplate()
|
buf, err := c.renderTemplate()
|
||||||
//if the controller has set layout, then first get the tplName's content set the content to the layout
|
// if the controller has set layout, then first get the tplName's content set the content to the layout
|
||||||
if err == nil && c.Layout != "" {
|
if err == nil && c.Layout != "" {
|
||||||
c.Data["LayoutContent"] = template.HTML(buf.String())
|
c.Data["LayoutContent"] = template.HTML(buf.String())
|
||||||
|
|
||||||
@ -642,12 +642,13 @@ func (c *Controller) DelSession(name interface{}) {
|
|||||||
|
|
||||||
// SessionRegenerateID regenerates session id for this session.
|
// SessionRegenerateID regenerates session id for this session.
|
||||||
// the session data have no changes.
|
// the session data have no changes.
|
||||||
func (c *Controller) SessionRegenerateID() {
|
func (c *Controller) SessionRegenerateID() (err error) {
|
||||||
if c.CruSession != nil {
|
if c.CruSession != nil {
|
||||||
c.CruSession.SessionRelease(c.Ctx.ResponseWriter)
|
c.CruSession.SessionRelease(c.Ctx.ResponseWriter)
|
||||||
}
|
}
|
||||||
c.CruSession = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request)
|
c.CruSession, err = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||||
c.Ctx.Input.CruSession = c.CruSession
|
c.Ctx.Input.CruSession = c.CruSession
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestroySession cleans session data and session cookie.
|
// DestroySession cleans session data and session cookie.
|
||||||
|
@ -19,9 +19,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetInt(t *testing.T) {
|
func TestGetInt(t *testing.T) {
|
||||||
@ -125,8 +126,8 @@ func TestGetUint64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAdditionalViewPaths(t *testing.T) {
|
func TestAdditionalViewPaths(t *testing.T) {
|
||||||
dir1 := "_beeTmp"
|
dir1 := tmpDir("TestAdditionalViewPaths1")
|
||||||
dir2 := "_beeTmp2"
|
dir2 := tmpDir("TestAdditionalViewPaths2")
|
||||||
defer os.RemoveAll(dir1)
|
defer os.RemoveAll(dir1)
|
||||||
defer os.RemoveAll(dir2)
|
defer os.RemoveAll(dir2)
|
||||||
|
|
||||||
|
16
error.go
16
error.go
@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errorTypeHandler = iota
|
errorTypeHandler = iota
|
||||||
errorTypeController
|
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) {
|
func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
|
||||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||||
data := M{
|
data := M{
|
||||||
|
3
go.mod
3
go.mod
@ -22,6 +22,7 @@ require (
|
|||||||
github.com/lib/pq v1.0.0
|
github.com/lib/pq v1.0.0
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
github.com/pelletier/go-toml v1.2.0 // indirect
|
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/prometheus/client_golang v1.7.0
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
|
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
|
||||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec
|
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/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect
|
||||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
|
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
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
|
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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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-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/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 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
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/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.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.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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=
|
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-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 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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-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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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-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-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-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/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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/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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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=
|
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,
|
"504": gatewayTimeout,
|
||||||
"417": invalidxsrf,
|
"417": invalidxsrf,
|
||||||
"422": missingxsrf,
|
"422": missingxsrf,
|
||||||
|
"413": payloadTooLarge,
|
||||||
}
|
}
|
||||||
for e, h := range m {
|
for e, h := range m {
|
||||||
if _, ok := ErrorMaps[e]; !ok {
|
if _, ok := ErrorMaps[e]; !ok {
|
||||||
@ -60,6 +61,7 @@ func registerSession() error {
|
|||||||
conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
|
conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
|
||||||
conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
||||||
conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
||||||
|
conf.CookieSameSite = BConfig.WebConfig.Session.SessionCookieSameSite
|
||||||
} else {
|
} else {
|
||||||
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
|
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -144,6 +144,7 @@ type BeegoHTTPSettings struct {
|
|||||||
Gzip bool
|
Gzip bool
|
||||||
DumpBody bool
|
DumpBody bool
|
||||||
Retries int // if set to -1 means will retry forever
|
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.
|
// 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
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest {
|
||||||
|
b.setting.RetryDelay = delay
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// DumpBody setting whether need to Dump the Body.
|
// DumpBody setting whether need to Dump the Body.
|
||||||
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
||||||
b.setting.DumpBody = isdump
|
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 default value is 0, it will run once.
|
||||||
// retries equal to -1, it will run forever until success
|
// retries equal to -1, it will run forever until success
|
||||||
// retries is setted, it will retries fixed times.
|
// 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++ {
|
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
||||||
resp, err = client.Do(b.req)
|
resp, err = client.Do(b.req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
time.Sleep(b.setting.RetryDelay)
|
||||||
}
|
}
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package httplib
|
package httplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -33,6 +34,34 @@ func TestResponse(t *testing.T) {
|
|||||||
t.Log(resp)
|
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) {
|
func TestGet(t *testing.T) {
|
||||||
req := Get("http://httpbin.org/get")
|
req := Get("http://httpbin.org/get")
|
||||||
b, err := req.Bytes()
|
b, err := req.Bytes()
|
||||||
|
@ -16,9 +16,9 @@ package logs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +63,10 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
|
|||||||
defer c.innerWriter.Close()
|
defer c.innerWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.lg.writeln(when, msg)
|
_, err := c.lg.writeln(when, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +104,6 @@ func (c *connWriter) connect() error {
|
|||||||
|
|
||||||
func (c *connWriter) needToConnectOnMsg() bool {
|
func (c *connWriter) needToConnectOnMsg() bool {
|
||||||
if c.Reconnect {
|
if c.Reconnect {
|
||||||
c.Reconnect = false
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,65 @@
|
|||||||
package logs
|
package logs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
"testing"
|
"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) {
|
func TestConn(t *testing.T) {
|
||||||
log := NewLogger(1000)
|
log := NewLogger(1000)
|
||||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||||
log.Informational("informational")
|
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 {
|
if info == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.Hourly {
|
if w.Hourly {
|
||||||
if !info.IsDir() && info.ModTime().Add(1 * time.Hour * time.Duration(w.MaxHours)).Before(time.Now()) {
|
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)) &&
|
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||||
os.Remove(path)
|
os.Remove(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if w.Daily {
|
} else if w.Daily {
|
||||||
if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {
|
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)) &&
|
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||||
os.Remove(path)
|
os.Remove(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ func TestFileDailyRotate_06(t *testing.T) { //test file mode
|
|||||||
|
|
||||||
func TestFileHourlyRotate_01(t *testing.T) {
|
func TestFileHourlyRotate_01(t *testing.T) {
|
||||||
log := NewLogger(10000)
|
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.Debug("debug")
|
||||||
log.Info("info")
|
log.Info("info")
|
||||||
log.Notice("notice")
|
log.Notice("notice")
|
||||||
@ -237,7 +237,7 @@ func TestFileHourlyRotate_05(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileHourlyRotate_06(t *testing.T) { //test file mode
|
func TestFileHourlyRotate_06(t *testing.T) { //test file mode
|
||||||
log := NewLogger(10000)
|
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.Debug("debug")
|
||||||
log.Info("info")
|
log.Info("info")
|
||||||
log.Notice("notice")
|
log.Notice("notice")
|
||||||
@ -269,19 +269,19 @@ func testFileRotate(t *testing.T, fn1, fn2 string, daily, hourly bool) {
|
|||||||
RotatePerm: "0440",
|
RotatePerm: "0440",
|
||||||
}
|
}
|
||||||
|
|
||||||
if daily {
|
if daily {
|
||||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
||||||
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
||||||
fw.dailyOpenDate = fw.dailyOpenTime.Day()
|
fw.dailyOpenDate = fw.dailyOpenTime.Day()
|
||||||
}
|
}
|
||||||
|
|
||||||
if hourly {
|
if hourly {
|
||||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
|
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
|
||||||
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
|
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
|
||||||
fw.hourlyOpenDate = fw.hourlyOpenTime.Day()
|
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} {
|
for _, file := range []string{fn1, fn2} {
|
||||||
_, err := os.Stat(file)
|
_, err := os.Stat(file)
|
||||||
@ -328,8 +328,8 @@ func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
|
|||||||
|
|
||||||
func testFileHourlyRotate(t *testing.T, fn1, fn2 string) {
|
func testFileHourlyRotate(t *testing.T, fn1, fn2 string) {
|
||||||
fw := &fileLogWriter{
|
fw := &fileLogWriter{
|
||||||
Hourly: true,
|
Hourly: true,
|
||||||
MaxHours: 168,
|
MaxHours: 168,
|
||||||
Rotate: true,
|
Rotate: true,
|
||||||
Level: LevelTrace,
|
Level: LevelTrace,
|
||||||
Perm: "0660",
|
Perm: "0660",
|
||||||
|
@ -30,11 +30,12 @@ func newLogWriter(wr io.Writer) *logWriter {
|
|||||||
return &logWriter{writer: wr}
|
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()
|
lg.Lock()
|
||||||
h, _, _ := formatTimeHeader(when)
|
h, _, _ := formatTimeHeader(when)
|
||||||
lg.writer.Write(append(append(h, msg...), '\n'))
|
n, err := lg.writer.Write(append(append(h, msg...), '\n'))
|
||||||
lg.Unlock()
|
lg.Unlock()
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,7 +37,7 @@ func PrometheusMiddleWare(next http.Handler) http.Handler {
|
|||||||
"appname": beego.BConfig.AppName,
|
"appname": beego.BConfig.AppName,
|
||||||
},
|
},
|
||||||
Help: "The statics info for http request",
|
Help: "The statics info for http request",
|
||||||
}, []string{"pattern", "method", "status", "duration"})
|
}, []string{"pattern", "method", "status"})
|
||||||
|
|
||||||
prometheus.MustRegister(summaryVec)
|
prometheus.MustRegister(summaryVec)
|
||||||
|
|
||||||
@ -57,15 +57,15 @@ func registerBuildInfo() {
|
|||||||
Subsystem: "build_info",
|
Subsystem: "build_info",
|
||||||
Help: "The building information",
|
Help: "The building information",
|
||||||
ConstLabels: map[string]string{
|
ConstLabels: map[string]string{
|
||||||
"appname": beego.BConfig.AppName,
|
"appname": beego.BConfig.AppName,
|
||||||
"build_version": beego.BuildVersion,
|
"build_version": beego.BuildVersion,
|
||||||
"build_revision": beego.BuildGitRevision,
|
"build_revision": beego.BuildGitRevision,
|
||||||
"build_status": beego.BuildStatus,
|
"build_status": beego.BuildStatus,
|
||||||
"build_tag": beego.BuildTag,
|
"build_tag": beego.BuildTag,
|
||||||
"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
|
"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
|
||||||
"go_version": beego.GoVersion,
|
"go_version": beego.GoVersion,
|
||||||
"git_branch": beego.GitBranch,
|
"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{})
|
}, []string{})
|
||||||
|
|
||||||
@ -95,5 +95,5 @@ func report(dur time.Duration, writer http.ResponseWriter, q *http.Request, vec
|
|||||||
logs.Warn("we can not find the router info for this request, so request will be recorded as UNKNOWN: " + q.URL.String())
|
logs.Warn("we can not find the router info for this request, so request will be recorded as UNKNOWN: " + q.URL.String())
|
||||||
}
|
}
|
||||||
ms := dur / time.Millisecond
|
ms := dur / time.Millisecond
|
||||||
vec.WithLabelValues(ptn, q.Method, strconv.Itoa(status), strconv.Itoa(int(ms))).Observe(float64(ms))
|
vec.WithLabelValues(ptn, q.Method, strconv.Itoa(status)).Observe(float64(ms))
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ func TestPrometheusMiddleWare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
}
|
}
|
||||||
vec := prometheus.NewSummaryVec(prometheus.SummaryOpts{}, []string{"pattern", "method", "status", "duration"})
|
vec := prometheus.NewSummaryVec(prometheus.SummaryOpts{}, []string{"pattern", "method", "status"})
|
||||||
|
|
||||||
report(time.Second, writer, request, vec)
|
report(time.Second, writer, request, vec)
|
||||||
middleware.ServeHTTP(writer, request)
|
middleware.ServeHTTP(writer, request)
|
||||||
|
@ -197,9 +197,9 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
|||||||
if strings.Contains(column, "%COL%") {
|
if strings.Contains(column, "%COL%") {
|
||||||
column = strings.Replace(column, "%COL%", fi.column, -1)
|
column = strings.Replace(column, "%COL%", fi.column, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.description != "" && al.Driver!=DRSqlite {
|
if fi.description != "" && al.Driver != DRSqlite {
|
||||||
column += " " + fmt.Sprintf("COMMENT '%s'",fi.description)
|
column += " " + fmt.Sprintf("COMMENT '%s'", fi.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
|
18
orm/db.go
18
orm/db.go
@ -36,10 +36,11 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
operators = map[string]bool{
|
operators = map[string]bool{
|
||||||
"exact": true,
|
"exact": true,
|
||||||
"iexact": true,
|
"iexact": true,
|
||||||
"contains": true,
|
"strictexact": true,
|
||||||
"icontains": true,
|
"contains": true,
|
||||||
|
"icontains": true,
|
||||||
// "regex": true,
|
// "regex": true,
|
||||||
// "iregex": true,
|
// "iregex": true,
|
||||||
"gt": true,
|
"gt": true,
|
||||||
@ -702,6 +703,13 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if num > 0 {
|
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)
|
err := d.deleteRels(q, mi, args, tz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return num, err
|
return num, err
|
||||||
@ -1195,7 +1203,7 @@ func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator stri
|
|||||||
}
|
}
|
||||||
sql = d.ins.OperatorSQL(operator)
|
sql = d.ins.OperatorSQL(operator)
|
||||||
switch operator {
|
switch operator {
|
||||||
case "exact":
|
case "exact", "strictexact":
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
params[0] = "IS NULL"
|
params[0] = "IS NULL"
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DriverType database driver constant int.
|
// DriverType database driver constant int.
|
||||||
@ -424,8 +425,7 @@ func GetDB(aliasNames ...string) (*sql.DB, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stmtDecorator struct {
|
type stmtDecorator struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
lastUse int64
|
|
||||||
stmt *sql.Stmt
|
stmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,9 +433,12 @@ func (s *stmtDecorator) getStmt() *sql.Stmt {
|
|||||||
return s.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() {
|
func (s *stmtDecorator) acquire() {
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
s.lastUse = time.Now().Unix()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stmtDecorator) release() {
|
func (s *stmtDecorator) release() {
|
||||||
@ -453,12 +456,13 @@ func (s *stmtDecorator) destroy() {
|
|||||||
func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator {
|
func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator {
|
||||||
return &stmtDecorator{
|
return &stmtDecorator{
|
||||||
stmt: sqlStmt,
|
stmt: sqlStmt,
|
||||||
lastUse: time.Now().Unix(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStmtDecoratorLruWithEvict() *lru.Cache {
|
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()
|
value.(*stmtDecorator).destroy()
|
||||||
})
|
})
|
||||||
return cache
|
return cache
|
||||||
|
@ -22,10 +22,11 @@ import (
|
|||||||
|
|
||||||
// mysql operators.
|
// mysql operators.
|
||||||
var mysqlOperators = map[string]string{
|
var mysqlOperators = map[string]string{
|
||||||
"exact": "= ?",
|
"exact": "= ?",
|
||||||
"iexact": "LIKE ?",
|
"iexact": "LIKE ?",
|
||||||
"contains": "LIKE BINARY ?",
|
"strictexact": "= BINARY ?",
|
||||||
"icontains": "LIKE ?",
|
"contains": "LIKE BINARY ?",
|
||||||
|
"icontains": "LIKE ?",
|
||||||
// "regex": "REGEXP BINARY ?",
|
// "regex": "REGEXP BINARY ?",
|
||||||
// "iregex": "REGEXP ?",
|
// "iregex": "REGEXP ?",
|
||||||
"gt": "> ?",
|
"gt": "> ?",
|
||||||
|
@ -53,18 +53,24 @@ func (e *SliceStringField) FieldType() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *SliceStringField) SetRaw(value interface{}) error {
|
func (e *SliceStringField) SetRaw(value interface{}) error {
|
||||||
switch d := value.(type) {
|
f := func(str string) {
|
||||||
case []string:
|
if len(str) > 0 {
|
||||||
e.Set(d)
|
parts := strings.Split(str, ",")
|
||||||
case string:
|
|
||||||
if len(d) > 0 {
|
|
||||||
parts := strings.Split(d, ",")
|
|
||||||
v := make([]string, 0, len(parts))
|
v := make([]string, 0, len(parts))
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
v = append(v, strings.TrimSpace(p))
|
v = append(v, strings.TrimSpace(p))
|
||||||
}
|
}
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch d := value.(type) {
|
||||||
|
case []string:
|
||||||
|
e.Set(d)
|
||||||
|
case string:
|
||||||
|
f(d)
|
||||||
|
case []byte:
|
||||||
|
f(string(d))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<SliceStringField.SetRaw> unknown value `%v`", value)
|
return fmt.Errorf("<SliceStringField.SetRaw> unknown value `%v`", value)
|
||||||
}
|
}
|
||||||
@ -96,6 +102,8 @@ func (e *JSONFieldTest) SetRaw(value interface{}) error {
|
|||||||
switch d := value.(type) {
|
switch d := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
return json.Unmarshal([]byte(d), e)
|
return json.Unmarshal([]byte(d), e)
|
||||||
|
case []byte:
|
||||||
|
return json.Unmarshal(d, e)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<JSONField.SetRaw> unknown value `%v`", value)
|
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) {
|
func (o *orm) Delete(md interface{}, cols ...string) (int64, error) {
|
||||||
mi, ind := o.getMiInd(md, true)
|
mi, ind := o.getMiInd(md, true)
|
||||||
num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols)
|
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
|
// 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()
|
con += " - " + err.Error()
|
||||||
}
|
}
|
||||||
logMap["sql"] = fmt.Sprintf("%s-`%s`", query, strings.Join(cons, "`, `"))
|
logMap["sql"] = fmt.Sprintf("%s-`%s`", query, strings.Join(cons, "`, `"))
|
||||||
if LogFunc != nil{
|
if LogFunc != nil {
|
||||||
LogFunc(logMap)
|
LogFunc(logMap)
|
||||||
}
|
}
|
||||||
DebugLog.Println(con)
|
DebugLog.Println(con)
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// raw sql string prepared statement
|
// raw sql string prepared statement
|
||||||
@ -368,7 +370,15 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
|
|||||||
field.Set(mf)
|
field.Set(mf)
|
||||||
field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex)
|
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 {
|
} else {
|
||||||
@ -509,7 +519,15 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
|||||||
field.Set(mf)
|
field.Set(mf)
|
||||||
field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex)
|
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 {
|
} else {
|
||||||
|
@ -769,6 +769,20 @@ func TestCustomField(t *testing.T) {
|
|||||||
|
|
||||||
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
||||||
throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
|
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) {
|
func TestExpr(t *testing.T) {
|
||||||
@ -808,6 +822,17 @@ func TestOperators(t *testing.T) {
|
|||||||
throwFail(t, err)
|
throwFail(t, err)
|
||||||
throwFail(t, AssertIs(num, 1))
|
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()
|
num, err = qs.Filter("user_name__contains", "e").Count()
|
||||||
throwFail(t, err)
|
throwFail(t, err)
|
||||||
throwFail(t, AssertIs(num, 2))
|
throwFail(t, AssertIs(num, 2))
|
||||||
|
13
parser.go
13
parser.go
@ -49,7 +49,6 @@ func init() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
lastupdateFilename = "lastupdate.tmp"
|
lastupdateFilename = "lastupdate.tmp"
|
||||||
commentFilename string
|
|
||||||
pkgLastupdate map[string]int64
|
pkgLastupdate map[string]int64
|
||||||
genInfoList map[string][]ControllerComments
|
genInfoList map[string][]ControllerComments
|
||||||
|
|
||||||
@ -70,16 +69,13 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const commentPrefix = "commentsRouter_"
|
const commentFilename = "commentsRouter.go"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pkgLastupdate = make(map[string]int64)
|
pkgLastupdate = make(map[string]int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parserPkg(pkgRealpath, pkgpath string) error {
|
func parserPkg(pkgRealpath, pkgpath string) error {
|
||||||
rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_")
|
|
||||||
commentFilename, _ = filepath.Rel(AppPath, pkgRealpath)
|
|
||||||
commentFilename = commentPrefix + rep.Replace(commentFilename) + ".go"
|
|
||||||
if !compareFile(pkgRealpath) {
|
if !compareFile(pkgRealpath) {
|
||||||
logs.Info(pkgRealpath + " no changed")
|
logs.Info(pkgRealpath + " no changed")
|
||||||
return nil
|
return nil
|
||||||
@ -102,7 +98,10 @@ func parserPkg(pkgRealpath, pkgpath string) error {
|
|||||||
if specDecl.Recv != nil {
|
if specDecl.Recv != nil {
|
||||||
exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
|
exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
|
||||||
if ok {
|
if ok {
|
||||||
parserComments(specDecl, fmt.Sprint(exp.X), pkgpath)
|
err = parserComments(specDecl, fmt.Sprint(exp.X), pkgpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,7 +499,7 @@ func genRouterCode(pkgRealpath string) {
|
|||||||
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
|
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
|
||||||
beego.ControllerComments{
|
beego.ControllerComments{
|
||||||
Method: "` + strings.TrimSpace(c.Method) + `",
|
Method: "` + strings.TrimSpace(c.Method) + `",
|
||||||
` + `Router: "` + c.Router + `"` + `,
|
` + "Router: `" + c.Router + "`" + `,
|
||||||
AllowHTTPMethods: ` + allmethod + `,
|
AllowHTTPMethods: ` + allmethod + `,
|
||||||
MethodParams: ` + methodParams + `,
|
MethodParams: ` + methodParams + `,
|
||||||
Filters: ` + filters + `,
|
Filters: ` + filters + `,
|
||||||
|
@ -40,10 +40,11 @@
|
|||||||
package authz
|
package authz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
"github.com/casbin/casbin"
|
"github.com/casbin/casbin"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAuthorizer returns the authorizer.
|
// NewAuthorizer returns the authorizer.
|
||||||
|
@ -15,13 +15,14 @@
|
|||||||
package authz
|
package authz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
"github.com/astaxie/beego/plugins/auth"
|
"github.com/astaxie/beego/plugins/auth"
|
||||||
"github.com/casbin/casbin"
|
"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) {
|
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 r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||||
if BConfig.CopyRequestBody && !context.Input.IsUpload() {
|
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.CopyBody(BConfig.MaxMemory)
|
||||||
}
|
}
|
||||||
context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
|
context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
@ -71,7 +72,6 @@ func (tc *TestController) GetEmptyBody() {
|
|||||||
tc.Ctx.Output.Body(res)
|
tc.Ctx.Output.Body(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type JSONController struct {
|
type JSONController struct {
|
||||||
Controller
|
Controller
|
||||||
}
|
}
|
||||||
@ -656,17 +656,14 @@ func beegoBeforeRouter1(ctx *context.Context) {
|
|||||||
ctx.WriteString("|BeforeRouter1")
|
ctx.WriteString("|BeforeRouter1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func beegoBeforeExec1(ctx *context.Context) {
|
func beegoBeforeExec1(ctx *context.Context) {
|
||||||
ctx.WriteString("|BeforeExec1")
|
ctx.WriteString("|BeforeExec1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func beegoAfterExec1(ctx *context.Context) {
|
func beegoAfterExec1(ctx *context.Context) {
|
||||||
ctx.WriteString("|AfterExec1")
|
ctx.WriteString("|AfterExec1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func beegoFinishRouter1(ctx *context.Context) {
|
func beegoFinishRouter1(ctx *context.Context) {
|
||||||
ctx.WriteString("|FinishRouter1")
|
ctx.WriteString("|FinishRouter1")
|
||||||
}
|
}
|
||||||
@ -709,3 +706,27 @@ func TestYAMLPrepare(t *testing.T) {
|
|||||||
t.Errorf(w.Body.String())
|
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
|
// more docs: http://beego.me/docs/module/session.md
|
||||||
package redis_cluster
|
package redis_cluster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
rediss "github.com/go-redis/redis"
|
rediss "github.com/go-redis/redis"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var redispder = &Provider{}
|
var redispder = &Provider{}
|
||||||
@ -101,7 +103,7 @@ func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
c := rs.p
|
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
|
// Provider redis_cluster session provider
|
||||||
@ -146,10 +148,10 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
} else {
|
} else {
|
||||||
rp.dbNum = 0
|
rp.dbNum = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
|
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
|
||||||
Addrs: strings.Split(rp.savePath, ";"),
|
Addrs: strings.Split(rp.savePath, ";"),
|
||||||
Password: rp.password,
|
Password: rp.password,
|
||||||
PoolSize: rp.poolsize,
|
PoolSize: rp.poolsize,
|
||||||
})
|
})
|
||||||
return rp.poollist.Ping().Err()
|
return rp.poollist.Ping().Err()
|
||||||
@ -186,15 +188,15 @@ func (rp *Provider) SessionExist(sid string) bool {
|
|||||||
// SessionRegenerate generate new sid for redis_cluster session
|
// SessionRegenerate generate new sid for redis_cluster session
|
||||||
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
|
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
|
||||||
c := rp.poollist
|
c := rp.poollist
|
||||||
|
|
||||||
if existed, err := c.Exists(oldsid).Result(); err != nil || existed == 0 {
|
if existed, err := c.Exists(oldsid).Result(); err != nil || existed == 0 {
|
||||||
// oldsid doesn't exists, set the new sid directly
|
// oldsid doesn't exists, set the new sid directly
|
||||||
// ignore error here, since if it return error
|
// ignore error here, since if it return error
|
||||||
// the existed value will be 0
|
// 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 {
|
} else {
|
||||||
c.Rename(oldsid, sid)
|
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)
|
return rp.SessionRead(sid)
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,14 @@
|
|||||||
package redis_sentinel
|
package redis_sentinel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/astaxie/beego/session"
|
|
||||||
"github.com/go-redis/redis"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/session"
|
||||||
|
"github.com/go-redis/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
var redispder = &Provider{}
|
var redispder = &Provider{}
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"errors"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -180,6 +180,11 @@ func (fp *FileProvider) SessionExist(sid string) bool {
|
|||||||
filepder.lock.Lock()
|
filepder.lock.Lock()
|
||||||
defer filepder.lock.Unlock()
|
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))
|
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||||
return err == nil
|
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
|
// ManagerConfig define the session config
|
||||||
type ManagerConfig struct {
|
type ManagerConfig struct {
|
||||||
CookieName string `json:"cookieName"`
|
CookieName string `json:"cookieName"`
|
||||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||||
Gclifetime int64 `json:"gclifetime"`
|
Gclifetime int64 `json:"gclifetime"`
|
||||||
Maxlifetime int64 `json:"maxLifetime"`
|
Maxlifetime int64 `json:"maxLifetime"`
|
||||||
DisableHTTPOnly bool `json:"disableHTTPOnly"`
|
DisableHTTPOnly bool `json:"disableHTTPOnly"`
|
||||||
Secure bool `json:"secure"`
|
Secure bool `json:"secure"`
|
||||||
CookieLifeTime int `json:"cookieLifeTime"`
|
CookieLifeTime int `json:"cookieLifeTime"`
|
||||||
ProviderConfig string `json:"providerConfig"`
|
ProviderConfig string `json:"providerConfig"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
SessionIDLength int64 `json:"sessionIDLength"`
|
SessionIDLength int64 `json:"sessionIDLength"`
|
||||||
EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
|
EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
|
||||||
SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
|
SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
|
||||||
EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
|
EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
|
||||||
SessionIDPrefix string `json:"sessionIDPrefix"`
|
SessionIDPrefix string `json:"sessionIDPrefix"`
|
||||||
|
CookieSameSite http.SameSite `json:"cookieSameSite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager contains Provider and its configuration.
|
// 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,
|
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||||
Secure: manager.isSecure(r),
|
Secure: manager.isSecure(r),
|
||||||
Domain: manager.config.Domain,
|
Domain: manager.config.Domain,
|
||||||
|
SameSite: manager.config.CookieSameSite,
|
||||||
}
|
}
|
||||||
if manager.config.CookieLifeTime > 0 {
|
if manager.config.CookieLifeTime > 0 {
|
||||||
cookie.MaxAge = manager.config.CookieLifeTime
|
cookie.MaxAge = manager.config.CookieLifeTime
|
||||||
@ -271,7 +273,9 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
|||||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||||
Expires: expiration,
|
Expires: expiration,
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
Domain: manager.config.Domain}
|
Domain: manager.config.Domain,
|
||||||
|
SameSite: manager.config.CookieSameSite,
|
||||||
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
}
|
}
|
||||||
@ -291,25 +295,36 @@ func (manager *Manager) GC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request.
|
// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request.
|
||||||
func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) (session Store) {
|
func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) (Store, error) {
|
||||||
sid, err := manager.sessionID()
|
sid, err := manager.sessionID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var session Store
|
||||||
cookie, err := r.Cookie(manager.config.CookieName)
|
cookie, err := r.Cookie(manager.config.CookieName)
|
||||||
if err != nil || cookie.Value == "" {
|
if err != nil || cookie.Value == "" {
|
||||||
//delete old cookie
|
// delete old cookie
|
||||||
session, _ = manager.provider.SessionRead(sid)
|
session, err = manager.provider.SessionRead(sid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
cookie = &http.Cookie{Name: manager.config.CookieName,
|
cookie = &http.Cookie{Name: manager.config.CookieName,
|
||||||
Value: url.QueryEscape(sid),
|
Value: url.QueryEscape(sid),
|
||||||
Path: "/",
|
Path: "/",
|
||||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||||
Secure: manager.isSecure(r),
|
Secure: manager.isSecure(r),
|
||||||
Domain: manager.config.Domain,
|
Domain: manager.config.Domain,
|
||||||
|
SameSite: manager.config.CookieSameSite,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
oldsid, _ := url.QueryUnescape(cookie.Value)
|
oldsid, err := url.QueryUnescape(cookie.Value)
|
||||||
session, _ = manager.provider.SessionRegenerate(oldsid, sid)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
session, err = manager.provider.SessionRegenerate(oldsid, sid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
cookie.Value = url.QueryEscape(sid)
|
cookie.Value = url.QueryEscape(sid)
|
||||||
cookie.HttpOnly = true
|
cookie.HttpOnly = true
|
||||||
cookie.Path = "/"
|
cookie.Path = "/"
|
||||||
@ -328,7 +343,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
|
|||||||
w.Header().Set(manager.config.SessionNameInHTTPHeader, sid)
|
w.Header().Set(manager.config.SessionNameInHTTPHeader, sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveSession Get all active sessions count number.
|
// GetActiveSession Get all active sessions count number.
|
||||||
|
@ -202,7 +202,7 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
|
|||||||
if !strings.Contains(requestPath, prefix) {
|
if !strings.Contains(requestPath, prefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
|
if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filePath := path.Join(staticDir, requestPath[len(prefix):])
|
filePath := path.Join(staticDir, requestPath[len(prefix):])
|
||||||
|
@ -16,12 +16,13 @@ package beego
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/astaxie/beego/testdata"
|
|
||||||
"github.com/elazarl/go-bindata-assetfs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/testdata"
|
||||||
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var header = `{{define "header"}}
|
var header = `{{define "header"}}
|
||||||
@ -45,8 +46,12 @@ var block = `{{define "block"}}
|
|||||||
<h1>Hello, blocks!</h1>
|
<h1>Hello, blocks!</h1>
|
||||||
{{end}}`
|
{{end}}`
|
||||||
|
|
||||||
|
func tmpDir(s string) string {
|
||||||
|
return filepath.Join(os.TempDir(), s)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTemplate(t *testing.T) {
|
func TestTemplate(t *testing.T) {
|
||||||
dir := "_beeTmp"
|
dir := tmpDir("TestTemplate")
|
||||||
files := []string{
|
files := []string{
|
||||||
"header.tpl",
|
"header.tpl",
|
||||||
"index.tpl",
|
"index.tpl",
|
||||||
@ -107,7 +112,7 @@ var user = `<!DOCTYPE html>
|
|||||||
`
|
`
|
||||||
|
|
||||||
func TestRelativeTemplate(t *testing.T) {
|
func TestRelativeTemplate(t *testing.T) {
|
||||||
dir := "_beeTmp"
|
dir := tmpDir("TestRelativeTemplate")
|
||||||
|
|
||||||
//Just add dir to known viewPaths
|
//Just add dir to known viewPaths
|
||||||
if err := AddViewPath(dir); err != nil {
|
if err := AddViewPath(dir); err != nil {
|
||||||
@ -218,7 +223,7 @@ var output = `<!DOCTYPE html>
|
|||||||
`
|
`
|
||||||
|
|
||||||
func TestTemplateLayout(t *testing.T) {
|
func TestTemplateLayout(t *testing.T) {
|
||||||
dir := "_beeTmp"
|
dir := tmpDir("TestTemplateLayout")
|
||||||
files := []string{
|
files := []string{
|
||||||
"add.tpl",
|
"add.tpl",
|
||||||
"layout_blog.tpl",
|
"layout_blog.tpl",
|
||||||
|
@ -362,7 +362,7 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e
|
|||||||
value = value[:25]
|
value = value[:25]
|
||||||
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
||||||
} else if strings.HasSuffix(strings.ToUpper(value), "Z") {
|
} 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 {
|
} else if len(value) >= 19 {
|
||||||
if strings.Contains(value, "T") {
|
if strings.Contains(value, "T") {
|
||||||
value = value[:19]
|
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"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/elazarl/go-bindata-assetfs"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
|
2
tree.go
2
tree.go
@ -341,7 +341,7 @@ func (t *Tree) match(treePattern string, pattern string, wildcardValues []string
|
|||||||
if runObject == nil && len(t.fixrouters) > 0 {
|
if runObject == nil && len(t.fixrouters) > 0 {
|
||||||
// Filter the .json .xml .html extension
|
// Filter the .json .xml .html extension
|
||||||
for _, str := range allowSuffixExt {
|
for _, str := range allowSuffixExt {
|
||||||
if strings.HasSuffix(seg, str) {
|
if strings.HasSuffix(seg, str) && strings.HasSuffix(treePattern, seg) {
|
||||||
for _, subTree := range t.fixrouters {
|
for _, subTree := range t.fixrouters {
|
||||||
if subTree.prefix == seg[:len(seg)-len(str)] {
|
if subTree.prefix == seg[:len(seg)-len(str)] {
|
||||||
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
|
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
|
||||||
|
@ -213,7 +213,7 @@ func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tParams, err := trim(name, key+"."+ name + "." + label, params)
|
tParams, err := trim(name, key+"."+name+"."+label, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,14 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/astaxie/beego/logs"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty
|
// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty
|
||||||
|
Reference in New Issue
Block a user