mirror of
https://gitee.com/johng/gf
synced 2026-06-19 23:02:56 +08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 650916c22a | |||
| 2804183325 | |||
| 2dc2610621 | |||
| 1736e71e6b | |||
| 69ee5375b9 | |||
| 3ac0a66887 | |||
| 20e873a1fc | |||
| 142484d89c | |||
| 76a9f4ca14 | |||
| c4e5679d5c | |||
| b08d7c3c38 | |||
| 5092d8e6c5 | |||
| 74d625ff97 | |||
| f1119e28e8 | |||
| 3082c7f761 | |||
| 5f36614dd7 | |||
| 1dcc7a4887 | |||
| 41e9d35487 |
19
.example/encoding/gjson/issue360.go
Normal file
19
.example/encoding/gjson/issue360.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := `
|
||||
{"apiVersion":"v1","kind":"Service","metadata":{"labels":{"name":"http-daemon"},"name":"http-daemon","namespace":"default"},"spec":{"ports":[{"name":"http-daemon","port":8080,"protocol":"TCP","targetPort":9212}],"selector":{"app":"http-daemon","version":"v0930-082326"}}}
|
||||
`
|
||||
js, err := gjson.DecodeToJson(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
//g.Dump(js.ToMap())
|
||||
y, _ := js.ToYamlString()
|
||||
fmt.Println(y)
|
||||
}
|
||||
21
.example/net/ghttp/server/middleware/issue355.go
Normal file
21
.example/net/ghttp/server/middleware/issue355.go
Normal file
@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindMiddlewareDefault(func(r *ghttp.Request) {
|
||||
fmt.Println("cors")
|
||||
r.Response.CORSDefault()
|
||||
r.Middleware.Next()
|
||||
})
|
||||
s.BindHandler("/api/captcha", func(r *ghttp.Request) {
|
||||
r.Response.Write("captcha")
|
||||
})
|
||||
s.SetPort(8010)
|
||||
s.Run()
|
||||
}
|
||||
16
.example/os/gview/build_in_funcs/issue359-1.go
Normal file
16
.example/os/gview/build_in_funcs/issue359-1.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tplContent := `
|
||||
{{"我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人"| strlimit 10 "..."}}
|
||||
`
|
||||
content, err := g.View().ParseContent(tplContent, nil)
|
||||
fmt.Println(err)
|
||||
fmt.Println(content)
|
||||
}
|
||||
19
.example/os/gview/build_in_funcs/issue359-2.go
Normal file
19
.example/os/gview/build_in_funcs/issue359-2.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人"
|
||||
tplContent := `
|
||||
{{.str | strlimit 10 "..."}}
|
||||
`
|
||||
content, err := g.View().ParseContent(tplContent, g.Map{
|
||||
"str": s,
|
||||
})
|
||||
fmt.Println(err)
|
||||
fmt.Println(content)
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// 演示slice类型属性的赋值
|
||||
func main() {
|
||||
type User struct {
|
||||
Scores []int
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
scores := []interface{}{99, 100, 60, 140}
|
||||
|
||||
// 通过map映射转换
|
||||
if err := gconv.Struct(g.Map{"Scores": scores}, user); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
g.Dump(user)
|
||||
}
|
||||
|
||||
// 通过变量映射转换,直接slice赋值
|
||||
if err := gconv.Struct(scores, user); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
g.Dump(user)
|
||||
}
|
||||
}
|
||||
@ -21,8 +21,8 @@ func main() {
|
||||
|
||||
user1 := new(User1)
|
||||
user2 := new(User2)
|
||||
scores := map[string]interface{}{
|
||||
"Scores": map[string]interface{}{
|
||||
scores := g.Map{
|
||||
"Scores": g.Map{
|
||||
"Name": "john",
|
||||
"Result": 100,
|
||||
},
|
||||
|
||||
@ -17,8 +17,8 @@ func main() {
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
scores := map[string]interface{}{
|
||||
"Scores": map[string]interface{}{
|
||||
scores := g.Map{
|
||||
"Scores": g.Map{
|
||||
"Name": "john",
|
||||
"Result": 100,
|
||||
},
|
||||
|
||||
@ -17,13 +17,13 @@ func main() {
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
scores := map[string]interface{}{
|
||||
"Scores": []interface{}{
|
||||
map[string]interface{}{
|
||||
scores := g.Map{
|
||||
"Scores": g.Slice{
|
||||
g.Map{
|
||||
"Name": "john",
|
||||
"Result": 100,
|
||||
},
|
||||
map[string]interface{}{
|
||||
g.Map{
|
||||
"Name": "smith",
|
||||
"Result": 60,
|
||||
},
|
||||
|
||||
@ -14,7 +14,7 @@ English | [简体中文](README_ZH.MD)
|
||||
|
||||
# Installation
|
||||
```
|
||||
go get -u github.com/gogf/gf
|
||||
go get -u -v github.com/gogf/gf
|
||||
```
|
||||
suggested using `go.mod`:
|
||||
```
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便及可维护性为宗旨;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 致力于项目的通用方案;
|
||||
|
||||
@ -584,14 +584,8 @@ func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -610,9 +604,26 @@ func (a *Array) CountValues() map[interface{}]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *Array) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -610,7 +610,7 @@ func (a *IntArray) CountValues() map[int]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *IntArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
@ -592,7 +592,7 @@ func (a *StrArray) Join(glue string) string {
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
buffer.WriteString(v)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -611,9 +611,20 @@ func (a *StrArray) CountValues() map[string]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *StrArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -529,14 +529,8 @@ func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -555,9 +549,26 @@ func (a *SortedArray) CountValues() map[interface{}]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
for k, v := range a.array {
|
||||
s = gconv.String(v)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -535,7 +535,7 @@ func (a *SortedIntArray) CountValues() map[int]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedIntArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
@ -517,7 +517,7 @@ func (a *SortedStrArray) Join(glue string) string {
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
buffer.WriteString(v)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -536,9 +536,20 @@ func (a *SortedStrArray) CountValues() map[string]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedStrArray) String() string {
|
||||
return "[" + a.Join(",") + "]"
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`)
|
||||
if k != len(a.array)-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -261,7 +261,7 @@ func TestArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `0.1."\"a\""."\\a"`)
|
||||
gtest.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -248,12 +248,12 @@ func TestStrArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `"0"."1"."2"."3"."4"."5"."6"`)
|
||||
gtest.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", `"a"`, `\a`}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `"0"."1"."\"a\""."\\a"`)
|
||||
gtest.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -394,14 +394,14 @@ func TestSortedArray_Join(t *testing.T) {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
gtest.Assert(array1.Join(","), `"a","c","d"`)
|
||||
gtest.Assert(array1.Join("."), `"a"."c"."d"`)
|
||||
gtest.Assert(array1.Join(","), `a,c,d`)
|
||||
gtest.Assert(array1.Join("."), `a.c.d`)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
gtest.Assert(array1.Join("."), `"\"a\"".0.1."\\a"`)
|
||||
gtest.Assert(array1.Join("."), `"a".0.1.\a`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -290,14 +290,14 @@ func TestSortedStrArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join(","), `"a","d","e"`)
|
||||
gtest.Assert(array1.Join("."), `"a"."d"."e"`)
|
||||
gtest.Assert(array1.Join(","), `a,d,e`)
|
||||
gtest.Assert(array1.Join("."), `a.d.e`)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"a", `"b"`, `\c`}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `"\"b\""."\\c"."a"`)
|
||||
gtest.Assert(array1.Join("."), `"b".\c.a`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -71,6 +71,48 @@ func (set *Set) Add(item ...interface{}) *Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *Set) AddIfNotExistFunc(item interface{}, f func() interface{}) *Set {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() interface{}) *Set {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interface{} {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value
|
||||
}
|
||||
}
|
||||
set.data[item] = struct{}{}
|
||||
return item
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *Set) Contains(item interface{}) bool {
|
||||
set.mu.RLock()
|
||||
@ -121,6 +163,24 @@ func (set *Set) Join(glue string) string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(gconv.String(k))
|
||||
if i != l-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *Set) String() string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
@ -132,18 +192,14 @@ func (set *Set) Join(glue string) string {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
if i != l-1 {
|
||||
buffer.WriteString(glue)
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
i++
|
||||
}
|
||||
buffer.WriteByte(']')
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
func (set *Set) String() string {
|
||||
return "[" + set.Join(",") + "]"
|
||||
}
|
||||
|
||||
// LockFunc locks writing with callback function <f>.
|
||||
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) {
|
||||
set.mu.Lock()
|
||||
@ -64,6 +64,48 @@ func (set *IntSet) Add(item ...int) *IntSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *IntSet) AddIfNotExistFunc(item int, f func() int) *IntSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() int) *IntSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *IntSet) doAddWithLockCheck(item int, value interface{}) int {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() int); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value.(int)
|
||||
}
|
||||
}
|
||||
set.data[item] = struct{}{}
|
||||
return item
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *IntSet) Contains(item int) bool {
|
||||
set.mu.RLock()
|
||||
@ -126,7 +168,7 @@ func (set *IntSet) Join(glue string) string {
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *IntSet) String() string {
|
||||
return "[" + set.Join(",") + "]"
|
||||
}
|
||||
|
||||
@ -65,6 +65,48 @@ func (set *StrSet) Add(item ...string) *StrSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *StrSet) AddIfNotExistFunc(item string, f func() string) *StrSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() string) *StrSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() string); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value.(string)
|
||||
}
|
||||
}
|
||||
set.data[item] = struct{}{}
|
||||
return item
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *StrSet) Contains(item string) bool {
|
||||
set.mu.RLock()
|
||||
@ -119,11 +161,7 @@ func (set *StrSet) Join(glue string) string {
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
for k, _ := range set.data {
|
||||
if gstr.IsNumeric(k) {
|
||||
buffer.WriteString(k)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(k)
|
||||
if i != l-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -132,9 +170,21 @@ func (set *StrSet) Join(glue string) string {
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *StrSet) String() string {
|
||||
return "[" + set.Join(",") + "]"
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
|
||||
if i != l-1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
i++
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with callback function <f>.
|
||||
|
||||
@ -220,8 +220,8 @@ func TestSet_Join(t *testing.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add("a").Add(`"b"`).Add(`\c`)
|
||||
str1 := s1.Join(",")
|
||||
gtest.Assert(strings.Contains(str1, `\"b\"`), true)
|
||||
gtest.Assert(strings.Contains(str1, `\\c`), true)
|
||||
gtest.Assert(strings.Contains(str1, `"b"`), true)
|
||||
gtest.Assert(strings.Contains(str1, `\c`), true)
|
||||
gtest.Assert(strings.Contains(str1, `a`), true)
|
||||
})
|
||||
}
|
||||
@ -305,3 +305,43 @@ func TestSet_Json(t *testing.T) {
|
||||
gtest.Assert(a3.Contains("e"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.New(true)
|
||||
s.Add(1)
|
||||
gtest.Assert(s.Contains(1), true)
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFunc(2, func() interface{} {
|
||||
return 3
|
||||
})
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFunc(3, func() interface{} {
|
||||
return 4
|
||||
})
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
gtest.Assert(s.Contains(4), false)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.New(true)
|
||||
s.Add(1)
|
||||
gtest.Assert(s.Contains(1), true)
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFuncLock(2, func() interface{} {
|
||||
return 3
|
||||
})
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFuncLock(3, func() interface{} {
|
||||
return 4
|
||||
})
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
gtest.Assert(s.Contains(4), false)
|
||||
})
|
||||
}
|
||||
@ -259,3 +259,43 @@ func TestIntSet_Json(t *testing.T) {
|
||||
gtest.Assert(a2.Contains(5), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
gtest.Assert(s.Contains(1), true)
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFunc(2, func() int {
|
||||
return 3
|
||||
})
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFunc(3, func() int {
|
||||
return 4
|
||||
})
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
gtest.Assert(s.Contains(4), false)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
gtest.Assert(s.Contains(1), true)
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFuncLock(2, func() int {
|
||||
return 3
|
||||
})
|
||||
gtest.Assert(s.Contains(2), false)
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFuncLock(3, func() int {
|
||||
return 4
|
||||
})
|
||||
gtest.Assert(s.Contains(3), true)
|
||||
gtest.Assert(s.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -207,8 +207,8 @@ func TestStrSet_Join(t *testing.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s1.Add("a").Add(`"b"`).Add(`\c`)
|
||||
str1 := s1.Join(",")
|
||||
gtest.Assert(strings.Contains(str1, `\"b\"`), true)
|
||||
gtest.Assert(strings.Contains(str1, `\\c`), true)
|
||||
gtest.Assert(strings.Contains(str1, `"b"`), true)
|
||||
gtest.Assert(strings.Contains(str1, `\c`), true)
|
||||
gtest.Assert(strings.Contains(str1, `a`), true)
|
||||
})
|
||||
}
|
||||
@ -304,3 +304,43 @@ func TestStrSet_Json(t *testing.T) {
|
||||
gtest.Assert(a3.Contains("e"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
gtest.Assert(s.Contains("1"), true)
|
||||
gtest.Assert(s.Contains("2"), false)
|
||||
|
||||
s.AddIfNotExistFunc("2", func() string {
|
||||
return "3"
|
||||
})
|
||||
gtest.Assert(s.Contains("2"), false)
|
||||
gtest.Assert(s.Contains("3"), true)
|
||||
|
||||
s.AddIfNotExistFunc("3", func() string {
|
||||
return "4"
|
||||
})
|
||||
gtest.Assert(s.Contains("3"), true)
|
||||
gtest.Assert(s.Contains("4"), false)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
gtest.Assert(s.Contains("1"), true)
|
||||
gtest.Assert(s.Contains("2"), false)
|
||||
|
||||
s.AddIfNotExistFuncLock("2", func() string {
|
||||
return "3"
|
||||
})
|
||||
gtest.Assert(s.Contains("2"), false)
|
||||
gtest.Assert(s.Contains("3"), true)
|
||||
|
||||
s.AddIfNotExistFuncLock("3", func() string {
|
||||
return "4"
|
||||
})
|
||||
gtest.Assert(s.Contains("3"), true)
|
||||
gtest.Assert(s.Contains("4"), false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
@ -43,9 +42,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
defer f.Close()
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
@ -30,9 +29,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
defer f.Close()
|
||||
h := sha1.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
|
||||
@ -687,12 +687,16 @@ func (bs *dbBase) formatWhere(where interface{}, args []interface{}) (newWhere s
|
||||
if value == nil {
|
||||
buffer.WriteString(key)
|
||||
} else {
|
||||
// 支持key带操作符号
|
||||
// 支持key带操作符号,注意like也算是操作符号
|
||||
key = gstr.Trim(key)
|
||||
if gstr.Pos(key, "?") == -1 {
|
||||
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
|
||||
like := " like"
|
||||
if len(key) > len(like) && gstr.Equal(key[len(key)-len(like):], like) {
|
||||
buffer.WriteString(key + " ?")
|
||||
} else if key[len(key)-1] != '<' && key[len(key)-1] != '>' && key[len(key)-1] != '=' {
|
||||
buffer.WriteString(key + "=?")
|
||||
} else {
|
||||
buffer.WriteString(key + "?")
|
||||
buffer.WriteString(key + " ?")
|
||||
}
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
|
||||
@ -146,12 +146,7 @@ func convertParam(value interface{}) interface{} {
|
||||
// 格式化错误信息
|
||||
func formatError(err error, query string, args ...interface{}) error {
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
|
||||
errStr += fmt.Sprintf("DB QUERY: %s\n", query)
|
||||
if len(args) > 0 {
|
||||
errStr += fmt.Sprintf("DB PARAM: %v\n", args)
|
||||
}
|
||||
err = errors.New(errStr)
|
||||
return errors.New(fmt.Sprintf("%s, %s\n", err.Error(), bindArgsToQuery(query, args)))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -209,6 +204,9 @@ func bindArgsToQuery(query string, args []interface{}) string {
|
||||
newQuery, _ := gregex.ReplaceStringFunc(`\?`, query, func(s string) string {
|
||||
index++
|
||||
if len(args) > index {
|
||||
if args[index] == nil {
|
||||
return "null"
|
||||
}
|
||||
rv := reflect.ValueOf(args[index])
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
@ -217,7 +215,7 @@ func bindArgsToQuery(query string, args []interface{}) string {
|
||||
}
|
||||
switch kind {
|
||||
case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
|
||||
return "'" + gstr.QuoteMeta(gconv.String(args[index]), "'") + "'"
|
||||
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
|
||||
}
|
||||
return gconv.String(args[index])
|
||||
}
|
||||
|
||||
@ -282,8 +282,8 @@ func (md *Model) Batch(batch int) *Model {
|
||||
|
||||
// 查询缓存/清除缓存操作,需要注意的是,事务查询不支持缓存。
|
||||
// 当time < 0时表示清除缓存, time=0时表示不过期, time > 0时表示过期时间,time过期时间单位:秒;
|
||||
// name表示自定义的缓存名称,便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,清理缓存时可以给定清理的缓存名称进行精准清理。
|
||||
// name表示自定义的缓存名称(注意不要出现重复),便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,在特定的业务逻辑中清理缓存时可以给定缓存名称进行精准清理。
|
||||
func (md *Model) Cache(time int, name ...string) *Model {
|
||||
model := md.getModel()
|
||||
model.cacheTime = time
|
||||
|
||||
@ -25,7 +25,7 @@ func (r Record) Xml(rootTag ...string) string {
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// 将Record转换为Map,其中最主要的区别是里面的键值被强制转换为string类型,方便json处理
|
||||
// 将Record转换为Map类型
|
||||
func (r Record) Map() Map {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range r {
|
||||
@ -34,7 +34,7 @@ func (r Record) Map() Map {
|
||||
return m
|
||||
}
|
||||
|
||||
// 将Record转换为gmap.StrAnyMap类型
|
||||
// 将Record转换为常用的gmap.StrAnyMap类型
|
||||
func (r Record) GMap() *gmap.StrAnyMap {
|
||||
return gmap.NewStrAnyMapFrom(r.Map())
|
||||
}
|
||||
|
||||
@ -656,6 +656,16 @@ func Test_Model_Where(t *testing.T) {
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// map like
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(g.Map{
|
||||
"passport like": "user_1%",
|
||||
}).OrderBy("id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0].GMap().Get("id"), 1)
|
||||
gtest.Assert(result[1].GMap().Get("id"), 10)
|
||||
})
|
||||
// map + slice parameter
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(g.Map{
|
||||
|
||||
@ -75,6 +75,8 @@ func New(config Config) *Redis {
|
||||
pool: pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} {
|
||||
return &redis.Pool{
|
||||
IdleTimeout: config.IdleTimeout,
|
||||
MaxActive: config.MaxActive,
|
||||
MaxIdle: config.MaxIdle,
|
||||
MaxConnLifetime: config.MaxConnLifetime,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
|
||||
|
||||
@ -103,7 +103,10 @@ func Decode(data interface{}) (interface{}, error) {
|
||||
// The <v> should be a pointer type.
|
||||
func DecodeTo(data interface{}, v interface{}) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
|
||||
decoder.UseNumber()
|
||||
// Do not use number, it converts float64 to json.Number type,
|
||||
// which actually a string type. It causes converting issue for other data formats,
|
||||
// for example: yaml.
|
||||
//decoder.UseNumber()
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
|
||||
@ -184,7 +187,10 @@ func doLoadContent(dataType string, data []byte, safe ...bool) (*Json, error) {
|
||||
return nil, err
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.UseNumber()
|
||||
// Do not use number, it converts float64 to json.Number type,
|
||||
// which actually a string type. It causes converting issue for other data formats,
|
||||
// for example: yaml.
|
||||
//decoder.UseNumber()
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Load_JSON(t *testing.T) {
|
||||
func Test_Load_JSON1(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
@ -42,6 +42,19 @@ func Test_Load_JSON(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_JSON2(t *testing.T) {
|
||||
data := []byte(`{"n":123456789000000000000, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j, err := gjson.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789000000000000")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_XML(t *testing.T) {
|
||||
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
|
||||
// XML
|
||||
|
||||
@ -62,6 +62,14 @@ func (r *Request) GetDeleteInt(key string, def ...interface{}) int {
|
||||
return r.GetDeleteVar(key, def...).Int()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetDeleteVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetDeleteVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteInts(key string, def ...interface{}) []int {
|
||||
return r.GetDeleteVar(key, def...).Ints()
|
||||
}
|
||||
@ -70,6 +78,14 @@ func (r *Request) GetDeleteUint(key string, def ...interface{}) uint {
|
||||
return r.GetDeleteVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetDeleteVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetDeleteVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
func (r *Request) GetDeleteFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetDeleteVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
@ -95,6 +95,14 @@ func (r *Request) GetPostInt(key string, def ...interface{}) int {
|
||||
return r.GetPostVar(key, def...).Int()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetPostVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetPostVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostInts(key string, def ...interface{}) []int {
|
||||
return r.GetPostVar(key, def...).Ints()
|
||||
}
|
||||
@ -103,6 +111,14 @@ func (r *Request) GetPostUint(key string, def ...interface{}) uint {
|
||||
return r.GetPostVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetPostVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetPostVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetPostVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
@ -61,6 +61,14 @@ func (r *Request) GetPutInt(key string, def ...interface{}) int {
|
||||
return r.GetPutVar(key, def...).Int()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetPutVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetPutVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutInts(key string, def ...interface{}) []int {
|
||||
return r.GetPutVar(key, def...).Ints()
|
||||
}
|
||||
@ -69,6 +77,14 @@ func (r *Request) GetPutUint(key string, def ...interface{}) uint {
|
||||
return r.GetPutVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetPutVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetPutVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
func (r *Request) GetPutFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetPutVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
@ -66,6 +66,14 @@ func (r *Request) GetQueryInt(key string, def ...interface{}) int {
|
||||
return r.GetQueryVar(key, def...).Int()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetQueryVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetQueryVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryInts(key string, def ...interface{}) []int {
|
||||
return r.GetQueryVar(key, def...).Ints()
|
||||
}
|
||||
@ -74,6 +82,14 @@ func (r *Request) GetQueryUint(key string, def ...interface{}) uint {
|
||||
return r.GetQueryVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetQueryVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetQueryVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
func (r *Request) GetQueryFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetQueryVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
@ -15,33 +15,33 @@ import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// 中间件对象
|
||||
// Middleware is the plugin for request handling.
|
||||
type Middleware struct {
|
||||
served bool // 是否带有请求服务函数,用以识别是否404
|
||||
request *Request // 请求对象
|
||||
served bool // Is the request served, which is used for checking response status 404.
|
||||
request *Request // The request object pointer.
|
||||
}
|
||||
|
||||
// 执行下一个请求流程处理函数
|
||||
// Next calls the next workflow handler.
|
||||
func (m *Middleware) Next() {
|
||||
item := (*handlerParsedItem)(nil)
|
||||
loop := true
|
||||
for loop {
|
||||
// 是否停止请求执行
|
||||
// Check whether the request is exited.
|
||||
if m.request.IsExited() || m.request.handlerIndex >= len(m.request.handlers) {
|
||||
return
|
||||
break
|
||||
}
|
||||
item = m.request.handlers[m.request.handlerIndex]
|
||||
m.request.handlerIndex++
|
||||
// 中间件执行时不执行钩子函数,由另外的逻辑进行控制
|
||||
// Filter the HOOK handlers, which are designed to be called in another standalone procedure.
|
||||
if item.handler.itemType == gHANDLER_TYPE_HOOK {
|
||||
continue
|
||||
}
|
||||
// 路由参数赋值
|
||||
// Router values switching.
|
||||
for k, v := range item.values {
|
||||
m.request.routerMap[k] = v
|
||||
}
|
||||
m.request.Router = item.handler.router
|
||||
// 执行函数处理
|
||||
|
||||
gutil.TryCatch(func() {
|
||||
switch item.handler.itemType {
|
||||
case gHANDLER_TYPE_CONTROLLER:
|
||||
@ -98,8 +98,8 @@ func (m *Middleware) Next() {
|
||||
niceCallFunc(func() {
|
||||
item.handler.itemFunc(m.request)
|
||||
})
|
||||
// 中间件默认不会进一步执行,
|
||||
// 需要内部调用Next方法决定是否进一步执行,以便于请求流程控制。
|
||||
// It does not continue calling next middleware after another middleware done.
|
||||
// There should be a "Next" function to be called in the middleware in order to manage the workflow.
|
||||
loop = false
|
||||
}
|
||||
}, func(exception interface{}) {
|
||||
@ -107,4 +107,12 @@ func (m *Middleware) Next() {
|
||||
m.request.Response.WriteStatus(http.StatusInternalServerError, exception)
|
||||
})
|
||||
}
|
||||
// Check the http status code after all handler and middleware done.
|
||||
if m.request.Response.Status == 0 {
|
||||
if m.request.Middleware.served || m.request.Response.buffer.Len() > 0 {
|
||||
m.request.Response.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
m.request.Response.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +67,14 @@ func (r *Request) GetRequestInt(key string, def ...interface{}) int {
|
||||
return r.GetRequestVar(key, def...).Int()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetRequestVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetRequestVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestInts(key string, def ...interface{}) []int {
|
||||
return r.GetRequestVar(key, def...).Ints()
|
||||
}
|
||||
@ -75,6 +83,14 @@ func (r *Request) GetRequestUint(key string, def ...interface{}) uint {
|
||||
return r.GetRequestVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetRequestVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetRequestVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
func (r *Request) GetRequestFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetRequestVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// 服务端请求返回对象。
|
||||
// 注意该对象并没有实现http.ResponseWriter接口,而是依靠ghttp.ResponseWriter实现。
|
||||
// Response is the writer for response buffer.
|
||||
// Note that it implements the http.ResponseWriter interface with buffering feature.
|
||||
type Response struct {
|
||||
*ResponseWriter // Underlying ResponseWriter.
|
||||
Server *Server // Parent server.
|
||||
@ -29,7 +29,7 @@ type Response struct {
|
||||
Request *Request // According request.
|
||||
}
|
||||
|
||||
// 创建一个ghttp.Response对象指针
|
||||
// newResponse creates and returns a new Response object.
|
||||
func newResponse(s *Server, w http.ResponseWriter) *Response {
|
||||
r := &Response{
|
||||
Server: s,
|
||||
@ -42,12 +42,12 @@ func newResponse(s *Server, w http.ResponseWriter) *Response {
|
||||
return r
|
||||
}
|
||||
|
||||
// 返回信息,任何变量自动转换为bytes
|
||||
// Write writes <content> to the response buffer.
|
||||
func (r *Response) Write(content ...interface{}) {
|
||||
if len(content) == 0 {
|
||||
return
|
||||
}
|
||||
if r.Status == 0 && r.Request.hasServeHandler {
|
||||
if r.Status == 0 {
|
||||
r.Status = http.StatusOK
|
||||
}
|
||||
for _, v := range content {
|
||||
@ -62,27 +62,32 @@ func (r *Response) Write(content ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// 返回信息,支持自定义format格式
|
||||
// WriteOver overwrites the response buffer with <content>.
|
||||
func (r *Response) WriteOver(content ...interface{}) {
|
||||
r.ClearBuffer()
|
||||
r.Write(content...)
|
||||
}
|
||||
|
||||
// Writef writes the response with fmt.Sprintf.
|
||||
func (r *Response) Writef(format string, params ...interface{}) {
|
||||
r.Write(fmt.Sprintf(format, params...))
|
||||
}
|
||||
|
||||
// 返回信息,末尾增加换行标识符"\n"
|
||||
// Writef writes the response with <content> and new line.
|
||||
func (r *Response) Writeln(content ...interface{}) {
|
||||
if len(content) == 0 {
|
||||
r.Write("\n")
|
||||
return
|
||||
}
|
||||
content = append(content, "\n")
|
||||
r.Write(content...)
|
||||
r.Write(append(content, "\n")...)
|
||||
}
|
||||
|
||||
// 返回信息,末尾增加换行标识符"\n"
|
||||
// Writefln writes the response with fmt.Sprintf and new line.
|
||||
func (r *Response) Writefln(format string, params ...interface{}) {
|
||||
r.Writeln(fmt.Sprintf(format, params...))
|
||||
}
|
||||
|
||||
// 返回JSON
|
||||
// WriteJson writes <content> to the response with JSON format.
|
||||
func (r *Response) WriteJson(content interface{}) error {
|
||||
if b, err := json.Marshal(content); err != nil {
|
||||
return err
|
||||
@ -93,7 +98,8 @@ func (r *Response) WriteJson(content interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 返回JSONP
|
||||
// WriteJson writes <content> to the response with JSONP format.
|
||||
// Note that there should be a "callback" parameter in the request for JSONP format.
|
||||
func (r *Response) WriteJsonP(content interface{}) error {
|
||||
if b, err := json.Marshal(content); err != nil {
|
||||
return err
|
||||
@ -112,7 +118,7 @@ func (r *Response) WriteJsonP(content interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 返回XML
|
||||
// WriteJson writes <content> to the response with XML format.
|
||||
func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
|
||||
if b, err := gparser.VarToXml(content, rootTag...); err != nil {
|
||||
return err
|
||||
@ -123,36 +129,21 @@ func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 返回HTTP Code状态码
|
||||
// WriteStatus writes HTTP <status> and <content> to the response.
|
||||
func (r *Response) WriteStatus(status int, content ...interface{}) {
|
||||
if r.buffer.Len() == 0 {
|
||||
// 状态码注册回调函数处理
|
||||
if status != http.StatusOK {
|
||||
if f := r.Request.Server.getStatusHandler(status, r.Request); f != nil {
|
||||
niceCallFunc(func() {
|
||||
f(r.Request)
|
||||
})
|
||||
// 防止多次设置(http: multiple response.WriteHeader calls)
|
||||
if r.Status == 0 {
|
||||
r.WriteHeader(status)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if r.Header().Get("Content-Type") == "" {
|
||||
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
//r.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
}
|
||||
if len(content) > 0 {
|
||||
r.Write(content...)
|
||||
} else {
|
||||
r.Write(http.StatusText(status))
|
||||
}
|
||||
}
|
||||
r.WriteHeader(status)
|
||||
if len(content) > 0 {
|
||||
r.Write(content...)
|
||||
} else {
|
||||
r.Write(http.StatusText(status))
|
||||
}
|
||||
if r.Header().Get("Content-Type") == "" {
|
||||
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
//r.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
}
|
||||
}
|
||||
|
||||
// 静态文件处理
|
||||
// ServeFile serves the file to the response.
|
||||
func (r *Response) ServeFile(path string, allowIndex ...bool) {
|
||||
serveFile := (*staticServeFile)(nil)
|
||||
if file := gres.Get(path); file != nil {
|
||||
@ -171,7 +162,7 @@ func (r *Response) ServeFile(path string, allowIndex ...bool) {
|
||||
r.Server.serveFile(r.Request, serveFile, allowIndex...)
|
||||
}
|
||||
|
||||
// 静态文件下载处理
|
||||
// ServeFileDownload serves file as file downloading to the response.
|
||||
func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
serveFile := (*staticServeFile)(nil)
|
||||
downloadName := ""
|
||||
@ -203,46 +194,45 @@ func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
r.Server.serveFile(r.Request, serveFile)
|
||||
}
|
||||
|
||||
// 返回location标识,引导客户端跳转。
|
||||
// 注意这里要先把设置的cookie输出,否则会被忽略。
|
||||
// RedirectTo redirects client to another location.
|
||||
func (r *Response) RedirectTo(location string) {
|
||||
r.Header().Set("Location", location)
|
||||
r.WriteHeader(http.StatusFound)
|
||||
r.Request.Exit()
|
||||
}
|
||||
|
||||
// 返回location标识,引导客户端跳转到来源页面
|
||||
// RedirectBack redirects client back to referer.
|
||||
func (r *Response) RedirectBack() {
|
||||
r.RedirectTo(r.Request.GetReferer())
|
||||
}
|
||||
|
||||
// 获取当前缓冲区中的数据
|
||||
// BufferString returns the buffer content as []byte.
|
||||
func (r *Response) Buffer() []byte {
|
||||
return r.buffer.Bytes()
|
||||
}
|
||||
|
||||
// 获取当前缓冲区中的数据(string)
|
||||
// BufferString returns the buffer content as string.
|
||||
func (r *Response) BufferString() string {
|
||||
return r.buffer.String()
|
||||
}
|
||||
|
||||
// 获取当前缓冲区中的数据大小
|
||||
// BufferLength returns the length of the buffer content.
|
||||
func (r *Response) BufferLength() int {
|
||||
return r.buffer.Len()
|
||||
}
|
||||
|
||||
// 手动设置缓冲区内容
|
||||
// SetBuffer overwrites the buffer with <data>.
|
||||
func (r *Response) SetBuffer(data []byte) {
|
||||
r.buffer.Reset()
|
||||
r.buffer.Write(data)
|
||||
}
|
||||
|
||||
// 清空缓冲区内容
|
||||
// ClearBuffer clears the response buffer.
|
||||
func (r *Response) ClearBuffer() {
|
||||
r.buffer.Reset()
|
||||
}
|
||||
|
||||
// 输出缓冲区数据到客户端.
|
||||
// Output outputs the buffer content to the client.
|
||||
func (r *Response) Output() {
|
||||
r.Header().Set("Server", r.Server.config.ServerAgent)
|
||||
//r.handleGzip()
|
||||
|
||||
@ -16,9 +16,15 @@ import (
|
||||
|
||||
// Custom ResponseWriter, which is used for controlling the output buffer.
|
||||
type ResponseWriter struct {
|
||||
Status int // HTTP status.
|
||||
writer http.ResponseWriter // The underlying ResponseWriter.
|
||||
buffer *bytes.Buffer // The output buffer.
|
||||
Status int // HTTP status.
|
||||
writer http.ResponseWriter // The underlying ResponseWriter.
|
||||
buffer *bytes.Buffer // The output buffer.
|
||||
wroteHeader bool // Is header wrote, avoiding error: superfluous/multiple response.WriteHeader call.
|
||||
}
|
||||
|
||||
// RawWriter returns the underlying ResponseWriter.
|
||||
func (w *ResponseWriter) RawWriter() http.ResponseWriter {
|
||||
return w.writer
|
||||
}
|
||||
|
||||
// Header implements the interface function of http.ResponseWriter.Header.
|
||||
@ -44,9 +50,13 @@ func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
|
||||
// OutputBuffer outputs the buffer to client.
|
||||
func (w *ResponseWriter) OutputBuffer() {
|
||||
if w.Status != 0 {
|
||||
if w.Status != 0 && !w.wroteHeader {
|
||||
w.writer.WriteHeader(w.Status)
|
||||
}
|
||||
// Default status text output.
|
||||
if w.Status != http.StatusOK && w.buffer.Len() == 0 {
|
||||
w.buffer.WriteString(http.StatusText(w.Status))
|
||||
}
|
||||
if w.buffer.Len() > 0 {
|
||||
w.writer.Write(w.buffer.Bytes())
|
||||
w.buffer.Reset()
|
||||
|
||||
23
net/ghttp/ghttp_server_error_logger.go
Normal file
23
net/ghttp/ghttp_server_error_logger.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
// errorLogger is the error logging logger for underlying net/http.Server.
|
||||
type errorLogger struct {
|
||||
logger *glog.Logger
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface.
|
||||
func (l *errorLogger) Write(p []byte) (n int, err error) {
|
||||
l.logger.Skip(1).Error(string(bytes.TrimRight(p, "\r\n")))
|
||||
return len(p), nil
|
||||
}
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -53,6 +54,7 @@ func (s *Server) newHttpServer(itemFunc string) *http.Server {
|
||||
WriteTimeout: s.config.WriteTimeout,
|
||||
IdleTimeout: s.config.IdleTimeout,
|
||||
MaxHeaderBytes: s.config.MaxHeaderBytes,
|
||||
ErrorLog: log.New(&errorLogger{logger: s.logger}, "", 0),
|
||||
}
|
||||
server.SetKeepAlivesEnabled(s.config.KeepAlive)
|
||||
return server
|
||||
|
||||
@ -66,15 +66,6 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
// 设置请求完成时间
|
||||
request.LeaveTime = gtime.Microsecond()
|
||||
// 如果没有产生异常状态,那么设置返回状态为200
|
||||
if request.Response.Status == 0 {
|
||||
if request.Middleware.served || request.Response.buffer.Len() > 0 {
|
||||
request.Response.Status = http.StatusOK
|
||||
} else {
|
||||
request.Response.WriteStatus(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// error log
|
||||
if request.error != nil {
|
||||
s.handleErrorLog(request.error, request)
|
||||
@ -84,7 +75,6 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleErrorLog(gerror.Newf("%v", exception), request)
|
||||
}
|
||||
}
|
||||
|
||||
// access log
|
||||
s.handleAccessLog(request)
|
||||
}()
|
||||
@ -104,12 +94,10 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// 动态服务检索
|
||||
if serveFile == nil || serveFile.dir {
|
||||
request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)
|
||||
}
|
||||
request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)
|
||||
|
||||
// 判断最终对该请求提供的服务方式
|
||||
if serveFile != nil && serveFile.dir && request.handlers != nil {
|
||||
if serveFile != nil && serveFile.dir && request.hasServeHandler {
|
||||
request.isFileRequest = false
|
||||
}
|
||||
|
||||
@ -122,7 +110,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
// 静态服务
|
||||
s.serveFile(request, serveFile)
|
||||
} else {
|
||||
if request.hasServeHandler {
|
||||
if len(request.handlers) > 0 {
|
||||
// 动态服务
|
||||
request.Middleware.Next()
|
||||
} else {
|
||||
@ -133,7 +121,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if len(request.Response.Header()) == 0 &&
|
||||
request.Response.Status == 0 &&
|
||||
request.Response.BufferLength() == 0 {
|
||||
request.Response.WriteStatus(http.StatusNotFound)
|
||||
request.Response.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,6 +137,25 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if !request.IsExited() {
|
||||
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
|
||||
}
|
||||
|
||||
// HTTP status checking.
|
||||
if request.Response.Status == 0 {
|
||||
if request.Middleware.served || request.Response.buffer.Len() > 0 {
|
||||
request.Response.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
request.Response.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
// HTTP status handler.
|
||||
if request.Response.Status != http.StatusOK {
|
||||
if f := s.getStatusHandler(request.Response.Status, request); f != nil {
|
||||
// Call custom status handler.
|
||||
niceCallFunc(func() {
|
||||
f(request)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 设置Session Id到Cookie中
|
||||
if request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() {
|
||||
request.Cookie.SetSessionId(request.Session.Id())
|
||||
@ -167,18 +174,18 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 查找静态文件的绝对路径
|
||||
func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
// 优先查找URI映射关系
|
||||
var file *gres.File
|
||||
var path string
|
||||
var dir bool
|
||||
// Firstly search the StaticPaths mapping.
|
||||
if len(s.config.StaticPaths) > 0 {
|
||||
for _, item := range s.config.StaticPaths {
|
||||
if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) {
|
||||
// 防止类似 /static/style 映射到 /static/style.css 的情况
|
||||
// To avoid case like: /static/style -> /static/style.css
|
||||
if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
|
||||
continue
|
||||
}
|
||||
// 优先检索资源管理器
|
||||
// Firstly searching resource manager.
|
||||
file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
|
||||
if file != nil {
|
||||
return &staticServeFile{
|
||||
@ -186,7 +193,7 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
dir: file.FileInfo().IsDir(),
|
||||
}
|
||||
}
|
||||
// 其次检索文件系统
|
||||
// Secondly searching the file system.
|
||||
path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
|
||||
if path != "" {
|
||||
return &staticServeFile{
|
||||
@ -242,6 +249,7 @@ func (s *Server) serveFile(r *Request, f *staticServeFile, allowIndex ...bool) {
|
||||
}
|
||||
} else {
|
||||
info := f.file.FileInfo()
|
||||
r.Response.wroteHeader = true
|
||||
http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), f.file)
|
||||
}
|
||||
return
|
||||
@ -253,6 +261,11 @@ func (s *Server) serveFile(r *Request, f *staticServeFile, allowIndex ...bool) {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Clear the response buffer before file serving.
|
||||
// It ignores all custom buffer content and uses the file content.
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
info, _ := file.Stat()
|
||||
if info.IsDir() {
|
||||
if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
|
||||
@ -261,7 +274,8 @@ func (s *Server) serveFile(r *Request, f *staticServeFile, allowIndex ...bool) {
|
||||
r.Response.WriteStatus(http.StatusForbidden)
|
||||
}
|
||||
} else {
|
||||
http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), file)
|
||||
r.Response.wroteHeader = true
|
||||
http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
net/ghttp/ghttp_unit_client_test.go
Normal file
42
net/ghttp/ghttp_unit_client_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Client_Basic(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/hello", func(r *ghttp.Request) {
|
||||
r.Response.Write("hello")
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
url := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(url)
|
||||
|
||||
gtest.Assert(ghttp.GetContent(""), ``)
|
||||
gtest.Assert(client.GetContent("/hello"), `hello`)
|
||||
|
||||
_, err := ghttp.Post("")
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
@ -33,8 +33,7 @@ func Test_Cookie(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
|
||||
@ -8,6 +8,9 @@ package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
@ -46,14 +49,13 @@ func Test_BindMiddleware_Basic1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test"), "1342")
|
||||
gtest.Assert(client.GetContent("/test/test"), "57test86")
|
||||
})
|
||||
}
|
||||
@ -87,15 +89,14 @@ func Test_BindMiddleware_Basic2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test"), "Not Found")
|
||||
gtest.Assert(client.PutContent("/test"), "Not Found")
|
||||
gtest.Assert(client.PutContent("/test"), "1342")
|
||||
gtest.Assert(client.PostContent("/test"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test/test"), "test")
|
||||
gtest.Assert(client.PutContent("/test/test"), "test")
|
||||
@ -103,6 +104,153 @@ func Test_BindMiddleware_Basic2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BindMiddleware_Must_Be_Called(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
r.Middleware.Next()
|
||||
})
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("2")
|
||||
})
|
||||
g.ALL("/test", func(r *ghttp.Request) {
|
||||
r.Response.Write("test")
|
||||
})
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "12")
|
||||
gtest.Assert(client.GetContent("/test"), "1test2")
|
||||
gtest.Assert(client.PutContent("/test/none"), "12")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Middleware_With_Static(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("2")
|
||||
})
|
||||
g.ALL("/user/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.SetServerRoot(gfile.Join(gdebug.CallerDirectory(), "testdata", "static1"))
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "index")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
gtest.Assert(client.GetContent("/none"), "12")
|
||||
gtest.Assert(client.GetContent("/user/list"), "1list2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Middleware_Status(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
r.Response.WriteOver(r.Response.Status)
|
||||
})
|
||||
g.ALL("/user/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "404")
|
||||
gtest.Assert(client.GetContent("/user/list"), "200")
|
||||
|
||||
resp, err := client.Get("/")
|
||||
defer resp.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp.StatusCode, 404)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Middleware_Hook_With_Static(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
a := garray.New(true)
|
||||
s.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
a.Append(1)
|
||||
fmt.Println("HOOK_BEFORE_SERVE")
|
||||
r.Response.Write("a")
|
||||
})
|
||||
g.Hook("/*", ghttp.HOOK_AFTER_SERVE, func(r *ghttp.Request) {
|
||||
a.Append(1)
|
||||
fmt.Println("HOOK_AFTER_SERVE")
|
||||
r.Response.Write("b")
|
||||
})
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("2")
|
||||
})
|
||||
g.ALL("/user/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
s.SetPort(p)
|
||||
//s.SetDumpRouteMap(false)
|
||||
s.SetServerRoot(gfile.Join(gdebug.CallerDirectory(), "testdata", "static1"))
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
// The length assert sometimes fails, so I added time.Sleep here for debug purpose.
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "index")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(a.Len(), 2)
|
||||
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(a.Len(), 4)
|
||||
|
||||
gtest.Assert(client.GetContent("/none"), "a12b")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(a.Len(), 6)
|
||||
|
||||
gtest.Assert(client.GetContent("/user/list"), "a1list2b")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(a.Len(), 8)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BindMiddleware_Status(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -117,8 +265,7 @@ func Test_BindMiddleware_Status(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -151,13 +298,12 @@ func Test_BindMiddlewareDefault_Basic1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/"), "1342")
|
||||
gtest.Assert(client.GetContent("/test/test"), "13test42")
|
||||
})
|
||||
}
|
||||
@ -183,15 +329,14 @@ func Test_BindMiddlewareDefault_Basic2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.PutContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test/test"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/"), "1342")
|
||||
gtest.Assert(client.PutContent("/"), "1342")
|
||||
gtest.Assert(client.GetContent("/test/test"), "1342")
|
||||
gtest.Assert(client.PutContent("/test/test"), "13test42")
|
||||
})
|
||||
}
|
||||
@ -215,13 +360,12 @@ func Test_BindMiddlewareDefault_Basic3(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/"), "12")
|
||||
gtest.Assert(client.GetContent("/test/test"), "1test2")
|
||||
})
|
||||
}
|
||||
@ -245,13 +389,12 @@ func Test_BindMiddlewareDefault_Basic4(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/"), "21")
|
||||
gtest.Assert(client.GetContent("/test/test"), "2test1")
|
||||
})
|
||||
}
|
||||
@ -275,13 +418,12 @@ func Test_BindMiddlewareDefault_Basic5(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/"), "12")
|
||||
gtest.Assert(client.GetContent("/test/test"), "12test")
|
||||
})
|
||||
}
|
||||
@ -300,8 +442,7 @@ func Test_BindMiddlewareDefault_Status(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -352,18 +493,17 @@ func Test_BindMiddlewareDefault_Basic6(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "13100Object Index20042")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/init"), "1342")
|
||||
gtest.Assert(client.GetContent("/shut"), "1342")
|
||||
gtest.Assert(client.GetContent("/index"), "13100Object Index20042")
|
||||
gtest.Assert(client.GetContent("/show"), "13100Object Show20042")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "1342")
|
||||
})
|
||||
}
|
||||
|
||||
@ -400,13 +540,12 @@ func Test_Hook_Middleware_Basic1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "acbd")
|
||||
gtest.Assert(client.GetContent("/"), "ac1342bd")
|
||||
gtest.Assert(client.GetContent("/test/test"), "ac13test42bd")
|
||||
})
|
||||
}
|
||||
@ -438,13 +577,13 @@ func Test_Middleware_CORSAndAuth(t *testing.T) {
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/list"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/list", "token=123456"), "list")
|
||||
})
|
||||
|
||||
@ -94,8 +94,7 @@ func Test_Params_Json(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -56,8 +56,7 @@ func Test_Params_Struct(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -222,8 +222,7 @@ func Test_Params_Basic(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -40,8 +40,7 @@ func Test_Router_Basic(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -67,8 +66,7 @@ func Test_Router_Method(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -116,8 +114,7 @@ func Test_Router_Status(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -164,8 +161,7 @@ func Test_Router_CustomStatusHandler(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -191,8 +187,7 @@ func Test_Router_404(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -69,8 +69,7 @@ func Test_Router_ControllerRest(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -53,8 +53,7 @@ func Test_Router_Controller1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -84,8 +83,7 @@ func Test_Router_Controller2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -111,8 +109,7 @@ func Test_Router_ControllerMethod(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -42,8 +42,7 @@ func Test_Router_DomainBasic(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -86,8 +85,7 @@ func Test_Router_DomainMethod(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -186,8 +184,7 @@ func Test_Router_DomainStatus(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -278,8 +275,7 @@ func Test_Router_DomainCustomStatusHandler(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -316,8 +312,7 @@ func Test_Router_Domain404(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -69,8 +69,7 @@ func Test_Router_DomainControllerRest(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -51,8 +51,7 @@ func Test_Router_DomainController1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -102,8 +101,7 @@ func Test_Router_DomainController2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -156,8 +154,7 @@ func Test_Router_DomainControllerMethod(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -64,8 +64,7 @@ func Test_Router_DomainObjectRest(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -47,8 +47,7 @@ func Test_Router_DomainObject1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -97,8 +96,7 @@ func Test_Router_DomainObject2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -149,8 +147,7 @@ func Test_Router_DomainObjectMethod(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -35,8 +35,7 @@ func Test_Router_Exit(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -74,8 +73,7 @@ func Test_Router_ExitHook(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -113,8 +111,7 @@ func Test_Router_ExitAll(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -61,23 +61,23 @@ func Test_Router_Group_Group(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2"), "12")
|
||||
gtest.Assert(client.GetContent("/api.v2/test"), "1test2")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook"), "hook any")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook/name"), "hook namehook any")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook/name/any"), "hook any")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook"), "hook any12")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook/name"), "hook namehook any12")
|
||||
gtest.Assert(client.GetContent("/api.v2/hook/name/any"), "hook any12")
|
||||
gtest.Assert(client.GetContent("/api.v2/order/list"), "1list2")
|
||||
gtest.Assert(client.GetContent("/api.v2/order/update"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2/order/update"), "12")
|
||||
gtest.Assert(client.PutContent("/api.v2/order/update"), "1update2")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/drop"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/drop"), "12")
|
||||
gtest.Assert(client.DeleteContent("/api.v2/user/drop"), "1drop2")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/edit"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/edit"), "12")
|
||||
gtest.Assert(client.PostContent("/api.v2/user/edit"), "1edit2")
|
||||
gtest.Assert(client.GetContent("/api.v2/user/info"), "1info2")
|
||||
})
|
||||
|
||||
@ -35,7 +35,7 @@ func Test_Router_Group_Hook1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -64,7 +64,7 @@ func Test_Router_Group_Hook2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -95,7 +95,7 @@ func Test_Router_Group_Hook3(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -112,7 +112,7 @@ func Test_Router_GroupRest(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -90,7 +90,7 @@ func Test_Router_GroupBasic1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -138,7 +138,7 @@ func Test_Router_Basic2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -174,7 +174,7 @@ func Test_Router_GroupBuildInVar(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -33,8 +33,7 @@ func Test_Router_Hook_Basic(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -44,6 +43,50 @@ func Test_Router_Hook_Basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Hook_Fuzzy_Router(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
i := 1000
|
||||
pattern1 := "/:name/info"
|
||||
s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
r.SetParam("uid", i)
|
||||
i++
|
||||
},
|
||||
})
|
||||
s.BindHandler(pattern1, func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Get("uid"))
|
||||
})
|
||||
|
||||
pattern2 := "/{object}/list/{page}.java"
|
||||
s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) {
|
||||
r.Response.SetBuffer([]byte(
|
||||
fmt.Sprint(r.Get("object"), "&", r.Get("page"), "&", i),
|
||||
))
|
||||
},
|
||||
})
|
||||
s.BindHandler(pattern2, func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Router.Uri)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/john"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/john/info"), "1000")
|
||||
gtest.Assert(client.GetContent("/john/info"), "1001")
|
||||
gtest.Assert(client.GetContent("/john/list/1.java"), "john&1&1002")
|
||||
gtest.Assert(client.GetContent("/john/list/2.java"), "john&2&1002")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Hook_Priority(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -71,8 +114,7 @@ func Test_Router_Hook_Priority(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -106,8 +148,7 @@ func Test_Router_Hook_Multi(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -32,8 +32,7 @@ func Test_NameToUri_FullName(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
@ -54,8 +53,7 @@ func Test_NameToUri_AllLower(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
@ -76,8 +74,7 @@ func Test_NameToUri_Camel(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
@ -98,8 +95,7 @@ func Test_NameToUri_Default(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
|
||||
@ -64,8 +64,7 @@ func Test_Router_ObjectRest(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -48,8 +48,7 @@ func Test_Router_Object1(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -79,8 +78,7 @@ func Test_Router_Object2(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -106,8 +104,7 @@ func Test_Router_ObjectMethod(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -36,8 +36,7 @@ func Test_Session_Cookie(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
@ -84,8 +83,7 @@ func Test_Session_Header(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -128,7 +126,7 @@ func Test_Session_StorageFile(t *testing.T) {
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
@ -152,7 +150,7 @@ func Test_Session_StorageFile(t *testing.T) {
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -4,11 +4,13 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// 静态文件服务测试
|
||||
// static service testing.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -32,7 +34,7 @@ func Test_Static_ServerRoot(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -51,7 +53,7 @@ func Test_Static_ServerRoot(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -60,6 +62,27 @@ func Test_Static_ServerRoot(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_ServerRoot_Security(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetServerRoot(gfile.Join(gdebug.CallerDirectory(), "testdata", "static1"))
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "index")
|
||||
gtest.Assert(client.GetContent("/index.htm"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index.html"), "index")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
gtest.Assert(client.GetContent("/../main.html"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/..%2Fmain.html"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_Folder_Forbidden(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
@ -71,7 +94,7 @@ func Test_Static_Folder_Forbidden(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -93,7 +116,7 @@ func Test_Static_IndexFolder(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -116,7 +139,7 @@ func Test_Static_IndexFiles1(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -138,7 +161,7 @@ func Test_Static_IndexFiles2(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -162,7 +185,7 @@ func Test_Static_AddSearchPath1(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -186,7 +209,7 @@ func Test_Static_AddSearchPath2(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -210,7 +233,7 @@ func Test_Static_AddStaticPath(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -235,7 +258,7 @@ func Test_Static_AddStaticPath_Priority(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
@ -262,7 +285,7 @@ func Test_Static_Rewrite(t *testing.T) {
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
|
||||
@ -41,8 +41,7 @@ func Test_WebSocket(t *testing.T) {
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
conn, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("ws://127.0.0.1:%d/ws", p), nil)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
1
net/ghttp/testdata/main.html
vendored
Normal file
1
net/ghttp/testdata/main.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
main
|
||||
1
net/ghttp/testdata/static1/index.html
vendored
Normal file
1
net/ghttp/testdata/static1/index.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
index
|
||||
1
net/ghttp/testdata/static1/test.html
vendored
Normal file
1
net/ghttp/testdata/static1/test.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
test
|
||||
@ -13,8 +13,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// TCP connection object.
|
||||
@ -208,9 +206,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
}
|
||||
@ -220,9 +216,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
}
|
||||
|
||||
@ -10,8 +10,6 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -77,9 +75,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
}
|
||||
@ -161,9 +157,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gpool"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// 链接池链接对象
|
||||
@ -120,9 +119,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
}
|
||||
@ -132,9 +129,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
}
|
||||
|
||||
@ -8,8 +8,6 @@ package gtcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据
|
||||
@ -46,9 +44,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
}
|
||||
@ -58,9 +54,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
}
|
||||
|
||||
@ -10,8 +10,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// Conn handles the UDP connection.
|
||||
@ -183,9 +181,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
}
|
||||
@ -195,9 +191,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
}
|
||||
|
||||
@ -6,9 +6,8 @@
|
||||
|
||||
// Package gspath implements file index and search for folders.
|
||||
//
|
||||
// 搜索目录管理,
|
||||
// 可以添加搜索目录,按照添加的优先级进行文件检索,并在内部进行高效缓存处理(可选)。
|
||||
// 注意:当开启缓存功能后,在新增/删除文件时,会存在检索延迟。
|
||||
// It searches file internally with high performance in order by the directory adding sequence.
|
||||
// Note that: If caching feature enabled, there would be a searching delay after adding/deleting files.
|
||||
package gspath
|
||||
|
||||
import (
|
||||
@ -24,25 +23,24 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// 文件目录搜索管理对象
|
||||
// SPath manages the path searching feature.
|
||||
type SPath struct {
|
||||
paths *garray.StrArray // 搜索路径,按照优先级进行排序
|
||||
cache *gmap.StrStrMap // 搜索结果缓存map(如果未nil表示未启用缓存功能)
|
||||
paths *garray.StrArray // The searching directories array.
|
||||
cache *gmap.StrStrMap // Searching cache map, it is not enabled if it's nil.
|
||||
}
|
||||
|
||||
// 文件搜索缓存项
|
||||
// SPathCacheItem is a cache item for searching.
|
||||
type SPathCacheItem struct {
|
||||
path string // 文件/目录绝对路径
|
||||
isDir bool // 是否目录
|
||||
path string // Absolute path for file/dir.
|
||||
isDir bool // Is directory or not.
|
||||
}
|
||||
|
||||
var (
|
||||
// 单个目录路径对应的SPath对象指针,用于路径检索对象复用
|
||||
pathsMap = gmap.NewStrAnyMap(true)
|
||||
pathsCacheMap = gmap.NewStrAnyMap(true)
|
||||
// Path to searching object mapping, used for instance management.
|
||||
pathsMap = gmap.NewStrAnyMap(true)
|
||||
)
|
||||
|
||||
// 创建一个搜索对象
|
||||
// New creates and returns a new path searching manager.
|
||||
func New(path string, cache bool) *SPath {
|
||||
sp := &SPath{
|
||||
paths: garray.NewStrArray(true),
|
||||
@ -58,7 +56,10 @@ func New(path string, cache bool) *SPath {
|
||||
return sp
|
||||
}
|
||||
|
||||
// 创建/获取一个单例的搜索对象, root必须为目录的绝对路径
|
||||
// Get creates and returns a instance of searching manager for given path.
|
||||
// The parameter <cache> specifies whether using cache feature for this manager.
|
||||
// If cache feature is enabled, it asynchronously and recursively scans the path
|
||||
// and updates all sub files/folders to the cache using package gfsnotify.
|
||||
func Get(root string, cache bool) *SPath {
|
||||
if root == "" {
|
||||
root = "/"
|
||||
@ -68,17 +69,29 @@ func Get(root string, cache bool) *SPath {
|
||||
}).(*SPath)
|
||||
}
|
||||
|
||||
// 检索root目录(必须为绝对路径)下面的name文件的绝对路径,indexFiles用于指定当检索到的结果为目录时,同时检索是否存在这些indexFiles文件
|
||||
// Search searches file <name> under path <root>.
|
||||
// The parameter <root> should be a absolute path. It will not automatically
|
||||
// convert <root> to absolute path for performance reason.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
func Search(root string, name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
return Get(root, false).Search(name, indexFiles...)
|
||||
}
|
||||
|
||||
// 检索root目录(必须为绝对路径)下面的name文件的绝对路径,indexFiles用于指定当检索到的结果为目录时,同时检索是否存在这些indexFiles文件
|
||||
// SearchWithCache searches file <name> under path <root> with cache feature enabled.
|
||||
// The parameter <root> should be a absolute path. It will not automatically
|
||||
// convert <root> to absolute path for performance reason.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
func SearchWithCache(root string, name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
return Get(root, true).Search(name, indexFiles...)
|
||||
}
|
||||
|
||||
// 设置搜索路径,只保留当前设置项,其他搜索路径被清空
|
||||
// Set deletes all other searching directories and sets the searching directory for this manager.
|
||||
func (sp *SPath) Set(path string) (realPath string, err error) {
|
||||
realPath = gfile.RealPath(path)
|
||||
if realPath == "" {
|
||||
@ -90,7 +103,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) {
|
||||
if realPath == "" {
|
||||
return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
|
||||
}
|
||||
// 设置的搜索路径必须为目录
|
||||
// The set path must be a directory.
|
||||
if gfile.IsDir(realPath) {
|
||||
realPath = strings.TrimRight(realPath, gfile.Separator)
|
||||
if sp.paths.Search(realPath) != -1 {
|
||||
@ -111,7 +124,8 @@ func (sp *SPath) Set(path string) (realPath string, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加搜索路径
|
||||
// Add adds more searching directory to the manager.
|
||||
// The manager will search file in added order.
|
||||
func (sp *SPath) Add(path string) (realPath string, err error) {
|
||||
realPath = gfile.RealPath(path)
|
||||
if realPath == "" {
|
||||
@ -123,10 +137,10 @@ func (sp *SPath) Add(path string) (realPath string, err error) {
|
||||
if realPath == "" {
|
||||
return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
|
||||
}
|
||||
// 添加的搜索路径必须为目录
|
||||
// The added path must be a directory.
|
||||
if gfile.IsDir(realPath) {
|
||||
//fmt.Println("gspath:", realPath, sp.paths.Search(realPath))
|
||||
// 如果已经添加则不再添加
|
||||
// It will not add twice for the same directory.
|
||||
if sp.paths.Search(realPath) < 0 {
|
||||
realPath = strings.TrimRight(realPath, gfile.Separator)
|
||||
sp.paths.Append(realPath)
|
||||
@ -139,20 +153,26 @@ func (sp *SPath) Add(path string) (realPath string, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 给定的name只是相对文件路径,找不到该文件时,返回空字符串;
|
||||
// 当给定indexFiles时,如果name是一个目录,那么会进一步检索其下对应的indexFiles文件是否存在,存在则返回indexFile绝对路径;
|
||||
// 否则返回name目录绝对路径。
|
||||
// Search searches file <name> in the manager.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
// 不使用缓存
|
||||
// No cache enabled.
|
||||
if sp.cache == nil {
|
||||
sp.paths.LockFunc(func(array []string) {
|
||||
path := ""
|
||||
for _, v := range array {
|
||||
path = v + gfile.Separator + name
|
||||
path = gfile.Join(v, name)
|
||||
if stat, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
filePath = path
|
||||
isDir = stat.IsDir()
|
||||
break
|
||||
path = gfile.Abs(path)
|
||||
// Security check: the result file path must be under the searching directory.
|
||||
if len(path) >= len(v) && path[:len(v)] == v {
|
||||
filePath = path
|
||||
isDir = stat.IsDir()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -172,7 +192,7 @@ func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isD
|
||||
}
|
||||
return
|
||||
}
|
||||
// 使用缓存功能
|
||||
// Using cache feature.
|
||||
name = sp.formatCacheName(name)
|
||||
if v := sp.cache.Get(name); v != "" {
|
||||
filePath, isDir = sp.parseCacheValue(v)
|
||||
@ -190,8 +210,8 @@ func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isD
|
||||
return
|
||||
}
|
||||
|
||||
// 从搜索路径中移除指定的文件,这样该文件无法给搜索。
|
||||
// path可以是绝对路径,也可以相对路径。
|
||||
// Remove deletes the <path> from cache files of the manager.
|
||||
// The parameter <path> can be either a absolute path or just a relative file name.
|
||||
func (sp *SPath) Remove(path string) {
|
||||
if sp.cache == nil {
|
||||
return
|
||||
@ -208,12 +228,12 @@ func (sp *SPath) Remove(path string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 返回当前对象搜索目录路径列表
|
||||
// Paths returns all searching directories.
|
||||
func (sp *SPath) Paths() []string {
|
||||
return sp.paths.Slice()
|
||||
}
|
||||
|
||||
// 返回当前对象缓存的所有路径列表
|
||||
// AllPaths returns all paths cached in the manager.
|
||||
func (sp *SPath) AllPaths() []string {
|
||||
if sp.cache == nil {
|
||||
return nil
|
||||
@ -225,7 +245,7 @@ func (sp *SPath) AllPaths() []string {
|
||||
return paths
|
||||
}
|
||||
|
||||
// 当前的搜索路径数量
|
||||
// Size returns the count of the searching directories.
|
||||
func (sp *SPath) Size() int {
|
||||
return sp.paths.Len()
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// 递归添加目录下的文件
|
||||
// updateCacheByPath adds all files under <path> recursively.
|
||||
func (sp *SPath) updateCacheByPath(path string) {
|
||||
if sp.cache == nil {
|
||||
return
|
||||
@ -26,7 +26,9 @@ func (sp *SPath) updateCacheByPath(path string) {
|
||||
sp.addToCache(path, path)
|
||||
}
|
||||
|
||||
// 格式化name返回符合规范的缓存名称,分隔符号统一为'/',且前缀必须以'/'开头(类似HTTP URI).
|
||||
// formatCacheName formats <name> with following rules:
|
||||
// 1. The separator is unified to char '/'.
|
||||
// 2. The name should be started with '/' (similar as HTTP URI).
|
||||
func (sp *SPath) formatCacheName(name string) string {
|
||||
if runtime.GOOS != "linux" {
|
||||
name = gstr.Replace(name, "\\", "/")
|
||||
@ -34,14 +36,14 @@ func (sp *SPath) formatCacheName(name string) string {
|
||||
return "/" + strings.Trim(name, "./")
|
||||
}
|
||||
|
||||
// 根据path计算出对应的缓存name, dirPath为检索根目录路径
|
||||
// nameFromPath converts <filePath> to cache name.
|
||||
func (sp *SPath) nameFromPath(filePath, rootPath string) string {
|
||||
name := gstr.Replace(filePath, rootPath, "")
|
||||
name = sp.formatCacheName(name)
|
||||
return name
|
||||
}
|
||||
|
||||
// 按照一定数据结构生成缓存的数据项字符串
|
||||
// makeCacheValue formats <filePath> to cache value.
|
||||
func (sp *SPath) makeCacheValue(filePath string, isDir bool) string {
|
||||
if isDir {
|
||||
return filePath + "_D_"
|
||||
@ -49,7 +51,7 @@ func (sp *SPath) makeCacheValue(filePath string, isDir bool) string {
|
||||
return filePath + "_F_"
|
||||
}
|
||||
|
||||
// 按照一定数据结构解析数据项字符串
|
||||
// parseCacheValue parses cache value to file path and type.
|
||||
func (sp *SPath) parseCacheValue(value string) (filePath string, isDir bool) {
|
||||
if value[len(value)-2 : len(value)-1][0] == 'F' {
|
||||
return value[:len(value)-3], false
|
||||
@ -57,12 +59,16 @@ func (sp *SPath) parseCacheValue(value string) (filePath string, isDir bool) {
|
||||
return value[:len(value)-3], true
|
||||
}
|
||||
|
||||
// 添加path到缓存中(递归)
|
||||
// addToCache adds an item to cache.
|
||||
// If <filePath> is a directory, it also adds its all sub files/directories recursively
|
||||
// to the cache.
|
||||
func (sp *SPath) addToCache(filePath, rootPath string) {
|
||||
// 首先添加自身
|
||||
// Add itself firstly.
|
||||
idDir := gfile.IsDir(filePath)
|
||||
sp.cache.SetIfNotExist(sp.nameFromPath(filePath, rootPath), sp.makeCacheValue(filePath, idDir))
|
||||
// 如果添加的是目录,那么需要递归添加
|
||||
sp.cache.SetIfNotExist(
|
||||
sp.nameFromPath(filePath, rootPath), sp.makeCacheValue(filePath, idDir),
|
||||
)
|
||||
// If it's a directory, it adds its all sub files/directories recursively.
|
||||
if idDir {
|
||||
if files, err := gfile.ScanDir(filePath, "*", true); err == nil {
|
||||
//fmt.Println("gspath add to cache:", filePath, files)
|
||||
@ -75,8 +81,11 @@ func (sp *SPath) addToCache(filePath, rootPath string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加文件目录监控(递归),当目录下的文件有更新时,会同时更新缓存。
|
||||
// 这里需要注意的点是,由于添加监听是递归添加的,那么假如删除一个目录,那么该目录下的文件(包括目录)也会产生一条删除事件,总共会产生N条事件。
|
||||
// addMonitorByPath adds gfsnotify monitoring recursively.
|
||||
// When the files under the directory are updated, the cache will be updated meanwhile.
|
||||
// Note that since the listener is added recursively, if you delete a directory, the files (including the directory)
|
||||
// under the directory will also generate delete events, which means it will generate N+1 events in total
|
||||
// if a directory deleted and there're N files under it.
|
||||
func (sp *SPath) addMonitorByPath(path string) {
|
||||
if sp.cache == nil {
|
||||
return
|
||||
@ -98,7 +107,7 @@ func (sp *SPath) addMonitorByPath(path string) {
|
||||
}, true)
|
||||
}
|
||||
|
||||
// 删除监听(递归)
|
||||
// removeMonitorByPath removes gfsnotify monitoring of <path> recursively.
|
||||
func (sp *SPath) removeMonitorByPath(path string) {
|
||||
if sp.cache == nil {
|
||||
return
|
||||
|
||||
@ -17,21 +17,21 @@ import (
|
||||
func TestSPath_Api(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
pwd := gfile.Pwd()
|
||||
root := pwd + gfile.Separator
|
||||
gfile.Create(root + "gf_tmp" + gfile.Separator + "gf.txt")
|
||||
defer gfile.Remove(root + "gf_tmp")
|
||||
root := pwd
|
||||
gfile.Create(gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
defer gfile.Remove(gfile.Join(root, "gf_tmp"))
|
||||
fp, isDir := gspath.Search(root, "gf_tmp")
|
||||
gtest.Assert(fp, root+"gf_tmp")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(isDir, true)
|
||||
fp, isDir = gspath.Search(root, "gf_tmp", "gf.txt")
|
||||
gtest.Assert(fp, root+"gf_tmp"+gfile.Separator+"gf.txt")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.Assert(isDir, false)
|
||||
|
||||
fp, isDir = gspath.SearchWithCache(root, "gf_tmp")
|
||||
gtest.Assert(fp, root+"gf_tmp")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(isDir, true)
|
||||
fp, isDir = gspath.SearchWithCache(root, "gf_tmp", "gf.txt")
|
||||
gtest.Assert(fp, root+"gf_tmp"+gfile.Separator+"gf.txt")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.Assert(isDir, false)
|
||||
})
|
||||
}
|
||||
@ -39,67 +39,72 @@ func TestSPath_Api(t *testing.T) {
|
||||
func TestSPath_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
pwd := gfile.Pwd()
|
||||
root := pwd + gfile.Separator
|
||||
gfile.Create(root + "gf_tmp" + gfile.Separator + "gf.txt")
|
||||
defer gfile.Remove(root + "gf_tmp")
|
||||
root := pwd
|
||||
|
||||
gfile.Create(gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
defer gfile.Remove(gfile.Join(root, "gf_tmp"))
|
||||
gsp := gspath.New(root, false)
|
||||
realPath, err := gsp.Add(root + "gf_tmp")
|
||||
realPath, err := gsp.Add(gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(realPath, root+"gf_tmp")
|
||||
gtest.Assert(realPath, gfile.Join(root, "gf_tmp"))
|
||||
realPath, err = gsp.Add("gf_tmp1")
|
||||
gtest.Assert(err != nil, true)
|
||||
gtest.Assert(realPath, "")
|
||||
realPath, err = gsp.Add(root + "gf_tmp" + gfile.Separator + "gf.txt")
|
||||
realPath, err = gsp.Add(gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.Assert(err != nil, true)
|
||||
gtest.Assert(realPath, "")
|
||||
gsp.Remove("gf_tmp1")
|
||||
gtest.Assert(gsp.Size(), 2)
|
||||
gtest.Assert(len(gsp.Paths()), 2)
|
||||
gtest.Assert(len(gsp.AllPaths()), 0)
|
||||
realPath, err = gsp.Set(root + "gf_tmp1")
|
||||
realPath, err = gsp.Set(gfile.Join(root, "gf_tmp1"))
|
||||
gtest.Assert(err != nil, true)
|
||||
gtest.Assert(realPath, "")
|
||||
realPath, err = gsp.Set(root + "gf_tmp" + gfile.Separator + "gf.txt")
|
||||
gtest.Assert(err != nil, true)
|
||||
realPath, err = gsp.Set(gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(realPath, "")
|
||||
gsp.Set(root)
|
||||
|
||||
realPath, err = gsp.Set(root)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(realPath, root)
|
||||
|
||||
fp, isDir := gsp.Search("gf_tmp")
|
||||
gtest.Assert(fp, root+"gf_tmp")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(isDir, true)
|
||||
fp, isDir = gsp.Search("gf_tmp", "gf.txt")
|
||||
gtest.Assert(fp, root+"gf_tmp"+gfile.Separator+"gf.txt")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.Assert(isDir, false)
|
||||
fp, isDir = gsp.Search("/", "gf.txt")
|
||||
gtest.Assert(fp, root+gfile.Separator)
|
||||
gtest.Assert(fp, root)
|
||||
gtest.Assert(isDir, true)
|
||||
|
||||
gsp = gspath.New(root, true)
|
||||
realPath, err = gsp.Add(root + "gf_tmp")
|
||||
realPath, err = gsp.Add(gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(realPath, root+"gf_tmp")
|
||||
gtest.Assert(realPath, gfile.Join(root, "gf_tmp"))
|
||||
|
||||
gfile.Mkdir(root + "gf_tmp1")
|
||||
gfile.Rename(root+"gf_tmp1", root+"gf_tmp2")
|
||||
gfile.Rename(root+"gf_tmp2", root+"gf_tmp1")
|
||||
defer gfile.Remove(root + "gf_tmp1")
|
||||
gfile.Mkdir(gfile.Join(root, "gf_tmp1"))
|
||||
gfile.Rename(gfile.Join(root, "gf_tmp1"), gfile.Join(root, "gf_tmp2"))
|
||||
gfile.Rename(gfile.Join(root, "gf_tmp2"), gfile.Join(root, "gf_tmp1"))
|
||||
defer gfile.Remove(gfile.Join(root, "gf_tmp1"))
|
||||
realPath, err = gsp.Add("gf_tmp1")
|
||||
gtest.Assert(err != nil, false)
|
||||
gtest.Assert(realPath, root+"gf_tmp1")
|
||||
gtest.Assert(realPath, gfile.Join(root, "gf_tmp1"))
|
||||
realPath, err = gsp.Add("gf_tmp3")
|
||||
gtest.Assert(err != nil, true)
|
||||
gtest.Assert(realPath, "")
|
||||
gsp.Remove(root + "gf_tmp")
|
||||
gsp.Remove(root + "gf_tmp1")
|
||||
gsp.Remove(root + "gf_tmp3")
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp"))
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp1"))
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp3"))
|
||||
gtest.Assert(gsp.Size(), 3)
|
||||
gtest.Assert(len(gsp.Paths()), 3)
|
||||
gsp.AllPaths()
|
||||
gsp.Set(root)
|
||||
fp, isDir = gsp.Search("gf_tmp")
|
||||
gtest.Assert(fp, root+"gf_tmp")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp"))
|
||||
gtest.Assert(isDir, true)
|
||||
fp, isDir = gsp.Search("gf_tmp", "gf.txt")
|
||||
gtest.Assert(fp, root+"gf_tmp"+gfile.Separator+"gf.txt")
|
||||
gtest.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
gtest.Assert(isDir, false)
|
||||
fp, isDir = gsp.Search("/", "gf.txt")
|
||||
gtest.Assert(fp, pwd)
|
||||
|
||||
@ -186,7 +186,7 @@ func SubStr(str string, start int, length ...int) (substr string) {
|
||||
// then the <suffix> will be appended to the result string.
|
||||
func StrLimit(str string, length int, suffix ...string) string {
|
||||
rs := []rune(str)
|
||||
if len(str) < length {
|
||||
if len(rs) < length {
|
||||
return str
|
||||
}
|
||||
addStr := "..."
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.9.7"
|
||||
const VERSION = "v1.9.9"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user