├── .gitignore ├── CHANGELOG └── 01.md ├── LICENSE ├── README.md ├── TUTORIAL_EN.md ├── TUTORIAL_ZH.md ├── chanrpc ├── chanrpc.go └── example_test.go ├── cluster └── cluster.go ├── conf └── conf.go ├── console ├── command.go └── console.go ├── db ├── mongodb │ ├── example_test.go │ └── mongodb.go └── redisdb │ └── redisdb.go ├── demos └── server │ ├── base │ └── skeleton.go │ ├── conf │ ├── conf.go │ └── json.go │ ├── game │ ├── external.go │ └── internal │ │ ├── chanrpc.go │ │ ├── handler.go │ │ └── module.go │ ├── gamedata │ └── reader.go │ ├── gate │ ├── external.go │ ├── internal │ │ └── module.go │ └── router.go │ ├── login │ ├── external.go │ └── internal │ │ ├── handler.go │ │ └── module.go │ ├── main.go │ ├── msg │ └── msg.go │ └── server.json ├── gate ├── agent.go └── gate.go ├── go ├── example_test.go └── go.go ├── leaf.go ├── log ├── example_test.go ├── log.go └── tag_log.go ├── module ├── module.go └── skeleton.go ├── network ├── agent.go ├── conn.go ├── json │ └── json.go ├── processor.go ├── protobuf │ └── protobuf.go ├── tcp_client.go ├── tcp_conn.go ├── tcp_msg.go ├── tcp_server.go ├── ws_client.go ├── ws_conn.go └── ws_server.go ├── recordfile ├── example_test.go ├── recordfile.go └── test.txt ├── server └── server.go ├── timer ├── cronexpr.go ├── example_test.go └── timer.go ├── util ├── deepcopy.go ├── example_test.go ├── map.go ├── rand.go └── semaphore.go ├── vendor ├── github.com │ ├── garyburd │ │ └── redigo │ │ │ ├── LICENSE │ │ │ ├── internal │ │ │ ├── commandinfo.go │ │ │ └── commandinfo_test.go │ │ │ └── redis │ │ │ ├── conn.go │ │ │ ├── conn_test.go │ │ │ ├── doc.go │ │ │ ├── go17.go │ │ │ ├── log.go │ │ │ ├── pool.go │ │ │ ├── pool_test.go │ │ │ ├── pre_go17.go │ │ │ ├── pubsub.go │ │ │ ├── pubsub_test.go │ │ │ ├── redis.go │ │ │ ├── reply.go │ │ │ ├── reply_test.go │ │ │ ├── scan.go │ │ │ ├── scan_test.go │ │ │ ├── script.go │ │ │ ├── script_test.go │ │ │ ├── test_test.go │ │ │ └── zpop_example_test.go │ ├── golang │ │ └── protobuf │ │ │ ├── LICENSE │ │ │ ├── proto │ │ │ ├── Makefile │ │ │ ├── all_test.go │ │ │ ├── any_test.go │ │ │ ├── clone.go │ │ │ ├── clone_test.go │ │ │ ├── decode.go │ │ │ ├── decode_test.go │ │ │ ├── encode.go │ │ │ ├── encode_test.go │ │ │ ├── equal.go │ │ │ ├── equal_test.go │ │ │ ├── extensions.go │ │ │ ├── extensions_test.go │ │ │ ├── lib.go │ │ │ ├── map_test.go │ │ │ ├── message_set.go │ │ │ ├── message_set_test.go │ │ │ ├── pointer_reflect.go │ │ │ ├── pointer_unsafe.go │ │ │ ├── properties.go │ │ │ ├── proto3_proto │ │ │ │ ├── proto3.pb.go │ │ │ │ └── proto3.proto │ │ │ ├── proto3_test.go │ │ │ ├── size2_test.go │ │ │ ├── size_test.go │ │ │ ├── testdata │ │ │ │ ├── Makefile │ │ │ │ ├── golden_test.go │ │ │ │ ├── test.pb.go │ │ │ │ └── test.proto │ │ │ ├── text.go │ │ │ ├── text_parser.go │ │ │ ├── text_parser_test.go │ │ │ └── text_test.go │ │ │ ├── protoc-gen-go │ │ │ └── descriptor │ │ │ │ ├── Makefile │ │ │ │ ├── descriptor.pb.go │ │ │ │ └── descriptor.proto │ │ │ └── ptypes │ │ │ ├── any.go │ │ │ ├── any │ │ │ ├── any.pb.go │ │ │ └── any.proto │ │ │ ├── any_test.go │ │ │ ├── doc.go │ │ │ ├── duration.go │ │ │ ├── duration │ │ │ ├── duration.pb.go │ │ │ └── duration.proto │ │ │ ├── duration_test.go │ │ │ ├── regen.sh │ │ │ ├── timestamp.go │ │ │ ├── timestamp │ │ │ ├── timestamp.pb.go │ │ │ └── timestamp.proto │ │ │ └── timestamp_test.go │ └── gorilla │ │ └── websocket │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── client.go │ │ ├── client_clone.go │ │ ├── client_clone_legacy.go │ │ ├── client_server_test.go │ │ ├── client_test.go │ │ ├── compression.go │ │ ├── compression_test.go │ │ ├── conn.go │ │ ├── conn_broadcast_test.go │ │ ├── conn_read.go │ │ ├── conn_read_legacy.go │ │ ├── conn_test.go │ │ ├── doc.go │ │ ├── example_test.go │ │ ├── json.go │ │ ├── json_test.go │ │ ├── mask.go │ │ ├── mask_safe.go │ │ ├── mask_test.go │ │ ├── prepared.go │ │ ├── prepared_test.go │ │ ├── server.go │ │ ├── server_test.go │ │ ├── util.go │ │ └── util_test.go ├── golang.org │ └── x │ │ ├── net │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── context │ │ │ ├── context.go │ │ │ ├── context_test.go │ │ │ ├── go17.go │ │ │ ├── go19.go │ │ │ ├── pre_go17.go │ │ │ ├── pre_go19.go │ │ │ └── withtimeout_test.go │ │ └── sync │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── errgroup │ │ ├── errgroup.go │ │ ├── errgroup_example_md5all_test.go │ │ └── errgroup_test.go └── vendor.json └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/gopkg.in 2 | -------------------------------------------------------------------------------- /CHANGELOG/01.md: -------------------------------------------------------------------------------- 1 | ## 第一版改动 2 | 3 | * 增加握手 4 | > 服务器内部的网络端口需要额外的认证才能握手成功,现阶段连接发需要发送LeafNo.1字符进行握手 5 | * 修改通讯协议, 在原来的基础上 添加了#字符进行分界 6 | * 只支持小端字节序,并且包长度只支持16位 7 | * 将leaf_server 集成进 demos 8 | > 我这里没有一个比较合适的项目区验证底层,所以将一个demos放置到一起方便开发和验证 9 | * 集成redis驱动 10 | > 有些服务器共享的数据会写到redis里面,这里只是一种同步手段,真正开发中可能会采用别的手段,同事也展示下如何方便的集成其他数据库 11 | * 使用govendor进行包管理 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Leaf_Cluster 2 | === 3 | a **experimental** game server base on leaf with cluster support, the code has not been completed yet. 4 | 5 | Leaf 6 | ----- 7 | A pragmatic game server framework in Go (golang). 8 | 9 | Features 10 | --------- 11 | 12 | * Extremely easy to use 13 | * Reliable 14 | * Multicore support 15 | * Modularity 16 | 17 | Community 18 | --------- 19 | 20 | * QQ 群:376389675 (the leaf) 21 | * QQ 群:549675095 (the leaf cluster) 22 | 23 | Documentation 24 | --------- 25 | 26 | * [中文文档](https://github.com/zsai001/leaf_cluster/blob/master/TUTORIAL_ZH.md) 27 | * [English](https://github.com/zsai001/leaf_cluster/blob/master/TUTORIAL_EN.md) 28 | 29 | Licensing 30 | --------- 31 | 32 | Leaf is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/zsai001/leaf_cluster/blob/master/LICENSE) for the full license text. 33 | -------------------------------------------------------------------------------- /chanrpc/example_test.go: -------------------------------------------------------------------------------- 1 | package chanrpc_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/chanrpc" 6 | "sync" 7 | ) 8 | 9 | func Example() { 10 | s := chanrpc.NewServer(10) 11 | 12 | var wg sync.WaitGroup 13 | wg.Add(1) 14 | 15 | // goroutine 1 16 | go func() { 17 | s.Register("f0", func(args []interface{}) { 18 | 19 | }) 20 | 21 | s.Register("f1", func(args []interface{}) interface{} { 22 | return 1 23 | }) 24 | 25 | s.Register("fn", func(args []interface{}) []interface{} { 26 | return []interface{}{1, 2, 3} 27 | }) 28 | 29 | s.Register("add", func(args []interface{}) interface{} { 30 | n1 := args[0].(int) 31 | n2 := args[1].(int) 32 | return n1 + n2 33 | }) 34 | 35 | wg.Done() 36 | 37 | for { 38 | s.Exec(<-s.ChanCall) 39 | } 40 | }() 41 | 42 | wg.Wait() 43 | wg.Add(1) 44 | 45 | // goroutine 2 46 | go func() { 47 | c := s.Open(10) 48 | 49 | // sync 50 | err := c.Call0("f0") 51 | if err != nil { 52 | fmt.Println(err) 53 | } 54 | 55 | r1, err := c.Call1("f1") 56 | if err != nil { 57 | fmt.Println(err) 58 | } else { 59 | fmt.Println(r1) 60 | } 61 | 62 | rn, err := c.CallN("fn") 63 | if err != nil { 64 | fmt.Println(err) 65 | } else { 66 | fmt.Println(rn[0], rn[1], rn[2]) 67 | } 68 | 69 | ra, err := c.Call1("add", 1, 2) 70 | if err != nil { 71 | fmt.Println(err) 72 | } else { 73 | fmt.Println(ra) 74 | } 75 | 76 | // asyn 77 | c.AsynCall("f0", func(err error) { 78 | if err != nil { 79 | fmt.Println(err) 80 | } 81 | }) 82 | 83 | c.AsynCall("f1", func(ret interface{}, err error) { 84 | if err != nil { 85 | fmt.Println(err) 86 | } else { 87 | fmt.Println(ret) 88 | } 89 | }) 90 | 91 | c.AsynCall("fn", func(ret []interface{}, err error) { 92 | if err != nil { 93 | fmt.Println(err) 94 | } else { 95 | fmt.Println(ret[0], ret[1], ret[2]) 96 | } 97 | }) 98 | 99 | c.AsynCall("add", 1, 2, func(ret interface{}, err error) { 100 | if err != nil { 101 | fmt.Println(err) 102 | } else { 103 | fmt.Println(ret) 104 | } 105 | }) 106 | 107 | c.Cb(<-c.ChanAsynRet) 108 | c.Cb(<-c.ChanAsynRet) 109 | c.Cb(<-c.ChanAsynRet) 110 | c.Cb(<-c.ChanAsynRet) 111 | 112 | // go 113 | s.Go("f0") 114 | 115 | wg.Done() 116 | }() 117 | 118 | wg.Wait() 119 | 120 | // Output: 121 | // 1 122 | // 1 2 3 123 | // 3 124 | // 1 125 | // 1 2 3 126 | // 3 127 | } 128 | -------------------------------------------------------------------------------- /cluster/cluster.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/network" 5 | "github.com/zsai001/leaf_cluster/conf" 6 | "math" 7 | "time" 8 | ) 9 | 10 | var ( 11 | server *network.TCPServer 12 | clients []*network.TCPClient 13 | ) 14 | 15 | func Init() { 16 | if conf.ListenAddr != "" { 17 | server = new(network.TCPServer) 18 | server.Addr = conf.ListenAddr 19 | server.MaxConnNum = int(math.MaxInt32) 20 | server.PendingWriteNum = conf.PendingWriteNum 21 | server.LenMsgLen = 4 22 | server.MaxMsgLen = math.MaxUint32 23 | server.NewAgent = newAgent 24 | 25 | server.Start() 26 | } 27 | 28 | for _, addr := range conf.ConnAddrs { 29 | client := new(network.TCPClient) 30 | client.Addr = addr 31 | client.ConnNum = 1 32 | client.ConnectInterval = 3 * time.Second 33 | client.PendingWriteNum = conf.PendingWriteNum 34 | client.LenMsgLen = 4 35 | client.MaxMsgLen = math.MaxUint32 36 | client.NewAgent = newAgent 37 | 38 | client.Start() 39 | clients = append(clients, client) 40 | } 41 | } 42 | 43 | func Destroy() { 44 | if server != nil { 45 | server.Close() 46 | } 47 | 48 | for _, client := range clients { 49 | client.Close() 50 | } 51 | } 52 | 53 | type Agent struct { 54 | conn *network.TCPConn 55 | } 56 | 57 | func newAgent(conn *network.TCPConn) network.Agent { 58 | a := new(Agent) 59 | a.conn = conn 60 | return a 61 | } 62 | 63 | func (a *Agent) Run() {} 64 | 65 | func (a *Agent) OnClose() {} 66 | -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | var ( 4 | LenStackBuf = 4096 5 | 6 | // log 7 | LogLevel string 8 | LogPath string 9 | LogFlag int 10 | 11 | // console 12 | ConsolePort int 13 | ConsolePrompt string = "Leaf# " 14 | ProfilePath string 15 | 16 | // cluster 17 | ListenAddr string 18 | ConnAddrs []string 19 | PendingWriteNum int 20 | ) 21 | -------------------------------------------------------------------------------- /console/console.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | import ( 4 | "bufio" 5 | "github.com/zsai001/leaf_cluster/conf" 6 | "github.com/zsai001/leaf_cluster/network" 7 | "math" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var server *network.TCPServer 13 | 14 | func Init() { 15 | if conf.ConsolePort == 0 { 16 | return 17 | } 18 | 19 | server = new(network.TCPServer) 20 | server.Addr = "localhost:" + strconv.Itoa(conf.ConsolePort) 21 | server.MaxConnNum = int(math.MaxInt32) 22 | server.PendingWriteNum = 100 23 | server.NewAgent = newAgent 24 | 25 | server.Start() 26 | } 27 | 28 | func Destroy() { 29 | if server != nil { 30 | server.Close() 31 | } 32 | } 33 | 34 | type Agent struct { 35 | conn *network.TCPConn 36 | reader *bufio.Reader 37 | } 38 | 39 | func newAgent(conn *network.TCPConn) network.Agent { 40 | a := new(Agent) 41 | a.conn = conn 42 | a.reader = bufio.NewReader(conn) 43 | return a 44 | } 45 | 46 | func (a *Agent) Run() { 47 | for { 48 | if conf.ConsolePrompt != "" { 49 | a.conn.Write([]byte(conf.ConsolePrompt)) 50 | } 51 | 52 | line, err := a.reader.ReadString('\n') 53 | if err != nil { 54 | break 55 | } 56 | line = strings.TrimSuffix(line[:len(line)-1], "\r") 57 | 58 | args := strings.Fields(line) 59 | if len(args) == 0 { 60 | continue 61 | } 62 | if args[0] == "quit" { 63 | break 64 | } 65 | var c Command 66 | for _, _c := range commands { 67 | if _c.name() == args[0] { 68 | c = _c 69 | break 70 | } 71 | } 72 | if c == nil { 73 | a.conn.Write([]byte("command not found, try `help` for help\r\n")) 74 | continue 75 | } 76 | output := c.run(args[1:]) 77 | if output != "" { 78 | a.conn.Write([]byte(output + "\r\n")) 79 | } 80 | } 81 | } 82 | 83 | func (a *Agent) OnClose() {} 84 | -------------------------------------------------------------------------------- /db/mongodb/example_test.go: -------------------------------------------------------------------------------- 1 | package mongodb_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/db/mongodb" 6 | "gopkg.in/mgo.v2" 7 | ) 8 | 9 | func Example() { 10 | c, err := mongodb.Dial("localhost", 10) 11 | if err != nil { 12 | fmt.Println(err) 13 | return 14 | } 15 | defer c.Close() 16 | 17 | // session 18 | s := c.Ref() 19 | defer c.UnRef(s) 20 | err = s.DB("test").C("counters").RemoveId("test") 21 | if err != nil && err != mgo.ErrNotFound { 22 | fmt.Println(err) 23 | return 24 | } 25 | 26 | // auto increment 27 | err = c.EnsureCounter("test", "counters", "test") 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | for i := 0; i < 3; i++ { 33 | id, err := c.NextSeq("test", "counters", "test") 34 | if err != nil { 35 | fmt.Println(err) 36 | return 37 | } 38 | fmt.Println(id) 39 | } 40 | 41 | // index 42 | c.EnsureUniqueIndex("test", "counters", []string{"key1"}) 43 | 44 | // Output: 45 | // 1 46 | // 2 47 | // 3 48 | } 49 | -------------------------------------------------------------------------------- /db/mongodb/mongodb.go: -------------------------------------------------------------------------------- 1 | package mongodb 2 | 3 | import ( 4 | "container/heap" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // session 13 | type Session struct { 14 | *mgo.Session 15 | ref int 16 | index int 17 | } 18 | 19 | // session heap 20 | type SessionHeap []*Session 21 | 22 | func (h SessionHeap) Len() int { 23 | return len(h) 24 | } 25 | 26 | func (h SessionHeap) Less(i, j int) bool { 27 | return h[i].ref < h[j].ref 28 | } 29 | 30 | func (h SessionHeap) Swap(i, j int) { 31 | h[i], h[j] = h[j], h[i] 32 | h[i].index = i 33 | h[j].index = j 34 | } 35 | 36 | func (h *SessionHeap) Push(s interface{}) { 37 | s.(*Session).index = len(*h) 38 | *h = append(*h, s.(*Session)) 39 | } 40 | 41 | func (h *SessionHeap) Pop() interface{} { 42 | l := len(*h) 43 | s := (*h)[l-1] 44 | s.index = -1 45 | *h = (*h)[:l-1] 46 | return s 47 | } 48 | 49 | type DialContext struct { 50 | sync.Mutex 51 | sessions SessionHeap 52 | } 53 | 54 | // goroutine safe 55 | func Dial(url string, sessionNum int) (*DialContext, error) { 56 | c, err := DialWithTimeout(url, sessionNum, 10*time.Second, 5*time.Minute) 57 | return c, err 58 | } 59 | 60 | // goroutine safe 61 | func DialWithTimeout(url string, sessionNum int, dialTimeout time.Duration, timeout time.Duration) (*DialContext, error) { 62 | if sessionNum <= 0 { 63 | sessionNum = 100 64 | log.Release("invalid sessionNum, reset to %v", sessionNum) 65 | } 66 | 67 | s, err := mgo.DialWithTimeout(url, dialTimeout) 68 | if err != nil { 69 | return nil, err 70 | } 71 | s.SetSyncTimeout(timeout) 72 | s.SetSocketTimeout(timeout) 73 | 74 | c := new(DialContext) 75 | 76 | // sessions 77 | c.sessions = make(SessionHeap, sessionNum) 78 | c.sessions[0] = &Session{s, 0, 0} 79 | for i := 1; i < sessionNum; i++ { 80 | c.sessions[i] = &Session{s.New(), 0, i} 81 | } 82 | heap.Init(&c.sessions) 83 | 84 | return c, nil 85 | } 86 | 87 | // goroutine safe 88 | func (c *DialContext) Close() { 89 | c.Lock() 90 | for _, s := range c.sessions { 91 | s.Close() 92 | if s.ref != 0 { 93 | log.Error("session ref = %v", s.ref) 94 | } 95 | } 96 | c.Unlock() 97 | } 98 | 99 | // goroutine safe 100 | func (c *DialContext) Ref() *Session { 101 | c.Lock() 102 | s := c.sessions[0] 103 | if s.ref == 0 { 104 | s.Refresh() 105 | } 106 | s.ref++ 107 | heap.Fix(&c.sessions, 0) 108 | c.Unlock() 109 | 110 | return s 111 | } 112 | 113 | // goroutine safe 114 | func (c *DialContext) UnRef(s *Session) { 115 | c.Lock() 116 | s.ref-- 117 | heap.Fix(&c.sessions, s.index) 118 | c.Unlock() 119 | } 120 | 121 | // goroutine safe 122 | func (c *DialContext) EnsureCounter(db string, collection string, id string) error { 123 | s := c.Ref() 124 | defer c.UnRef(s) 125 | 126 | err := s.DB(db).C(collection).Insert(bson.M{ 127 | "_id": id, 128 | "seq": 0, 129 | }) 130 | if mgo.IsDup(err) { 131 | return nil 132 | } else { 133 | return err 134 | } 135 | } 136 | 137 | // goroutine safe 138 | func (c *DialContext) NextSeq(db string, collection string, id string) (int, error) { 139 | s := c.Ref() 140 | defer c.UnRef(s) 141 | 142 | var res struct { 143 | Seq int 144 | } 145 | _, err := s.DB(db).C(collection).FindId(id).Apply(mgo.Change{ 146 | Update: bson.M{"$inc": bson.M{"seq": 1}}, 147 | ReturnNew: true, 148 | }, &res) 149 | 150 | return res.Seq, err 151 | } 152 | 153 | // goroutine safe 154 | func (c *DialContext) EnsureIndex(db string, collection string, key []string) error { 155 | s := c.Ref() 156 | defer c.UnRef(s) 157 | 158 | return s.DB(db).C(collection).EnsureIndex(mgo.Index{ 159 | Key: key, 160 | Unique: false, 161 | Sparse: true, 162 | }) 163 | } 164 | 165 | // goroutine safe 166 | func (c *DialContext) EnsureUniqueIndex(db string, collection string, key []string) error { 167 | s := c.Ref() 168 | defer c.UnRef(s) 169 | 170 | return s.DB(db).C(collection).EnsureIndex(mgo.Index{ 171 | Key: key, 172 | Unique: true, 173 | Sparse: true, 174 | }) 175 | } 176 | -------------------------------------------------------------------------------- /db/redisdb/redisdb.go: -------------------------------------------------------------------------------- 1 | package redisdb 2 | 3 | import ( 4 | "github.com/garyburd/redigo/redis" 5 | ) 6 | 7 | type Session struct { 8 | redis.Conn 9 | } -------------------------------------------------------------------------------- /demos/server/base/skeleton.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/chanrpc" 5 | "github.com/zsai001/leaf_cluster/module" 6 | "github.com/zsai001/leaf_cluster/demos/server/conf" 7 | ) 8 | 9 | func NewSkeleton() *module.Skeleton { 10 | skeleton := &module.Skeleton{ 11 | GoLen: conf.GoLen, 12 | TimerDispatcherLen: conf.TimerDispatcherLen, 13 | AsynCallLen: conf.AsynCallLen, 14 | ChanRPCServer: chanrpc.NewServer(conf.ChanRPCLen), 15 | } 16 | skeleton.Init() 17 | return skeleton 18 | } 19 | -------------------------------------------------------------------------------- /demos/server/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | var ( 9 | // log conf 10 | LogFlag = log.LstdFlags 11 | 12 | // gate conf 13 | PendingWriteNum = 2000 14 | MaxMsgLen uint32 = 4096 15 | HTTPTimeout = 10 * time.Second 16 | LenMsgLen = 2 17 | LittleEndian = false 18 | 19 | // skeleton conf 20 | GoLen = 10000 21 | TimerDispatcherLen = 10000 22 | AsynCallLen = 10000 23 | ChanRPCLen = 10000 24 | ) 25 | -------------------------------------------------------------------------------- /demos/server/conf/json.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "io/ioutil" 7 | ) 8 | 9 | var Server struct { 10 | LogLevel string 11 | LogPath string 12 | WSAddr string 13 | CertFile string 14 | KeyFile string 15 | TCPAddr string 16 | MaxConnNum int 17 | ConsolePort int 18 | ProfilePath string 19 | } 20 | 21 | func init() { 22 | data, err := ioutil.ReadFile("server.json") 23 | if err != nil { 24 | log.Fatal("%v", err) 25 | } 26 | err = json.Unmarshal(data, &Server) 27 | if err != nil { 28 | log.Fatal("%v", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demos/server/game/external.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/demos/server/game/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /demos/server/game/internal/chanrpc.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/gate" 5 | ) 6 | 7 | func init() { 8 | skeleton.RegisterChanRPC("NewAgent", rpcNewAgent) 9 | skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent) 10 | } 11 | 12 | func rpcNewAgent(args []interface{}) { 13 | a := args[0].(gate.Agent) 14 | _ = a 15 | } 16 | 17 | func rpcCloseAgent(args []interface{}) { 18 | a := args[0].(gate.Agent) 19 | _ = a 20 | } 21 | -------------------------------------------------------------------------------- /demos/server/game/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | -------------------------------------------------------------------------------- /demos/server/game/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/module" 5 | "github.com/zsai001/leaf_cluster/demos/server/base" 6 | ) 7 | 8 | var ( 9 | skeleton = base.NewSkeleton() 10 | ChanRPC = skeleton.ChanRPCServer 11 | ) 12 | 13 | type Module struct { 14 | *module.Skeleton 15 | } 16 | 17 | func (m *Module) OnInit() { 18 | m.Skeleton = skeleton 19 | } 20 | 21 | func (m *Module) OnDestroy() { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /demos/server/gamedata/reader.go: -------------------------------------------------------------------------------- 1 | package gamedata 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/log" 5 | "github.com/zsai001/leaf_cluster/recordfile" 6 | "reflect" 7 | ) 8 | 9 | func readRf(st interface{}) *recordfile.RecordFile { 10 | rf, err := recordfile.New(st) 11 | if err != nil { 12 | log.Fatal("%v", err) 13 | } 14 | fn := reflect.TypeOf(st).Name() + ".txt" 15 | err = rf.Read("gamedata/" + fn) 16 | if err != nil { 17 | log.Fatal("%v: %v", fn, err) 18 | } 19 | 20 | return rf 21 | } 22 | -------------------------------------------------------------------------------- /demos/server/gate/external.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/demos/server/gate/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /demos/server/gate/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/gate" 5 | "github.com/zsai001/leaf_cluster/demos/server/conf" 6 | "github.com/zsai001/leaf_cluster/demos/server/game" 7 | "github.com/zsai001/leaf_cluster/demos/server/msg" 8 | ) 9 | 10 | type Module struct { 11 | *gate.Gate 12 | } 13 | 14 | func (m *Module) OnInit() { 15 | m.Gate = &gate.Gate{ 16 | MaxConnNum: conf.Server.MaxConnNum, 17 | PendingWriteNum: conf.PendingWriteNum, 18 | MaxMsgLen: conf.MaxMsgLen, 19 | WSAddr: conf.Server.WSAddr, 20 | HTTPTimeout: conf.HTTPTimeout, 21 | CertFile: conf.Server.CertFile, 22 | KeyFile: conf.Server.KeyFile, 23 | TCPAddr: conf.Server.TCPAddr, 24 | LenMsgLen: conf.LenMsgLen, 25 | LittleEndian: conf.LittleEndian, 26 | Processor: msg.Processor, 27 | AgentChanRPC: game.ChanRPC, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demos/server/gate/router.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | func init() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /demos/server/login/external.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/demos/server/login/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /demos/server/login/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func handleMsg(m interface{}, h interface{}) { 8 | skeleton.RegisterChanRPC(reflect.TypeOf(m), h) 9 | } 10 | 11 | func init() { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /demos/server/login/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/module" 5 | "github.com/zsai001/leaf_cluster/demos/server/base" 6 | ) 7 | 8 | var ( 9 | skeleton = base.NewSkeleton() 10 | ChanRPC = skeleton.ChanRPCServer 11 | ) 12 | 13 | type Module struct { 14 | *module.Skeleton 15 | } 16 | 17 | func (m *Module) OnInit() { 18 | m.Skeleton = skeleton 19 | } 20 | 21 | func (m *Module) OnDestroy() { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /demos/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster" 5 | lconf "github.com/zsai001/leaf_cluster/conf" 6 | "github.com/zsai001/leaf_cluster/demos/server/conf" 7 | "github.com/zsai001/leaf_cluster/demos/server/game" 8 | "github.com/zsai001/leaf_cluster/demos/server/gate" 9 | "github.com/zsai001/leaf_cluster/demos/server/login" 10 | ) 11 | 12 | func main() { 13 | lconf.LogLevel = conf.Server.LogLevel 14 | lconf.LogPath = conf.Server.LogPath 15 | lconf.LogFlag = conf.LogFlag 16 | lconf.ConsolePort = conf.Server.ConsolePort 17 | lconf.ProfilePath = conf.Server.ProfilePath 18 | 19 | leaf.Run( 20 | game.Module, 21 | gate.Module, 22 | login.Module, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /demos/server/msg/msg.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/network" 5 | ) 6 | 7 | var Processor network.Processor 8 | 9 | func init() { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /demos/server/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "LogLevel": "debug", 3 | "LogPath": "", 4 | "TCPAddr": "127.0.0.1:3563", 5 | "MaxConnNum": 20000 6 | } 7 | -------------------------------------------------------------------------------- /gate/agent.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type Agent interface { 8 | WriteMsg(msg interface{}) 9 | LocalAddr() net.Addr 10 | RemoteAddr() net.Addr 11 | Close() 12 | Destroy() 13 | UserData() interface{} 14 | SetUserData(data interface{}) 15 | } 16 | -------------------------------------------------------------------------------- /gate/gate.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/chanrpc" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "github.com/zsai001/leaf_cluster/network" 7 | "net" 8 | "reflect" 9 | "time" 10 | ) 11 | 12 | type Gate struct { 13 | MaxConnNum int 14 | PendingWriteNum int 15 | MaxMsgLen uint32 16 | Processor network.Processor 17 | AgentChanRPC *chanrpc.Server 18 | 19 | // websocket 20 | WSAddr string 21 | HTTPTimeout time.Duration 22 | CertFile string 23 | KeyFile string 24 | 25 | // tcp 26 | TCPAddr string 27 | LenMsgLen int 28 | LittleEndian bool 29 | } 30 | 31 | func (gate *Gate) Run(closeSig chan bool) { 32 | var wsServer *network.WSServer 33 | if gate.WSAddr != "" { 34 | wsServer = new(network.WSServer) 35 | wsServer.Addr = gate.WSAddr 36 | wsServer.MaxConnNum = gate.MaxConnNum 37 | wsServer.PendingWriteNum = gate.PendingWriteNum 38 | wsServer.MaxMsgLen = gate.MaxMsgLen 39 | wsServer.HTTPTimeout = gate.HTTPTimeout 40 | wsServer.CertFile = gate.CertFile 41 | wsServer.KeyFile = gate.KeyFile 42 | wsServer.NewAgent = func(conn *network.WSConn) network.Agent { 43 | a := &agent{conn: conn, gate: gate} 44 | if gate.AgentChanRPC != nil { 45 | gate.AgentChanRPC.Go("NewAgent", a) 46 | } 47 | return a 48 | } 49 | } 50 | 51 | var tcpServer *network.TCPServer 52 | if gate.TCPAddr != "" { 53 | tcpServer = new(network.TCPServer) 54 | tcpServer.Addr = gate.TCPAddr 55 | tcpServer.MaxConnNum = gate.MaxConnNum 56 | tcpServer.PendingWriteNum = gate.PendingWriteNum 57 | tcpServer.LenMsgLen = gate.LenMsgLen 58 | tcpServer.MaxMsgLen = gate.MaxMsgLen 59 | tcpServer.LittleEndian = gate.LittleEndian 60 | tcpServer.NewAgent = func(conn *network.TCPConn) network.Agent { 61 | a := &agent{conn: conn, gate: gate} 62 | if gate.AgentChanRPC != nil { 63 | gate.AgentChanRPC.Go("NewAgent", a) 64 | } 65 | return a 66 | } 67 | } 68 | 69 | if wsServer != nil { 70 | wsServer.Start() 71 | } 72 | if tcpServer != nil { 73 | tcpServer.Start() 74 | } 75 | <-closeSig 76 | if wsServer != nil { 77 | wsServer.Close() 78 | } 79 | if tcpServer != nil { 80 | tcpServer.Close() 81 | } 82 | } 83 | 84 | func (gate *Gate) OnDestroy() {} 85 | 86 | type agent struct { 87 | conn network.Conn 88 | gate *Gate 89 | userData interface{} 90 | } 91 | 92 | func (a *agent) Run() { 93 | for { 94 | data, err := a.conn.ReadMsg() 95 | if err != nil { 96 | log.Debug("read message: %v", err) 97 | break 98 | } 99 | 100 | if a.gate.Processor != nil { 101 | msg, err := a.gate.Processor.Unmarshal(data) 102 | if err != nil { 103 | log.Debug("unmarshal message error: %v", err) 104 | break 105 | } 106 | err = a.gate.Processor.Route(msg, a) 107 | if err != nil { 108 | log.Debug("route message error: %v", err) 109 | break 110 | } 111 | } 112 | } 113 | } 114 | 115 | func (a *agent) OnClose() { 116 | if a.gate.AgentChanRPC != nil { 117 | err := a.gate.AgentChanRPC.Call0("CloseAgent", a) 118 | if err != nil { 119 | log.Error("chanrpc error: %v", err) 120 | } 121 | } 122 | } 123 | 124 | func (a *agent) WriteMsg(msg interface{}) { 125 | if a.gate.Processor != nil { 126 | data, err := a.gate.Processor.Marshal(msg) 127 | if err != nil { 128 | log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err) 129 | return 130 | } 131 | err = a.conn.WriteMsg(data...) 132 | if err != nil { 133 | log.Error("write message %v error: %v", reflect.TypeOf(msg), err) 134 | } 135 | } 136 | } 137 | 138 | func (a *agent) LocalAddr() net.Addr { 139 | return a.conn.LocalAddr() 140 | } 141 | 142 | func (a *agent) RemoteAddr() net.Addr { 143 | return a.conn.RemoteAddr() 144 | } 145 | 146 | func (a *agent) Close() { 147 | a.conn.Close() 148 | } 149 | 150 | func (a *agent) Destroy() { 151 | a.conn.Destroy() 152 | } 153 | 154 | func (a *agent) UserData() interface{} { 155 | return a.userData 156 | } 157 | 158 | func (a *agent) SetUserData(data interface{}) { 159 | a.userData = data 160 | } 161 | -------------------------------------------------------------------------------- /go/example_test.go: -------------------------------------------------------------------------------- 1 | package g_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/go" 6 | "time" 7 | ) 8 | 9 | func Example() { 10 | d := g.New(10) 11 | 12 | // go 1 13 | var res int 14 | d.Go(func() { 15 | fmt.Println("1 + 1 = ?") 16 | res = 1 + 1 17 | }, func() { 18 | fmt.Println(res) 19 | }) 20 | 21 | d.Cb(<-d.ChanCb) 22 | 23 | // go 2 24 | d.Go(func() { 25 | fmt.Print("My name is ") 26 | }, func() { 27 | fmt.Println("Leaf") 28 | }) 29 | 30 | d.Close() 31 | 32 | // Output: 33 | // 1 + 1 = ? 34 | // 2 35 | // My name is Leaf 36 | } 37 | 38 | func ExampleLinearContext() { 39 | d := g.New(10) 40 | 41 | // parallel 42 | d.Go(func() { 43 | time.Sleep(time.Second / 2) 44 | fmt.Println("1") 45 | }, nil) 46 | d.Go(func() { 47 | fmt.Println("2") 48 | }, nil) 49 | 50 | d.Cb(<-d.ChanCb) 51 | d.Cb(<-d.ChanCb) 52 | 53 | // linear 54 | c := d.NewLinearContext() 55 | c.Go(func() { 56 | time.Sleep(time.Second / 2) 57 | fmt.Println("1") 58 | }, nil) 59 | c.Go(func() { 60 | fmt.Println("2") 61 | }, nil) 62 | 63 | d.Close() 64 | 65 | // Output: 66 | // 2 67 | // 1 68 | // 1 69 | // 2 70 | } 71 | -------------------------------------------------------------------------------- /go/go.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "container/list" 5 | "github.com/zsai001/leaf_cluster/conf" 6 | "github.com/zsai001/leaf_cluster/log" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | // one Go per goroutine (goroutine not safe) 12 | type Go struct { 13 | ChanCb chan func() 14 | pendingGo int 15 | } 16 | 17 | type LinearGo struct { 18 | f func() 19 | cb func() 20 | } 21 | 22 | type LinearContext struct { 23 | g *Go 24 | linearGo *list.List 25 | mutexLinearGo sync.Mutex 26 | mutexExecution sync.Mutex 27 | } 28 | 29 | func New(l int) *Go { 30 | g := new(Go) 31 | g.ChanCb = make(chan func(), l) 32 | return g 33 | } 34 | 35 | func (g *Go) Go(f func(), cb func()) { 36 | g.pendingGo++ 37 | 38 | go func() { 39 | defer func() { 40 | g.ChanCb <- cb 41 | if r := recover(); r != nil { 42 | if conf.LenStackBuf > 0 { 43 | buf := make([]byte, conf.LenStackBuf) 44 | l := runtime.Stack(buf, false) 45 | log.Error("%v: %s", r, buf[:l]) 46 | } else { 47 | log.Error("%v", r) 48 | } 49 | } 50 | }() 51 | 52 | f() 53 | }() 54 | } 55 | 56 | func (g *Go) Cb(cb func()) { 57 | defer func() { 58 | g.pendingGo-- 59 | if r := recover(); r != nil { 60 | if conf.LenStackBuf > 0 { 61 | buf := make([]byte, conf.LenStackBuf) 62 | l := runtime.Stack(buf, false) 63 | log.Error("%v: %s", r, buf[:l]) 64 | } else { 65 | log.Error("%v", r) 66 | } 67 | } 68 | }() 69 | 70 | if cb != nil { 71 | cb() 72 | } 73 | } 74 | 75 | func (g *Go) Close() { 76 | for g.pendingGo > 0 { 77 | g.Cb(<-g.ChanCb) 78 | } 79 | } 80 | 81 | func (g *Go) Idle() bool { 82 | return g.pendingGo == 0 83 | } 84 | 85 | func (g *Go) NewLinearContext() *LinearContext { 86 | c := new(LinearContext) 87 | c.g = g 88 | c.linearGo = list.New() 89 | return c 90 | } 91 | 92 | func (c *LinearContext) Go(f func(), cb func()) { 93 | c.g.pendingGo++ 94 | 95 | c.mutexLinearGo.Lock() 96 | c.linearGo.PushBack(&LinearGo{f: f, cb: cb}) 97 | c.mutexLinearGo.Unlock() 98 | 99 | go func() { 100 | c.mutexExecution.Lock() 101 | defer c.mutexExecution.Unlock() 102 | 103 | c.mutexLinearGo.Lock() 104 | e := c.linearGo.Remove(c.linearGo.Front()).(*LinearGo) 105 | c.mutexLinearGo.Unlock() 106 | 107 | defer func() { 108 | c.g.ChanCb <- e.cb 109 | if r := recover(); r != nil { 110 | if conf.LenStackBuf > 0 { 111 | buf := make([]byte, conf.LenStackBuf) 112 | l := runtime.Stack(buf, false) 113 | log.Error("%v: %s", r, buf[:l]) 114 | } else { 115 | log.Error("%v", r) 116 | } 117 | } 118 | }() 119 | 120 | e.f() 121 | }() 122 | } 123 | -------------------------------------------------------------------------------- /leaf.go: -------------------------------------------------------------------------------- 1 | package leaf 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/cluster" 5 | "github.com/zsai001/leaf_cluster/conf" 6 | "github.com/zsai001/leaf_cluster/console" 7 | "github.com/zsai001/leaf_cluster/log" 8 | "github.com/zsai001/leaf_cluster/module" 9 | "os" 10 | "os/signal" 11 | ) 12 | 13 | func Run(mods ...module.Module) { 14 | // logger 15 | if conf.LogLevel != "" { 16 | logger, err := log.New(conf.LogLevel, conf.LogPath, conf.LogFlag) 17 | if err != nil { 18 | panic(err) 19 | } 20 | log.Export(logger) 21 | defer logger.Close() 22 | } 23 | 24 | log.Release("Leaf %v starting up", version) 25 | 26 | // module 27 | for i := 0; i < len(mods); i++ { 28 | module.Register(mods[i]) 29 | } 30 | module.Init() 31 | 32 | // cluster 33 | cluster.Init() 34 | 35 | // console 36 | console.Init() 37 | 38 | // close 39 | c := make(chan os.Signal, 1) 40 | signal.Notify(c, os.Interrupt, os.Kill) 41 | sig := <-c 42 | log.Release("Leaf closing down (signal: %v)", sig) 43 | console.Destroy() 44 | cluster.Destroy() 45 | module.Destroy() 46 | } 47 | -------------------------------------------------------------------------------- /log/example_test.go: -------------------------------------------------------------------------------- 1 | package log_test 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/log" 5 | l "log" 6 | ) 7 | 8 | func Example() { 9 | name := "Leaf" 10 | 11 | log.Debug("My name is %v", name) 12 | log.Release("My name is %v", name) 13 | log.Error("My name is %v", name) 14 | // log.Fatal("My name is %v", name) 15 | 16 | logger, err := log.New("release", "", l.LstdFlags) 17 | if err != nil { 18 | return 19 | } 20 | defer logger.Close() 21 | 22 | logger.Debug("will not print") 23 | logger.Release("My name is %v", name) 24 | 25 | log.Export(logger) 26 | 27 | log.Debug("will not print") 28 | log.Release("My name is %v", name) 29 | } 30 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // levels 14 | const ( 15 | debugLevel = 0 16 | releaseLevel = 1 17 | errorLevel = 2 18 | fatalLevel = 3 19 | ) 20 | 21 | const ( 22 | printDebugLevel = "[debug ] " 23 | printReleaseLevel = "[release] " 24 | printErrorLevel = "[error ] " 25 | printFatalLevel = "[fatal ] " 26 | ) 27 | 28 | type Logger struct { 29 | level int 30 | baseLogger *log.Logger 31 | baseFile *os.File 32 | } 33 | 34 | func New(strLevel string, pathname string, flag int) (*Logger, error) { 35 | // level 36 | var level int 37 | switch strings.ToLower(strLevel) { 38 | case "debug": 39 | level = debugLevel 40 | case "release": 41 | level = releaseLevel 42 | case "error": 43 | level = errorLevel 44 | case "fatal": 45 | level = fatalLevel 46 | default: 47 | return nil, errors.New("unknown level: " + strLevel) 48 | } 49 | 50 | // logger 51 | var baseLogger *log.Logger 52 | var baseFile *os.File 53 | if pathname != "" { 54 | now := time.Now() 55 | 56 | filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", 57 | now.Year(), 58 | now.Month(), 59 | now.Day(), 60 | now.Hour(), 61 | now.Minute(), 62 | now.Second()) 63 | 64 | file, err := os.Create(path.Join(pathname, filename)) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | baseLogger = log.New(file, "", flag) 70 | baseFile = file 71 | } else { 72 | baseLogger = log.New(os.Stdout, "", flag) 73 | } 74 | 75 | // new 76 | logger := new(Logger) 77 | logger.level = level 78 | logger.baseLogger = baseLogger 79 | logger.baseFile = baseFile 80 | 81 | return logger, nil 82 | } 83 | 84 | // It's dangerous to call the method on logging 85 | func (logger *Logger) Close() { 86 | if logger.baseFile != nil { 87 | logger.baseFile.Close() 88 | } 89 | 90 | logger.baseLogger = nil 91 | logger.baseFile = nil 92 | } 93 | 94 | func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) { 95 | if level < logger.level { 96 | return 97 | } 98 | if logger.baseLogger == nil { 99 | panic("logger closed") 100 | } 101 | 102 | format = printLevel + format 103 | logger.baseLogger.Output(3, fmt.Sprintf(format, a...)) 104 | 105 | if level == fatalLevel { 106 | os.Exit(1) 107 | } 108 | } 109 | 110 | func (logger *Logger) Debug(format string, a ...interface{}) { 111 | logger.doPrintf(debugLevel, printDebugLevel, format, a...) 112 | } 113 | 114 | func (logger *Logger) Release(format string, a ...interface{}) { 115 | logger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 116 | } 117 | 118 | func (logger *Logger) Error(format string, a ...interface{}) { 119 | logger.doPrintf(errorLevel, printErrorLevel, format, a...) 120 | } 121 | 122 | func (logger *Logger) Fatal(format string, a ...interface{}) { 123 | logger.doPrintf(fatalLevel, printFatalLevel, format, a...) 124 | } 125 | 126 | var gLogger, _ = New("debug", "", log.LstdFlags) 127 | 128 | // It's dangerous to call the method on logging 129 | func Export(logger *Logger) { 130 | if logger != nil { 131 | gLogger = logger 132 | } 133 | } 134 | 135 | func Debug(format string, a ...interface{}) { 136 | gLogger.doPrintf(debugLevel, printDebugLevel, format, a...) 137 | } 138 | 139 | func Release(format string, a ...interface{}) { 140 | gLogger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 141 | } 142 | 143 | func Error(format string, a ...interface{}) { 144 | gLogger.doPrintf(errorLevel, printErrorLevel, format, a...) 145 | } 146 | 147 | func Fatal(format string, a ...interface{}) { 148 | gLogger.doPrintf(fatalLevel, printFatalLevel, format, a...) 149 | } 150 | 151 | func Close() { 152 | gLogger.Close() 153 | } 154 | -------------------------------------------------------------------------------- /log/tag_log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "fmt" 4 | 5 | type tagLog struct { 6 | tag interface{} 7 | } 8 | 9 | func (logger *tagLog) doPrintf(level int, printLevel string, format string, a ...interface{}) { 10 | fmt.Println(logger.tag, level, printLevel, format, a) 11 | } 12 | 13 | func (logger *tagLog) Debug(format string, a ...interface{}) { 14 | if !logger.matchLog(debugLevel) { 15 | return 16 | } 17 | logger.doPrintf(debugLevel, printDebugLevel, format, a...) 18 | } 19 | 20 | func (logger *tagLog) Release(format string, a ...interface{}) { 21 | if !logger.matchLog(releaseLevel) { 22 | return 23 | } 24 | logger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 25 | } 26 | 27 | func (logger *tagLog) Error(format string, a ...interface{}) { 28 | if !logger.matchLog(errorLevel) { 29 | return 30 | } 31 | logger.doPrintf(errorLevel, printErrorLevel, format, a...) 32 | } 33 | 34 | func (logger *tagLog) Fatal(format string, a ...interface{}) { 35 | if !logger.matchLog(fatalLevel) { 36 | return 37 | } 38 | logger.doPrintf(fatalLevel, printFatalLevel, format, a...) 39 | } 40 | 41 | func (Logger *tagLog) matchLog(level int) bool{ 42 | return true 43 | } 44 | 45 | func TagLog(tag interface{}) tagLog { 46 | return tagLog{tag:tag} 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/conf" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "runtime" 7 | "sync" 8 | ) 9 | 10 | type Module interface { 11 | OnInit() 12 | OnDestroy() 13 | Run(closeSig chan bool) 14 | } 15 | 16 | type module struct { 17 | mi Module 18 | closeSig chan bool 19 | wg sync.WaitGroup 20 | } 21 | 22 | var mods []*module 23 | 24 | func Register(mi Module) { 25 | m := new(module) 26 | m.mi = mi 27 | m.closeSig = make(chan bool, 1) 28 | 29 | mods = append(mods, m) 30 | } 31 | 32 | func Init() { 33 | for i := 0; i < len(mods); i++ { 34 | mods[i].mi.OnInit() 35 | } 36 | 37 | for i := 0; i < len(mods); i++ { 38 | m := mods[i] 39 | m.wg.Add(1) 40 | go run(m) 41 | } 42 | } 43 | 44 | func Destroy() { 45 | for i := len(mods) - 1; i >= 0; i-- { 46 | m := mods[i] 47 | m.closeSig <- true 48 | m.wg.Wait() 49 | destroy(m) 50 | } 51 | } 52 | 53 | func run(m *module) { 54 | m.mi.Run(m.closeSig) 55 | m.wg.Done() 56 | } 57 | 58 | func destroy(m *module) { 59 | defer func() { 60 | if r := recover(); r != nil { 61 | if conf.LenStackBuf > 0 { 62 | buf := make([]byte, conf.LenStackBuf) 63 | l := runtime.Stack(buf, false) 64 | log.Error("%v: %s", r, buf[:l]) 65 | } else { 66 | log.Error("%v", r) 67 | } 68 | } 69 | }() 70 | 71 | m.mi.OnDestroy() 72 | } 73 | -------------------------------------------------------------------------------- /module/skeleton.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/chanrpc" 5 | "github.com/zsai001/leaf_cluster/console" 6 | "github.com/zsai001/leaf_cluster/go" 7 | "github.com/zsai001/leaf_cluster/timer" 8 | "time" 9 | ) 10 | 11 | type Skeleton struct { 12 | GoLen int 13 | TimerDispatcherLen int 14 | AsynCallLen int 15 | ChanRPCServer *chanrpc.Server 16 | g *g.Go 17 | dispatcher *timer.Dispatcher 18 | client *chanrpc.Client 19 | server *chanrpc.Server 20 | commandServer *chanrpc.Server 21 | } 22 | 23 | func (s *Skeleton) Init() { 24 | if s.GoLen <= 0 { 25 | s.GoLen = 0 26 | } 27 | if s.TimerDispatcherLen <= 0 { 28 | s.TimerDispatcherLen = 0 29 | } 30 | if s.AsynCallLen <= 0 { 31 | s.AsynCallLen = 0 32 | } 33 | 34 | s.g = g.New(s.GoLen) 35 | s.dispatcher = timer.NewDispatcher(s.TimerDispatcherLen) 36 | s.client = chanrpc.NewClient(s.AsynCallLen) 37 | s.server = s.ChanRPCServer 38 | 39 | if s.server == nil { 40 | s.server = chanrpc.NewServer(0) 41 | } 42 | s.commandServer = chanrpc.NewServer(0) 43 | } 44 | 45 | func (s *Skeleton) Run(closeSig chan bool) { 46 | for { 47 | select { 48 | case <-closeSig: 49 | s.commandServer.Close() 50 | s.server.Close() 51 | for !s.g.Idle() || !s.client.Idle() { 52 | s.g.Close() 53 | s.client.Close() 54 | } 55 | return 56 | case ri := <-s.client.ChanAsynRet: 57 | s.client.Cb(ri) 58 | case ci := <-s.server.ChanCall: 59 | s.server.Exec(ci) 60 | case ci := <-s.commandServer.ChanCall: 61 | s.commandServer.Exec(ci) 62 | case cb := <-s.g.ChanCb: 63 | s.g.Cb(cb) 64 | case t := <-s.dispatcher.ChanTimer: 65 | t.Cb() 66 | } 67 | } 68 | } 69 | 70 | func (s *Skeleton) AfterFunc(d time.Duration, cb func()) *timer.Timer { 71 | if s.TimerDispatcherLen == 0 { 72 | panic("invalid TimerDispatcherLen") 73 | } 74 | 75 | return s.dispatcher.AfterFunc(d, cb) 76 | } 77 | 78 | func (s *Skeleton) CronFunc(cronExpr *timer.CronExpr, cb func()) *timer.Cron { 79 | if s.TimerDispatcherLen == 0 { 80 | panic("invalid TimerDispatcherLen") 81 | } 82 | 83 | return s.dispatcher.CronFunc(cronExpr, cb) 84 | } 85 | 86 | func (s *Skeleton) Go(f func(), cb func()) { 87 | if s.GoLen == 0 { 88 | panic("invalid GoLen") 89 | } 90 | 91 | s.g.Go(f, cb) 92 | } 93 | 94 | func (s *Skeleton) NewLinearContext() *g.LinearContext { 95 | if s.GoLen == 0 { 96 | panic("invalid GoLen") 97 | } 98 | 99 | return s.g.NewLinearContext() 100 | } 101 | 102 | func (s *Skeleton) AsynCall(server *chanrpc.Server, id interface{}, args ...interface{}) { 103 | if s.AsynCallLen == 0 { 104 | panic("invalid AsynCallLen") 105 | } 106 | 107 | s.client.Attach(server) 108 | s.client.AsynCall(id, args...) 109 | } 110 | 111 | func (s *Skeleton) RegisterChanRPC(id interface{}, f interface{}) { 112 | if s.ChanRPCServer == nil { 113 | panic("invalid ChanRPCServer") 114 | } 115 | 116 | s.server.Register(id, f) 117 | } 118 | 119 | func (s *Skeleton) RegisterCommand(name string, help string, f interface{}) { 120 | console.Register(name, help, f, s.commandServer) 121 | } 122 | -------------------------------------------------------------------------------- /network/agent.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Agent interface { 4 | Run() 5 | OnClose() 6 | } 7 | -------------------------------------------------------------------------------- /network/conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type Conn interface { 8 | ReadMsg() ([]byte, error) 9 | WriteMsg(args ...[]byte) error 10 | LocalAddr() net.Addr 11 | RemoteAddr() net.Addr 12 | Close() 13 | Destroy() 14 | } 15 | -------------------------------------------------------------------------------- /network/processor.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Processor interface { 4 | // must goroutine safe 5 | Route(msg interface{}, userData interface{}) error 6 | // must goroutine safe 7 | Unmarshal(data []byte) (interface{}, error) 8 | // must goroutine safe 9 | Marshal(msg interface{}) ([][]byte, error) 10 | } 11 | -------------------------------------------------------------------------------- /network/tcp_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/log" 5 | "net" 6 | "sync" 7 | "time" 8 | "github.com/pkg/errors" 9 | "io" 10 | ) 11 | 12 | type TCPClient struct { 13 | sync.Mutex 14 | Addr string 15 | ConnNum int 16 | ConnectInterval time.Duration 17 | PendingWriteNum int 18 | AutoReconnect bool 19 | NewAgent func(*TCPConn) Agent 20 | conns ConnSet 21 | wg sync.WaitGroup 22 | closeFlag bool 23 | 24 | // msg parser 25 | LenMsgLen int 26 | MinMsgLen uint32 27 | MaxMsgLen uint32 28 | LittleEndian bool 29 | msgParser *MsgParser 30 | } 31 | 32 | func (client *TCPClient) Start() { 33 | client.init() 34 | 35 | for i := 0; i < client.ConnNum; i++ { 36 | client.wg.Add(1) 37 | go client.connect() 38 | } 39 | } 40 | 41 | func (client *TCPClient) init() { 42 | client.Lock() 43 | defer client.Unlock() 44 | 45 | if client.ConnNum <= 0 { 46 | client.ConnNum = 1 47 | log.Release("invalid ConnNum, reset to %v", client.ConnNum) 48 | } 49 | if client.ConnectInterval <= 0 { 50 | client.ConnectInterval = 3 * time.Second 51 | log.Release("invalid ConnectInterval, reset to %v", client.ConnectInterval) 52 | } 53 | if client.PendingWriteNum <= 0 { 54 | client.PendingWriteNum = 100 55 | log.Release("invalid PendingWriteNum, reset to %v", client.PendingWriteNum) 56 | } 57 | if client.NewAgent == nil { 58 | log.Fatal("NewAgent must not be nil") 59 | } 60 | if client.conns != nil { 61 | log.Fatal("client is running") 62 | } 63 | 64 | client.conns = make(ConnSet) 65 | client.closeFlag = false 66 | 67 | // msg parser 68 | msgParser := NewMsgParser() 69 | msgParser.SetMsgLen(client.MinMsgLen, client.MaxMsgLen) 70 | msgParser.SetByteOrder(client.LittleEndian) 71 | client.msgParser = msgParser 72 | } 73 | 74 | func (client *TCPClient) dial() net.Conn { 75 | for { 76 | conn, err := net.Dial("tcp", client.Addr) 77 | if err == nil || client.closeFlag { 78 | err = client.shakeHand(conn) 79 | if err != nil { 80 | conn.Close() 81 | }else { 82 | return conn 83 | } 84 | } 85 | 86 | log.Release("connect to %v error: %v", client.Addr, err) 87 | time.Sleep(client.ConnectInterval) 88 | continue 89 | } 90 | } 91 | 92 | func (client *TCPClient) shakeHand(conn net.Conn) error { 93 | check := []byte("LeafNO.1") 94 | conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) 95 | size, err := conn.Write(check) 96 | if err != nil { 97 | return err 98 | } 99 | if size != len(check) { 100 | return errors.New("unable to send full check data size") 101 | } 102 | data := make([]byte, len(check)) 103 | conn.SetReadDeadline(time.Now().Add(1 * time.Second)) 104 | _, err = io.ReadAtLeast(conn, data, len(check)) 105 | if err != nil { 106 | return err 107 | } 108 | if string(data) != string(check) { 109 | return errors.New("check data not match") 110 | } 111 | return nil 112 | } 113 | 114 | func (client *TCPClient) connect() { 115 | defer client.wg.Done() 116 | 117 | reconnect: 118 | conn := client.dial() 119 | if conn == nil { 120 | return 121 | } 122 | 123 | client.Lock() 124 | if client.closeFlag { 125 | client.Unlock() 126 | conn.Close() 127 | return 128 | } 129 | client.conns[conn] = struct{}{} 130 | client.Unlock() 131 | 132 | tcpConn := newTCPConn(conn, client.PendingWriteNum, client.msgParser) 133 | agent := client.NewAgent(tcpConn) 134 | agent.Run() 135 | 136 | // cleanup 137 | tcpConn.Close() 138 | client.Lock() 139 | delete(client.conns, conn) 140 | client.Unlock() 141 | agent.OnClose() 142 | 143 | if client.AutoReconnect { 144 | time.Sleep(client.ConnectInterval) 145 | goto reconnect 146 | } 147 | } 148 | 149 | func (client *TCPClient) Close() { 150 | client.Lock() 151 | client.closeFlag = true 152 | for conn := range client.conns { 153 | conn.Close() 154 | } 155 | client.conns = nil 156 | client.Unlock() 157 | 158 | client.wg.Wait() 159 | } 160 | -------------------------------------------------------------------------------- /network/tcp_conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/log" 5 | "net" 6 | "sync" 7 | ) 8 | 9 | type ConnSet map[net.Conn]struct{} 10 | 11 | type TCPConn struct { 12 | sync.Mutex 13 | conn net.Conn 14 | writeChan chan []byte 15 | closeFlag bool 16 | msgParser *MsgParser 17 | } 18 | 19 | func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser) *TCPConn { 20 | tcpConn := new(TCPConn) 21 | tcpConn.conn = conn 22 | tcpConn.writeChan = make(chan []byte, pendingWriteNum) 23 | tcpConn.msgParser = msgParser 24 | 25 | go func() { 26 | for b := range tcpConn.writeChan { 27 | if b == nil { 28 | break 29 | } 30 | 31 | _, err := conn.Write(b) 32 | if err != nil { 33 | break 34 | } 35 | } 36 | 37 | conn.Close() 38 | tcpConn.Lock() 39 | tcpConn.closeFlag = true 40 | tcpConn.Unlock() 41 | }() 42 | 43 | return tcpConn 44 | } 45 | 46 | func (tcpConn *TCPConn) doDestroy() { 47 | tcpConn.conn.(*net.TCPConn).SetLinger(0) 48 | tcpConn.conn.Close() 49 | 50 | if !tcpConn.closeFlag { 51 | close(tcpConn.writeChan) 52 | tcpConn.closeFlag = true 53 | } 54 | } 55 | 56 | func (tcpConn *TCPConn) Destroy() { 57 | tcpConn.Lock() 58 | defer tcpConn.Unlock() 59 | 60 | tcpConn.doDestroy() 61 | } 62 | 63 | func (tcpConn *TCPConn) Close() { 64 | tcpConn.Lock() 65 | defer tcpConn.Unlock() 66 | if tcpConn.closeFlag { 67 | return 68 | } 69 | 70 | tcpConn.doWrite(nil) 71 | tcpConn.closeFlag = true 72 | } 73 | 74 | func (tcpConn *TCPConn) doWrite(b []byte) { 75 | if len(tcpConn.writeChan) == cap(tcpConn.writeChan) { 76 | log.Debug("close conn: channel full") 77 | tcpConn.doDestroy() 78 | return 79 | } 80 | 81 | tcpConn.writeChan <- b 82 | } 83 | 84 | // b must not be modified by the others goroutines 85 | func (tcpConn *TCPConn) Write(b []byte) { 86 | tcpConn.Lock() 87 | defer tcpConn.Unlock() 88 | if tcpConn.closeFlag || b == nil { 89 | return 90 | } 91 | 92 | tcpConn.doWrite(b) 93 | } 94 | 95 | func (tcpConn *TCPConn) Read(b []byte) (int, error) { 96 | return tcpConn.conn.Read(b) 97 | } 98 | 99 | func (tcpConn *TCPConn) LocalAddr() net.Addr { 100 | return tcpConn.conn.LocalAddr() 101 | } 102 | 103 | func (tcpConn *TCPConn) RemoteAddr() net.Addr { 104 | return tcpConn.conn.RemoteAddr() 105 | } 106 | 107 | func (tcpConn *TCPConn) ReadMsg() ([]byte, error) { 108 | return tcpConn.msgParser.Read(tcpConn) 109 | } 110 | 111 | func (tcpConn *TCPConn) WriteMsg(args ...[]byte) error { 112 | return tcpConn.msgParser.Write(tcpConn, args...) 113 | } 114 | -------------------------------------------------------------------------------- /network/tcp_msg.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | // ------------------------ 10 | // | len | type |data | # | 11 | // ------------------------ 12 | 13 | type MsgParser struct { 14 | minMsgLen uint32 15 | maxMsgLen uint32 16 | littleEndian bool 17 | } 18 | 19 | const ( 20 | DefaultMaxLen = 4096 21 | DefaultMinLen = 4 22 | ) 23 | 24 | func NewMsgParser() *MsgParser { 25 | p := new(MsgParser) 26 | p.minMsgLen = DefaultMinLen 27 | p.maxMsgLen = DefaultMaxLen 28 | 29 | return p 30 | } 31 | 32 | // It's dangerous to call the method on reading or writing 33 | func (p *MsgParser) SetMsgLen(minMsgLen uint32, maxMsgLen uint32) { 34 | if minMsgLen != 0 { 35 | p.minMsgLen = minMsgLen 36 | } 37 | if maxMsgLen != 0 { 38 | p.maxMsgLen = maxMsgLen 39 | } 40 | } 41 | 42 | // It's dangerous to call the method on reading or writing 43 | func (p *MsgParser) SetByteOrder(littleEndian bool) { 44 | p.littleEndian = littleEndian 45 | } 46 | 47 | // goroutine safe 48 | func (p *MsgParser) Read(conn *TCPConn) ([]byte, error) { 49 | var bufMsgLen = make([]byte, 2) 50 | // read len 51 | if _, err := io.ReadFull(conn, bufMsgLen[0:]); err != nil { 52 | return nil, err 53 | } 54 | 55 | // parse len 56 | var msgLen uint32 57 | msgLen = uint32(binary.LittleEndian.Uint16(bufMsgLen[0:2])) 58 | // check len 59 | if msgLen > p.maxMsgLen { 60 | return nil, errors.New("message too long") 61 | } else if msgLen < p.minMsgLen { 62 | return nil, errors.New("message too short") 63 | } 64 | // data 65 | msgData := make([]byte, msgLen) 66 | if _, err := io.ReadFull(conn, msgData); err != nil { 67 | return nil, err 68 | } 69 | if msgData[msgLen] != '#' { 70 | return nil, errors.New("invalid message end") 71 | } 72 | return msgData, nil 73 | } 74 | 75 | // goroutine safe 76 | func (p *MsgParser) Write(conn *TCPConn, args ...[]byte) error { 77 | // get len 78 | var msgLen uint32 79 | for i := 0; i < len(args); i++ { 80 | msgLen += uint32(len(args[i])) 81 | } 82 | 83 | // check len 84 | if msgLen > p.maxMsgLen { 85 | return errors.New("message too long") 86 | } else if msgLen < p.minMsgLen { 87 | return errors.New("message too short") 88 | } 89 | 90 | msg := make([]byte, 2+msgLen+1) 91 | // write len 92 | binary.LittleEndian.PutUint16(msg, uint16(msgLen)) 93 | // write data 94 | l := 2 95 | for i := 0; i < len(args); i++ { 96 | copy(msg[l:], args[i]) 97 | l += len(args[i]) 98 | } 99 | msg[l] = '#' 100 | conn.Write(msg) 101 | 102 | return nil 103 | } 104 | -------------------------------------------------------------------------------- /network/tcp_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/log" 5 | "net" 6 | "sync" 7 | "time" 8 | "io" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | type TCPServer struct { 13 | Addr string 14 | MaxConnNum int 15 | PendingWriteNum int 16 | NewAgent func(*TCPConn) Agent 17 | ln net.Listener 18 | conns ConnSet 19 | mutexConns sync.Mutex 20 | wgLn sync.WaitGroup 21 | wgConns sync.WaitGroup 22 | 23 | // msg parser 24 | LenMsgLen int 25 | MinMsgLen uint32 26 | MaxMsgLen uint32 27 | LittleEndian bool 28 | msgParser *MsgParser 29 | } 30 | 31 | func (server *TCPServer) Start() { 32 | server.init() 33 | go server.run() 34 | } 35 | 36 | func (server *TCPServer) init() { 37 | ln, err := net.Listen("tcp", server.Addr) 38 | if err != nil { 39 | log.Fatal("%v", err) 40 | } 41 | 42 | if server.MaxConnNum <= 0 { 43 | server.MaxConnNum = 100 44 | log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum) 45 | } 46 | if server.PendingWriteNum <= 0 { 47 | server.PendingWriteNum = 100 48 | log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum) 49 | } 50 | if server.NewAgent == nil { 51 | log.Fatal("NewAgent must not be nil") 52 | } 53 | 54 | server.ln = ln 55 | server.conns = make(ConnSet) 56 | 57 | // msg parser 58 | msgParser := NewMsgParser() 59 | msgParser.SetMsgLen(server.MinMsgLen, server.MaxMsgLen) 60 | msgParser.SetByteOrder(server.LittleEndian) 61 | server.msgParser = msgParser 62 | } 63 | 64 | func (server *TCPServer) shakeHand(conn net.Conn) error { 65 | check := []byte("LeafNo.1") 66 | data := make([]byte, len(check)) 67 | conn.SetReadDeadline(time.Now().Add(1 * time.Second)) 68 | _, err := io.ReadAtLeast(conn, data, len(check)) 69 | if err != nil { 70 | return err 71 | } 72 | if string(data) != string(check) { 73 | return errors.New("shake hand failed with invalid check data") 74 | } 75 | conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) 76 | size, err := conn.Write(check) 77 | if err != nil { 78 | return err 79 | } 80 | if size != len(data) { 81 | return errors.New("shake hand failed with unable send full data size") 82 | } 83 | return nil 84 | } 85 | 86 | func (server *TCPServer) run() { 87 | server.wgLn.Add(1) 88 | defer server.wgLn.Done() 89 | 90 | var tempDelay time.Duration 91 | for { 92 | conn, err := server.ln.Accept() 93 | if err != nil { 94 | if ne, ok := err.(net.Error); ok && ne.Temporary() { 95 | if tempDelay == 0 { 96 | tempDelay = 5 * time.Millisecond 97 | } else { 98 | tempDelay *= 2 99 | } 100 | if max := 1 * time.Second; tempDelay > max { 101 | tempDelay = max 102 | } 103 | log.Release("accept error: %v; retrying in %v", err, tempDelay) 104 | time.Sleep(tempDelay) 105 | continue 106 | } 107 | return 108 | } 109 | tempDelay = 0 110 | 111 | err = server.shakeHand(conn) 112 | if err != nil { 113 | conn.Close() 114 | continue 115 | } 116 | 117 | server.mutexConns.Lock() 118 | if len(server.conns) >= server.MaxConnNum { 119 | server.mutexConns.Unlock() 120 | conn.Close() 121 | log.Debug("too many connections") 122 | continue 123 | } 124 | 125 | server.conns[conn] = struct{}{} 126 | server.mutexConns.Unlock() 127 | 128 | server.wgConns.Add(1) 129 | 130 | tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser) 131 | agent := server.NewAgent(tcpConn) 132 | go func() { 133 | agent.Run() 134 | 135 | // cleanup 136 | tcpConn.Close() 137 | server.mutexConns.Lock() 138 | delete(server.conns, conn) 139 | server.mutexConns.Unlock() 140 | agent.OnClose() 141 | 142 | server.wgConns.Done() 143 | }() 144 | } 145 | } 146 | 147 | func (server *TCPServer) Close() { 148 | server.ln.Close() 149 | server.wgLn.Wait() 150 | 151 | server.mutexConns.Lock() 152 | for conn := range server.conns { 153 | conn.Close() 154 | } 155 | server.conns = nil 156 | server.mutexConns.Unlock() 157 | server.wgConns.Wait() 158 | } 159 | -------------------------------------------------------------------------------- /network/ws_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type WSClient struct { 11 | sync.Mutex 12 | Addr string 13 | ConnNum int 14 | ConnectInterval time.Duration 15 | PendingWriteNum int 16 | MaxMsgLen uint32 17 | HandshakeTimeout time.Duration 18 | AutoReconnect bool 19 | NewAgent func(*WSConn) Agent 20 | dialer websocket.Dialer 21 | conns WebsocketConnSet 22 | wg sync.WaitGroup 23 | closeFlag bool 24 | } 25 | 26 | func (client *WSClient) Start() { 27 | client.init() 28 | 29 | for i := 0; i < client.ConnNum; i++ { 30 | client.wg.Add(1) 31 | go client.connect() 32 | } 33 | } 34 | 35 | func (client *WSClient) init() { 36 | client.Lock() 37 | defer client.Unlock() 38 | 39 | if client.ConnNum <= 0 { 40 | client.ConnNum = 1 41 | log.Release("invalid ConnNum, reset to %v", client.ConnNum) 42 | } 43 | if client.ConnectInterval <= 0 { 44 | client.ConnectInterval = 3 * time.Second 45 | log.Release("invalid ConnectInterval, reset to %v", client.ConnectInterval) 46 | } 47 | if client.PendingWriteNum <= 0 { 48 | client.PendingWriteNum = 100 49 | log.Release("invalid PendingWriteNum, reset to %v", client.PendingWriteNum) 50 | } 51 | if client.MaxMsgLen <= 0 { 52 | client.MaxMsgLen = 4096 53 | log.Release("invalid MaxMsgLen, reset to %v", client.MaxMsgLen) 54 | } 55 | if client.HandshakeTimeout <= 0 { 56 | client.HandshakeTimeout = 10 * time.Second 57 | log.Release("invalid HandshakeTimeout, reset to %v", client.HandshakeTimeout) 58 | } 59 | if client.NewAgent == nil { 60 | log.Fatal("NewAgent must not be nil") 61 | } 62 | if client.conns != nil { 63 | log.Fatal("client is running") 64 | } 65 | 66 | client.conns = make(WebsocketConnSet) 67 | client.closeFlag = false 68 | client.dialer = websocket.Dialer{ 69 | HandshakeTimeout: client.HandshakeTimeout, 70 | } 71 | } 72 | 73 | func (client *WSClient) dial() *websocket.Conn { 74 | for { 75 | conn, _, err := client.dialer.Dial(client.Addr, nil) 76 | if err == nil || client.closeFlag { 77 | return conn 78 | } 79 | 80 | log.Release("connect to %v error: %v", client.Addr, err) 81 | time.Sleep(client.ConnectInterval) 82 | continue 83 | } 84 | } 85 | 86 | func (client *WSClient) connect() { 87 | defer client.wg.Done() 88 | 89 | reconnect: 90 | conn := client.dial() 91 | if conn == nil { 92 | return 93 | } 94 | conn.SetReadLimit(int64(client.MaxMsgLen)) 95 | 96 | client.Lock() 97 | if client.closeFlag { 98 | client.Unlock() 99 | conn.Close() 100 | return 101 | } 102 | client.conns[conn] = struct{}{} 103 | client.Unlock() 104 | 105 | wsConn := newWSConn(conn, client.PendingWriteNum, client.MaxMsgLen) 106 | agent := client.NewAgent(wsConn) 107 | agent.Run() 108 | 109 | // cleanup 110 | wsConn.Close() 111 | client.Lock() 112 | delete(client.conns, conn) 113 | client.Unlock() 114 | agent.OnClose() 115 | 116 | if client.AutoReconnect { 117 | time.Sleep(client.ConnectInterval) 118 | goto reconnect 119 | } 120 | } 121 | 122 | func (client *WSClient) Close() { 123 | client.Lock() 124 | client.closeFlag = true 125 | for conn := range client.conns { 126 | conn.Close() 127 | } 128 | client.conns = nil 129 | client.Unlock() 130 | 131 | client.wg.Wait() 132 | } 133 | -------------------------------------------------------------------------------- /network/ws_conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "errors" 5 | "github.com/gorilla/websocket" 6 | "github.com/zsai001/leaf_cluster/log" 7 | "net" 8 | "sync" 9 | ) 10 | 11 | type WebsocketConnSet map[*websocket.Conn]struct{} 12 | 13 | type WSConn struct { 14 | sync.Mutex 15 | conn *websocket.Conn 16 | writeChan chan []byte 17 | maxMsgLen uint32 18 | closeFlag bool 19 | } 20 | 21 | func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32) *WSConn { 22 | wsConn := new(WSConn) 23 | wsConn.conn = conn 24 | wsConn.writeChan = make(chan []byte, pendingWriteNum) 25 | wsConn.maxMsgLen = maxMsgLen 26 | 27 | go func() { 28 | for b := range wsConn.writeChan { 29 | if b == nil { 30 | break 31 | } 32 | 33 | err := conn.WriteMessage(websocket.BinaryMessage, b) 34 | if err != nil { 35 | break 36 | } 37 | } 38 | 39 | conn.Close() 40 | wsConn.Lock() 41 | wsConn.closeFlag = true 42 | wsConn.Unlock() 43 | }() 44 | 45 | return wsConn 46 | } 47 | 48 | func (wsConn *WSConn) doDestroy() { 49 | wsConn.conn.UnderlyingConn().(*net.TCPConn).SetLinger(0) 50 | wsConn.conn.Close() 51 | 52 | if !wsConn.closeFlag { 53 | close(wsConn.writeChan) 54 | wsConn.closeFlag = true 55 | } 56 | } 57 | 58 | func (wsConn *WSConn) Destroy() { 59 | wsConn.Lock() 60 | defer wsConn.Unlock() 61 | 62 | wsConn.doDestroy() 63 | } 64 | 65 | func (wsConn *WSConn) Close() { 66 | wsConn.Lock() 67 | defer wsConn.Unlock() 68 | if wsConn.closeFlag { 69 | return 70 | } 71 | 72 | wsConn.doWrite(nil) 73 | wsConn.closeFlag = true 74 | } 75 | 76 | func (wsConn *WSConn) doWrite(b []byte) { 77 | if len(wsConn.writeChan) == cap(wsConn.writeChan) { 78 | log.Debug("close conn: channel full") 79 | wsConn.doDestroy() 80 | return 81 | } 82 | 83 | wsConn.writeChan <- b 84 | } 85 | 86 | func (wsConn *WSConn) LocalAddr() net.Addr { 87 | return wsConn.conn.LocalAddr() 88 | } 89 | 90 | func (wsConn *WSConn) RemoteAddr() net.Addr { 91 | return wsConn.conn.RemoteAddr() 92 | } 93 | 94 | // goroutine not safe 95 | func (wsConn *WSConn) ReadMsg() ([]byte, error) { 96 | _, b, err := wsConn.conn.ReadMessage() 97 | return b, err 98 | } 99 | 100 | // args must not be modified by the others goroutines 101 | func (wsConn *WSConn) WriteMsg(args ...[]byte) error { 102 | wsConn.Lock() 103 | defer wsConn.Unlock() 104 | if wsConn.closeFlag { 105 | return nil 106 | } 107 | 108 | // get len 109 | var msgLen uint32 110 | for i := 0; i < len(args); i++ { 111 | msgLen += uint32(len(args[i])) 112 | } 113 | 114 | // check len 115 | if msgLen > wsConn.maxMsgLen { 116 | return errors.New("message too long") 117 | } else if msgLen < 1 { 118 | return errors.New("message too short") 119 | } 120 | 121 | // don't copy 122 | if len(args) == 1 { 123 | wsConn.doWrite(args[0]) 124 | return nil 125 | } 126 | 127 | // merge the args 128 | msg := make([]byte, msgLen) 129 | l := 0 130 | for i := 0; i < len(args); i++ { 131 | copy(msg[l:], args[i]) 132 | l += len(args[i]) 133 | } 134 | 135 | wsConn.doWrite(msg) 136 | 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /network/ws_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "crypto/tls" 5 | "github.com/gorilla/websocket" 6 | "github.com/zsai001/leaf_cluster/log" 7 | "net" 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type WSServer struct { 14 | Addr string 15 | MaxConnNum int 16 | PendingWriteNum int 17 | MaxMsgLen uint32 18 | HTTPTimeout time.Duration 19 | CertFile string 20 | KeyFile string 21 | NewAgent func(*WSConn) Agent 22 | ln net.Listener 23 | handler *WSHandler 24 | } 25 | 26 | type WSHandler struct { 27 | maxConnNum int 28 | pendingWriteNum int 29 | maxMsgLen uint32 30 | newAgent func(*WSConn) Agent 31 | upgrader websocket.Upgrader 32 | conns WebsocketConnSet 33 | mutexConns sync.Mutex 34 | wg sync.WaitGroup 35 | } 36 | 37 | func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 38 | if r.Method != "GET" { 39 | http.Error(w, "Method not allowed", 405) 40 | return 41 | } 42 | conn, err := handler.upgrader.Upgrade(w, r, nil) 43 | if err != nil { 44 | log.Debug("upgrade error: %v", err) 45 | return 46 | } 47 | conn.SetReadLimit(int64(handler.maxMsgLen)) 48 | 49 | handler.wg.Add(1) 50 | defer handler.wg.Done() 51 | 52 | handler.mutexConns.Lock() 53 | if handler.conns == nil { 54 | handler.mutexConns.Unlock() 55 | conn.Close() 56 | return 57 | } 58 | if len(handler.conns) >= handler.maxConnNum { 59 | handler.mutexConns.Unlock() 60 | conn.Close() 61 | log.Debug("too many connections") 62 | return 63 | } 64 | handler.conns[conn] = struct{}{} 65 | handler.mutexConns.Unlock() 66 | 67 | wsConn := newWSConn(conn, handler.pendingWriteNum, handler.maxMsgLen) 68 | agent := handler.newAgent(wsConn) 69 | agent.Run() 70 | 71 | // cleanup 72 | wsConn.Close() 73 | handler.mutexConns.Lock() 74 | delete(handler.conns, conn) 75 | handler.mutexConns.Unlock() 76 | agent.OnClose() 77 | } 78 | 79 | func (server *WSServer) Start() { 80 | ln, err := net.Listen("tcp", server.Addr) 81 | if err != nil { 82 | log.Fatal("%v", err) 83 | } 84 | 85 | if server.MaxConnNum <= 0 { 86 | server.MaxConnNum = 100 87 | log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum) 88 | } 89 | if server.PendingWriteNum <= 0 { 90 | server.PendingWriteNum = 100 91 | log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum) 92 | } 93 | if server.MaxMsgLen <= 0 { 94 | server.MaxMsgLen = 4096 95 | log.Release("invalid MaxMsgLen, reset to %v", server.MaxMsgLen) 96 | } 97 | if server.HTTPTimeout <= 0 { 98 | server.HTTPTimeout = 10 * time.Second 99 | log.Release("invalid HTTPTimeout, reset to %v", server.HTTPTimeout) 100 | } 101 | if server.NewAgent == nil { 102 | log.Fatal("NewAgent must not be nil") 103 | } 104 | 105 | if server.CertFile != "" || server.KeyFile != "" { 106 | config := &tls.Config{} 107 | config.NextProtos = []string{"http/1.1"} 108 | 109 | var err error 110 | config.Certificates = make([]tls.Certificate, 1) 111 | config.Certificates[0], err = tls.LoadX509KeyPair(server.CertFile, server.KeyFile) 112 | if err != nil { 113 | log.Fatal("%v", err) 114 | } 115 | 116 | ln = tls.NewListener(ln, config) 117 | } 118 | 119 | server.ln = ln 120 | server.handler = &WSHandler{ 121 | maxConnNum: server.MaxConnNum, 122 | pendingWriteNum: server.PendingWriteNum, 123 | maxMsgLen: server.MaxMsgLen, 124 | newAgent: server.NewAgent, 125 | conns: make(WebsocketConnSet), 126 | upgrader: websocket.Upgrader{ 127 | HandshakeTimeout: server.HTTPTimeout, 128 | CheckOrigin: func(_ *http.Request) bool { return true }, 129 | }, 130 | } 131 | 132 | httpServer := &http.Server{ 133 | Addr: server.Addr, 134 | Handler: server.handler, 135 | ReadTimeout: server.HTTPTimeout, 136 | WriteTimeout: server.HTTPTimeout, 137 | MaxHeaderBytes: 1024, 138 | } 139 | 140 | go httpServer.Serve(ln) 141 | } 142 | 143 | func (server *WSServer) Close() { 144 | server.ln.Close() 145 | 146 | server.handler.mutexConns.Lock() 147 | for conn := range server.handler.conns { 148 | conn.Close() 149 | } 150 | server.handler.conns = nil 151 | server.handler.mutexConns.Unlock() 152 | 153 | server.handler.wg.Wait() 154 | } 155 | -------------------------------------------------------------------------------- /recordfile/example_test.go: -------------------------------------------------------------------------------- 1 | package recordfile_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/recordfile" 6 | ) 7 | 8 | func Example() { 9 | type Record struct { 10 | // index 0 11 | IndexInt int "index" 12 | // index 1 13 | IndexStr string "index" 14 | _Number int32 15 | Str string 16 | Arr1 [2]int 17 | Arr2 [3][2]int 18 | Arr3 []int 19 | St struct { 20 | Name string "name" 21 | Num int "num" 22 | } 23 | M map[string]int 24 | } 25 | 26 | rf, err := recordfile.New(Record{}) 27 | if err != nil { 28 | return 29 | } 30 | 31 | err = rf.Read("test.txt") 32 | if err != nil { 33 | return 34 | } 35 | 36 | for i := 0; i < rf.NumRecord(); i++ { 37 | r := rf.Record(i).(*Record) 38 | fmt.Println(r.IndexInt) 39 | } 40 | 41 | r := rf.Index(2).(*Record) 42 | fmt.Println(r.Str) 43 | 44 | r = rf.Indexes(0)[2].(*Record) 45 | fmt.Println(r.Str) 46 | 47 | r = rf.Indexes(1)["three"].(*Record) 48 | fmt.Println(r.Str) 49 | fmt.Println(r.Arr1[1]) 50 | fmt.Println(r.Arr2[2][0]) 51 | fmt.Println(r.Arr3[0]) 52 | fmt.Println(r.St.Name) 53 | fmt.Println(r.M["key6"]) 54 | 55 | // Output: 56 | // 1 57 | // 2 58 | // 3 59 | // cat 60 | // cat 61 | // book 62 | // 6 63 | // 4 64 | // 6 65 | // name5566 66 | // 6 67 | } 68 | -------------------------------------------------------------------------------- /recordfile/test.txt: -------------------------------------------------------------------------------- 1 | 数字索引 字符串索引 数字类型 字符串类型 数组类型 嵌套数组 变长数组 结构体类型 map类型 2 | 1 one 0 knife "[1, 2]" "[[0,1], [1,2], [2,3]]" "[1, 2, 3]" "{""name"": ""name5566"", ""num"": 1}" "{""key1"": 1, ""key2"": 2}" 3 | 2 two 0 cat "[3, 4]" "[[1,2], [2,3], [3,4]]" "[4, 5]" "{""name"": ""name5566"", ""num"": 2}" "{""key3"": 3, ""key4"": 4}" 4 | 3 three 0 book "[5, 6]" "[[2,3], [3,4], [4,5]]" [6] "{""name"": ""name5566"", ""num"": 3}" "{""key5"": 5, ""key6"": 6}" 5 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | 4 | type Server interface { 5 | 6 | } -------------------------------------------------------------------------------- /timer/example_test.go: -------------------------------------------------------------------------------- 1 | package timer_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/timer" 6 | "time" 7 | ) 8 | 9 | func ExampleTimer() { 10 | d := timer.NewDispatcher(10) 11 | 12 | // timer 1 13 | d.AfterFunc(1, func() { 14 | fmt.Println("My name is Leaf") 15 | }) 16 | 17 | // timer 2 18 | t := d.AfterFunc(1, func() { 19 | fmt.Println("will not print") 20 | }) 21 | t.Stop() 22 | 23 | // dispatch 24 | (<-d.ChanTimer).Cb() 25 | 26 | // Output: 27 | // My name is Leaf 28 | } 29 | 30 | func ExampleCronExpr() { 31 | cronExpr, err := timer.NewCronExpr("0 * * * *") 32 | if err != nil { 33 | return 34 | } 35 | 36 | fmt.Println(cronExpr.Next(time.Date( 37 | 2000, 1, 1, 38 | 20, 10, 5, 39 | 0, time.UTC, 40 | ))) 41 | 42 | // Output: 43 | // 2000-01-01 21:00:00 +0000 UTC 44 | } 45 | 46 | func ExampleCron() { 47 | d := timer.NewDispatcher(10) 48 | 49 | // cron expr 50 | cronExpr, err := timer.NewCronExpr("* * * * * *") 51 | if err != nil { 52 | return 53 | } 54 | 55 | // cron 56 | var c *timer.Cron 57 | c = d.CronFunc(cronExpr, func() { 58 | fmt.Println("My name is Leaf") 59 | c.Stop() 60 | }) 61 | 62 | // dispatch 63 | (<-d.ChanTimer).Cb() 64 | 65 | // Output: 66 | // My name is Leaf 67 | } 68 | -------------------------------------------------------------------------------- /timer/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "github.com/zsai001/leaf_cluster/conf" 5 | "github.com/zsai001/leaf_cluster/log" 6 | "runtime" 7 | "time" 8 | ) 9 | 10 | // one dispatcher per goroutine (goroutine not safe) 11 | type Dispatcher struct { 12 | ChanTimer chan *Timer 13 | } 14 | 15 | func NewDispatcher(l int) *Dispatcher { 16 | disp := new(Dispatcher) 17 | disp.ChanTimer = make(chan *Timer, l) 18 | return disp 19 | } 20 | 21 | // Timer 22 | type Timer struct { 23 | t *time.Timer 24 | cb func() 25 | } 26 | 27 | func (t *Timer) Stop() { 28 | t.t.Stop() 29 | t.cb = nil 30 | } 31 | 32 | func (t *Timer) Cb() { 33 | defer func() { 34 | t.cb = nil 35 | if r := recover(); r != nil { 36 | if conf.LenStackBuf > 0 { 37 | buf := make([]byte, conf.LenStackBuf) 38 | l := runtime.Stack(buf, false) 39 | log.Error("%v: %s", r, buf[:l]) 40 | } else { 41 | log.Error("%v", r) 42 | } 43 | } 44 | }() 45 | 46 | if t.cb != nil { 47 | t.cb() 48 | } 49 | } 50 | 51 | func (disp *Dispatcher) AfterFunc(d time.Duration, cb func()) *Timer { 52 | t := new(Timer) 53 | t.cb = cb 54 | t.t = time.AfterFunc(d, func() { 55 | disp.ChanTimer <- t 56 | }) 57 | return t 58 | } 59 | 60 | // Cron 61 | type Cron struct { 62 | t *Timer 63 | } 64 | 65 | func (c *Cron) Stop() { 66 | if c.t != nil { 67 | c.t.Stop() 68 | } 69 | } 70 | 71 | func (disp *Dispatcher) CronFunc(cronExpr *CronExpr, _cb func()) *Cron { 72 | c := new(Cron) 73 | 74 | now := time.Now() 75 | nextTime := cronExpr.Next(now) 76 | if nextTime.IsZero() { 77 | return c 78 | } 79 | 80 | // callback 81 | var cb func() 82 | cb = func() { 83 | defer _cb() 84 | 85 | now := time.Now() 86 | nextTime := cronExpr.Next(now) 87 | if nextTime.IsZero() { 88 | return 89 | } 90 | c.t = disp.AfterFunc(nextTime.Sub(now), cb) 91 | } 92 | 93 | c.t = disp.AfterFunc(nextTime.Sub(now), cb) 94 | return c 95 | } 96 | -------------------------------------------------------------------------------- /util/deepcopy.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // reference: https://github.com/mohae/deepcopy 4 | import ( 5 | "reflect" 6 | ) 7 | 8 | func deepCopy(dst, src reflect.Value) { 9 | switch src.Kind() { 10 | case reflect.Interface: 11 | value := src.Elem() 12 | if !value.IsValid() { 13 | return 14 | } 15 | newValue := reflect.New(value.Type()).Elem() 16 | deepCopy(newValue, value) 17 | dst.Set(newValue) 18 | case reflect.Ptr: 19 | value := src.Elem() 20 | if !value.IsValid() { 21 | return 22 | } 23 | dst.Set(reflect.New(value.Type())) 24 | deepCopy(dst.Elem(), value) 25 | case reflect.Map: 26 | dst.Set(reflect.MakeMap(src.Type())) 27 | keys := src.MapKeys() 28 | for _, key := range keys { 29 | value := src.MapIndex(key) 30 | newValue := reflect.New(value.Type()).Elem() 31 | deepCopy(newValue, value) 32 | dst.SetMapIndex(key, newValue) 33 | } 34 | case reflect.Slice: 35 | dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap())) 36 | for i := 0; i < src.Len(); i++ { 37 | deepCopy(dst.Index(i), src.Index(i)) 38 | } 39 | case reflect.Struct: 40 | typeSrc := src.Type() 41 | for i := 0; i < src.NumField(); i++ { 42 | value := src.Field(i) 43 | tag := typeSrc.Field(i).Tag 44 | if value.CanSet() && tag.Get("deepcopy") != "-" { 45 | deepCopy(dst.Field(i), value) 46 | } 47 | } 48 | default: 49 | dst.Set(src) 50 | } 51 | } 52 | 53 | func DeepCopy(dst, src interface{}) { 54 | typeDst := reflect.TypeOf(dst) 55 | typeSrc := reflect.TypeOf(src) 56 | if typeDst != typeSrc { 57 | panic("DeepCopy: " + typeDst.String() + " != " + typeSrc.String()) 58 | } 59 | if typeSrc.Kind() != reflect.Ptr { 60 | panic("DeepCopy: pass arguments by address") 61 | } 62 | 63 | valueDst := reflect.ValueOf(dst).Elem() 64 | valueSrc := reflect.ValueOf(src).Elem() 65 | if !valueDst.IsValid() || !valueSrc.IsValid() { 66 | panic("DeepCopy: invalid arguments") 67 | } 68 | 69 | deepCopy(valueDst, valueSrc) 70 | } 71 | 72 | func DeepClone(v interface{}) interface{} { 73 | dst := reflect.New(reflect.TypeOf(v)).Elem() 74 | deepCopy(dst, reflect.ValueOf(v)) 75 | return dst.Interface() 76 | } 77 | -------------------------------------------------------------------------------- /util/example_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zsai001/leaf_cluster/util" 6 | ) 7 | 8 | func ExampleMap() { 9 | m := new(util.Map) 10 | 11 | fmt.Println(m.Get("key")) 12 | m.Set("key", "value") 13 | fmt.Println(m.Get("key")) 14 | m.Del("key") 15 | fmt.Println(m.Get("key")) 16 | 17 | m.Set(1, "1") 18 | m.Set(2, 2) 19 | m.Set("3", 3) 20 | 21 | fmt.Println(m.Len()) 22 | 23 | // Output: 24 | // 25 | // value 26 | // 27 | // 3 28 | } 29 | 30 | func ExampleRandGroup() { 31 | i := util.RandGroup(0, 0, 50, 50) 32 | switch i { 33 | case 2, 3: 34 | fmt.Println("ok") 35 | } 36 | 37 | // Output: 38 | // ok 39 | } 40 | 41 | func ExampleRandInterval() { 42 | v := util.RandInterval(-1, 1) 43 | switch v { 44 | case -1, 0, 1: 45 | fmt.Println("ok") 46 | } 47 | 48 | // Output: 49 | // ok 50 | } 51 | 52 | func ExampleRandIntervalN() { 53 | r := util.RandIntervalN(-1, 0, 2) 54 | if r[0] == -1 && r[1] == 0 || 55 | r[0] == 0 && r[1] == -1 { 56 | fmt.Println("ok") 57 | } 58 | 59 | // Output: 60 | // ok 61 | } 62 | 63 | func ExampleDeepCopy() { 64 | src := []int{1, 2, 3} 65 | 66 | var dst []int 67 | util.DeepCopy(&dst, &src) 68 | 69 | for _, v := range dst { 70 | fmt.Println(v) 71 | } 72 | 73 | // Output: 74 | // 1 75 | // 2 76 | // 3 77 | } 78 | 79 | func ExampleDeepClone() { 80 | src := []int{1, 2, 3} 81 | 82 | dst := util.DeepClone(src).([]int) 83 | 84 | for _, v := range dst { 85 | fmt.Println(v) 86 | } 87 | 88 | // Output: 89 | // 1 90 | // 2 91 | // 3 92 | } 93 | -------------------------------------------------------------------------------- /util/map.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Map struct { 8 | sync.RWMutex 9 | m map[interface{}]interface{} 10 | } 11 | 12 | func (m *Map) init() { 13 | if m.m == nil { 14 | m.m = make(map[interface{}]interface{}) 15 | } 16 | } 17 | 18 | func (m *Map) UnsafeGet(key interface{}) interface{} { 19 | if m.m == nil { 20 | return nil 21 | } else { 22 | return m.m[key] 23 | } 24 | } 25 | 26 | func (m *Map) Get(key interface{}) interface{} { 27 | m.RLock() 28 | defer m.RUnlock() 29 | return m.UnsafeGet(key) 30 | } 31 | 32 | func (m *Map) UnsafeSet(key interface{}, value interface{}) { 33 | m.init() 34 | m.m[key] = value 35 | } 36 | 37 | func (m *Map) Set(key interface{}, value interface{}) { 38 | m.Lock() 39 | defer m.Unlock() 40 | m.UnsafeSet(key, value) 41 | } 42 | 43 | func (m *Map) TestAndSet(key interface{}, value interface{}) interface{} { 44 | m.Lock() 45 | defer m.Unlock() 46 | 47 | m.init() 48 | 49 | if v, ok := m.m[key]; ok { 50 | return v 51 | } else { 52 | m.m[key] = value 53 | return nil 54 | } 55 | } 56 | 57 | func (m *Map) UnsafeDel(key interface{}) { 58 | m.init() 59 | delete(m.m, key) 60 | } 61 | 62 | func (m *Map) Del(key interface{}) { 63 | m.Lock() 64 | defer m.Unlock() 65 | m.UnsafeDel(key) 66 | } 67 | 68 | func (m *Map) UnsafeLen() int { 69 | if m.m == nil { 70 | return 0 71 | } else { 72 | return len(m.m) 73 | } 74 | } 75 | 76 | func (m *Map) Len() int { 77 | m.RLock() 78 | defer m.RUnlock() 79 | return m.UnsafeLen() 80 | } 81 | 82 | func (m *Map) UnsafeRange(f func(interface{}, interface{})) { 83 | if m.m == nil { 84 | return 85 | } 86 | for k, v := range m.m { 87 | f(k, v) 88 | } 89 | } 90 | 91 | func (m *Map) RLockRange(f func(interface{}, interface{})) { 92 | m.RLock() 93 | defer m.RUnlock() 94 | m.UnsafeRange(f) 95 | } 96 | 97 | func (m *Map) LockRange(f func(interface{}, interface{})) { 98 | m.Lock() 99 | defer m.Unlock() 100 | m.UnsafeRange(f) 101 | } 102 | -------------------------------------------------------------------------------- /util/rand.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | func RandGroup(p ...uint32) int { 13 | if p == nil { 14 | panic("args not found") 15 | } 16 | 17 | r := make([]uint32, len(p)) 18 | for i := 0; i < len(p); i++ { 19 | if i == 0 { 20 | r[0] = p[0] 21 | } else { 22 | r[i] = r[i-1] + p[i] 23 | } 24 | } 25 | 26 | rl := r[len(r)-1] 27 | if rl == 0 { 28 | return 0 29 | } 30 | 31 | rn := uint32(rand.Int63n(int64(rl))) 32 | for i := 0; i < len(r); i++ { 33 | if rn < r[i] { 34 | return i 35 | } 36 | } 37 | 38 | panic("bug") 39 | } 40 | 41 | func RandInterval(b1, b2 int32) int32 { 42 | if b1 == b2 { 43 | return b1 44 | } 45 | 46 | min, max := int64(b1), int64(b2) 47 | if min > max { 48 | min, max = max, min 49 | } 50 | return int32(rand.Int63n(max-min+1) + min) 51 | } 52 | 53 | func RandIntervalN(b1, b2 int32, n uint32) []int32 { 54 | if b1 == b2 { 55 | return []int32{b1} 56 | } 57 | 58 | min, max := int64(b1), int64(b2) 59 | if min > max { 60 | min, max = max, min 61 | } 62 | l := max - min + 1 63 | if int64(n) > l { 64 | n = uint32(l) 65 | } 66 | 67 | r := make([]int32, n) 68 | m := make(map[int32]int32) 69 | for i := uint32(0); i < n; i++ { 70 | v := int32(rand.Int63n(l) + min) 71 | 72 | if mv, ok := m[v]; ok { 73 | r[i] = mv 74 | } else { 75 | r[i] = v 76 | } 77 | 78 | lv := int32(l - 1 + min) 79 | if v != lv { 80 | if mv, ok := m[lv]; ok { 81 | m[v] = mv 82 | } else { 83 | m[v] = lv 84 | } 85 | } 86 | 87 | l-- 88 | } 89 | 90 | return r 91 | } 92 | -------------------------------------------------------------------------------- /util/semaphore.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | type Semaphore chan struct{} 4 | 5 | func MakeSemaphore(n int) Semaphore { 6 | return make(Semaphore, n) 7 | } 8 | 9 | func (s Semaphore) Acquire() { 10 | s <- struct{}{} 11 | } 12 | 13 | func (s Semaphore) Release() { 14 | <-s 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package internal // import "github.com/garyburd/redigo/internal" 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | const ( 22 | WatchState = 1 << iota 23 | MultiState 24 | SubscribeState 25 | MonitorState 26 | ) 27 | 28 | type CommandInfo struct { 29 | Set, Clear int 30 | } 31 | 32 | var commandInfos = map[string]CommandInfo{ 33 | "WATCH": {Set: WatchState}, 34 | "UNWATCH": {Clear: WatchState}, 35 | "MULTI": {Set: MultiState}, 36 | "EXEC": {Clear: WatchState | MultiState}, 37 | "DISCARD": {Clear: WatchState | MultiState}, 38 | "PSUBSCRIBE": {Set: SubscribeState}, 39 | "SUBSCRIBE": {Set: SubscribeState}, 40 | "MONITOR": {Set: MonitorState}, 41 | } 42 | 43 | func init() { 44 | for n, ci := range commandInfos { 45 | commandInfos[strings.ToLower(n)] = ci 46 | } 47 | } 48 | 49 | func LookupCommandInfo(commandName string) CommandInfo { 50 | if ci, ok := commandInfos[commandName]; ok { 51 | return ci 52 | } 53 | return commandInfos[strings.ToUpper(commandName)] 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "testing" 4 | 5 | func TestLookupCommandInfo(t *testing.T) { 6 | for _, n := range []string{"watch", "WATCH", "wAtch"} { 7 | if LookupCommandInfo(n) == (CommandInfo{}) { 8 | t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n) 9 | } 10 | } 11 | } 12 | 13 | func benchmarkLookupCommandInfo(b *testing.B, names ...string) { 14 | for i := 0; i < b.N; i++ { 15 | for _, c := range names { 16 | LookupCommandInfo(c) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) { 22 | benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR") 23 | } 24 | 25 | func BenchmarkLookupCommandInfoMixedCase(b *testing.B) { 26 | benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR") 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/go17.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | // similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case 8 | func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { 9 | if cfg == nil { 10 | return &tls.Config{InsecureSkipVerify: skipVerify} 11 | } 12 | return &tls.Config{ 13 | Rand: cfg.Rand, 14 | Time: cfg.Time, 15 | Certificates: cfg.Certificates, 16 | NameToCertificate: cfg.NameToCertificate, 17 | GetCertificate: cfg.GetCertificate, 18 | RootCAs: cfg.RootCAs, 19 | NextProtos: cfg.NextProtos, 20 | ServerName: cfg.ServerName, 21 | ClientAuth: cfg.ClientAuth, 22 | ClientCAs: cfg.ClientCAs, 23 | InsecureSkipVerify: cfg.InsecureSkipVerify, 24 | CipherSuites: cfg.CipherSuites, 25 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 26 | ClientSessionCache: cfg.ClientSessionCache, 27 | MinVersion: cfg.MinVersion, 28 | MaxVersion: cfg.MaxVersion, 29 | CurvePreferences: cfg.CurvePreferences, 30 | DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, 31 | Renegotiation: cfg.Renegotiation, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "log" 21 | ) 22 | 23 | // NewLoggingConn returns a logging wrapper around a connection. 24 | func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn { 25 | if prefix != "" { 26 | prefix = prefix + "." 27 | } 28 | return &loggingConn{conn, logger, prefix} 29 | } 30 | 31 | type loggingConn struct { 32 | Conn 33 | logger *log.Logger 34 | prefix string 35 | } 36 | 37 | func (c *loggingConn) Close() error { 38 | err := c.Conn.Close() 39 | var buf bytes.Buffer 40 | fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err) 41 | c.logger.Output(2, buf.String()) 42 | return err 43 | } 44 | 45 | func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) { 46 | const chop = 32 47 | switch v := v.(type) { 48 | case []byte: 49 | if len(v) > chop { 50 | fmt.Fprintf(buf, "%q...", v[:chop]) 51 | } else { 52 | fmt.Fprintf(buf, "%q", v) 53 | } 54 | case string: 55 | if len(v) > chop { 56 | fmt.Fprintf(buf, "%q...", v[:chop]) 57 | } else { 58 | fmt.Fprintf(buf, "%q", v) 59 | } 60 | case []interface{}: 61 | if len(v) == 0 { 62 | buf.WriteString("[]") 63 | } else { 64 | sep := "[" 65 | fin := "]" 66 | if len(v) > chop { 67 | v = v[:chop] 68 | fin = "...]" 69 | } 70 | for _, vv := range v { 71 | buf.WriteString(sep) 72 | c.printValue(buf, vv) 73 | sep = ", " 74 | } 75 | buf.WriteString(fin) 76 | } 77 | default: 78 | fmt.Fprint(buf, v) 79 | } 80 | } 81 | 82 | func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) { 83 | var buf bytes.Buffer 84 | fmt.Fprintf(&buf, "%s%s(", c.prefix, method) 85 | if method != "Receive" { 86 | buf.WriteString(commandName) 87 | for _, arg := range args { 88 | buf.WriteString(", ") 89 | c.printValue(&buf, arg) 90 | } 91 | } 92 | buf.WriteString(") -> (") 93 | if method != "Send" { 94 | c.printValue(&buf, reply) 95 | buf.WriteString(", ") 96 | } 97 | fmt.Fprintf(&buf, "%v)", err) 98 | c.logger.Output(3, buf.String()) 99 | } 100 | 101 | func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) { 102 | reply, err := c.Conn.Do(commandName, args...) 103 | c.print("Do", commandName, args, reply, err) 104 | return reply, err 105 | } 106 | 107 | func (c *loggingConn) Send(commandName string, args ...interface{}) error { 108 | err := c.Conn.Send(commandName, args...) 109 | c.print("Send", commandName, args, nil, err) 110 | return err 111 | } 112 | 113 | func (c *loggingConn) Receive() (interface{}, error) { 114 | reply, err := c.Conn.Receive() 115 | c.print("Receive", "", nil, reply, err) 116 | return reply, err 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pre_go17.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | // similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case 8 | func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { 9 | if cfg == nil { 10 | return &tls.Config{InsecureSkipVerify: skipVerify} 11 | } 12 | return &tls.Config{ 13 | Rand: cfg.Rand, 14 | Time: cfg.Time, 15 | Certificates: cfg.Certificates, 16 | NameToCertificate: cfg.NameToCertificate, 17 | GetCertificate: cfg.GetCertificate, 18 | RootCAs: cfg.RootCAs, 19 | NextProtos: cfg.NextProtos, 20 | ServerName: cfg.ServerName, 21 | ClientAuth: cfg.ClientAuth, 22 | ClientCAs: cfg.ClientCAs, 23 | InsecureSkipVerify: cfg.InsecureSkipVerify, 24 | CipherSuites: cfg.CipherSuites, 25 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 26 | ClientSessionCache: cfg.ClientSessionCache, 27 | MinVersion: cfg.MinVersion, 28 | MaxVersion: cfg.MaxVersion, 29 | CurvePreferences: cfg.CurvePreferences, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import "errors" 18 | 19 | // Subscription represents a subscribe or unsubscribe notification. 20 | type Subscription struct { 21 | 22 | // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" 23 | Kind string 24 | 25 | // The channel that was changed. 26 | Channel string 27 | 28 | // The current number of subscriptions for connection. 29 | Count int 30 | } 31 | 32 | // Message represents a message notification. 33 | type Message struct { 34 | 35 | // The originating channel. 36 | Channel string 37 | 38 | // The message data. 39 | Data []byte 40 | } 41 | 42 | // PMessage represents a pmessage notification. 43 | type PMessage struct { 44 | 45 | // The matched pattern. 46 | Pattern string 47 | 48 | // The originating channel. 49 | Channel string 50 | 51 | // The message data. 52 | Data []byte 53 | } 54 | 55 | // Pong represents a pubsub pong notification. 56 | type Pong struct { 57 | Data string 58 | } 59 | 60 | // PubSubConn wraps a Conn with convenience methods for subscribers. 61 | type PubSubConn struct { 62 | Conn Conn 63 | } 64 | 65 | // Close closes the connection. 66 | func (c PubSubConn) Close() error { 67 | return c.Conn.Close() 68 | } 69 | 70 | // Subscribe subscribes the connection to the specified channels. 71 | func (c PubSubConn) Subscribe(channel ...interface{}) error { 72 | c.Conn.Send("SUBSCRIBE", channel...) 73 | return c.Conn.Flush() 74 | } 75 | 76 | // PSubscribe subscribes the connection to the given patterns. 77 | func (c PubSubConn) PSubscribe(channel ...interface{}) error { 78 | c.Conn.Send("PSUBSCRIBE", channel...) 79 | return c.Conn.Flush() 80 | } 81 | 82 | // Unsubscribe unsubscribes the connection from the given channels, or from all 83 | // of them if none is given. 84 | func (c PubSubConn) Unsubscribe(channel ...interface{}) error { 85 | c.Conn.Send("UNSUBSCRIBE", channel...) 86 | return c.Conn.Flush() 87 | } 88 | 89 | // PUnsubscribe unsubscribes the connection from the given patterns, or from all 90 | // of them if none is given. 91 | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error { 92 | c.Conn.Send("PUNSUBSCRIBE", channel...) 93 | return c.Conn.Flush() 94 | } 95 | 96 | // Ping sends a PING to the server with the specified data. 97 | func (c PubSubConn) Ping(data string) error { 98 | c.Conn.Send("PING", data) 99 | return c.Conn.Flush() 100 | } 101 | 102 | // Receive returns a pushed message as a Subscription, Message, PMessage, Pong 103 | // or error. The return value is intended to be used directly in a type switch 104 | // as illustrated in the PubSubConn example. 105 | func (c PubSubConn) Receive() interface{} { 106 | reply, err := Values(c.Conn.Receive()) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | var kind string 112 | reply, err = Scan(reply, &kind) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | switch kind { 118 | case "message": 119 | var m Message 120 | if _, err := Scan(reply, &m.Channel, &m.Data); err != nil { 121 | return err 122 | } 123 | return m 124 | case "pmessage": 125 | var pm PMessage 126 | if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil { 127 | return err 128 | } 129 | return pm 130 | case "subscribe", "psubscribe", "unsubscribe", "punsubscribe": 131 | s := Subscription{Kind: kind} 132 | if _, err := Scan(reply, &s.Channel, &s.Count); err != nil { 133 | return err 134 | } 135 | return s 136 | case "pong": 137 | var p Pong 138 | if _, err := Scan(reply, &p.Data); err != nil { 139 | return err 140 | } 141 | return p 142 | } 143 | return errors.New("redigo: unknown pubsub notification") 144 | } 145 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "sync" 21 | "testing" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | func publish(channel, value interface{}) { 27 | c, err := dial() 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | defer c.Close() 33 | c.Do("PUBLISH", channel, value) 34 | } 35 | 36 | // Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. 37 | func ExamplePubSubConn() { 38 | c, err := dial() 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | defer c.Close() 44 | var wg sync.WaitGroup 45 | wg.Add(2) 46 | 47 | psc := redis.PubSubConn{Conn: c} 48 | 49 | // This goroutine receives and prints pushed notifications from the server. 50 | // The goroutine exits when the connection is unsubscribed from all 51 | // channels or there is an error. 52 | go func() { 53 | defer wg.Done() 54 | for { 55 | switch n := psc.Receive().(type) { 56 | case redis.Message: 57 | fmt.Printf("Message: %s %s\n", n.Channel, n.Data) 58 | case redis.PMessage: 59 | fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) 60 | case redis.Subscription: 61 | fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) 62 | if n.Count == 0 { 63 | return 64 | } 65 | case error: 66 | fmt.Printf("error: %v\n", n) 67 | return 68 | } 69 | } 70 | }() 71 | 72 | // This goroutine manages subscriptions for the connection. 73 | go func() { 74 | defer wg.Done() 75 | 76 | psc.Subscribe("example") 77 | psc.PSubscribe("p*") 78 | 79 | // The following function calls publish a message using another 80 | // connection to the Redis server. 81 | publish("example", "hello") 82 | publish("example", "world") 83 | publish("pexample", "foo") 84 | publish("pexample", "bar") 85 | 86 | // Unsubscribe from all connections. This will cause the receiving 87 | // goroutine to exit. 88 | psc.Unsubscribe() 89 | psc.PUnsubscribe() 90 | }() 91 | 92 | wg.Wait() 93 | 94 | // Output: 95 | // Subscription: subscribe example 1 96 | // Subscription: psubscribe p* 2 97 | // Message: example hello 98 | // Message: example world 99 | // PMessage: p* pexample foo 100 | // PMessage: p* pexample bar 101 | // Subscription: unsubscribe example 1 102 | // Subscription: punsubscribe p* 0 103 | } 104 | 105 | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { 106 | actual := c.Receive() 107 | if !reflect.DeepEqual(actual, expected) { 108 | t.Errorf("%s = %v, want %v", message, actual, expected) 109 | } 110 | } 111 | 112 | func TestPushed(t *testing.T) { 113 | pc, err := redis.DialDefaultServer() 114 | if err != nil { 115 | t.Fatalf("error connection to database, %v", err) 116 | } 117 | defer pc.Close() 118 | 119 | sc, err := redis.DialDefaultServer() 120 | if err != nil { 121 | t.Fatalf("error connection to database, %v", err) 122 | } 123 | defer sc.Close() 124 | 125 | c := redis.PubSubConn{Conn: sc} 126 | 127 | c.Subscribe("c1") 128 | expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) 129 | c.Subscribe("c2") 130 | expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) 131 | c.PSubscribe("p1") 132 | expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) 133 | c.PSubscribe("p2") 134 | expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) 135 | c.PUnsubscribe() 136 | expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) 137 | expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) 138 | 139 | pc.Do("PUBLISH", "c1", "hello") 140 | expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) 141 | 142 | c.Ping("hello") 143 | expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"}) 144 | 145 | c.Conn.Send("PING") 146 | c.Conn.Flush() 147 | expectPushed(t, c, `Send("PING")`, redis.Pong{}) 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | // Error represents an error returned in a command reply. 18 | type Error string 19 | 20 | func (err Error) Error() string { return string(err) } 21 | 22 | // Conn represents a connection to a Redis server. 23 | type Conn interface { 24 | // Close closes the connection. 25 | Close() error 26 | 27 | // Err returns a non-nil value when the connection is not usable. 28 | Err() error 29 | 30 | // Do sends a command to the server and returns the received reply. 31 | Do(commandName string, args ...interface{}) (reply interface{}, err error) 32 | 33 | // Send writes the command to the client's output buffer. 34 | Send(commandName string, args ...interface{}) error 35 | 36 | // Flush flushes the output buffer to the Redis server. 37 | Flush() error 38 | 39 | // Receive receives a single reply from the Redis server 40 | Receive() (reply interface{}, err error) 41 | } 42 | 43 | // Argument is implemented by types which want to control how their value is 44 | // interpreted when used as an argument to a redis command. 45 | type Argument interface { 46 | // RedisArg returns the interface that represents the value to be used 47 | // in redis commands. 48 | RedisArg() interface{} 49 | } 50 | 51 | // Scanner is implemented by types which want to control how their value is 52 | // interpreted when read from redis. 53 | type Scanner interface { 54 | // RedisScan assigns a value from a redis value. 55 | // 56 | // An error should be returned if the value cannot be stored without 57 | // loss of information. 58 | RedisScan(src interface{}) error 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/reply_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type valueError struct { 26 | v interface{} 27 | err error 28 | } 29 | 30 | func ve(v interface{}, err error) valueError { 31 | return valueError{v, err} 32 | } 33 | 34 | var replyTests = []struct { 35 | name interface{} 36 | actual valueError 37 | expected valueError 38 | }{ 39 | { 40 | "ints([v1, v2])", 41 | ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), 42 | ve([]int{4, 5}, nil), 43 | }, 44 | { 45 | "ints(nil)", 46 | ve(redis.Ints(nil, nil)), 47 | ve([]int(nil), redis.ErrNil), 48 | }, 49 | { 50 | "strings([v1, v2])", 51 | ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 52 | ve([]string{"v1", "v2"}, nil), 53 | }, 54 | { 55 | "strings(nil)", 56 | ve(redis.Strings(nil, nil)), 57 | ve([]string(nil), redis.ErrNil), 58 | }, 59 | { 60 | "byteslices([v1, v2])", 61 | ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 62 | ve([][]byte{[]byte("v1"), []byte("v2")}, nil), 63 | }, 64 | { 65 | "byteslices(nil)", 66 | ve(redis.ByteSlices(nil, nil)), 67 | ve([][]byte(nil), redis.ErrNil), 68 | }, 69 | { 70 | "values([v1, v2])", 71 | ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 72 | ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), 73 | }, 74 | { 75 | "values(nil)", 76 | ve(redis.Values(nil, nil)), 77 | ve([]interface{}(nil), redis.ErrNil), 78 | }, 79 | { 80 | "float64(1.0)", 81 | ve(redis.Float64([]byte("1.0"), nil)), 82 | ve(float64(1.0), nil), 83 | }, 84 | { 85 | "float64(nil)", 86 | ve(redis.Float64(nil, nil)), 87 | ve(float64(0.0), redis.ErrNil), 88 | }, 89 | { 90 | "uint64(1)", 91 | ve(redis.Uint64(int64(1), nil)), 92 | ve(uint64(1), nil), 93 | }, 94 | { 95 | "uint64(-1)", 96 | ve(redis.Uint64(int64(-1), nil)), 97 | ve(uint64(0), redis.ErrNegativeInt), 98 | }, 99 | { 100 | "positions([[1, 2], nil, [3, 4]])", 101 | ve(redis.Positions([]interface{}{[]interface{}{[]byte("1"), []byte("2")}, nil, []interface{}{[]byte("3"), []byte("4")}}, nil)), 102 | ve([]*[2]float64{{1.0, 2.0}, nil, {3.0, 4.0}}, nil), 103 | }, 104 | } 105 | 106 | func TestReply(t *testing.T) { 107 | for _, rt := range replyTests { 108 | if rt.actual.err != rt.expected.err { 109 | t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) 110 | continue 111 | } 112 | if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { 113 | t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) 114 | } 115 | } 116 | } 117 | 118 | // dial wraps DialDefaultServer() with a more suitable function name for examples. 119 | func dial() (redis.Conn, error) { 120 | return redis.DialDefaultServer() 121 | } 122 | 123 | func ExampleBool() { 124 | c, err := dial() 125 | if err != nil { 126 | fmt.Println(err) 127 | return 128 | } 129 | defer c.Close() 130 | 131 | c.Do("SET", "foo", 1) 132 | exists, _ := redis.Bool(c.Do("EXISTS", "foo")) 133 | fmt.Printf("%#v\n", exists) 134 | // Output: 135 | // true 136 | } 137 | 138 | func ExampleInt() { 139 | c, err := dial() 140 | if err != nil { 141 | fmt.Println(err) 142 | return 143 | } 144 | defer c.Close() 145 | 146 | c.Do("SET", "k1", 1) 147 | n, _ := redis.Int(c.Do("GET", "k1")) 148 | fmt.Printf("%#v\n", n) 149 | n, _ = redis.Int(c.Do("INCR", "k1")) 150 | fmt.Printf("%#v\n", n) 151 | // Output: 152 | // 1 153 | // 2 154 | } 155 | 156 | func ExampleInts() { 157 | c, err := dial() 158 | if err != nil { 159 | fmt.Println(err) 160 | return 161 | } 162 | defer c.Close() 163 | 164 | c.Do("SADD", "set_with_integers", 4, 5, 6) 165 | ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) 166 | fmt.Printf("%#v\n", ints) 167 | // Output: 168 | // []int{4, 5, 6} 169 | } 170 | 171 | func ExampleString() { 172 | c, err := dial() 173 | if err != nil { 174 | fmt.Println(err) 175 | return 176 | } 177 | defer c.Close() 178 | 179 | c.Do("SET", "hello", "world") 180 | s, err := redis.String(c.Do("GET", "hello")) 181 | fmt.Printf("%#v\n", s) 182 | // Output: 183 | // "world" 184 | } 185 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "crypto/sha1" 19 | "encoding/hex" 20 | "io" 21 | "strings" 22 | ) 23 | 24 | // Script encapsulates the source, hash and key count for a Lua script. See 25 | // http://redis.io/commands/eval for information on scripts in Redis. 26 | type Script struct { 27 | keyCount int 28 | src string 29 | hash string 30 | } 31 | 32 | // NewScript returns a new script object. If keyCount is greater than or equal 33 | // to zero, then the count is automatically inserted in the EVAL command 34 | // argument list. If keyCount is less than zero, then the application supplies 35 | // the count as the first value in the keysAndArgs argument to the Do, Send and 36 | // SendHash methods. 37 | func NewScript(keyCount int, src string) *Script { 38 | h := sha1.New() 39 | io.WriteString(h, src) 40 | return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))} 41 | } 42 | 43 | func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} { 44 | var args []interface{} 45 | if s.keyCount < 0 { 46 | args = make([]interface{}, 1+len(keysAndArgs)) 47 | args[0] = spec 48 | copy(args[1:], keysAndArgs) 49 | } else { 50 | args = make([]interface{}, 2+len(keysAndArgs)) 51 | args[0] = spec 52 | args[1] = s.keyCount 53 | copy(args[2:], keysAndArgs) 54 | } 55 | return args 56 | } 57 | 58 | // Hash returns the script hash. 59 | func (s *Script) Hash() string { 60 | return s.hash 61 | } 62 | 63 | // Do evaluates the script. Under the covers, Do optimistically evaluates the 64 | // script using the EVALSHA command. If the command fails because the script is 65 | // not loaded, then Do evaluates the script using the EVAL command (thus 66 | // causing the script to load). 67 | func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) { 68 | v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...) 69 | if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") { 70 | v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...) 71 | } 72 | return v, err 73 | } 74 | 75 | // SendHash evaluates the script without waiting for the reply. The script is 76 | // evaluated with the EVALSHA command. The application must ensure that the 77 | // script is loaded by a previous call to Send, Do or Load methods. 78 | func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error { 79 | return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...) 80 | } 81 | 82 | // Send evaluates the script without waiting for the reply. 83 | func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error { 84 | return c.Send("EVAL", s.args(s.src, keysAndArgs)...) 85 | } 86 | 87 | // Load loads the script without evaluating it. 88 | func (s *Script) Load(c Conn) error { 89 | _, err := c.Do("SCRIPT", "LOAD", s.src) 90 | return err 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | "time" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | var ( 27 | // These variables are declared at package level to remove distracting 28 | // details from the examples. 29 | c redis.Conn 30 | reply interface{} 31 | err error 32 | ) 33 | 34 | func ExampleScript() { 35 | // Initialize a package-level variable with a script. 36 | var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) 37 | 38 | // In a function, use the script Do method to evaluate the script. The Do 39 | // method optimistically uses the EVALSHA command. If the script is not 40 | // loaded, then the Do method falls back to the EVAL command. 41 | reply, err = getScript.Do(c, "foo") 42 | } 43 | 44 | func TestScript(t *testing.T) { 45 | c, err := redis.DialDefaultServer() 46 | if err != nil { 47 | t.Fatalf("error connection to database, %v", err) 48 | } 49 | defer c.Close() 50 | 51 | // To test fall back in Do, we make script unique by adding comment with current time. 52 | script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) 53 | s := redis.NewScript(2, script) 54 | reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} 55 | 56 | v, err := s.Do(c, "key1", "key2", "arg1", "arg2") 57 | if err != nil { 58 | t.Errorf("s.Do(c, ...) returned %v", err) 59 | } 60 | 61 | if !reflect.DeepEqual(v, reply) { 62 | t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) 63 | } 64 | 65 | err = s.Load(c) 66 | if err != nil { 67 | t.Errorf("s.Load(c) returned %v", err) 68 | } 69 | 70 | err = s.SendHash(c, "key1", "key2", "arg1", "arg2") 71 | if err != nil { 72 | t.Errorf("s.SendHash(c, ...) returned %v", err) 73 | } 74 | 75 | err = c.Flush() 76 | if err != nil { 77 | t.Errorf("c.Flush() returned %v", err) 78 | } 79 | 80 | v, err = c.Receive() 81 | if !reflect.DeepEqual(v, reply) { 82 | t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) 83 | } 84 | 85 | err = s.Send(c, "key1", "key2", "arg1", "arg2") 86 | if err != nil { 87 | t.Errorf("s.Send(c, ...) returned %v", err) 88 | } 89 | 90 | err = c.Flush() 91 | if err != nil { 92 | t.Errorf("c.Flush() returned %v", err) 93 | } 94 | 95 | v, err = c.Receive() 96 | if !reflect.DeepEqual(v, reply) { 97 | t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/zpop_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/garyburd/redigo/redis" 21 | ) 22 | 23 | // zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. 24 | func zpop(c redis.Conn, key string) (result string, err error) { 25 | 26 | defer func() { 27 | // Return connection to normal state on error. 28 | if err != nil { 29 | c.Do("DISCARD") 30 | } 31 | }() 32 | 33 | // Loop until transaction is successful. 34 | for { 35 | if _, err := c.Do("WATCH", key); err != nil { 36 | return "", err 37 | } 38 | 39 | members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) 40 | if err != nil { 41 | return "", err 42 | } 43 | if len(members) != 1 { 44 | return "", redis.ErrNil 45 | } 46 | 47 | c.Send("MULTI") 48 | c.Send("ZREM", key, members[0]) 49 | queued, err := c.Do("EXEC") 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | if queued != nil { 55 | result = members[0] 56 | break 57 | } 58 | } 59 | 60 | return result, nil 61 | } 62 | 63 | // zpopScript pops a value from a ZSET. 64 | var zpopScript = redis.NewScript(1, ` 65 | local r = redis.call('ZRANGE', KEYS[1], 0, 0) 66 | if r ~= nil then 67 | r = r[1] 68 | redis.call('ZREM', KEYS[1], r) 69 | end 70 | return r 71 | `) 72 | 73 | // This example implements ZPOP as described at 74 | // http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. 75 | func Example_zpop() { 76 | c, err := dial() 77 | if err != nil { 78 | fmt.Println(err) 79 | return 80 | } 81 | defer c.Close() 82 | 83 | // Add test data using a pipeline. 84 | 85 | for i, member := range []string{"red", "blue", "green"} { 86 | c.Send("ZADD", "zset", i, member) 87 | } 88 | if _, err := c.Do(""); err != nil { 89 | fmt.Println(err) 90 | return 91 | } 92 | 93 | // Pop using WATCH/MULTI/EXEC 94 | 95 | v, err := zpop(c, "zset") 96 | if err != nil { 97 | fmt.Println(err) 98 | return 99 | } 100 | fmt.Println(v) 101 | 102 | // Pop using a script. 103 | 104 | v, err = redis.String(zpopScript.Do(c, "zset")) 105 | if err != nil { 106 | fmt.Println(err) 107 | return 108 | } 109 | fmt.Println(v) 110 | 111 | // Output: 112 | // red 113 | // blue 114 | } 115 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/LICENSE: -------------------------------------------------------------------------------- 1 | Go support for Protocol Buffers - Google's data interchange format 2 | 3 | Copyright 2010 The Go Authors. All rights reserved. 4 | https://github.com/golang/protobuf 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following disclaimer 14 | in the documentation and/or other materials provided with the 15 | distribution. 16 | * Neither the name of Google Inc. nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/proto/Makefile: -------------------------------------------------------------------------------- 1 | # Go support for Protocol Buffers - Google's data interchange format 2 | # 3 | # Copyright 2010 The Go Authors. All rights reserved. 4 | # https://github.com/golang/protobuf 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of Google Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | install: 33 | go install 34 | 35 | test: install generate-test-pbs 36 | go test 37 | 38 | 39 | generate-test-pbs: 40 | make install 41 | make -C testdata 42 | protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto 43 | make 44 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/proto/encode_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2010 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | // +build go1.7 33 | 34 | package proto_test 35 | 36 | import ( 37 | "strconv" 38 | "testing" 39 | 40 | "github.com/golang/protobuf/proto" 41 | tpb "github.com/golang/protobuf/proto/proto3_proto" 42 | "github.com/golang/protobuf/ptypes" 43 | ) 44 | 45 | var ( 46 | blackhole []byte 47 | ) 48 | 49 | // BenchmarkAny creates increasingly large arbitrary Any messages. The type is always the 50 | // same. 51 | func BenchmarkAny(b *testing.B) { 52 | data := make([]byte, 1<<20) 53 | quantum := 1 << 10 54 | for i := uint(0); i <= 10; i++ { 55 | b.Run(strconv.Itoa(quantum< terrain = 10; 61 | testdata.SubDefaults proto2_field = 11; 62 | map proto2_value = 13; 63 | 64 | google.protobuf.Any anything = 14; 65 | repeated google.protobuf.Any many_things = 15; 66 | 67 | Message submessage = 17; 68 | repeated Message children = 18; 69 | } 70 | 71 | message Nested { 72 | string bunny = 1; 73 | bool cute = 2; 74 | } 75 | 76 | message MessageWithMap { 77 | map byte_mapping = 1; 78 | } 79 | 80 | 81 | message IntMap { 82 | map rtt = 1; 83 | } 84 | 85 | message IntMaps { 86 | repeated IntMap maps = 1; 87 | } 88 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/proto/size2_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2012 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | package proto 33 | 34 | import ( 35 | "testing" 36 | ) 37 | 38 | // This is a separate file and package from size_test.go because that one uses 39 | // generated messages and thus may not be in package proto without having a circular 40 | // dependency, whereas this file tests unexported details of size.go. 41 | 42 | func TestVarintSize(t *testing.T) { 43 | // Check the edge cases carefully. 44 | testCases := []struct { 45 | n uint64 46 | size int 47 | }{ 48 | {0, 1}, 49 | {1, 1}, 50 | {127, 1}, 51 | {128, 2}, 52 | {16383, 2}, 53 | {16384, 3}, 54 | {1<<63 - 1, 9}, 55 | {1 << 63, 10}, 56 | } 57 | for _, tc := range testCases { 58 | size := sizeVarint(tc.n) 59 | if size != tc.size { 60 | t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/proto/testdata/Makefile: -------------------------------------------------------------------------------- 1 | # Go support for Protocol Buffers - Google's data interchange format 2 | # 3 | # Copyright 2010 The Go Authors. All rights reserved. 4 | # https://github.com/golang/protobuf 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of Google Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | include ../../Make.protobuf 34 | 35 | all: regenerate 36 | 37 | regenerate: 38 | rm -f test.pb.go 39 | make test.pb.go 40 | 41 | # The following rules are just aids to development. Not needed for typical testing. 42 | 43 | diff: regenerate 44 | git diff test.pb.go 45 | 46 | restore: 47 | cp test.pb.go.golden test.pb.go 48 | 49 | preserve: 50 | cp test.pb.go test.pb.go.golden 51 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/proto/testdata/golden_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2012 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | // Verify that the compiler output for test.proto is unchanged. 33 | 34 | package testdata 35 | 36 | import ( 37 | "crypto/sha1" 38 | "fmt" 39 | "io/ioutil" 40 | "os" 41 | "os/exec" 42 | "path/filepath" 43 | "testing" 44 | ) 45 | 46 | // sum returns in string form (for easy comparison) the SHA-1 hash of the named file. 47 | func sum(t *testing.T, name string) string { 48 | data, err := ioutil.ReadFile(name) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | t.Logf("sum(%q): length is %d", name, len(data)) 53 | hash := sha1.New() 54 | _, err = hash.Write(data) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | return fmt.Sprintf("% x", hash.Sum(nil)) 59 | } 60 | 61 | func run(t *testing.T, name string, args ...string) { 62 | cmd := exec.Command(name, args...) 63 | cmd.Stdin = os.Stdin 64 | cmd.Stdout = os.Stdout 65 | cmd.Stderr = os.Stderr 66 | err := cmd.Run() 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | } 71 | 72 | func TestGolden(t *testing.T) { 73 | // Compute the original checksum. 74 | goldenSum := sum(t, "test.pb.go") 75 | // Run the proto compiler. 76 | run(t, "protoc", "--go_out="+os.TempDir(), "test.proto") 77 | newFile := filepath.Join(os.TempDir(), "test.pb.go") 78 | defer os.Remove(newFile) 79 | // Compute the new checksum. 80 | newSum := sum(t, newFile) 81 | // Verify 82 | if newSum != goldenSum { 83 | run(t, "diff", "-u", "test.pb.go", newFile) 84 | t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile: -------------------------------------------------------------------------------- 1 | # Go support for Protocol Buffers - Google's data interchange format 2 | # 3 | # Copyright 2010 The Go Authors. All rights reserved. 4 | # https://github.com/golang/protobuf 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of Google Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | # Not stored here, but descriptor.proto is in https://github.com/google/protobuf/ 33 | # at src/google/protobuf/descriptor.proto 34 | regenerate: 35 | @echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION 36 | cp $(HOME)/src/protobuf/include/google/protobuf/descriptor.proto . 37 | protoc --go_out=../../../../.. -I$(HOME)/src/protobuf/include $(HOME)/src/protobuf/include/google/protobuf/descriptor.proto 38 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/ptypes/any_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2016 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | package ptypes 33 | 34 | import ( 35 | "testing" 36 | 37 | "github.com/golang/protobuf/proto" 38 | pb "github.com/golang/protobuf/protoc-gen-go/descriptor" 39 | "github.com/golang/protobuf/ptypes/any" 40 | ) 41 | 42 | func TestMarshalUnmarshal(t *testing.T) { 43 | orig := &any.Any{Value: []byte("test")} 44 | 45 | packed, err := MarshalAny(orig) 46 | if err != nil { 47 | t.Errorf("MarshalAny(%+v): got: _, %v exp: _, nil", orig, err) 48 | } 49 | 50 | unpacked := &any.Any{} 51 | err = UnmarshalAny(packed, unpacked) 52 | if err != nil || !proto.Equal(unpacked, orig) { 53 | t.Errorf("got: %v, %+v; want nil, %+v", err, unpacked, orig) 54 | } 55 | } 56 | 57 | func TestIs(t *testing.T) { 58 | a, err := MarshalAny(&pb.FileDescriptorProto{}) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | if Is(a, &pb.DescriptorProto{}) { 63 | t.Error("FileDescriptorProto is not a DescriptorProto, but Is says it is") 64 | } 65 | if !Is(a, &pb.FileDescriptorProto{}) { 66 | t.Error("FileDescriptorProto is indeed a FileDescriptorProto, but Is says it is not") 67 | } 68 | } 69 | 70 | func TestIsDifferentUrlPrefixes(t *testing.T) { 71 | m := &pb.FileDescriptorProto{} 72 | a := &any.Any{TypeUrl: "foo/bar/" + proto.MessageName(m)} 73 | if !Is(a, m) { 74 | t.Errorf("message with type url %q didn't satisfy Is for type %q", a.TypeUrl, proto.MessageName(m)) 75 | } 76 | } 77 | 78 | func TestUnmarshalDynamic(t *testing.T) { 79 | want := &pb.FileDescriptorProto{Name: proto.String("foo")} 80 | a, err := MarshalAny(want) 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | var got DynamicAny 85 | if err := UnmarshalAny(a, &got); err != nil { 86 | t.Fatal(err) 87 | } 88 | if !proto.Equal(got.Message, want) { 89 | t.Errorf("invalid result from UnmarshalAny, got %q want %q", got.Message, want) 90 | } 91 | } 92 | 93 | func TestEmpty(t *testing.T) { 94 | want := &pb.FileDescriptorProto{} 95 | a, err := MarshalAny(want) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | got, err := Empty(a) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | if !proto.Equal(got, want) { 104 | t.Errorf("unequal empty message, got %q, want %q", got, want) 105 | } 106 | 107 | // that's a valid type_url for a message which shouldn't be linked into this 108 | // test binary. We want an error. 109 | a.TypeUrl = "type.googleapis.com/google.protobuf.FieldMask" 110 | if _, err := Empty(a); err == nil { 111 | t.Errorf("got no error for an attempt to create a message of type %q, which shouldn't be linked in", a.TypeUrl) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/ptypes/doc.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2016 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | /* 33 | Package ptypes contains code for interacting with well-known types. 34 | */ 35 | package ptypes 36 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/ptypes/duration.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers - Google's data interchange format 2 | // 3 | // Copyright 2016 The Go Authors. All rights reserved. 4 | // https://github.com/golang/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | package ptypes 33 | 34 | // This file implements conversions between google.protobuf.Duration 35 | // and time.Duration. 36 | 37 | import ( 38 | "errors" 39 | "fmt" 40 | "time" 41 | 42 | durpb "github.com/golang/protobuf/ptypes/duration" 43 | ) 44 | 45 | const ( 46 | // Range of a durpb.Duration in seconds, as specified in 47 | // google/protobuf/duration.proto. This is about 10,000 years in seconds. 48 | maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60) 49 | minSeconds = -maxSeconds 50 | ) 51 | 52 | // validateDuration determines whether the durpb.Duration is valid according to the 53 | // definition in google/protobuf/duration.proto. A valid durpb.Duration 54 | // may still be too large to fit into a time.Duration (the range of durpb.Duration 55 | // is about 10,000 years, and the range of time.Duration is about 290). 56 | func validateDuration(d *durpb.Duration) error { 57 | if d == nil { 58 | return errors.New("duration: nil Duration") 59 | } 60 | if d.Seconds < minSeconds || d.Seconds > maxSeconds { 61 | return fmt.Errorf("duration: %v: seconds out of range", d) 62 | } 63 | if d.Nanos <= -1e9 || d.Nanos >= 1e9 { 64 | return fmt.Errorf("duration: %v: nanos out of range", d) 65 | } 66 | // Seconds and Nanos must have the same sign, unless d.Nanos is zero. 67 | if (d.Seconds < 0 && d.Nanos > 0) || (d.Seconds > 0 && d.Nanos < 0) { 68 | return fmt.Errorf("duration: %v: seconds and nanos have different signs", d) 69 | } 70 | return nil 71 | } 72 | 73 | // Duration converts a durpb.Duration to a time.Duration. Duration 74 | // returns an error if the durpb.Duration is invalid or is too large to be 75 | // represented in a time.Duration. 76 | func Duration(p *durpb.Duration) (time.Duration, error) { 77 | if err := validateDuration(p); err != nil { 78 | return 0, err 79 | } 80 | d := time.Duration(p.Seconds) * time.Second 81 | if int64(d/time.Second) != p.Seconds { 82 | return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) 83 | } 84 | if p.Nanos != 0 { 85 | d += time.Duration(p.Nanos) 86 | if (d < 0) != (p.Nanos < 0) { 87 | return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) 88 | } 89 | } 90 | return d, nil 91 | } 92 | 93 | // DurationProto converts a time.Duration to a durpb.Duration. 94 | func DurationProto(d time.Duration) *durpb.Duration { 95 | nanos := d.Nanoseconds() 96 | secs := nanos / 1e9 97 | nanos -= secs * 1e9 98 | return &durpb.Duration{ 99 | Seconds: secs, 100 | Nanos: int32(nanos), 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /vendor/github.com/golang/protobuf/ptypes/regen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # 3 | # This script fetches and rebuilds the "well-known types" protocol buffers. 4 | # To run this you will need protoc and goprotobuf installed; 5 | # see https://github.com/golang/protobuf for instructions. 6 | # You also need Go and Git installed. 7 | 8 | PKG=github.com/golang/protobuf/ptypes 9 | UPSTREAM=https://github.com/google/protobuf 10 | UPSTREAM_SUBDIR=src/google/protobuf 11 | PROTO_FILES=(any duration empty struct timestamp wrappers) 12 | 13 | function die() { 14 | echo 1>&2 $* 15 | exit 1 16 | } 17 | 18 | # Sanity check that the right tools are accessible. 19 | for tool in go git protoc protoc-gen-go; do 20 | q=$(which $tool) || die "didn't find $tool" 21 | echo 1>&2 "$tool: $q" 22 | done 23 | 24 | tmpdir=$(mktemp -d -t regen-wkt.XXXXXX) 25 | trap 'rm -rf $tmpdir' EXIT 26 | 27 | echo -n 1>&2 "finding package dir... " 28 | pkgdir=$(go list -f '{{.Dir}}' $PKG) 29 | echo 1>&2 $pkgdir 30 | base=$(echo $pkgdir | sed "s,/$PKG\$,,") 31 | echo 1>&2 "base: $base" 32 | cd "$base" 33 | 34 | echo 1>&2 "fetching latest protos... " 35 | git clone -q $UPSTREAM $tmpdir 36 | 37 | for file in ${PROTO_FILES[@]}; do 38 | echo 1>&2 "* $file" 39 | protoc --go_out=. -I$tmpdir/src $tmpdir/src/google/protobuf/$file.proto || die 40 | cp $tmpdir/src/google/protobuf/$file.proto $PKG/$file 41 | done 42 | 43 | echo 1>&2 "All OK" 44 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | [![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) 7 | [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) 8 | 9 | ### Documentation 10 | 11 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 12 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 13 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 14 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 15 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 16 | 17 | ### Status 18 | 19 | The Gorilla WebSocket package provides a complete and tested implementation of 20 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 21 | package API is stable. 22 | 23 | ### Installation 24 | 25 | go get github.com/gorilla/websocket 26 | 27 | ### Protocol Compliance 28 | 29 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 30 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 31 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 32 | 33 | ### Gorilla WebSocket compared with other packages 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
53 | 54 | Notes: 55 | 56 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 57 | 2. The application can get the type of a received data message by implementing 58 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 59 | function. 60 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 61 | Read returns when the input buffer is full or a frame boundary is 62 | encountered. Each call to Write sends a single frame message. The Gorilla 63 | io.Reader and io.WriteCloser operate on a single WebSocket message. 64 | 65 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 12 | if cfg == nil { 13 | return &tls.Config{} 14 | } 15 | return cfg.Clone() 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | // cloneTLSConfig clones all public fields except the fields 12 | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the 13 | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a 14 | // config in active use. 15 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 16 | if cfg == nil { 17 | return &tls.Config{} 18 | } 19 | return &tls.Config{ 20 | Rand: cfg.Rand, 21 | Time: cfg.Time, 22 | Certificates: cfg.Certificates, 23 | NameToCertificate: cfg.NameToCertificate, 24 | GetCertificate: cfg.GetCertificate, 25 | RootCAs: cfg.RootCAs, 26 | NextProtos: cfg.NextProtos, 27 | ServerName: cfg.ServerName, 28 | ClientAuth: cfg.ClientAuth, 29 | ClientCAs: cfg.ClientCAs, 30 | InsecureSkipVerify: cfg.InsecureSkipVerify, 31 | CipherSuites: cfg.CipherSuites, 32 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 33 | ClientSessionCache: cfg.ClientSessionCache, 34 | MinVersion: cfg.MinVersion, 35 | MaxVersion: cfg.MaxVersion, 36 | CurvePreferences: cfg.CurvePreferences, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/url" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var parseURLTests = []struct { 14 | s string 15 | u *url.URL 16 | rui string 17 | }{ 18 | {"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, 19 | {"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, 20 | {"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"}, 21 | {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"}, 22 | {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"}, 23 | {"ss://example.com/a/b", nil, ""}, 24 | {"ws://webmaster@example.com/", nil, ""}, 25 | {"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"}, 26 | {"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"}, 27 | } 28 | 29 | func TestParseURL(t *testing.T) { 30 | for _, tt := range parseURLTests { 31 | u, err := parseURL(tt.s) 32 | if tt.u != nil && err != nil { 33 | t.Errorf("parseURL(%q) returned error %v", tt.s, err) 34 | continue 35 | } 36 | if tt.u == nil { 37 | if err == nil { 38 | t.Errorf("parseURL(%q) did not return error", tt.s) 39 | } 40 | continue 41 | } 42 | if !reflect.DeepEqual(u, tt.u) { 43 | t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u) 44 | continue 45 | } 46 | if u.RequestURI() != tt.rui { 47 | t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui) 48 | } 49 | } 50 | } 51 | 52 | var hostPortNoPortTests = []struct { 53 | u *url.URL 54 | hostPort, hostNoPort string 55 | }{ 56 | {&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"}, 57 | {&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"}, 58 | {&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 59 | {&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 60 | } 61 | 62 | func TestHostPortNoPort(t *testing.T) { 63 | for _, tt := range hostPortNoPortTests { 64 | hostPort, hostNoPort := hostPortNoPort(tt.u) 65 | if hostPort != tt.hostPort { 66 | t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort) 67 | } 68 | if hostNoPort != tt.hostNoPort { 69 | t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "compress/flate" 9 | "errors" 10 | "io" 11 | "strings" 12 | "sync" 13 | ) 14 | 15 | const ( 16 | minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 17 | maxCompressionLevel = flate.BestCompression 18 | defaultCompressionLevel = 1 19 | ) 20 | 21 | var ( 22 | flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool 23 | flateReaderPool = sync.Pool{New: func() interface{} { 24 | return flate.NewReader(nil) 25 | }} 26 | ) 27 | 28 | func decompressNoContextTakeover(r io.Reader) io.ReadCloser { 29 | const tail = 30 | // Add four bytes as specified in RFC 31 | "\x00\x00\xff\xff" + 32 | // Add final block to squelch unexpected EOF error from flate reader. 33 | "\x01\x00\x00\xff\xff" 34 | 35 | fr, _ := flateReaderPool.Get().(io.ReadCloser) 36 | fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) 37 | return &flateReadWrapper{fr} 38 | } 39 | 40 | func isValidCompressionLevel(level int) bool { 41 | return minCompressionLevel <= level && level <= maxCompressionLevel 42 | } 43 | 44 | func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { 45 | p := &flateWriterPools[level-minCompressionLevel] 46 | tw := &truncWriter{w: w} 47 | fw, _ := p.Get().(*flate.Writer) 48 | if fw == nil { 49 | fw, _ = flate.NewWriter(tw, level) 50 | } else { 51 | fw.Reset(tw) 52 | } 53 | return &flateWriteWrapper{fw: fw, tw: tw, p: p} 54 | } 55 | 56 | // truncWriter is an io.Writer that writes all but the last four bytes of the 57 | // stream to another io.Writer. 58 | type truncWriter struct { 59 | w io.WriteCloser 60 | n int 61 | p [4]byte 62 | } 63 | 64 | func (w *truncWriter) Write(p []byte) (int, error) { 65 | n := 0 66 | 67 | // fill buffer first for simplicity. 68 | if w.n < len(w.p) { 69 | n = copy(w.p[w.n:], p) 70 | p = p[n:] 71 | w.n += n 72 | if len(p) == 0 { 73 | return n, nil 74 | } 75 | } 76 | 77 | m := len(p) 78 | if m > len(w.p) { 79 | m = len(w.p) 80 | } 81 | 82 | if nn, err := w.w.Write(w.p[:m]); err != nil { 83 | return n + nn, err 84 | } 85 | 86 | copy(w.p[:], w.p[m:]) 87 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 88 | nn, err := w.w.Write(p[:len(p)-m]) 89 | return n + nn, err 90 | } 91 | 92 | type flateWriteWrapper struct { 93 | fw *flate.Writer 94 | tw *truncWriter 95 | p *sync.Pool 96 | } 97 | 98 | func (w *flateWriteWrapper) Write(p []byte) (int, error) { 99 | if w.fw == nil { 100 | return 0, errWriteClosed 101 | } 102 | return w.fw.Write(p) 103 | } 104 | 105 | func (w *flateWriteWrapper) Close() error { 106 | if w.fw == nil { 107 | return errWriteClosed 108 | } 109 | err1 := w.fw.Flush() 110 | w.p.Put(w.fw) 111 | w.fw = nil 112 | if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { 113 | return errors.New("websocket: internal error, unexpected bytes at end of flate stream") 114 | } 115 | err2 := w.tw.w.Close() 116 | if err1 != nil { 117 | return err1 118 | } 119 | return err2 120 | } 121 | 122 | type flateReadWrapper struct { 123 | fr io.ReadCloser 124 | } 125 | 126 | func (r *flateReadWrapper) Read(p []byte) (int, error) { 127 | if r.fr == nil { 128 | return 0, io.ErrClosedPipe 129 | } 130 | n, err := r.fr.Read(p) 131 | if err == io.EOF { 132 | // Preemptively place the reader back in the pool. This helps with 133 | // scenarios where the application does not call NextReader() soon after 134 | // this final read. 135 | r.Close() 136 | } 137 | return n, err 138 | } 139 | 140 | func (r *flateReadWrapper) Close() error { 141 | if r.fr == nil { 142 | return io.ErrClosedPipe 143 | } 144 | err := r.fr.Close() 145 | flateReaderPool.Put(r.fr) 146 | r.fr = nil 147 | return err 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression_test.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "testing" 9 | ) 10 | 11 | type nopCloser struct{ io.Writer } 12 | 13 | func (nopCloser) Close() error { return nil } 14 | 15 | func TestTruncWriter(t *testing.T) { 16 | const data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz987654321" 17 | for n := 1; n <= 10; n++ { 18 | var b bytes.Buffer 19 | w := &truncWriter{w: nopCloser{&b}} 20 | p := []byte(data) 21 | for len(p) > 0 { 22 | m := len(p) 23 | if m > n { 24 | m = n 25 | } 26 | w.Write(p[:m]) 27 | p = p[m:] 28 | } 29 | if b.String() != data[:len(data)-len(w.p)] { 30 | t.Errorf("%d: %q", n, b.String()) 31 | } 32 | } 33 | } 34 | 35 | func textMessages(num int) [][]byte { 36 | messages := make([][]byte, num) 37 | for i := 0; i < num; i++ { 38 | msg := fmt.Sprintf("planet: %d, country: %d, city: %d, street: %d", i, i, i, i) 39 | messages[i] = []byte(msg) 40 | } 41 | return messages 42 | } 43 | 44 | func BenchmarkWriteNoCompression(b *testing.B) { 45 | w := ioutil.Discard 46 | c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) 47 | messages := textMessages(100) 48 | b.ResetTimer() 49 | for i := 0; i < b.N; i++ { 50 | c.WriteMessage(TextMessage, messages[i%len(messages)]) 51 | } 52 | b.ReportAllocs() 53 | } 54 | 55 | func BenchmarkWriteWithCompression(b *testing.B) { 56 | w := ioutil.Discard 57 | c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) 58 | messages := textMessages(100) 59 | c.enableWriteCompression = true 60 | c.newCompressionWriter = compressNoContextTakeover 61 | b.ResetTimer() 62 | for i := 0; i < b.N; i++ { 63 | c.WriteMessage(TextMessage, messages[i%len(messages)]) 64 | } 65 | b.ReportAllocs() 66 | } 67 | 68 | func TestValidCompressionLevel(t *testing.T) { 69 | c := newConn(fakeNetConn{}, false, 1024, 1024) 70 | for _, level := range []int{minCompressionLevel - 1, maxCompressionLevel + 1} { 71 | if err := c.SetCompressionLevel(level); err == nil { 72 | t.Errorf("no error for level %d", level) 73 | } 74 | } 75 | for _, level := range []int{minCompressionLevel, maxCompressionLevel} { 76 | if err := c.SetCompressionLevel(level); err != nil { 77 | t.Errorf("error for level %d", level) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_broadcast_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.7 6 | 7 | package websocket 8 | 9 | import ( 10 | "io" 11 | "io/ioutil" 12 | "sync/atomic" 13 | "testing" 14 | ) 15 | 16 | // broadcastBench allows to run broadcast benchmarks. 17 | // In every broadcast benchmark we create many connections, then send the same 18 | // message into every connection and wait for all writes complete. This emulates 19 | // an application where many connections listen to the same data - i.e. PUB/SUB 20 | // scenarios with many subscribers in one channel. 21 | type broadcastBench struct { 22 | w io.Writer 23 | message *broadcastMessage 24 | closeCh chan struct{} 25 | doneCh chan struct{} 26 | count int32 27 | conns []*broadcastConn 28 | compression bool 29 | usePrepared bool 30 | } 31 | 32 | type broadcastMessage struct { 33 | payload []byte 34 | prepared *PreparedMessage 35 | } 36 | 37 | type broadcastConn struct { 38 | conn *Conn 39 | msgCh chan *broadcastMessage 40 | } 41 | 42 | func newBroadcastConn(c *Conn) *broadcastConn { 43 | return &broadcastConn{ 44 | conn: c, 45 | msgCh: make(chan *broadcastMessage, 1), 46 | } 47 | } 48 | 49 | func newBroadcastBench(usePrepared, compression bool) *broadcastBench { 50 | bench := &broadcastBench{ 51 | w: ioutil.Discard, 52 | doneCh: make(chan struct{}), 53 | closeCh: make(chan struct{}), 54 | usePrepared: usePrepared, 55 | compression: compression, 56 | } 57 | msg := &broadcastMessage{ 58 | payload: textMessages(1)[0], 59 | } 60 | if usePrepared { 61 | pm, _ := NewPreparedMessage(TextMessage, msg.payload) 62 | msg.prepared = pm 63 | } 64 | bench.message = msg 65 | bench.makeConns(10000) 66 | return bench 67 | } 68 | 69 | func (b *broadcastBench) makeConns(numConns int) { 70 | conns := make([]*broadcastConn, numConns) 71 | 72 | for i := 0; i < numConns; i++ { 73 | c := newConn(fakeNetConn{Reader: nil, Writer: b.w}, true, 1024, 1024) 74 | if b.compression { 75 | c.enableWriteCompression = true 76 | c.newCompressionWriter = compressNoContextTakeover 77 | } 78 | conns[i] = newBroadcastConn(c) 79 | go func(c *broadcastConn) { 80 | for { 81 | select { 82 | case msg := <-c.msgCh: 83 | if b.usePrepared { 84 | c.conn.WritePreparedMessage(msg.prepared) 85 | } else { 86 | c.conn.WriteMessage(TextMessage, msg.payload) 87 | } 88 | val := atomic.AddInt32(&b.count, 1) 89 | if val%int32(numConns) == 0 { 90 | b.doneCh <- struct{}{} 91 | } 92 | case <-b.closeCh: 93 | return 94 | } 95 | } 96 | }(conns[i]) 97 | } 98 | b.conns = conns 99 | } 100 | 101 | func (b *broadcastBench) close() { 102 | close(b.closeCh) 103 | } 104 | 105 | func (b *broadcastBench) runOnce() { 106 | for _, c := range b.conns { 107 | c.msgCh <- b.message 108 | } 109 | <-b.doneCh 110 | } 111 | 112 | func BenchmarkBroadcast(b *testing.B) { 113 | benchmarks := []struct { 114 | name string 115 | usePrepared bool 116 | compression bool 117 | }{ 118 | {"NoCompression", false, false}, 119 | {"WithCompression", false, true}, 120 | {"NoCompressionPrepared", true, false}, 121 | {"WithCompressionPrepared", true, true}, 122 | } 123 | for _, bm := range benchmarks { 124 | b.Run(bm.name, func(b *testing.B) { 125 | bench := newBroadcastBench(bm.usePrepared, bm.compression) 126 | defer bench.close() 127 | b.ResetTimer() 128 | for i := 0; i < b.N; i++ { 129 | bench.runOnce() 130 | } 131 | b.ReportAllocs() 132 | }) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | c.br.Discard(len(p)) 17 | return p, err 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | if len(p) > 0 { 17 | // advance over the bytes just read 18 | io.ReadFull(c.br, p) 19 | } 20 | return p, err 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket_test 6 | 7 | import ( 8 | "log" 9 | "net/http" 10 | "testing" 11 | 12 | "github.com/gorilla/websocket" 13 | ) 14 | 15 | var ( 16 | c *websocket.Conn 17 | req *http.Request 18 | ) 19 | 20 | // The websocket.IsUnexpectedCloseError function is useful for identifying 21 | // application and protocol errors. 22 | // 23 | // This server application works with a client application running in the 24 | // browser. The client application does not explicitly close the websocket. The 25 | // only expected close message from the client has the code 26 | // websocket.CloseGoingAway. All other other close messages are likely the 27 | // result of an application or protocol error and are logged to aid debugging. 28 | func ExampleIsUnexpectedCloseError() { 29 | 30 | for { 31 | messageType, p, err := c.ReadMessage() 32 | if err != nil { 33 | if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { 34 | log.Printf("error: %v, user-agent: %v", err, req.Header.Get("User-Agent")) 35 | } 36 | return 37 | } 38 | processMesage(messageType, p) 39 | } 40 | } 41 | 42 | func processMesage(mt int, p []byte) {} 43 | 44 | // TestX prevents godoc from showing this entire file in the example. Remove 45 | // this function when a second example is added. 46 | func TestX(t *testing.T) {} 47 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON writes the JSON encoding of v as a message. 13 | // 14 | // Deprecated: Use c.WriteJSON instead. 15 | func WriteJSON(c *Conn, v interface{}) error { 16 | return c.WriteJSON(v) 17 | } 18 | 19 | // WriteJSON writes the JSON encoding of v as a message. 20 | // 21 | // See the documentation for encoding/json Marshal for details about the 22 | // conversion of Go values to JSON. 23 | func (c *Conn) WriteJSON(v interface{}) error { 24 | w, err := c.NextWriter(TextMessage) 25 | if err != nil { 26 | return err 27 | } 28 | err1 := json.NewEncoder(w).Encode(v) 29 | err2 := w.Close() 30 | if err1 != nil { 31 | return err1 32 | } 33 | return err2 34 | } 35 | 36 | // ReadJSON reads the next JSON-encoded message from the connection and stores 37 | // it in the value pointed to by v. 38 | // 39 | // Deprecated: Use c.ReadJSON instead. 40 | func ReadJSON(c *Conn, v interface{}) error { 41 | return c.ReadJSON(v) 42 | } 43 | 44 | // ReadJSON reads the next JSON-encoded message from the connection and stores 45 | // it in the value pointed to by v. 46 | // 47 | // See the documentation for the encoding/json Unmarshal function for details 48 | // about the conversion of JSON to a Go value. 49 | func (c *Conn) ReadJSON(v interface{}) error { 50 | _, r, err := c.NextReader() 51 | if err != nil { 52 | return err 53 | } 54 | err = json.NewDecoder(r).Decode(v) 55 | if err == io.EOF { 56 | // One value is expected in the message. 57 | err = io.ErrUnexpectedEOF 58 | } 59 | return err 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "io" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | func TestJSON(t *testing.T) { 16 | var buf bytes.Buffer 17 | c := fakeNetConn{&buf, &buf} 18 | wc := newConn(c, true, 1024, 1024) 19 | rc := newConn(c, false, 1024, 1024) 20 | 21 | var actual, expect struct { 22 | A int 23 | B string 24 | } 25 | expect.A = 1 26 | expect.B = "hello" 27 | 28 | if err := wc.WriteJSON(&expect); err != nil { 29 | t.Fatal("write", err) 30 | } 31 | 32 | if err := rc.ReadJSON(&actual); err != nil { 33 | t.Fatal("read", err) 34 | } 35 | 36 | if !reflect.DeepEqual(&actual, &expect) { 37 | t.Fatal("equal", actual, expect) 38 | } 39 | } 40 | 41 | func TestPartialJSONRead(t *testing.T) { 42 | var buf bytes.Buffer 43 | c := fakeNetConn{&buf, &buf} 44 | wc := newConn(c, true, 1024, 1024) 45 | rc := newConn(c, false, 1024, 1024) 46 | 47 | var v struct { 48 | A int 49 | B string 50 | } 51 | v.A = 1 52 | v.B = "hello" 53 | 54 | messageCount := 0 55 | 56 | // Partial JSON values. 57 | 58 | data, err := json.Marshal(v) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | for i := len(data) - 1; i >= 0; i-- { 63 | if err := wc.WriteMessage(TextMessage, data[:i]); err != nil { 64 | t.Fatal(err) 65 | } 66 | messageCount++ 67 | } 68 | 69 | // Whitespace. 70 | 71 | if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil { 72 | t.Fatal(err) 73 | } 74 | messageCount++ 75 | 76 | // Close. 77 | 78 | if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil { 79 | t.Fatal(err) 80 | } 81 | 82 | for i := 0; i < messageCount; i++ { 83 | err := rc.ReadJSON(&v) 84 | if err != io.ErrUnexpectedEOF { 85 | t.Error("read", i, err) 86 | } 87 | } 88 | 89 | err = rc.ReadJSON(&v) 90 | if _, ok := err.(*CloseError); !ok { 91 | t.Error("final", err) 92 | } 93 | } 94 | 95 | func TestDeprecatedJSON(t *testing.T) { 96 | var buf bytes.Buffer 97 | c := fakeNetConn{&buf, &buf} 98 | wc := newConn(c, true, 1024, 1024) 99 | rc := newConn(c, false, 1024, 1024) 100 | 101 | var actual, expect struct { 102 | A int 103 | B string 104 | } 105 | expect.A = 1 106 | expect.B = "hello" 107 | 108 | if err := WriteJSON(wc, &expect); err != nil { 109 | t.Fatal("write", err) 110 | } 111 | 112 | if err := ReadJSON(rc, &actual); err != nil { 113 | t.Fatal("read", err) 114 | } 115 | 116 | if !reflect.DeepEqual(&actual, &expect) { 117 | t.Fatal("equal", actual, expect) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | package websocket 8 | 9 | import "unsafe" 10 | 11 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 12 | 13 | func maskBytes(key [4]byte, pos int, b []byte) int { 14 | 15 | // Mask one byte at a time for small buffers. 16 | if len(b) < 2*wordSize { 17 | for i := range b { 18 | b[i] ^= key[pos&3] 19 | pos++ 20 | } 21 | return pos & 3 22 | } 23 | 24 | // Mask one byte at a time to word boundary. 25 | if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { 26 | n = wordSize - n 27 | for i := range b[:n] { 28 | b[i] ^= key[pos&3] 29 | pos++ 30 | } 31 | b = b[n:] 32 | } 33 | 34 | // Create aligned word size key. 35 | var k [wordSize]byte 36 | for i := range k { 37 | k[i] = key[(pos+i)&3] 38 | } 39 | kw := *(*uintptr)(unsafe.Pointer(&k)) 40 | 41 | // Mask one word at a time. 42 | n := (len(b) / wordSize) * wordSize 43 | for i := 0; i < n; i += wordSize { 44 | *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw 45 | } 46 | 47 | // Mask one byte at a time for remaining bytes. 48 | b = b[n:] 49 | for i := range b { 50 | b[i] ^= key[pos&3] 51 | pos++ 52 | } 53 | 54 | return pos & 3 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask_safe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build appengine 6 | 7 | package websocket 8 | 9 | func maskBytes(key [4]byte, pos int, b []byte) int { 10 | for i := range b { 11 | b[i] ^= key[pos&3] 12 | pos++ 13 | } 14 | return pos & 3 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // Require 1.7 for sub-bencmarks 6 | // +build go1.7,!appengine 7 | 8 | package websocket 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | func maskBytesByByte(key [4]byte, pos int, b []byte) int { 16 | for i := range b { 17 | b[i] ^= key[pos&3] 18 | pos++ 19 | } 20 | return pos & 3 21 | } 22 | 23 | func notzero(b []byte) int { 24 | for i := range b { 25 | if b[i] != 0 { 26 | return i 27 | } 28 | } 29 | return -1 30 | } 31 | 32 | func TestMaskBytes(t *testing.T) { 33 | key := [4]byte{1, 2, 3, 4} 34 | for size := 1; size <= 1024; size++ { 35 | for align := 0; align < wordSize; align++ { 36 | for pos := 0; pos < 4; pos++ { 37 | b := make([]byte, size+align)[align:] 38 | maskBytes(key, pos, b) 39 | maskBytesByByte(key, pos, b) 40 | if i := notzero(b); i >= 0 { 41 | t.Errorf("size:%d, align:%d, pos:%d, offset:%d", size, align, pos, i) 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | func BenchmarkMaskBytes(b *testing.B) { 49 | for _, size := range []int{2, 4, 8, 16, 32, 512, 1024} { 50 | b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) { 51 | for _, align := range []int{wordSize / 2} { 52 | b.Run(fmt.Sprintf("align-%d", align), func(b *testing.B) { 53 | for _, fn := range []struct { 54 | name string 55 | fn func(key [4]byte, pos int, b []byte) int 56 | }{ 57 | {"byte", maskBytesByByte}, 58 | {"word", maskBytes}, 59 | } { 60 | b.Run(fn.name, func(b *testing.B) { 61 | key := newMaskKey() 62 | data := make([]byte, size+align)[align:] 63 | for i := 0; i < b.N; i++ { 64 | fn.fn(key, 0, data) 65 | } 66 | b.SetBytes(int64(len(data))) 67 | }) 68 | } 69 | }) 70 | } 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/prepared.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "net" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // PreparedMessage caches on the wire representations of a message payload. 15 | // Use PreparedMessage to efficiently send a message payload to multiple 16 | // connections. PreparedMessage is especially useful when compression is used 17 | // because the CPU and memory expensive compression operation can be executed 18 | // once for a given set of compression options. 19 | type PreparedMessage struct { 20 | messageType int 21 | data []byte 22 | err error 23 | mu sync.Mutex 24 | frames map[prepareKey]*preparedFrame 25 | } 26 | 27 | // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. 28 | type prepareKey struct { 29 | isServer bool 30 | compress bool 31 | compressionLevel int 32 | } 33 | 34 | // preparedFrame contains data in wire representation. 35 | type preparedFrame struct { 36 | once sync.Once 37 | data []byte 38 | } 39 | 40 | // NewPreparedMessage returns an initialized PreparedMessage. You can then send 41 | // it to connection using WritePreparedMessage method. Valid wire 42 | // representation will be calculated lazily only once for a set of current 43 | // connection options. 44 | func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { 45 | pm := &PreparedMessage{ 46 | messageType: messageType, 47 | frames: make(map[prepareKey]*preparedFrame), 48 | data: data, 49 | } 50 | 51 | // Prepare a plain server frame. 52 | _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | // To protect against caller modifying the data argument, remember the data 58 | // copied to the plain server frame. 59 | pm.data = frameData[len(frameData)-len(data):] 60 | return pm, nil 61 | } 62 | 63 | func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { 64 | pm.mu.Lock() 65 | frame, ok := pm.frames[key] 66 | if !ok { 67 | frame = &preparedFrame{} 68 | pm.frames[key] = frame 69 | } 70 | pm.mu.Unlock() 71 | 72 | var err error 73 | frame.once.Do(func() { 74 | // Prepare a frame using a 'fake' connection. 75 | // TODO: Refactor code in conn.go to allow more direct construction of 76 | // the frame. 77 | mu := make(chan bool, 1) 78 | mu <- true 79 | var nc prepareConn 80 | c := &Conn{ 81 | conn: &nc, 82 | mu: mu, 83 | isServer: key.isServer, 84 | compressionLevel: key.compressionLevel, 85 | enableWriteCompression: true, 86 | writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), 87 | } 88 | if key.compress { 89 | c.newCompressionWriter = compressNoContextTakeover 90 | } 91 | err = c.WriteMessage(pm.messageType, pm.data) 92 | frame.data = nc.buf.Bytes() 93 | }) 94 | return pm.messageType, frame.data, err 95 | } 96 | 97 | type prepareConn struct { 98 | buf bytes.Buffer 99 | net.Conn 100 | } 101 | 102 | func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } 103 | func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } 104 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/prepared_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "compress/flate" 10 | "math/rand" 11 | "testing" 12 | ) 13 | 14 | var preparedMessageTests = []struct { 15 | messageType int 16 | isServer bool 17 | enableWriteCompression bool 18 | compressionLevel int 19 | }{ 20 | // Server 21 | {TextMessage, true, false, flate.BestSpeed}, 22 | {TextMessage, true, true, flate.BestSpeed}, 23 | {TextMessage, true, true, flate.BestCompression}, 24 | {PingMessage, true, false, flate.BestSpeed}, 25 | {PingMessage, true, true, flate.BestSpeed}, 26 | 27 | // Client 28 | {TextMessage, false, false, flate.BestSpeed}, 29 | {TextMessage, false, true, flate.BestSpeed}, 30 | {TextMessage, false, true, flate.BestCompression}, 31 | {PingMessage, false, false, flate.BestSpeed}, 32 | {PingMessage, false, true, flate.BestSpeed}, 33 | } 34 | 35 | func TestPreparedMessage(t *testing.T) { 36 | for _, tt := range preparedMessageTests { 37 | var data = []byte("this is a test") 38 | var buf bytes.Buffer 39 | c := newConn(fakeNetConn{Reader: nil, Writer: &buf}, tt.isServer, 1024, 1024) 40 | if tt.enableWriteCompression { 41 | c.newCompressionWriter = compressNoContextTakeover 42 | } 43 | c.SetCompressionLevel(tt.compressionLevel) 44 | 45 | // Seed random number generator for consistent frame mask. 46 | rand.Seed(1234) 47 | 48 | if err := c.WriteMessage(tt.messageType, data); err != nil { 49 | t.Fatal(err) 50 | } 51 | want := buf.String() 52 | 53 | pm, err := NewPreparedMessage(tt.messageType, data) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | 58 | // Scribble on data to ensure that NewPreparedMessage takes a snapshot. 59 | copy(data, "hello world") 60 | 61 | // Seed random number generator for consistent frame mask. 62 | rand.Seed(1234) 63 | 64 | buf.Reset() 65 | if err := c.WritePreparedMessage(pm); err != nil { 66 | t.Fatal(err) 67 | } 68 | got := buf.String() 69 | 70 | if got != want { 71 | t.Errorf("write message != prepared message for %+v", tt) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/http" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var subprotocolTests = []struct { 14 | h string 15 | protocols []string 16 | }{ 17 | {"", nil}, 18 | {"foo", []string{"foo"}}, 19 | {"foo,bar", []string{"foo", "bar"}}, 20 | {"foo, bar", []string{"foo", "bar"}}, 21 | {" foo, bar", []string{"foo", "bar"}}, 22 | {" foo, bar ", []string{"foo", "bar"}}, 23 | } 24 | 25 | func TestSubprotocols(t *testing.T) { 26 | for _, st := range subprotocolTests { 27 | r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}} 28 | protocols := Subprotocols(&r) 29 | if !reflect.DeepEqual(st.protocols, protocols) { 30 | t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols) 31 | } 32 | } 33 | } 34 | 35 | var isWebSocketUpgradeTests = []struct { 36 | ok bool 37 | h http.Header 38 | }{ 39 | {false, http.Header{"Upgrade": {"websocket"}}}, 40 | {false, http.Header{"Connection": {"upgrade"}}}, 41 | {true, http.Header{"Connection": {"upgRade"}, "Upgrade": {"WebSocket"}}}, 42 | } 43 | 44 | func TestIsWebSocketUpgrade(t *testing.T) { 45 | for _, tt := range isWebSocketUpgradeTests { 46 | ok := IsWebSocketUpgrade(&http.Request{Header: tt.h}) 47 | if tt.ok != ok { 48 | t.Errorf("IsWebSocketUpgrade(%v) returned %v, want %v", tt.h, ok, tt.ok) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/http" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var tokenListContainsValueTests = []struct { 14 | value string 15 | ok bool 16 | }{ 17 | {"WebSocket", true}, 18 | {"WEBSOCKET", true}, 19 | {"websocket", true}, 20 | {"websockets", false}, 21 | {"x websocket", false}, 22 | {"websocket x", false}, 23 | {"other,websocket,more", true}, 24 | {"other, websocket, more", true}, 25 | } 26 | 27 | func TestTokenListContainsValue(t *testing.T) { 28 | for _, tt := range tokenListContainsValueTests { 29 | h := http.Header{"Upgrade": {tt.value}} 30 | ok := tokenListContainsValue(h, "Upgrade", "websocket") 31 | if ok != tt.ok { 32 | t.Errorf("tokenListContainsValue(h, n, %q) = %v, want %v", tt.value, ok, tt.ok) 33 | } 34 | } 35 | } 36 | 37 | var parseExtensionTests = []struct { 38 | value string 39 | extensions []map[string]string 40 | }{ 41 | {`foo`, []map[string]string{map[string]string{"": "foo"}}}, 42 | {`foo, bar; baz=2`, []map[string]string{ 43 | map[string]string{"": "foo"}, 44 | map[string]string{"": "bar", "baz": "2"}}}, 45 | {`foo; bar="b,a;z"`, []map[string]string{ 46 | map[string]string{"": "foo", "bar": "b,a;z"}}}, 47 | {`foo , bar; baz = 2`, []map[string]string{ 48 | map[string]string{"": "foo"}, 49 | map[string]string{"": "bar", "baz": "2"}}}, 50 | {`foo, bar; baz=2 junk`, []map[string]string{ 51 | map[string]string{"": "foo"}}}, 52 | {`foo junk, bar; baz=2 junk`, nil}, 53 | {`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{ 54 | map[string]string{"": "mux", "max-channels": "4", "flow-control": ""}, 55 | map[string]string{"": "deflate-stream"}}}, 56 | {`permessage-foo; x="10"`, []map[string]string{ 57 | map[string]string{"": "permessage-foo", "x": "10"}}}, 58 | {`permessage-foo; use_y, permessage-foo`, []map[string]string{ 59 | map[string]string{"": "permessage-foo", "use_y": ""}, 60 | map[string]string{"": "permessage-foo"}}}, 61 | {`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{ 62 | map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"}, 63 | map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}}, 64 | } 65 | 66 | func TestParseExtensions(t *testing.T) { 67 | for _, tt := range parseExtensionTests { 68 | h := http.Header{http.CanonicalHeaderKey("Sec-WebSocket-Extensions"): {tt.value}} 69 | extensions := parseExtensions(h) 70 | if !reflect.DeepEqual(extensions, tt.extensions) { 71 | t.Errorf("parseExtensions(%q)\n = %v,\nwant %v", tt.value, extensions, tt.extensions) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package context defines the Context type, which carries deadlines, 6 | // cancelation signals, and other request-scoped values across API boundaries 7 | // and between processes. 8 | // As of Go 1.7 this package is available in the standard library under the 9 | // name context. https://golang.org/pkg/context. 10 | // 11 | // Incoming requests to a server should create a Context, and outgoing calls to 12 | // servers should accept a Context. The chain of function calls between must 13 | // propagate the Context, optionally replacing it with a modified copy created 14 | // using WithDeadline, WithTimeout, WithCancel, or WithValue. 15 | // 16 | // Programs that use Contexts should follow these rules to keep interfaces 17 | // consistent across packages and enable static analysis tools to check context 18 | // propagation: 19 | // 20 | // Do not store Contexts inside a struct type; instead, pass a Context 21 | // explicitly to each function that needs it. The Context should be the first 22 | // parameter, typically named ctx: 23 | // 24 | // func DoSomething(ctx context.Context, arg Arg) error { 25 | // // ... use ctx ... 26 | // } 27 | // 28 | // Do not pass a nil Context, even if a function permits it. Pass context.TODO 29 | // if you are unsure about which Context to use. 30 | // 31 | // Use context Values only for request-scoped data that transits processes and 32 | // APIs, not for passing optional parameters to functions. 33 | // 34 | // The same Context may be passed to functions running in different goroutines; 35 | // Contexts are safe for simultaneous use by multiple goroutines. 36 | // 37 | // See http://blog.golang.org/context for example code for a server that uses 38 | // Contexts. 39 | package context // import "golang.org/x/net/context" 40 | 41 | // Background returns a non-nil, empty Context. It is never canceled, has no 42 | // values, and has no deadline. It is typically used by the main function, 43 | // initialization, and tests, and as the top-level Context for incoming 44 | // requests. 45 | func Background() Context { 46 | return background 47 | } 48 | 49 | // TODO returns a non-nil, empty Context. Code should use context.TODO when 50 | // it's unclear which Context to use or it is not yet available (because the 51 | // surrounding function has not yet been extended to accept a Context 52 | // parameter). TODO is recognized by static analysis tools that determine 53 | // whether Contexts are propagated correctly in a program. 54 | func TODO() Context { 55 | return todo 56 | } 57 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/go17.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.7 6 | 7 | package context 8 | 9 | import ( 10 | "context" // standard library's context, as of Go 1.7 11 | "time" 12 | ) 13 | 14 | var ( 15 | todo = context.TODO() 16 | background = context.Background() 17 | ) 18 | 19 | // Canceled is the error returned by Context.Err when the context is canceled. 20 | var Canceled = context.Canceled 21 | 22 | // DeadlineExceeded is the error returned by Context.Err when the context's 23 | // deadline passes. 24 | var DeadlineExceeded = context.DeadlineExceeded 25 | 26 | // WithCancel returns a copy of parent with a new Done channel. The returned 27 | // context's Done channel is closed when the returned cancel function is called 28 | // or when the parent context's Done channel is closed, whichever happens first. 29 | // 30 | // Canceling this context releases resources associated with it, so code should 31 | // call cancel as soon as the operations running in this Context complete. 32 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { 33 | ctx, f := context.WithCancel(parent) 34 | return ctx, CancelFunc(f) 35 | } 36 | 37 | // WithDeadline returns a copy of the parent context with the deadline adjusted 38 | // to be no later than d. If the parent's deadline is already earlier than d, 39 | // WithDeadline(parent, d) is semantically equivalent to parent. The returned 40 | // context's Done channel is closed when the deadline expires, when the returned 41 | // cancel function is called, or when the parent context's Done channel is 42 | // closed, whichever happens first. 43 | // 44 | // Canceling this context releases resources associated with it, so code should 45 | // call cancel as soon as the operations running in this Context complete. 46 | func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { 47 | ctx, f := context.WithDeadline(parent, deadline) 48 | return ctx, CancelFunc(f) 49 | } 50 | 51 | // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). 52 | // 53 | // Canceling this context releases resources associated with it, so code should 54 | // call cancel as soon as the operations running in this Context complete: 55 | // 56 | // func slowOperationWithTimeout(ctx context.Context) (Result, error) { 57 | // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) 58 | // defer cancel() // releases resources if slowOperation completes before timeout elapses 59 | // return slowOperation(ctx) 60 | // } 61 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { 62 | return WithDeadline(parent, time.Now().Add(timeout)) 63 | } 64 | 65 | // WithValue returns a copy of parent in which the value associated with key is 66 | // val. 67 | // 68 | // Use context Values only for request-scoped data that transits processes and 69 | // APIs, not for passing optional parameters to functions. 70 | func WithValue(parent Context, key interface{}, val interface{}) Context { 71 | return context.WithValue(parent, key, val) 72 | } 73 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/go19.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.9 6 | 7 | package context 8 | 9 | import "context" // standard library's context, as of Go 1.7 10 | 11 | // A Context carries a deadline, a cancelation signal, and other values across 12 | // API boundaries. 13 | // 14 | // Context's methods may be called by multiple goroutines simultaneously. 15 | type Context = context.Context 16 | 17 | // A CancelFunc tells an operation to abandon its work. 18 | // A CancelFunc does not wait for the work to stop. 19 | // After the first call, subsequent calls to a CancelFunc do nothing. 20 | type CancelFunc = context.CancelFunc 21 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/pre_go19.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.9 6 | 7 | package context 8 | 9 | import "time" 10 | 11 | // A Context carries a deadline, a cancelation signal, and other values across 12 | // API boundaries. 13 | // 14 | // Context's methods may be called by multiple goroutines simultaneously. 15 | type Context interface { 16 | // Deadline returns the time when work done on behalf of this context 17 | // should be canceled. Deadline returns ok==false when no deadline is 18 | // set. Successive calls to Deadline return the same results. 19 | Deadline() (deadline time.Time, ok bool) 20 | 21 | // Done returns a channel that's closed when work done on behalf of this 22 | // context should be canceled. Done may return nil if this context can 23 | // never be canceled. Successive calls to Done return the same value. 24 | // 25 | // WithCancel arranges for Done to be closed when cancel is called; 26 | // WithDeadline arranges for Done to be closed when the deadline 27 | // expires; WithTimeout arranges for Done to be closed when the timeout 28 | // elapses. 29 | // 30 | // Done is provided for use in select statements: 31 | // 32 | // // Stream generates values with DoSomething and sends them to out 33 | // // until DoSomething returns an error or ctx.Done is closed. 34 | // func Stream(ctx context.Context, out chan<- Value) error { 35 | // for { 36 | // v, err := DoSomething(ctx) 37 | // if err != nil { 38 | // return err 39 | // } 40 | // select { 41 | // case <-ctx.Done(): 42 | // return ctx.Err() 43 | // case out <- v: 44 | // } 45 | // } 46 | // } 47 | // 48 | // See http://blog.golang.org/pipelines for more examples of how to use 49 | // a Done channel for cancelation. 50 | Done() <-chan struct{} 51 | 52 | // Err returns a non-nil error value after Done is closed. Err returns 53 | // Canceled if the context was canceled or DeadlineExceeded if the 54 | // context's deadline passed. No other values for Err are defined. 55 | // After Done is closed, successive calls to Err return the same value. 56 | Err() error 57 | 58 | // Value returns the value associated with this context for key, or nil 59 | // if no value is associated with key. Successive calls to Value with 60 | // the same key returns the same result. 61 | // 62 | // Use context values only for request-scoped data that transits 63 | // processes and API boundaries, not for passing optional parameters to 64 | // functions. 65 | // 66 | // A key identifies a specific value in a Context. Functions that wish 67 | // to store values in Context typically allocate a key in a global 68 | // variable then use that key as the argument to context.WithValue and 69 | // Context.Value. A key can be any type that supports equality; 70 | // packages should define keys as an unexported type to avoid 71 | // collisions. 72 | // 73 | // Packages that define a Context key should provide type-safe accessors 74 | // for the values stores using that key: 75 | // 76 | // // Package user defines a User type that's stored in Contexts. 77 | // package user 78 | // 79 | // import "golang.org/x/net/context" 80 | // 81 | // // User is the type of value stored in the Contexts. 82 | // type User struct {...} 83 | // 84 | // // key is an unexported type for keys defined in this package. 85 | // // This prevents collisions with keys defined in other packages. 86 | // type key int 87 | // 88 | // // userKey is the key for user.User values in Contexts. It is 89 | // // unexported; clients use user.NewContext and user.FromContext 90 | // // instead of using this key directly. 91 | // var userKey key = 0 92 | // 93 | // // NewContext returns a new Context that carries value u. 94 | // func NewContext(ctx context.Context, u *User) context.Context { 95 | // return context.WithValue(ctx, userKey, u) 96 | // } 97 | // 98 | // // FromContext returns the User value stored in ctx, if any. 99 | // func FromContext(ctx context.Context) (*User, bool) { 100 | // u, ok := ctx.Value(userKey).(*User) 101 | // return u, ok 102 | // } 103 | Value(key interface{}) interface{} 104 | } 105 | 106 | // A CancelFunc tells an operation to abandon its work. 107 | // A CancelFunc does not wait for the work to stop. 108 | // After the first call, subsequent calls to a CancelFunc do nothing. 109 | type CancelFunc func() 110 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/withtimeout_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context_test 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | 11 | "golang.org/x/net/context" 12 | ) 13 | 14 | // This example passes a context with a timeout to tell a blocking function that 15 | // it should abandon its work after the timeout elapses. 16 | func ExampleWithTimeout() { 17 | // Pass a context with a timeout to tell a blocking function that it 18 | // should abandon its work after the timeout elapses. 19 | ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) 20 | defer cancel() 21 | 22 | select { 23 | case <-time.After(1 * time.Second): 24 | fmt.Println("overslept") 25 | case <-ctx.Done(): 26 | fmt.Println(ctx.Err()) // prints "context deadline exceeded" 27 | } 28 | 29 | // Output: 30 | // context deadline exceeded 31 | } 32 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sync/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sync/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sync/errgroup/errgroup.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package errgroup provides synchronization, error propagation, and Context 6 | // cancelation for groups of goroutines working on subtasks of a common task. 7 | package errgroup 8 | 9 | import ( 10 | "sync" 11 | 12 | "golang.org/x/net/context" 13 | ) 14 | 15 | // A Group is a collection of goroutines working on subtasks that are part of 16 | // the same overall task. 17 | // 18 | // A zero Group is valid and does not cancel on error. 19 | type Group struct { 20 | cancel func() 21 | 22 | wg sync.WaitGroup 23 | 24 | errOnce sync.Once 25 | err error 26 | } 27 | 28 | // WithContext returns a new Group and an associated Context derived from ctx. 29 | // 30 | // The derived Context is canceled the first time a function passed to Go 31 | // returns a non-nil error or the first time Wait returns, whichever occurs 32 | // first. 33 | func WithContext(ctx context.Context) (*Group, context.Context) { 34 | ctx, cancel := context.WithCancel(ctx) 35 | return &Group{cancel: cancel}, ctx 36 | } 37 | 38 | // Wait blocks until all function calls from the Go method have returned, then 39 | // returns the first non-nil error (if any) from them. 40 | func (g *Group) Wait() error { 41 | g.wg.Wait() 42 | if g.cancel != nil { 43 | g.cancel() 44 | } 45 | return g.err 46 | } 47 | 48 | // Go calls the given function in a new goroutine. 49 | // 50 | // The first call to return a non-nil error cancels the group; its error will be 51 | // returned by Wait. 52 | func (g *Group) Go(f func() error) { 53 | g.wg.Add(1) 54 | 55 | go func() { 56 | defer g.wg.Done() 57 | 58 | if err := f(); err != nil { 59 | g.errOnce.Do(func() { 60 | g.err = err 61 | if g.cancel != nil { 62 | g.cancel() 63 | } 64 | }) 65 | } 66 | }() 67 | } 68 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sync/errgroup/errgroup_example_md5all_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package errgroup_test 6 | 7 | import ( 8 | "crypto/md5" 9 | "fmt" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | 15 | "golang.org/x/net/context" 16 | "golang.org/x/sync/errgroup" 17 | ) 18 | 19 | // Pipeline demonstrates the use of a Group to implement a multi-stage 20 | // pipeline: a version of the MD5All function with bounded parallelism from 21 | // https://blog.golang.org/pipelines. 22 | func ExampleGroup_pipeline() { 23 | m, err := MD5All(context.Background(), ".") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | for k, sum := range m { 29 | fmt.Printf("%s:\t%x\n", k, sum) 30 | } 31 | } 32 | 33 | type result struct { 34 | path string 35 | sum [md5.Size]byte 36 | } 37 | 38 | // MD5All reads all the files in the file tree rooted at root and returns a map 39 | // from file path to the MD5 sum of the file's contents. If the directory walk 40 | // fails or any read operation fails, MD5All returns an error. 41 | func MD5All(ctx context.Context, root string) (map[string][md5.Size]byte, error) { 42 | // ctx is canceled when g.Wait() returns. When this version of MD5All returns 43 | // - even in case of error! - we know that all of the goroutines have finished 44 | // and the memory they were using can be garbage-collected. 45 | g, ctx := errgroup.WithContext(ctx) 46 | paths := make(chan string) 47 | 48 | g.Go(func() error { 49 | defer close(paths) 50 | return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 51 | if err != nil { 52 | return err 53 | } 54 | if !info.Mode().IsRegular() { 55 | return nil 56 | } 57 | select { 58 | case paths <- path: 59 | case <-ctx.Done(): 60 | return ctx.Err() 61 | } 62 | return nil 63 | }) 64 | }) 65 | 66 | // Start a fixed number of goroutines to read and digest files. 67 | c := make(chan result) 68 | const numDigesters = 20 69 | for i := 0; i < numDigesters; i++ { 70 | g.Go(func() error { 71 | for path := range paths { 72 | data, err := ioutil.ReadFile(path) 73 | if err != nil { 74 | return err 75 | } 76 | select { 77 | case c <- result{path, md5.Sum(data)}: 78 | case <-ctx.Done(): 79 | return ctx.Err() 80 | } 81 | } 82 | return nil 83 | }) 84 | } 85 | go func() { 86 | g.Wait() 87 | close(c) 88 | }() 89 | 90 | m := make(map[string][md5.Size]byte) 91 | for r := range c { 92 | m[r.path] = r.sum 93 | } 94 | // Check whether any of the goroutines failed. Since g is accumulating the 95 | // errors, we don't need to send them (or check for them) in the individual 96 | // results sent on the channel. 97 | if err := g.Wait(); err != nil { 98 | return nil, err 99 | } 100 | return m, nil 101 | } 102 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "", 4 | "package": [ 5 | { 6 | "checksumSHA1": "CugKDIiTN7Xl2ID3yQ17ytRmNOY=", 7 | "path": "github.com/garyburd/redigo/internal", 8 | "revision": "9e66b83d15a259978be267d0b61838c42c3904e3", 9 | "revisionTime": "2017-07-18T22:17:51Z" 10 | }, 11 | { 12 | "checksumSHA1": "inD9UoaSJv/n3VK0CeGi/lKWDlA=", 13 | "path": "github.com/garyburd/redigo/redis", 14 | "revision": "9e66b83d15a259978be267d0b61838c42c3904e3", 15 | "revisionTime": "2017-07-18T22:17:51Z" 16 | }, 17 | { 18 | "checksumSHA1": "l/a/XlzAQfUCP3a4POXGhxkJXug=", 19 | "path": "github.com/golang/protobuf/proto", 20 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 21 | "revisionTime": "2017-11-13T18:07:20Z" 22 | }, 23 | { 24 | "checksumSHA1": "qyalT+838zrkZMjOA62VGus0VcI=", 25 | "path": "github.com/golang/protobuf/proto/proto3_proto", 26 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 27 | "revisionTime": "2017-11-13T18:07:20Z" 28 | }, 29 | { 30 | "checksumSHA1": "ykKFnDOqloGM/pABMjTWkSrXmrw=", 31 | "path": "github.com/golang/protobuf/proto/testdata", 32 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 33 | "revisionTime": "2017-11-13T18:07:20Z" 34 | }, 35 | { 36 | "checksumSHA1": "XNHQiRltA7NQJV0RvUroY+cf+zg=", 37 | "path": "github.com/golang/protobuf/protoc-gen-go/descriptor", 38 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 39 | "revisionTime": "2017-11-13T18:07:20Z" 40 | }, 41 | { 42 | "checksumSHA1": "p8EG/0ssb7O49fYv1DfxbhDXiUw=", 43 | "path": "github.com/golang/protobuf/ptypes", 44 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 45 | "revisionTime": "2017-11-13T18:07:20Z" 46 | }, 47 | { 48 | "checksumSHA1": "UB9scpDxeFjQe5tEthuR4zCLRu4=", 49 | "path": "github.com/golang/protobuf/ptypes/any", 50 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 51 | "revisionTime": "2017-11-13T18:07:20Z" 52 | }, 53 | { 54 | "checksumSHA1": "hUjAj0dheFVDl84BAnSWj9qy2iY=", 55 | "path": "github.com/golang/protobuf/ptypes/duration", 56 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 57 | "revisionTime": "2017-11-13T18:07:20Z" 58 | }, 59 | { 60 | "checksumSHA1": "O2ItP5rmfrgxPufhjJXbFlXuyL8=", 61 | "path": "github.com/golang/protobuf/ptypes/timestamp", 62 | "revision": "1e59b77b52bf8e4b449a57e6f79f21226d571845", 63 | "revisionTime": "2017-11-13T18:07:20Z" 64 | }, 65 | { 66 | "checksumSHA1": "niXAtpjPumpCt4gOn7DtrQtuEKI=", 67 | "path": "github.com/gorilla/websocket", 68 | "revision": "a69d9f6de432e2c6b296a947d8a5ee88f68522cf", 69 | "revisionTime": "2017-07-18T20:23:41Z" 70 | }, 71 | { 72 | "checksumSHA1": "wRLo86qqt/7IwI8X43IjXLvveU0=", 73 | "path": "golang.org/x/net/context", 74 | "revision": "c7086645de248775cbf2373cf5ca4d2fa664b8c1", 75 | "revisionTime": "2017-11-10T09:49:23Z" 76 | }, 77 | { 78 | "checksumSHA1": "N4WObJwRFBC6QDe67fHtXLnAAB4=", 79 | "path": "golang.org/x/sync/errgroup", 80 | "revision": "fd80eb99c8f653c847d294a001bdf2a3a6f768f5", 81 | "revisionTime": "2017-11-01T19:49:15Z" 82 | }, 83 | { 84 | "checksumSHA1": "538I8ghUdvZa25NxgMUDpq14Qic=", 85 | "path": "gopkg.in/check.v1", 86 | "revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec", 87 | "revisionTime": "2016-12-08T18:13:25Z" 88 | }, 89 | { 90 | "checksumSHA1": "tefd2MHMp6qRXeE3jqzp3P40mNI=", 91 | "path": "gopkg.in/mgo.v2", 92 | "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655", 93 | "revisionTime": "2016-08-18T02:01:20Z" 94 | }, 95 | { 96 | "checksumSHA1": "+mZKlPX9t4fHOQvkQeCnWw5JjkQ=", 97 | "path": "gopkg.in/mgo.v2/bson", 98 | "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655", 99 | "revisionTime": "2016-08-18T02:01:20Z" 100 | }, 101 | { 102 | "checksumSHA1": "trGhCcEZSOZTwlLfWCRAoXXihW8=", 103 | "path": "gopkg.in/mgo.v2/internal/json", 104 | "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655", 105 | "revisionTime": "2016-08-18T02:01:20Z" 106 | }, 107 | { 108 | "checksumSHA1": "LEvMCnprte47qdAxWvQ/zRxVF1U=", 109 | "path": "gopkg.in/mgo.v2/internal/sasl", 110 | "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655", 111 | "revisionTime": "2016-08-18T02:01:20Z" 112 | }, 113 | { 114 | "checksumSHA1": "fOxeCpKRYNTTM2VCrsU2S/KEd1s=", 115 | "path": "gopkg.in/mgo.v2/internal/scram", 116 | "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655", 117 | "revisionTime": "2016-08-18T02:01:20Z" 118 | }, 119 | { 120 | "checksumSHA1": "QX69hdf0wsAInBxC21QNkmXXD4o=", 121 | "path": "gopkg.in/yaml.v2", 122 | "revision": "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b", 123 | "revisionTime": "2017-04-07T17:21:22Z" 124 | } 125 | ], 126 | "rootPath": "github.com/zsai001/leaf_cluster" 127 | } 128 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package leaf 2 | 3 | const version = "1.1.2" 4 | --------------------------------------------------------------------------------