mirror of
https://github.com/astaxie/beego.git
synced 2025-07-12 21:21:01 +00:00
Compare commits
288 Commits
develop-1.
...
master
Author | SHA1 | Date | |
---|---|---|---|
56d89cc55c | |||
e048377594 | |||
34334a52be | |||
7cdf96d21a | |||
fad897346f | |||
650fde66aa | |||
e284b0ddae | |||
20a0de6bd0 | |||
b4396c97bb | |||
471ebba64d | |||
d07a1eaa8e | |||
9524036aab | |||
45260e4119 | |||
02234dc503 | |||
05f4e0c146 | |||
ae108ec826 | |||
7c61eb058f | |||
03ba495b7f | |||
d26683799a | |||
f9075e8274 | |||
022ad862ac | |||
93bdf97068 | |||
3c48719999 | |||
cbb3de741d | |||
c510926cb8 | |||
140a4b90a3 | |||
c07acaebbc | |||
105b874477 | |||
3fc21ae6ec | |||
ccf873fa8b | |||
2572094a8d | |||
568626cd57 | |||
34d6a733e9 | |||
e44f16c672 | |||
d41abdb5e4 | |||
f1358cf78d | |||
14c1b76569 | |||
2708916f96 | |||
034cb3222e | |||
66804324f2 | |||
dc65055cf6 | |||
8cc74652a2 | |||
6aa6c55f07 | |||
ff762b561c | |||
d8e8f41230 | |||
9e6b8fcf34 | |||
48e98482f7 | |||
ff7a8b966b | |||
f9bef68aa9 | |||
484beb8bad | |||
43560dede4 | |||
c435d231ab | |||
b838683731 | |||
70cca5e298 | |||
f1cca45d8d | |||
4dc694411f | |||
3364c609de | |||
c5d43e87fe | |||
b89d9511ab | |||
325a0821c1 | |||
dd3f1ce9be | |||
463e96447a | |||
03498529b9 | |||
9a7c43c404 | |||
44127edefc | |||
089006525e | |||
2846043f2a | |||
e6a257f987 | |||
2473e69417 | |||
d455805a0a | |||
bd1cfefec7 | |||
7effdb0e7d | |||
b027968c0b | |||
961f300c14 | |||
7570abd310 | |||
7c8136710c | |||
a3ece98cec | |||
a31dce6216 | |||
a1782cc22d | |||
67f64afa85 | |||
2539fe3831 | |||
05c125ec2d | |||
6e638ef6c8 | |||
df043f22fc | |||
fbaf3380c6 | |||
b7bc57c4d1 | |||
32cdda96bd | |||
5995b00fa2 | |||
5618df8c76 | |||
c6c9ad46f9 | |||
5973ef107c | |||
b575fa1ebe | |||
6bbca96c6c | |||
63cd8e4e15 | |||
93736a8e66 | |||
654d87b210 | |||
0048b7d158 | |||
00e44952ff | |||
8982f5d702 | |||
6612bc4c2a | |||
f580a714d5 | |||
9ccd58bfff | |||
0f50b07a20 | |||
b86cf22fc4 | |||
6bf01eaeca | |||
8e015deee5 | |||
3acda41bc7 | |||
5b3dd7e50f | |||
f4a43814be | |||
35f1bd2119 | |||
f6c95ad534 | |||
f1950482c2 | |||
1dae2c9eb3 | |||
8ef9965eef | |||
3530457ff9 | |||
cbd51616f1 | |||
bdd8df6751 | |||
8fc4f8847c | |||
3bf5cde38c | |||
7a53baaf9b | |||
7574b91760 | |||
78d91062c9 | |||
23792401b5 | |||
e54dbabf0b | |||
cdc8110ea4 | |||
185d55eb46 | |||
8e879726fe | |||
33b052bc7a | |||
087399c44a | |||
f4f200cf04 | |||
0a58428220 | |||
c0462f75bf | |||
5cf33f2655 | |||
670064686e | |||
0019e0fc1b | |||
03bec05714 | |||
e831b97eb8 | |||
81b9a1382a | |||
0189e6329a | |||
6684924e99 | |||
e0a934af1d | |||
8178f035a0 | |||
2b39ff7837 | |||
c2361170b3 | |||
5b35bf6065 | |||
14c911e9d7 | |||
9472cba6c9 | |||
b83094ac1e | |||
1cb0ff560d | |||
cceecad8c2 | |||
d24f861629 | |||
c2471b22ad | |||
c5970766a3 | |||
48a98ec1a5 | |||
ed1d2c7f6e | |||
7a94996e22 | |||
597c55d547 | |||
026e8bc55a | |||
581e48679e | |||
09afe0ae8e | |||
08e49ca323 | |||
e1da804b2b | |||
705e091593 | |||
6bdedff457 | |||
f3be6dd2e9 | |||
9fe353dd0b | |||
9003ca3eef | |||
ff5ac3adf4 | |||
ca4a217783 | |||
e6ea307549 | |||
2c16c7b917 | |||
77ddc3338f | |||
ac3a549187 | |||
fe56de06b5 | |||
6c002a3124 | |||
cead72c6df | |||
7fe4eaef50 | |||
63599c0032 | |||
4db256c9fb | |||
c548764c8e | |||
b4a85c8f13 | |||
94f476fa39 | |||
8574e30b3a | |||
7442919f5a | |||
f6ec4efc70 | |||
7b899aa9af | |||
f73eee75ff | |||
739b8bab0c | |||
139c393f08 | |||
bdec93986b | |||
7ce0fde171 | |||
d6a2621b3c | |||
813a4df3c5 | |||
7267f5e573 | |||
5bc8d90d7f | |||
2d1c02e1c1 | |||
9ca9535c48 | |||
d1d9df74c7 | |||
d326d74c34 | |||
82178a487b | |||
a1b7fd3c93 | |||
0813471202 | |||
ce698aacf6 | |||
c22af4c611 | |||
f8c0e6fec5 | |||
882f1273c8 | |||
5a1fa4e1ec | |||
d05460237c | |||
75107f735e | |||
2e891152dd | |||
dec98f004c | |||
26b016a3a4 | |||
19aae0b7e1 | |||
2e192e1ed0 | |||
f9a3eae9d5 | |||
7fc1e4de96 | |||
993ccac2bd | |||
2fd65a469c | |||
a2fa073072 | |||
08cec9178f | |||
63b3fc4a99 | |||
1b4bb43df0 | |||
ec55edfbc4 | |||
2fce8f9d1b | |||
15489fa76a | |||
e7d8bab5d9 | |||
2f5683610f | |||
5c8c088684 | |||
009074725e | |||
ce50ca22d7 | |||
3052c64b6c | |||
5f2f6e4f86 | |||
3382a5baa1 | |||
882aa9b967 | |||
02972d8702 | |||
261b704d8b | |||
6c6cf91741 | |||
ae8461f95d | |||
aa3987f816 | |||
3dc5ec1060 | |||
1961c1e441 | |||
787bb60b42 | |||
12b984861d | |||
79ffef90e3 | |||
6f5c5bd3a6 | |||
28e6b3b924 | |||
9e1346ef4d | |||
9f295067b7 | |||
71776e4bef | |||
d4074b5004 | |||
22b8cae73b | |||
aa06a10493 | |||
9b58c2836c | |||
7312197732 | |||
e87de70c6d | |||
4304b40a82 | |||
e8facd28f5 | |||
54ef476600 | |||
756df9385f | |||
21f281655d | |||
2e7fb81348 | |||
ebc0207909 | |||
2386c9c80d | |||
b9f9fcca5f | |||
16d71893cd | |||
cfff0f3b46 | |||
93eb7c6b83 | |||
79c2157ad4 | |||
30eb889a91 | |||
9c51952db4 | |||
16b66509f6 | |||
d5695060c5 | |||
af2143f8fb | |||
a66b9950e7 | |||
44460bc457 | |||
41feb3a711 | |||
b6f7d30f9f | |||
4aad313de7 | |||
aefe21b63a | |||
32da446eb1 | |||
d9c016ed98 | |||
7258ef113a | |||
d6779c4a90 | |||
192a278a2a | |||
9d936c58bf | |||
ffe1d52120 | |||
8ef8fd2606 | |||
0cd80525e7 |
@ -8,15 +8,6 @@ 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,7 +21,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -72,7 +71,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) {
|
||||||
writeTemplate(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
|
execTpl(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.
|
||||||
@ -92,7 +91,7 @@ func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTemplate(rw, data, qpsTpl, defaultScriptsTpl)
|
execTpl(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.
|
||||||
@ -129,7 +128,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
data["Content"] = content
|
data["Content"] = content
|
||||||
data["Title"] = "Routers"
|
data["Title"] = "Routers"
|
||||||
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
case "filter":
|
case "filter":
|
||||||
var (
|
var (
|
||||||
content = M{
|
content = M{
|
||||||
@ -172,7 +171,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
data["Content"] = content
|
data["Content"] = content
|
||||||
data["Title"] = "Filters"
|
data["Title"] = "Filters"
|
||||||
writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
default:
|
default:
|
||||||
rw.Write([]byte("command not support"))
|
rw.Write([]byte("command not support"))
|
||||||
}
|
}
|
||||||
@ -280,7 +279,9 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,12 +290,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
|
|||||||
if command == "gc summary" {
|
if command == "gc summary" {
|
||||||
defaultTpl = gcAjaxTpl
|
defaultTpl = gcAjaxTpl
|
||||||
}
|
}
|
||||||
writeTemplate(rw, data, profillingTpl, defaultTpl)
|
execTpl(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, r *http.Request) {
|
func healthcheck(rw http.ResponseWriter, _ *http.Request) {
|
||||||
var (
|
var (
|
||||||
result []string
|
result []string
|
||||||
data = make(map[interface{}]interface{})
|
data = make(map[interface{}]interface{})
|
||||||
@ -321,49 +322,10 @@ func healthcheck(rw http.ResponseWriter, r *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).
|
||||||
@ -409,10 +371,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"
|
||||||
writeTemplate(rw, data, tasksTpl, defaultScriptsTpl)
|
execTpl(rw, data, tasksTpl, defaultScriptsTpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeTemplate(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
|
func execTpl(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,32 +1,10 @@
|
|||||||
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)
|
||||||
@ -97,143 +75,3 @@ 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 occured whie 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: BConfig.Listen.ClientAuth,
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"crypto/tls"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego/config"
|
"github.com/astaxie/beego/config"
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
@ -66,7 +65,6 @@ 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
|
||||||
@ -152,9 +150,6 @@ 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) {
|
||||||
@ -236,7 +231,6 @@ 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,
|
||||||
|
@ -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, "", "", true, true)
|
ctx.SetSecureCookie(key, "_xsrf", token, expire)
|
||||||
}
|
}
|
||||||
ctx._xsrfToken = token
|
ctx._xsrfToken = token
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,7 @@ 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) {
|
||||||
@ -47,8 +44,4 @@ 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"))
|
|
||||||
}
|
}
|
||||||
|
@ -332,15 +332,9 @@ func (input *BeegoInput) Query(key string) string {
|
|||||||
if val := input.Param(key); val != "" {
|
if val := input.Param(key); val != "" {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
if input.Context.Request.Form == nil {
|
|
||||||
input.dataLock.Lock()
|
|
||||||
if input.Context.Request.Form == nil {
|
if input.Context.Request.Form == nil {
|
||||||
input.Context.Request.ParseForm()
|
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,13 +205,3 @@ 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")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
14
error.go
14
error.go
@ -359,20 +359,6 @@ 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{
|
||||||
|
1
hooks.go
1
hooks.go
@ -34,7 +34,6 @@ 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 {
|
||||||
|
@ -144,7 +144,6 @@ 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.
|
||||||
@ -203,11 +202,6 @@ 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
|
||||||
@ -518,13 +512,11 @@ 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 inbetween 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,7 +15,6 @@
|
|||||||
package httplib
|
package httplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -34,34 +33,6 @@ 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()
|
||||||
|
@ -63,10 +63,7 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
|
|||||||
defer c.innerWriter.Close()
|
defer c.innerWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.lg.writeln(when, msg)
|
c.lg.writeln(when, msg)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +101,7 @@ 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,65 +15,11 @@
|
|||||||
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,12 +30,11 @@ func newLogWriter(wr io.Writer) *logWriter {
|
|||||||
return &logWriter{writer: wr}
|
return &logWriter{writer: wr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lg *logWriter) writeln(when time.Time, msg string) (int, error) {
|
func (lg *logWriter) writeln(when time.Time, msg string) {
|
||||||
lg.Lock()
|
lg.Lock()
|
||||||
h, _, _ := formatTimeHeader(when)
|
h, _, _ := formatTimeHeader(when)
|
||||||
n, err := lg.writer.Write(append(append(h, msg...), '\n'))
|
lg.writer.Write(append(append(h, msg...), '\n'))
|
||||||
lg.Unlock()
|
lg.Unlock()
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -702,13 +702,6 @@ 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
|
||||||
|
@ -425,6 +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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,12 +433,9 @@ 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() {
|
||||||
@ -455,6 +453,7 @@ 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,14 +242,8 @@ 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)
|
||||||
if err != nil {
|
|
||||||
return num, err
|
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
|
||||||
func (o *orm) QueryM2M(md interface{}, name string) QueryM2Mer {
|
func (o *orm) QueryM2M(md interface{}, name string) QueryM2Mer {
|
||||||
|
@ -742,12 +742,6 @@ 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,7 +15,6 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
@ -72,6 +71,7 @@ func (tc *TestController) GetEmptyBody() {
|
|||||||
tc.Ctx.Output.Body(res)
|
tc.Ctx.Output.Body(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type JSONController struct {
|
type JSONController struct {
|
||||||
Controller
|
Controller
|
||||||
}
|
}
|
||||||
@ -656,14 +656,17 @@ 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")
|
||||||
}
|
}
|
||||||
@ -706,27 +709,3 @@ 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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,11 +180,6 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -1,387 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
14
test.sh
14
test.sh
@ -1,14 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
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"
|
|
||||||
|
|
Reference in New Issue
Block a user