mirror of
https://gitee.com/johng/gf
synced 2026-06-18 14:27:31 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef4e128af7 | |||
| cb7c3a9fa4 | |||
| b0f859cc91 | |||
| 6d4da529ee | |||
| 6c7e536eeb | |||
| 36c2648be8 | |||
| a876b6133d | |||
| 03ff358da8 | |||
| 037f74c549 |
@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
array := []uint{1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6}
|
||||
for i := 0; i < len(array)-1; i++ {
|
||||
for j := i + 1; j < len(array); j++ {
|
||||
if array[i] == array[j] {
|
||||
array = append(array[:j], array[j+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(array)
|
||||
|
||||
}
|
||||
46
.example/container/garray/basic_array.go
Normal file
46
.example/container/garray/basic_array.go
Normal file
@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a int array, which is concurrent-unsafe in default.
|
||||
a := garray.NewIntArray()
|
||||
|
||||
// Appending items.
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// Get the length of the array.
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// Get the slice of the array.
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Get the item of specified index.
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// Insert after/before specified index.
|
||||
a.InsertAfter(9, 11)
|
||||
a.InsertBefore(10, 10)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Searching the item and returning the index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// Remove item of specified index.
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Clearing the array.
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建普通的int类型数组,并关闭默认的并发安全特性
|
||||
a := garray.NewIntArray(true)
|
||||
|
||||
// 添加数据项
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// 获取当前数组长度
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// 获取当前数据项列表
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 获取指定索引项
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// 在指定索引前插入数据项
|
||||
a.InsertAfter(9, 11)
|
||||
// 在指定索引后插入数据项
|
||||
a.InsertBefore(10, 10)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 修改指定索引的数据项
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 搜索数据项,返回搜索到的索引位置
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// 删除指定索引的数据项
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 并发安全,写锁操作
|
||||
a.LockFunc(func(array []int) {
|
||||
// 将末尾项改为100
|
||||
array[len(array)-1] = 100
|
||||
})
|
||||
|
||||
// 并发安全,读锁操作
|
||||
a.RLockFunc(func(array []int) {
|
||||
fmt.Println(array[len(array)-1])
|
||||
})
|
||||
|
||||
// 清空数组
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := garray.NewIntArray()
|
||||
a.Append(1, 2, 3)
|
||||
|
||||
v := a.Slice()
|
||||
v[0] = 4
|
||||
|
||||
g.Dump(a.Slice())
|
||||
g.Dump(v)
|
||||
}
|
||||
@ -1,20 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
array := garray.NewSortedStringArray()
|
||||
array.Add("1")
|
||||
array.Add("2")
|
||||
array.Add("3")
|
||||
array.Add("4")
|
||||
array.Add("5")
|
||||
array.Add("6")
|
||||
array.Add("7")
|
||||
array.Add("8")
|
||||
array := garray.NewSortedStrArray()
|
||||
array.Add("9")
|
||||
g.Dump(array.Slice())
|
||||
array.Add("8")
|
||||
array.Add("7")
|
||||
array.Add("6")
|
||||
array.Add("5")
|
||||
array.Add("4")
|
||||
array.Add("3")
|
||||
array.Add("2")
|
||||
array.Add("1")
|
||||
fmt.Println(array.Slice())
|
||||
// output:
|
||||
// [1 2 3 4 5 6 7 8 9]
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
array := garray.NewSortedStringArray()
|
||||
array.Add("/api/ctl/show")
|
||||
array.Add("/api/ctl/post")
|
||||
array.Add("/api/obj/rest")
|
||||
array.Add("/api/handler")
|
||||
array.Add("/api/obj/delete")
|
||||
array.Add("/api/obj/show")
|
||||
array.Add("/api/obj/my-show")
|
||||
array.Add("/api/*")
|
||||
array.Add("/api/ctl/rest")
|
||||
array.Add("/api/ctl/my-show")
|
||||
g.Dump(array.Slice())
|
||||
|
||||
fmt.Println(strings.Compare("/api/ctl/post", "/api/*"))
|
||||
fmt.Println(strings.Compare("/api/*", "/api/ctl/my-show"))
|
||||
}
|
||||
@ -8,13 +8,6 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
//m := gmap.New()
|
||||
//m.Set("k", "v")
|
||||
//m.Set("1", "2")
|
||||
//b, err := json.Marshal(m)
|
||||
//fmt.Println(err)
|
||||
//fmt.Println(string(b))
|
||||
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 2)
|
||||
m.Set(3, 4)
|
||||
|
||||
26
.example/container/gmap/gmap_map_clone_safe.go
Normal file
26
.example/container/gmap/gmap_map_clone_safe.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m1 := gmap.New()
|
||||
m1.Set("1", "1")
|
||||
|
||||
m2 := m1.Map()
|
||||
m2["2"] = "2"
|
||||
|
||||
g.Dump(m1.Clone())
|
||||
g.Dump(m2)
|
||||
//output:
|
||||
//{
|
||||
// "1": "1"
|
||||
//}
|
||||
//
|
||||
//{
|
||||
// "1": "1",
|
||||
// "2": "2"
|
||||
//}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := gmap.New()
|
||||
m.Set("1", "1")
|
||||
|
||||
m1 := m.Map()
|
||||
m1["2"] = "2"
|
||||
|
||||
g.Dump(m.Clone())
|
||||
g.Dump(m1)
|
||||
}
|
||||
@ -10,9 +10,9 @@ import (
|
||||
|
||||
func main() {
|
||||
array := g.Slice{2, 3, 1, 5, 4, 6, 8, 7, 9}
|
||||
hashMap := gmap.New(true)
|
||||
linkMap := gmap.NewLinkMap(true)
|
||||
treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)
|
||||
hashMap := gmap.New()
|
||||
linkMap := gmap.NewListMap()
|
||||
treeMap := gmap.NewTreeMap(gutil.ComparatorInt)
|
||||
for _, v := range array {
|
||||
hashMap.Set(v, v)
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 验证 map 的delete方法是否并发安全
|
||||
func main() {
|
||||
// 创建一个初始化的map
|
||||
m := make(map[int]int)
|
||||
for i := 0; i < 10000; i++ {
|
||||
m[i] = i
|
||||
}
|
||||
|
||||
fmt.Println("map size:", len(m))
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
ev := make(chan struct{}, 0)
|
||||
|
||||
// 创建10个并发的goroutine,使用ev控制并发开始事件,更容易模拟data race
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
<-ev
|
||||
fmt.Println("start")
|
||||
for i := 0; i < 10000; i++ {
|
||||
delete(m, i)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
close(ev)
|
||||
wg.Wait()
|
||||
}
|
||||
@ -30,8 +30,4 @@ func main() {
|
||||
s := new(Score)
|
||||
v.Struct(s)
|
||||
fmt.Println(s)
|
||||
|
||||
// 只读接口
|
||||
r := v.ReadOnly()
|
||||
fmt.Println(r.String())
|
||||
}
|
||||
|
||||
@ -519,7 +519,6 @@ func getQueriedSqls() {
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
fmt.Println("Func :", v.Func)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,9 +8,9 @@ import (
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetMaxIdleConns(10)
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetConnMaxLifetime(10)
|
||||
db.SetMaxIdleConnCount(10)
|
||||
db.SetMaxOpenConnCount(10)
|
||||
db.SetMaxConnLifetime(time.Minute)
|
||||
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
@ -520,7 +520,6 @@ func getQueriedSqls() {
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
fmt.Println("Func :", v.Func)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,47 +1,47 @@
|
||||
package main
|
||||
|
||||
//import (
|
||||
// _ "github.com/mattn/go-sqlite3"
|
||||
// "github.com/gogf/gf/database/gdb"
|
||||
// "github.com/gogf/gf/frame/g"
|
||||
// "fmt"
|
||||
//)
|
||||
//
|
||||
//func main() {
|
||||
// gdb.SetConfig(gdb.Config{
|
||||
// "default": gdb.ConfigGroup{
|
||||
// gdb.ConfigNode{
|
||||
// Name: "/tmp/my.db",
|
||||
// Type: "sqlite",
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// db := g.Database()
|
||||
// if db == nil {
|
||||
// panic("db create failed")
|
||||
// }
|
||||
// defer db.Close()
|
||||
//
|
||||
// // 创建表
|
||||
// sql := `CREATE TABLE user (
|
||||
// uid INT PRIMARY KEY NOT NULL,
|
||||
// name VARCHAR(30) NOT NULL
|
||||
// );`
|
||||
// if _, err := db.Exec(sql); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
// // 写入数据
|
||||
// result, err := db.Table("user").Data(g.Map{"uid" : 1, "name" : "john"}).Save()
|
||||
// if err == nil {
|
||||
// fmt.Println(result.RowsAffected())
|
||||
// } else {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
// // 删除表
|
||||
// sql = `DROP TABLE user;`
|
||||
// if _, err := db.Exec(sql); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//}
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gdb.SetConfig(gdb.Config{
|
||||
"default": gdb.ConfigGroup{
|
||||
gdb.ConfigNode{
|
||||
Name: "/tmp/my.db",
|
||||
Type: "sqlite",
|
||||
},
|
||||
},
|
||||
})
|
||||
db := g.DB()
|
||||
if db == nil {
|
||||
panic("db create failed")
|
||||
}
|
||||
|
||||
// 创建表
|
||||
sql := `CREATE TABLE user (
|
||||
uid INT PRIMARY KEY NOT NULL,
|
||||
name VARCHAR(30) NOT NULL
|
||||
);`
|
||||
if _, err := db.Exec(sql); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// 写入数据
|
||||
result, err := db.Table("user").Data(g.Map{"uid": 1, "name": "john"}).Save()
|
||||
if err == nil {
|
||||
fmt.Println(result.RowsAffected())
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// 删除表
|
||||
sql = `DROP TABLE user;`
|
||||
if _, err := db.Exec(sql); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/database/gredis"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
batchNumber := 1000
|
||||
redis1Config, err := gredis.ConfigFromStr("im-redis-slave:6379,9")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
redis2Config, err := gredis.ConfigFromStr("r-bp1f0a5d4efd8744.redis.rds.aliyuncs.com:6379,9")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gredis.SetConfig(redis1Config)
|
||||
|
||||
v, err := g.Redis().DoVar("keys", "*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
array := garray.NewStrArrayFrom(v.Strings())
|
||||
for {
|
||||
slice := array.PopLefts(batchNumber)
|
||||
if len(slice) > 0 {
|
||||
// `migrate %s %d "" 0 2000 copy replace auth %s keys %s`,
|
||||
params := g.Slice{
|
||||
redis2Config.Host,
|
||||
redis2Config.Port,
|
||||
"",
|
||||
redis2Config.Db,
|
||||
2000,
|
||||
"copy",
|
||||
"replace",
|
||||
"keys",
|
||||
}
|
||||
params = append(params, gconv.Interfaces(slice)...)
|
||||
fmt.Println(params)
|
||||
if gcmd.GetOpt("dryrun") == "0" {
|
||||
if v, err := g.Redis().DoVar("migrate", params...); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Println(v.String())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Println("done")
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package gbase64
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -9,26 +9,19 @@ import (
|
||||
|
||||
func main() {
|
||||
// 使用gbinary.Encoded对基本数据类型进行二进制打包
|
||||
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
fmt.Println(buffer)
|
||||
}
|
||||
fmt.Println(gbinary.Encode(18, 300, 1.01))
|
||||
|
||||
// 使用gbinary.Decode对整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,字长确定的类型,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64
|
||||
// 这里的1.01默认为float64类型(64位系统下)
|
||||
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
|
||||
buffer := gbinary.Encode(18, 300, 1.01)
|
||||
var i1 int8
|
||||
var i2 int16
|
||||
var f3 float64
|
||||
if err := gbinary.Decode(buffer, &i1, &i2, &f3); err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
var i1 int8
|
||||
var i2 int16
|
||||
var f3 float64
|
||||
if err := gbinary.Decode(buffer, &i1, &i2, &f3); err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
fmt.Println(i1, i2, f3)
|
||||
}
|
||||
fmt.Println(i1, i2, f3)
|
||||
}
|
||||
|
||||
// 编码/解析 int,自动识别变量长度
|
||||
|
||||
@ -15,6 +15,6 @@ func main() {
|
||||
}
|
||||
|
||||
redisCfg := new(RedisConfig)
|
||||
fmt.Println(g.Config().GetToStruct("redis", redisCfg))
|
||||
fmt.Println(g.Config().GetStruct("redis", redisCfg))
|
||||
fmt.Println(redisCfg)
|
||||
}
|
||||
|
||||
@ -15,4 +15,6 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(str)
|
||||
// output:
|
||||
// 花间一壶酒,独酌无相亲。
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ func getWithPattern1() {
|
||||
}
|
||||
}`
|
||||
|
||||
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
|
||||
@ -36,7 +36,7 @@ func getWithPattern2() {
|
||||
<body>Don't forget me this weekend!</body>
|
||||
</note>`
|
||||
|
||||
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
fmt.Println("Heading:", p.GetString("note.heading"))
|
||||
@ -52,7 +52,7 @@ func multiDots1() {
|
||||
},
|
||||
"users.count" : 101
|
||||
}`
|
||||
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
fmt.Println("Users Count:", p.Get("users.count"))
|
||||
@ -70,7 +70,7 @@ func multiDots2() {
|
||||
"count.type1" : 100
|
||||
}
|
||||
}`
|
||||
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
fmt.Println("Users Count:", p.Get("users.count.type1"))
|
||||
@ -88,7 +88,7 @@ func set1() {
|
||||
<list><title>gf article2</title><content>gf content2</content></list>
|
||||
<list><title>gf article3</title><content>gf content3</content></list>
|
||||
</article>`
|
||||
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
p.Set("article.list.0", nil)
|
||||
@ -105,7 +105,7 @@ func set2() {
|
||||
"count" : 100
|
||||
}
|
||||
}`
|
||||
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
|
||||
if p, e := gparser.LoadContent([]byte(data)); e != nil {
|
||||
glog.Error(e)
|
||||
} else {
|
||||
p.Set("users.count", 1)
|
||||
@ -116,7 +116,7 @@ func set2() {
|
||||
}
|
||||
|
||||
func makeXml1() {
|
||||
p := gparser.New()
|
||||
p := gparser.New(nil)
|
||||
p.Set("name", "john")
|
||||
p.Set("age", 18)
|
||||
p.Set("scores", map[string]int{
|
||||
@ -133,7 +133,7 @@ func makeJson1() {
|
||||
Id int `json:"id"`
|
||||
Price float32 `json:"price"`
|
||||
}
|
||||
p := gparser.New()
|
||||
p := gparser.New(nil)
|
||||
p.Set("orders.list.0", Order{1, 100})
|
||||
p.Set("orders.list.1", Order{2, 666})
|
||||
p.Set("orders.list.2", Order{3, 999.99})
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
@ -1,13 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/.example/frame/mvc/model/test"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.DB().SetDebug(true)
|
||||
user, err := test.ModelUser().One()
|
||||
g.Dump(err)
|
||||
g.Dump(user)
|
||||
}
|
||||
@ -6,7 +6,7 @@ import "github.com/gogf/gf/frame/g"
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.SetServerRoot("/Users/john/Temp")
|
||||
s.SetServerRoot("/Users/john/Downloads")
|
||||
s.AddSearchPath("/Users/john/Documents")
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := flock.NewFlock("/tmp/go-lock.lock")
|
||||
l.Lock()
|
||||
fmt.Printf("lock 1")
|
||||
l.Lock()
|
||||
fmt.Printf("lock 1")
|
||||
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gflock"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
"github.com/gogf/gf/os/gproc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := gflock.New("demo.lock")
|
||||
l.Lock()
|
||||
glog.Printf("locked by pid: %d", gproc.Pid())
|
||||
time.Sleep(10 * time.Second)
|
||||
l.UnLock()
|
||||
glog.Printf("unlocked by pid: %d", gproc.Pid())
|
||||
}
|
||||
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://github.com/gogf/gf#donators
|
||||
@ -13,6 +13,7 @@
|
||||
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00
|
||||
|[arden](https://github.com/arden)|alipay|¥10.00
|
||||
|[macnie](https://www.macnie.com)|wechat|¥100.00
|
||||
|lah|wechat|¥100.00
|
||||
|x*z|wechat|¥20.00
|
||||
|潘兄|wechat|¥100.00
|
||||
|Fly的狐狸|wechat|¥100.00
|
||||
|
||||
191
README.MD
191
README.MD
@ -38,7 +38,6 @@ golang version >= 1.10
|
||||
|
||||
# Quick Start
|
||||
|
||||
## Hello World
|
||||
```go
|
||||
package main
|
||||
|
||||
@ -55,196 +54,6 @@ func main() {
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Router & Middleware
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
|
||||
g.Middleware(func(r *ghttp.Request) {
|
||||
r.Response.Write("start")
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("end")
|
||||
})
|
||||
g.Group("/order", func(g *ghttp.RouterGroup) {
|
||||
g.GET("/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
g.Group("/user", func(g *ghttp.RouterGroup) {
|
||||
g.GET("/info", func(r *ghttp.Request) {
|
||||
r.Response.Write("info")
|
||||
})
|
||||
g.POST("/edit", func(r *ghttp.Request) {
|
||||
r.Response.Write("edit")
|
||||
})
|
||||
})
|
||||
g.Group("/hook", func(g *ghttp.RouterGroup) {
|
||||
g.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
g.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook name")
|
||||
})
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
## Multi ports & domains
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func Hello1(r *ghttp.Request) {
|
||||
r.Response.Write("127.0.0.1: Hello1!")
|
||||
}
|
||||
|
||||
func Hello2(r *ghttp.Request) {
|
||||
r.Response.Write("localhost: Hello2!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Domain("127.0.0.1").BindHandler("/", Hello1)
|
||||
s.Domain("localhost").BindHandler("/", Hello2)
|
||||
s.SetPort(8100, 8200, 8300)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Template Engine
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/template", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("index.tpl", g.Map{
|
||||
"id": 123,
|
||||
"name": "john",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## File Uploading
|
||||
```go
|
||||
func Upload(r *ghttp.Request) {
|
||||
if f, h, e := r.FormFile("upload-file"); e == nil {
|
||||
defer f.Close()
|
||||
name := gfile.Basename(h.Filename)
|
||||
buffer := make([]byte, h.Size)
|
||||
f.Read(buffer)
|
||||
gfile.PutBytes("/tmp/" + name, buffer)
|
||||
r.Response.Write(name + " uploaded successly")
|
||||
} else {
|
||||
r.Response.Write(e.Error())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ORM Operations
|
||||
|
||||
### 1. Retrieving instance
|
||||
```go
|
||||
db := g.DB()
|
||||
db := g.DB("user-center")
|
||||
```
|
||||
### 2. Chaining Operations
|
||||
|
||||
`Where + string`
|
||||
```go
|
||||
// SELECT * FROM user WHERE uid>1 LIMIT 0,10
|
||||
r, err := db.Table("user").Where("uid > ?", 1).Limit(0, 10).Select()
|
||||
|
||||
// SELECT uid,name FROM user WHERE uid>1 LIMIT 0,10
|
||||
r, err := db.Table("user").Fileds("uid,name").Where("uid > ?", 1).Limit(0, 10).Select()
|
||||
|
||||
// SELECT * FROM user WHERE uid=1
|
||||
r, err := db.Table("user").Where("u.uid=1",).One()
|
||||
r, err := db.Table("user").Where("u.uid", 1).One()
|
||||
r, err := db.Table("user").Where("u.uid=?", 1).One()
|
||||
// SELECT * FROM user WHERE (uid=1) AND (name='john')
|
||||
r, err := db.Table("user").Where("uid", 1).Where("name", "john").One()
|
||||
r, err := db.Table("user").Where("uid=?", 1).And("name=?", "john").One()
|
||||
// SELECT * FROM user WHERE (uid=1) OR (name='john')
|
||||
r, err := db.Table("user").Where("uid=?", 1).Or("name=?", "john").One()
|
||||
```
|
||||
`Where + map`
|
||||
```go
|
||||
// SELECT * FROM user WHERE uid=1 AND name='john'
|
||||
r, err := db.Table("user").Where(g.Map{"uid" : 1, "name" : "john"}).One()
|
||||
// SELECT * FROM user WHERE uid=1 AND age>18
|
||||
r, err := db.Table("user").Where(g.Map{"uid" : 1, "age>" : 18}).One()
|
||||
```
|
||||
`Where + struct/*struct`
|
||||
```go
|
||||
type User struct {
|
||||
Id int `json:"uid"`
|
||||
UserName string `gconv:"name"`
|
||||
}
|
||||
// SELECT * FROM user WHERE uid =1 AND name='john'
|
||||
r, err := db.Table("user").Where(User{ Id : 1, UserName : "john"}).One()
|
||||
// SELECT * FROM user WHERE uid =1
|
||||
r, err := db.Table("user").Where(&User{ Id : 1}).One()
|
||||
```
|
||||
### 3. Update & Delete
|
||||
```go
|
||||
// UPDATE user SET name='john guo' WHERE name='john'
|
||||
r, err := db.Table("user").Data(gdb.Map{"name" : "john guo"}).Where("name=?", "john").Update()
|
||||
r, err := db.Table("user").Data("name='john guo'").Where("name=?", "john").Update()
|
||||
// UPDATE user SET status=1 ORDER BY login_time asc LIMIT 10
|
||||
r, err := db.Table("user").Data("status", 1).OrderBy("login_time asc").Limit(10).Update
|
||||
|
||||
// DELETE FROM user WHERE uid=10
|
||||
r, err := db.Table("user").Where("uid=?", 10).Delete()
|
||||
// DELETE FROM user ORDER BY login_time asc LIMIT 10
|
||||
r, err := db.Table("user").OrderBy("login_time asc").Limit(10).Delete()
|
||||
```
|
||||
### 4. Insert & Replace & Save
|
||||
```go
|
||||
r, err := db.Table("user").Data(g.Map{"name": "john"}).Insert()
|
||||
r, err := db.Table("user").Data(g.Map{"uid": 10000, "name": "john"}).Replace()
|
||||
r, err := db.Table("user").Data(g.Map{"uid": 10001, "name": "john"}).Save()
|
||||
```
|
||||
### 5. Transaction
|
||||
```go
|
||||
if tx, err := db.Begin(); err == nil {
|
||||
r, err := tx.Save("user", g.Map{
|
||||
"uid" : 1,
|
||||
"name" : "john",
|
||||
})
|
||||
tx.Commit()
|
||||
}
|
||||
```
|
||||
### 6. Error Handling
|
||||
```go
|
||||
func GetOrderInfo(id int) (order *Order, err error) {
|
||||
err = g.DB().Table("order").Where("id", id).Struct(&order)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
[More Features...](https://goframe.org/start/index)
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ type DB interface {
|
||||
SetLogger(logger *glog.Logger)
|
||||
SetMaxIdleConnCount(n int)
|
||||
SetMaxOpenConnCount(n int)
|
||||
SetMaxConnLifetime(n int)
|
||||
SetMaxConnLifetime(d time.Duration)
|
||||
Tables() (tables []string, err error)
|
||||
TableFields(table string) (map[string]*TableField, error)
|
||||
|
||||
@ -126,7 +126,7 @@ type dbBase struct {
|
||||
logger *glog.Logger // 日志管理对象
|
||||
maxIdleConnCount int // 连接池最大限制的连接数
|
||||
maxOpenConnCount int // 连接池最大打开的连接数
|
||||
maxConnLifetime int // (单位秒)连接对象可重复使用的时间长度
|
||||
maxConnLifetime time.Duration // 连接对象可重复使用的时间长度
|
||||
}
|
||||
|
||||
// 执行的SQL对象
|
||||
|
||||
@ -9,6 +9,7 @@ package gdb
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/glog"
|
||||
|
||||
@ -27,20 +28,20 @@ type ConfigGroup []ConfigNode
|
||||
|
||||
// 数据库单项配置
|
||||
type ConfigNode struct {
|
||||
Host string // 地址
|
||||
Port string // 端口
|
||||
User string // 账号
|
||||
Pass string // 密码
|
||||
Name string // 数据库名称
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle
|
||||
Role string // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
|
||||
Debug bool // (可选)开启调试模式
|
||||
Weight int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
Charset string // (可选,默认为 utf8)编码,默认为 utf8
|
||||
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
MaxIdleConnCount int // (可选)连接池最大限制的连接数
|
||||
MaxOpenConnCount int // (可选)连接池最大打开的连接数
|
||||
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
|
||||
Host string // 地址
|
||||
Port string // 端口
|
||||
User string // 账号
|
||||
Pass string // 密码
|
||||
Name string // 数据库名称
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle
|
||||
Role string // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
|
||||
Debug bool // (可选)开启调试模式
|
||||
Weight int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
Charset string // (可选,默认为 utf8)编码,默认为 utf8
|
||||
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
MaxIdleConnCount int // (可选)连接池最大限制的连接数
|
||||
MaxOpenConnCount int // (可选)连接池最大打开的连接数
|
||||
MaxConnLifetime time.Duration // (可选)连接对象可重复使用的时间长度
|
||||
}
|
||||
|
||||
// 数据库配置包内对象
|
||||
@ -130,8 +131,8 @@ func (bs *dbBase) SetMaxOpenConnCount(n int) {
|
||||
|
||||
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
|
||||
// 如果 d <= 0 表示该链接会一直重复利用
|
||||
func (bs *dbBase) SetMaxConnLifetime(n int) {
|
||||
bs.maxConnLifetime = n
|
||||
func (bs *dbBase) SetMaxConnLifetime(d time.Duration) {
|
||||
bs.maxConnLifetime = d
|
||||
}
|
||||
|
||||
// 节点配置转换为字符串
|
||||
|
||||
@ -25,8 +25,13 @@ func Decode(dst []byte) ([]byte, error) {
|
||||
return src[:n], err
|
||||
}
|
||||
|
||||
// EncodeString encodes bytes with BASE64 algorithm.
|
||||
func EncodeString(src []byte) string {
|
||||
// EncodeString encodes string with BASE64 algorithm.
|
||||
func EncodeString(src string) string {
|
||||
return EncodeToString([]byte(src))
|
||||
}
|
||||
|
||||
// EncodeToString encodes bytes to string with BASE64 algorithm.
|
||||
func EncodeToString(src []byte) string {
|
||||
return string(Encode(src))
|
||||
}
|
||||
|
||||
@ -34,3 +39,9 @@ func EncodeString(src []byte) string {
|
||||
func DecodeString(str string) ([]byte, error) {
|
||||
return Decode([]byte(str))
|
||||
}
|
||||
|
||||
// DecodeString decodes string with BASE64 algorithm.
|
||||
func DecodeToString(str string) (string, error) {
|
||||
b, err := DecodeString(str)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
@ -12,11 +12,11 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
type testpair struct {
|
||||
type testPair struct {
|
||||
decoded, encoded string
|
||||
}
|
||||
|
||||
var pairs = []testpair{
|
||||
var pairs = []testPair{
|
||||
// RFC 3548 examples
|
||||
{"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"},
|
||||
{"\x14\xfb\x9c\x03\xd9", "FPucA9k="},
|
||||
@ -45,15 +45,20 @@ var pairs = []testpair{
|
||||
func TestBase64(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
for k := range pairs {
|
||||
// []byte
|
||||
// Encode
|
||||
gtest.Assert(gbase64.Encode([]byte(pairs[k].decoded)), []byte(pairs[k].encoded))
|
||||
e1, _ := gbase64.Decode([]byte(pairs[k].encoded))
|
||||
gtest.Assert(e1, []byte(pairs[k].decoded))
|
||||
gtest.Assert(gbase64.EncodeToString([]byte(pairs[k].decoded)), pairs[k].encoded)
|
||||
gtest.Assert(gbase64.EncodeString(pairs[k].decoded), pairs[k].encoded)
|
||||
|
||||
// string
|
||||
gtest.Assert(gbase64.EncodeString([]byte(pairs[k].decoded)), pairs[k].encoded)
|
||||
e2, _ := gbase64.DecodeString(pairs[k].encoded)
|
||||
gtest.Assert(e2, []byte(pairs[k].decoded))
|
||||
// Decode
|
||||
r1, _ := gbase64.Decode([]byte(pairs[k].encoded))
|
||||
gtest.Assert(r1, []byte(pairs[k].decoded))
|
||||
|
||||
r2, _ := gbase64.DecodeString(pairs[k].encoded)
|
||||
gtest.Assert(r2, []byte(pairs[k].decoded))
|
||||
|
||||
r3, _ := gbase64.DecodeToString(pairs[k].encoded)
|
||||
gtest.Assert(r3, pairs[k].decoded)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,25 +10,24 @@ package gyaml
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gf-third/yaml"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
yaml3 "github.com/gf-third/yaml/v3"
|
||||
)
|
||||
|
||||
func Encode(v interface{}) ([]byte, error) {
|
||||
return yaml3.Marshal(v)
|
||||
return yaml.Marshal(v)
|
||||
}
|
||||
|
||||
func Decode(v []byte) (interface{}, error) {
|
||||
var result map[string]interface{}
|
||||
if err := yaml3.Unmarshal(v, &result); err != nil {
|
||||
if err := yaml.Unmarshal(v, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gconv.MapDeep(result), nil
|
||||
}
|
||||
|
||||
func DecodeTo(v []byte, result interface{}) error {
|
||||
return yaml3.Unmarshal(v, result)
|
||||
return yaml.Unmarshal(v, result)
|
||||
}
|
||||
|
||||
func ToJson(v []byte) ([]byte, error) {
|
||||
|
||||
@ -215,10 +215,10 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
node.MaxConnLifetime = gconv.Duration(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxLifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
node.MaxConnLifetime = gconv.Duration(value)
|
||||
}
|
||||
// Parse link syntax.
|
||||
if node.LinkInfo != "" && node.Type == "" {
|
||||
|
||||
13
go.mod
13
go.mod
@ -1,21 +1,20 @@
|
||||
module github.com/gogf/gf
|
||||
|
||||
go 1.10
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/clbanning/mxj v1.8.4
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gf-third/mysql v1.4.2
|
||||
github.com/gf-third/yaml/v3 v3.0.0
|
||||
github.com/gofrs/flock v0.7.1 // indirect
|
||||
github.com/gf-third/yaml v1.0.1
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190916062342-6f856a90d556
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
github.com/theckman/go-flock v0.7.1
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 // indirect
|
||||
golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0 // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
google.golang.org/appengine v1.6.2 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
google.golang.org/appengine v1.6.3 // indirect
|
||||
)
|
||||
|
||||
@ -65,7 +65,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
// 会话处理
|
||||
request.Cookie = GetCookie(request)
|
||||
request.Session = s.sessionManager.New(request.GetSessionId())
|
||||
request.Response.request = request
|
||||
request.Response.Request = request
|
||||
request.Middleware = &Middleware{
|
||||
request: request,
|
||||
}
|
||||
@ -83,8 +83,8 @@ func (r *Request) WebSocket() (*WebSocket, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获得指定名称的参数字符串(Router/GET/POST),同 GetRequestString
|
||||
// 这是常用方法的简化别名
|
||||
// Get是GetRequest方法的别名,用于获得指定名称的参数值,注意键值可能是字符串、数组、Map类型。
|
||||
// 大多数场景下,你可能需要的是 GetString/GetVar。
|
||||
func (r *Request) Get(key string, def ...interface{}) interface{} {
|
||||
return r.GetRequest(key, def...)
|
||||
}
|
||||
@ -108,8 +108,8 @@ func (r *Request) GetRawString() string {
|
||||
}
|
||||
|
||||
// 获取原始json请求输入字符串,并解析为json对象
|
||||
func (r *Request) GetJson() *gjson.Json {
|
||||
return gjson.New(r.GetRaw())
|
||||
func (r *Request) GetJson() (*gjson.Json, error) {
|
||||
return gjson.LoadJson(r.GetRaw())
|
||||
}
|
||||
|
||||
func (r *Request) GetString(key string, def ...interface{}) string {
|
||||
|
||||
@ -23,7 +23,9 @@ func (r *Request) initRaw() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获得router、post或者get提交的参数,如果有同名参数,那么按照router->get->post优先级进行覆盖
|
||||
// 获得router、post或者get提交的参数值,如果有同名参数,
|
||||
// 那么按照 router->get->post->OtherHttpMethod 优先级进行覆盖。
|
||||
// 注意获得参数值可能是字符串、数组、Map三种类型。
|
||||
func (r *Request) GetRequest(key string, def ...interface{}) interface{} {
|
||||
v := r.GetRouterValue(key)
|
||||
if v == nil {
|
||||
|
||||
@ -23,22 +23,22 @@ import (
|
||||
// 服务端请求返回对象。
|
||||
// 注意该对象并没有实现http.ResponseWriter接口,而是依靠ghttp.ResponseWriter实现。
|
||||
type Response struct {
|
||||
ResponseWriter
|
||||
Server *Server // 所属Web Server
|
||||
Writer *ResponseWriter // ResponseWriter的别名
|
||||
request *Request // 关联的Request请求对象
|
||||
*ResponseWriter // Underlying ResponseWriter.
|
||||
Server *Server // Parent server.
|
||||
Writer *ResponseWriter // Alias of ResponseWriter.
|
||||
Request *Request // According request.
|
||||
}
|
||||
|
||||
// 创建一个ghttp.Response对象指针
|
||||
func newResponse(s *Server, w http.ResponseWriter) *Response {
|
||||
r := &Response{
|
||||
Server: s,
|
||||
ResponseWriter: ResponseWriter{
|
||||
ResponseWriter: w,
|
||||
buffer: bytes.NewBuffer(nil),
|
||||
ResponseWriter: &ResponseWriter{
|
||||
writer: w,
|
||||
buffer: bytes.NewBuffer(nil),
|
||||
},
|
||||
}
|
||||
r.Writer = &r.ResponseWriter
|
||||
r.Writer = r.ResponseWriter
|
||||
return r
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ func (r *Response) Write(content ...interface{}) {
|
||||
if len(content) == 0 {
|
||||
return
|
||||
}
|
||||
if r.Status == 0 && r.request.hasServeHandler {
|
||||
if r.Status == 0 && r.Request.hasServeHandler {
|
||||
r.Status = http.StatusOK
|
||||
}
|
||||
for _, v := range content {
|
||||
@ -99,7 +99,7 @@ func (r *Response) WriteJsonP(content interface{}) error {
|
||||
return err
|
||||
} else {
|
||||
//r.Header().Set("Content-Type", "application/json")
|
||||
if callback := r.request.GetString("callback"); callback != "" {
|
||||
if callback := r.Request.GetString("callback"); callback != "" {
|
||||
buffer := []byte(callback)
|
||||
buffer = append(buffer, byte('('))
|
||||
buffer = append(buffer, b...)
|
||||
@ -128,9 +128,9 @@ 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 {
|
||||
if f := r.Request.Server.getStatusHandler(status, r.Request); f != nil {
|
||||
niceCallFunc(func() {
|
||||
f(r.request)
|
||||
f(r.Request)
|
||||
})
|
||||
// 防止多次设置(http: multiple response.WriteHeader calls)
|
||||
if r.Status == 0 {
|
||||
@ -168,7 +168,7 @@ func (r *Response) ServeFile(path string, allowIndex ...bool) {
|
||||
}
|
||||
serveFile = &staticServeFile{path: path}
|
||||
}
|
||||
r.Server.serveFile(r.request, serveFile, allowIndex...)
|
||||
r.Server.serveFile(r.Request, serveFile, allowIndex...)
|
||||
}
|
||||
|
||||
// 静态文件下载处理
|
||||
@ -200,7 +200,7 @@ func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
r.Header().Set("Content-Type", "application/force-download")
|
||||
r.Header().Set("Accept-Ranges", "bytes")
|
||||
r.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s"`, downloadName))
|
||||
r.Server.serveFile(r.request, serveFile)
|
||||
r.Server.serveFile(r.Request, serveFile)
|
||||
}
|
||||
|
||||
// 返回location标识,引导客户端跳转。
|
||||
@ -208,12 +208,12 @@ func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
func (r *Response) RedirectTo(location string) {
|
||||
r.Header().Set("Location", location)
|
||||
r.WriteHeader(http.StatusFound)
|
||||
r.request.Exit()
|
||||
r.Request.Exit()
|
||||
}
|
||||
|
||||
// 返回location标识,引导客户端跳转到来源页面
|
||||
func (r *Response) RedirectBack() {
|
||||
r.RedirectTo(r.request.GetReferer())
|
||||
r.RedirectTo(r.Request.GetReferer())
|
||||
}
|
||||
|
||||
// 获取当前缓冲区中的数据
|
||||
|
||||
@ -32,12 +32,12 @@ func (r *Response) DefaultCORSOptions() CORSOptions {
|
||||
AllowOrigin: "*",
|
||||
AllowMethods: HTTP_METHODS,
|
||||
AllowCredentials: "true",
|
||||
AllowHeaders: "Origin, X-Requested-With, Content-Type, Accept, Key",
|
||||
AllowHeaders: "Origin,Content-Type,Accept,User-Agent,Cookie,Authorization,X-Auth-Token,X-Requested-With",
|
||||
MaxAge: 3628800,
|
||||
}
|
||||
if origin := r.Header().Get("Origin"); origin != "" {
|
||||
if origin := r.Request.Header.Get("Origin"); origin != "" {
|
||||
options.AllowOrigin = origin
|
||||
} else if referer := r.request.Referer(); referer != "" {
|
||||
} else if referer := r.Request.Referer(); referer != "" {
|
||||
if p := gstr.PosR(referer, "/", 6); p != -1 {
|
||||
options.AllowOrigin = referer[:p]
|
||||
} else {
|
||||
@ -75,9 +75,9 @@ func (r *Response) CORSAllowedOrigin(options CORSOptions) bool {
|
||||
if options.AllowDomain == nil {
|
||||
return true
|
||||
}
|
||||
origin := r.request.Header.Get("Origin")
|
||||
origin := r.Request.Header.Get("Origin")
|
||||
if origin == "" {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
parsed, err := url.Parse(origin)
|
||||
if err != nil {
|
||||
|
||||
@ -67,9 +67,9 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte
|
||||
if c := gins.Config(); c.FilePath() != "" {
|
||||
vars["Config"] = c.GetMap(".")
|
||||
}
|
||||
vars["Cookie"] = r.request.Cookie.Map()
|
||||
vars["Session"] = r.request.Session.Map()
|
||||
vars["Get"] = r.request.GetQueryMap()
|
||||
vars["Post"] = r.request.GetPostMap()
|
||||
vars["Get"] = r.Request.GetQueryMap()
|
||||
vars["Post"] = r.Request.GetPostMap()
|
||||
vars["Cookie"] = r.Request.Cookie.Map()
|
||||
vars["Session"] = r.Request.Session.Map()
|
||||
return vars
|
||||
}
|
||||
|
||||
@ -8,35 +8,47 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 自定义的ResponseWriter,用于写入流的控制
|
||||
// Custom ResponseWriter, which is used for controlling the output buffer.
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
Status int // http status
|
||||
buffer *bytes.Buffer // 缓冲区内容
|
||||
Status int // HTTP status.
|
||||
writer http.ResponseWriter // The underlying ResponseWriter.
|
||||
buffer *bytes.Buffer // The output buffer.
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法
|
||||
// Header implements the interface function of http.ResponseWriter.Header.
|
||||
func (w *ResponseWriter) Header() http.Header {
|
||||
return w.writer.Header()
|
||||
}
|
||||
|
||||
// Write implements the interface function of http.ResponseWriter.Write.
|
||||
func (w *ResponseWriter) Write(data []byte) (int, error) {
|
||||
w.buffer.Write(data)
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// 覆盖父级的WriteHeader方法, 这里只会记录Status做缓冲处理, 并不会立即输出到HEADER。
|
||||
// WriteHeader implements the interface of http.ResponseWriter.WriteHeader.
|
||||
func (w *ResponseWriter) WriteHeader(status int) {
|
||||
w.Status = status
|
||||
}
|
||||
|
||||
// 输出buffer数据到客户端.
|
||||
// Hijack implements the interface function of http.Hijacker.Hijack.
|
||||
func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.writer.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
// OutputBuffer outputs the buffer to client.
|
||||
func (w *ResponseWriter) OutputBuffer() {
|
||||
if w.Status != 0 {
|
||||
w.ResponseWriter.WriteHeader(w.Status)
|
||||
w.writer.WriteHeader(w.Status)
|
||||
}
|
||||
if w.buffer.Len() > 0 {
|
||||
w.ResponseWriter.Write(w.buffer.Bytes())
|
||||
w.writer.Write(w.buffer.Bytes())
|
||||
w.buffer.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,12 +151,12 @@ func (s *Server) SetConfigWithMap(m map[string]interface{}) {
|
||||
}
|
||||
|
||||
// 设置http server参数 - Addr
|
||||
func (s *Server) SetAddr(itemFunc string) {
|
||||
func (s *Server) SetAddr(address string) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
|
||||
return
|
||||
}
|
||||
s.config.Addr = itemFunc
|
||||
s.config.Addr = address
|
||||
}
|
||||
|
||||
// 设置http server参数 - Port
|
||||
@ -176,12 +176,12 @@ func (s *Server) SetPort(port ...int) {
|
||||
}
|
||||
|
||||
// 设置http server参数 - HTTPS Addr
|
||||
func (s *Server) SetHTTPSAddr(itemFunc string) {
|
||||
func (s *Server) SetHTTPSAddr(address string) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
|
||||
return
|
||||
}
|
||||
s.config.HTTPSAddr = itemFunc
|
||||
s.config.HTTPSAddr = address
|
||||
}
|
||||
|
||||
// 设置http server参数 - HTTPS Port
|
||||
|
||||
@ -12,6 +12,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/os/gres"
|
||||
@ -259,7 +261,6 @@ func (s *Server) serveFile(r *Request, f *staticServeFile, allowIndex ...bool) {
|
||||
r.Response.WriteStatus(http.StatusForbidden)
|
||||
}
|
||||
} else {
|
||||
// 读取文件内容返回, no buffer
|
||||
http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), file)
|
||||
}
|
||||
}
|
||||
@ -276,7 +277,11 @@ func (s *Server) listDir(r *Request, f http.File) {
|
||||
r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
}
|
||||
r.Response.Write(`<html>`)
|
||||
r.Response.Write(`<head></head>`)
|
||||
r.Response.Write(`<head>`)
|
||||
r.Response.Write(`<style>`)
|
||||
r.Response.Write(`body {font-family:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;}`)
|
||||
r.Response.Write(`</style>`)
|
||||
r.Response.Write(`</head>`)
|
||||
r.Response.Write(`<body>`)
|
||||
r.Response.Writef(`<h1>Index of %s</h1>`, r.URL.Path)
|
||||
r.Response.Writef(`<hr />`)
|
||||
@ -288,6 +293,7 @@ func (s *Server) listDir(r *Request, f http.File) {
|
||||
}
|
||||
name := ""
|
||||
size := ""
|
||||
prefix := gstr.TrimRight(r.URL.Path, "/")
|
||||
for _, file := range files {
|
||||
name = file.Name()
|
||||
size = gfile.FormatSize(file.Size())
|
||||
@ -296,9 +302,9 @@ func (s *Server) listDir(r *Request, f http.File) {
|
||||
size = "-"
|
||||
}
|
||||
r.Response.Write(`<tr>`)
|
||||
r.Response.Writef(`<td><a href="%s/%s">%s</a></td>`, r.URL.Path, name, ghtml.SpecialChars(name))
|
||||
r.Response.Writef(`<td><a href="%s/%s">%s</a></td>`, prefix, name, ghtml.SpecialChars(name))
|
||||
r.Response.Writef(`<td style="width:300px;text-align:center;">%s</td>`, gtime.New(file.ModTime()).ISO8601())
|
||||
r.Response.Writef(`<td style="width:80px;text-align:center;">%s</td>`, size)
|
||||
r.Response.Writef(`<td style="width:80px;text-align:right;">%s</td>`, size)
|
||||
r.Response.Write(`</tr>`)
|
||||
}
|
||||
r.Response.Write(`</table>`)
|
||||
|
||||
@ -133,7 +133,12 @@ func Test_Params_Basic(t *testing.T) {
|
||||
r.Response.Write(r.GetRaw())
|
||||
})
|
||||
s.BindHandler("/json", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetJson().Get("name"))
|
||||
j, err := r.GetJson()
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
r.Response.Write(j.Get("name"))
|
||||
})
|
||||
s.BindHandler("/struct", func(r *ghttp.Request) {
|
||||
if m := r.GetQueryMap(); len(m) > 0 {
|
||||
|
||||
@ -12,6 +12,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
@ -96,6 +98,7 @@ func Test_Static_IndexFolder(t *testing.T) {
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.AssertNE(client.GetContent("/"), "Forbidden")
|
||||
gtest.AssertNE(gstr.Pos(client.GetContent("/"), `<a href="/test.html"`), -1)
|
||||
gtest.Assert(client.GetContent("/index.html"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
|
||||
60
net/ghttp/ghttp_unit_websocket_test.go
Normal file
60
net/ghttp/ghttp_unit_websocket_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
// 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/gorilla/websocket"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_WebSocket(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/ws", func(r *ghttp.Request) {
|
||||
ws, err := r.WebSocket()
|
||||
if err != nil {
|
||||
r.Exit()
|
||||
}
|
||||
for {
|
||||
msgType, msg, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = ws.WriteMessage(msgType, msg); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(200 * 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)
|
||||
defer conn.Close()
|
||||
|
||||
msg := []byte("hello")
|
||||
err = conn.WriteMessage(websocket.TextMessage, msg)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
t, data, err := conn.ReadMessage()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(t, websocket.TextMessage)
|
||||
gtest.Assert(data, msg)
|
||||
})
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
// 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 gflock implements a concurrent-safe sync.Locker interface for file locking.
|
||||
package gflock
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
flock "github.com/theckman/go-flock"
|
||||
)
|
||||
|
||||
// File locker.
|
||||
type Locker struct {
|
||||
flock *flock.Flock // Underlying file locker.
|
||||
}
|
||||
|
||||
// New creates and returns a new file locker with given <file>.
|
||||
// The parameter <file> usually is a absolute file path.
|
||||
func New(file string) *Locker {
|
||||
dir := gfile.TempDir() + gfile.Separator + "gflock"
|
||||
if !gfile.Exists(dir) {
|
||||
_ = gfile.Mkdir(dir)
|
||||
}
|
||||
path := dir + gfile.Separator + file
|
||||
lock := flock.NewFlock(path)
|
||||
return &Locker{
|
||||
flock: lock,
|
||||
}
|
||||
}
|
||||
|
||||
// Path returns the file path of the locker.
|
||||
func (l *Locker) Path() string {
|
||||
return l.flock.Path()
|
||||
}
|
||||
|
||||
// IsLocked returns whether the locker is locked.
|
||||
func (l *Locker) IsLocked() bool {
|
||||
return l.flock.Locked()
|
||||
}
|
||||
|
||||
// IsRLocked returns whether the locker is rlocked.
|
||||
func (l *Locker) IsRLocked() bool {
|
||||
return l.flock.RLocked()
|
||||
}
|
||||
|
||||
// TryLock tries get the writing lock of the locker.
|
||||
// It returns true if success, or else returns false immediately.
|
||||
func (l *Locker) TryLock() bool {
|
||||
ok, _ := l.flock.TryLock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// TryRLock tries get the reading lock of the locker.
|
||||
// It returns true if success, or else returns false immediately.
|
||||
func (l *Locker) TryRLock() bool {
|
||||
ok, _ := l.flock.TryRLock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Lock is a blocking call to try and take an exclusive file lock. It will wait
|
||||
// until it is able to obtain the exclusive file lock. It's recommended that
|
||||
// TryLock() be used over this function. This function may block the ability to
|
||||
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||
//
|
||||
// If we are already exclusive-locked, this function short-circuits and returns
|
||||
// immediately assuming it can take the mutex lock.
|
||||
//
|
||||
// If the *Flock has a shared lock (RLock), this may transparently replace the
|
||||
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
|
||||
// careful when using exclusive locks in conjunction with shared locks
|
||||
// (RLock()), because calling Unlock() may accidentally release the exclusive
|
||||
// lock that was once a shared lock.
|
||||
func (l *Locker) Lock() (err error) {
|
||||
return l.flock.Lock()
|
||||
}
|
||||
|
||||
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||
//
|
||||
// This function short-circuits if we are unlocked already. If not, it calls
|
||||
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
|
||||
// remove the file from disk. It's up to your application to do.
|
||||
//
|
||||
// Please note, if your shared lock became an exclusive lock this may
|
||||
// unintentionally drop the exclusive lock if called by the consumer that
|
||||
// believes they have a shared lock. Please see Lock() for more details.
|
||||
func (l *Locker) Unlock() (err error) {
|
||||
return l.flock.Unlock()
|
||||
}
|
||||
|
||||
// RLock is a blocking call to try and take a ahred file lock. It will wait
|
||||
// until it is able to obtain the shared file lock. It's recommended that
|
||||
// TryRLock() be used over this function. This function may block the ability to
|
||||
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||
//
|
||||
// If we are already shared-locked, this function short-circuits and returns
|
||||
// immediately assuming it can take the mutex lock.
|
||||
func (l *Locker) RLock() (err error) {
|
||||
return l.flock.RLock()
|
||||
}
|
||||
|
||||
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||
//
|
||||
// This function short-circuits if we are unlocked already. If not, it calls
|
||||
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
|
||||
// remove the file from disk. It's up to your application to do.
|
||||
//
|
||||
// Please note, if your shared lock became an exclusive lock this may
|
||||
// unintentionally drop the exclusive lock if called by the consumer that
|
||||
// believes they have a shared lock. Please see Lock() for more details.
|
||||
func (l *Locker) RUnlock() (err error) {
|
||||
return l.flock.Unlock()
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
// Copyright 2019 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 gflock_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/os/gflock"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_GFlock_Base(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
fileName := "test"
|
||||
lock := gflock.New(fileName)
|
||||
gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
|
||||
gtest.Assert(lock.IsLocked(), false)
|
||||
lock.Lock()
|
||||
gtest.Assert(lock.IsLocked(), true)
|
||||
lock.Unlock()
|
||||
gtest.Assert(lock.IsLocked(), false)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
fileName := "test"
|
||||
lock := gflock.New(fileName)
|
||||
gtest.Assert(lock.Path(), gfile.TempDir()+gfile.Separator+"gflock"+gfile.Separator+fileName)
|
||||
gtest.Assert(lock.IsRLocked(), false)
|
||||
lock.RLock()
|
||||
gtest.Assert(lock.IsRLocked(), true)
|
||||
lock.RUnlock()
|
||||
gtest.Assert(lock.IsRLocked(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GFlock_Lock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
fileName := "testLock"
|
||||
array := garray.New(true)
|
||||
lock := gflock.New(fileName)
|
||||
lock2 := gflock.New(fileName)
|
||||
|
||||
go func() {
|
||||
lock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
lock.Unlock()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
lock2.Lock()
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GFlock_RLock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
fileName := "testRLock"
|
||||
array := garray.New(true)
|
||||
lock := gflock.New(fileName)
|
||||
lock2 := gflock.New(fileName)
|
||||
|
||||
go func() {
|
||||
lock.RLock()
|
||||
array.Append(1)
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
lock.RUnlock()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
lock2.RLock()
|
||||
array.Append(1)
|
||||
lock2.RUnlock()
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GFlock_TryLock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
fileName := "testTryLock"
|
||||
array := garray.New(true)
|
||||
lock := gflock.New(fileName)
|
||||
lock2 := gflock.New(fileName)
|
||||
|
||||
go func() {
|
||||
lock.TryLock()
|
||||
array.Append(1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
lock.Unlock()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if lock2.TryLock() {
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if lock2.TryLock() {
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GFlock_TryRLock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
fileName := "testTryRLock"
|
||||
array := garray.New(true)
|
||||
lock := gflock.New(fileName)
|
||||
lock2 := gflock.New(fileName)
|
||||
go func() {
|
||||
lock.TryRLock()
|
||||
array.Append(1)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
lock.Unlock()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
if lock2.TryRLock() {
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
if lock2.TryRLock() {
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
if lock2.TryRLock() {
|
||||
array.Append(1)
|
||||
lock2.Unlock()
|
||||
}
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
})
|
||||
}
|
||||
@ -25,7 +25,7 @@ func Test_Intn(t *testing.T) {
|
||||
for i := 0; i < 1000000; i++ {
|
||||
n := grand.Intn(-100)
|
||||
gtest.AssertLE(n, 0)
|
||||
gtest.AssertGT(n, -100)
|
||||
gtest.Assert(n, -100)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.9.3"
|
||||
const VERSION = "v1.9.4"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user