├── room ├── readme.md ├── utils_test.go ├── utils.go ├── timeout_table.go ├── Interface.go ├── base_room.go ├── base_player_imp.go ├── options.go ├── queue_table.go ├── base_table_imp.go └── unified_send_message_table.go ├── component ├── rsync │ ├── README.md │ ├── test-data │ │ ├── golang-modified.bmp │ │ ├── golang-original.bmp │ │ ├── text-modified.txt │ │ └── text-original.txt │ ├── rsync_test.go │ ├── hton.go │ ├── dart │ │ ├── rsync.dart │ │ └── crclib.dart │ ├── javascript │ │ ├── crc.js │ │ └── rsync.js │ └── rsync.go └── data_sync.go ├── go.mod ├── .gitignore ├── sms ├── rediskeys.go ├── utils_test.go ├── readme.md ├── utils.go └── module.go ├── tools └── redis.go └── go.sum /room/readme.md: -------------------------------------------------------------------------------- 1 | # 使用教程 2 | 3 | [教程](http://docs.mqant.com/) 4 | -------------------------------------------------------------------------------- /component/rsync/README.md: -------------------------------------------------------------------------------- 1 | A Golang implementation of the rsync algorithm. 2 | -------------------------------------------------------------------------------- /component/rsync/test-data/golang-modified.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangdas/mqant-modules/HEAD/component/rsync/test-data/golang-modified.bmp -------------------------------------------------------------------------------- /component/rsync/test-data/golang-original.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangdas/mqant-modules/HEAD/component/rsync/test-data/golang-original.bmp -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/liangdas/mqant-modules 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/gomodule/redigo v2.0.0+incompatible 7 | github.com/liangdas/mqant v1.3.9 8 | github.com/pkg/errors v0.8.1 9 | github.com/yireyun/go-queue v0.0.0-20180809062148-5e6897360dac 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | .idea 3 | bin/logs 4 | #bin/sslkey 5 | bin/server 6 | bin/client 7 | #src/hitball 8 | #src/server/hitball 9 | #src/gopkg.in 10 | src/github.com/quick-know-master 11 | pkg 12 | node_modules 13 | *.o 14 | *.a 15 | *.so 16 | *.iml 17 | 18 | # Folders 19 | _obj 20 | _test 21 | 22 | # Architecture specific extensions/prefixes 23 | *.[568vq] 24 | [568vq].out 25 | 26 | *.cgo1.go 27 | *.cgo2.c 28 | _cgo_defun.c 29 | _cgo_gotypes.go 30 | _cgo_export.* 31 | 32 | _testmain.go 33 | 34 | *.exe 35 | *.test 36 | *.prof 37 | 38 | server.conf.bat 39 | 40 | 41 | -------------------------------------------------------------------------------- /room/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import "testing" 17 | 18 | func assertEqual(t *testing.T, val interface{}, exp interface{}) { 19 | if val != exp { 20 | t.Errorf("Expected %v, got %v.", exp, val) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sms/rediskeys.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package sms 15 | 16 | var ( 17 | MobileTTLFormat = "mobile:verify:code:%s" //手机验证码TTL控制 %s=mobile 18 | MobileSmsCodeFormat = "mobile:verify:code:%s:%s" //手机验证码存储 %s=mobile %s=smsCode EXPIRE=60s 19 | 20 | ) 21 | -------------------------------------------------------------------------------- /room/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import ( 17 | "math/rand" 18 | "time" 19 | ) 20 | 21 | //生成随机字符串 22 | func GetRandomString(lenght int) string { 23 | str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 24 | bytes := []byte(str) 25 | result := []byte{} 26 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 27 | for i := 0; i < lenght; i++ { 28 | result = append(result, bytes[r.Intn(len(bytes))]) 29 | } 30 | return string(result) 31 | } 32 | -------------------------------------------------------------------------------- /sms/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 liangdas Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sms 16 | 17 | import "testing" 18 | 19 | func TestSendCloudSignature(t *testing.T) { 20 | param := map[string]string{ 21 | "smsCode": "123456", 22 | "content": "大家好", 23 | } 24 | t.Fatal(SendCloudSignature("sss", param)) 25 | } 26 | 27 | func TestAliyunPOPSignature(t *testing.T) { 28 | param := map[string]string{ 29 | "smsCode": "123456", 30 | "content": "大家好", 31 | } 32 | t.Fatal(AliyunPOPSignature("POST", "sss", "SSS", param)) 33 | } 34 | -------------------------------------------------------------------------------- /tools/redis.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/gomodule/redigo/redis" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var factory *RedisFactory 10 | 11 | func GetRedisFactory() *RedisFactory { 12 | if factory == nil { 13 | factory = &RedisFactory{} 14 | } 15 | return factory 16 | } 17 | 18 | type RedisFactory struct { 19 | pools sync.Map 20 | } 21 | 22 | func (this RedisFactory) GetPool(url string) *redis.Pool { 23 | if pool, ok := this.pools.Load(url); ok { 24 | return pool.(*redis.Pool) 25 | } 26 | pool := &redis.Pool{ 27 | // 最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态 28 | MaxIdle: 10, 29 | // 最大的激活连接数,表示同时最多有N个连接 ,为0事表示没有限制 30 | MaxActive: 100, 31 | //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭 32 | IdleTimeout: 240 * time.Second, 33 | // 当链接数达到最大后是否阻塞,如果不的话,达到最大后返回错误 34 | Wait: true, 35 | Dial: func() (redis.Conn, error) { 36 | c, err := redis.DialURL(url) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return c, err 41 | }, 42 | TestOnBorrow: func(c redis.Conn, t time.Time) error { 43 | _, err := c.Do("PING") 44 | return err 45 | }, 46 | } 47 | if pool != nil { 48 | this.pools.Store(url, pool) 49 | } 50 | return pool 51 | } 52 | func (this RedisFactory) CloseAllPool() { 53 | this.pools.Range(func(key, value interface{}) bool { 54 | value.(*redis.Pool).Close() 55 | this.pools.Delete(key) 56 | return true 57 | }) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /room/timeout_table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import "time" 17 | 18 | /** 19 | table超时处理机制 20 | */ 21 | type TimeOutTable struct { 22 | subtable SubTable 23 | timeout int64 //默认超时时间单位秒 24 | lastCommunicationDate int64 25 | } 26 | 27 | func (this *TimeOutTable) TimeOutTableInit(subtable SubTable, timeout int64) { 28 | this.subtable = subtable 29 | this.timeout = timeout 30 | this.lastCommunicationDate = time.Now().Unix() 31 | } 32 | func (this *TimeOutTable) ResetTimeOut() { 33 | this.lastCommunicationDate = time.Now().Unix() 34 | } 35 | 36 | /** 37 | 检查整个table是否已超时 38 | 39 | 检查规则: 40 | 1. 所有玩家超过指定时间未连接 41 | 2. 所有玩家网络中断时间超过指定时间(依赖table内会定期广播消息给玩家) 42 | */ 43 | func (this *TimeOutTable) CheckTimeOut() { 44 | for _, player := range this.subtable.GetSeats() { 45 | if player != nil { 46 | if this.lastCommunicationDate < player.GetLastReqResDate() { 47 | this.lastCommunicationDate = player.GetLastReqResDate() 48 | } 49 | } 50 | } 51 | if this.timeout > 0 { 52 | if time.Now().Unix() > (this.lastCommunicationDate + this.timeout) { 53 | this.subtable.OnTimeOut() 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /room/Interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import ( 17 | "github.com/liangdas/mqant/gate" 18 | ) 19 | 20 | var ( 21 | Uninitialized = 0 //未初始化 22 | Initialized = 1 //已初始化的 23 | Active = 2 //活跃状态 24 | Finished = 4 //已停止状态 25 | ) 26 | 27 | type BaseTable interface { 28 | Options() Options 29 | TableId() string 30 | 31 | OnCreate() //可以进行一些初始化的工作在table第一次被创建的时候调用,可接受处理消息 32 | OnDestroy() //在table销毁时调用 销毁:onPause()->onStop()->onDestroy() 33 | OnTimeOut() //当table超时了 34 | 35 | Runing() bool //table是否在Runing中,只要在Runing中就能接收和处理消息 36 | Run() 37 | Finish() //停止table 38 | 39 | Register(id string, f interface{}) 40 | SetReceive(receive QueueReceive) 41 | PutQueue(_func string, params ...interface{}) error 42 | ExecuteEvent(arge interface{}) 43 | } 44 | 45 | type BasePlayer interface { 46 | IsBind() bool 47 | Bind(session gate.Session) BasePlayer 48 | 49 | UnBind() error 50 | /** 51 | 玩家主动发请求时触发 52 | */ 53 | OnRequest(session gate.Session) 54 | /** 55 | 服务器主动发送消息给玩家时触发 56 | */ 57 | OnResponse(session gate.Session) 58 | /* 59 | 服务器跟玩家最后一次成功通信时间 60 | */ 61 | GetLastReqResDate() int64 62 | Body() interface{} 63 | SetBody(body interface{}) 64 | Session() gate.Session 65 | Type() string 66 | } 67 | -------------------------------------------------------------------------------- /room/base_room.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import ( 17 | "github.com/liangdas/mqant/module" 18 | "sync" 19 | ) 20 | 21 | type Room struct { 22 | app module.App 23 | tables sync.Map 24 | roomId int 25 | } 26 | 27 | type NewTableFunc func(module module.App, tableId string) (BaseTable, error) 28 | 29 | func NewRoom(module module.App) *Room { 30 | room := &Room{ 31 | app: module, 32 | } 33 | return room 34 | } 35 | 36 | func (self *Room) OnInit(module module.App, roomId int) error { 37 | self.app = module 38 | self.roomId = roomId 39 | return nil 40 | } 41 | 42 | func (self *Room) RoomId() int { 43 | return self.roomId 44 | } 45 | 46 | func (self *Room) CreateById(app module.App, tableId string, newTablefunc NewTableFunc) (BaseTable, error) { 47 | if table, ok := self.tables.Load(tableId); ok { 48 | table.(BaseTable).Run() 49 | return table.(BaseTable), nil 50 | } 51 | table, err := newTablefunc(app, tableId) 52 | if err != nil { 53 | return nil, err 54 | } 55 | self.tables.Store(table.TableId(), table) 56 | return table, nil 57 | } 58 | 59 | func (self *Room) GetTable(tableId string) BaseTable { 60 | if table, ok := self.tables.Load(tableId); ok { 61 | table.(BaseTable).Run() 62 | return table.(BaseTable) 63 | } 64 | return nil 65 | } 66 | 67 | func (self *Room) DestroyTable(tableId string) error { 68 | self.tables.Delete(tableId) 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /component/data_sync.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | . "github.com/liangdas/mqant-modules/component/rsync" 5 | "github.com/liangdas/mqant/log" 6 | "hash/crc32" 7 | "time" 8 | ) 9 | 10 | const ( 11 | FULL = iota 12 | PATCH 13 | NEWEST //无需同步 14 | ) 15 | 16 | type InterDataSync interface { 17 | ResetData() error 18 | UpdateData() 19 | SyncDate() 20 | Marshal(table interface{}) ([]byte, int, error) 21 | } 22 | 23 | type SyncBytes interface { 24 | Source(table interface{}) ([]byte, error) //数据源 25 | } 26 | 27 | type DataSync struct { 28 | sub SyncBytes 29 | original []byte //上一次同步数据 30 | blockSize int //数据块大小,根据实际情况调整,默认可设置为 24 31 | syncDate int64 //数据同步给客户端的日期 32 | updateData int64 //数据更新日期 33 | } 34 | 35 | func (ds *DataSync) OnInitDataSync(Sub SyncBytes, blockSize int) error { 36 | ds.sub = Sub 37 | ds.blockSize = blockSize 38 | return nil 39 | } 40 | 41 | /** 42 | 重置补丁 43 | */ 44 | func (ds *DataSync) ResetData() error { 45 | ds.original = nil 46 | ds.updateData = time.Now().UnixNano() 47 | return nil 48 | } 49 | func (ds *DataSync) UpdateData() { 50 | ds.updateData = time.Now().UnixNano() 51 | } 52 | func (ds *DataSync) SyncDate() { 53 | ds.syncDate = time.Now().UnixNano() 54 | } 55 | 56 | /** 57 | 补丁数据 58 | */ 59 | func (ds *DataSync) Marshal(table interface{}) ([]byte, int, error) { 60 | if ds.updateData <= ds.syncDate { 61 | return nil, NEWEST, nil 62 | } 63 | modified, err := ds.sub.Source(table) 64 | if err != nil { 65 | return nil, 0, err 66 | } 67 | if ds.original == nil { 68 | ds.original = modified 69 | ds.SyncDate() 70 | log.TInfo(nil, "ds.original=modified") 71 | return modified, FULL, err 72 | } 73 | rs := &LRsync{ 74 | BlockSize: ds.blockSize, 75 | } 76 | hashes := rs.CalculateBlockHashes(ds.original) 77 | opsChannel := rs.CalculateDifferences(modified, hashes) 78 | ieee := crc32.NewIEEE() 79 | ieee.Write(modified) 80 | s := ieee.Sum32() 81 | delta := rs.CreateDelta(opsChannel, len(modified), s) 82 | ds.original = modified 83 | ds.SyncDate() 84 | return delta, PATCH, err 85 | } 86 | -------------------------------------------------------------------------------- /room/base_player_imp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 loolgame Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package room 15 | 16 | import ( 17 | "github.com/liangdas/mqant/gate" 18 | "time" 19 | ) 20 | 21 | type BasePlayerImp struct { 22 | session gate.Session 23 | lastNewsDate int64 //玩家最后一次成功通信时间 单位秒 24 | body interface{} 25 | } 26 | 27 | func (self *BasePlayerImp) Type() string { 28 | return "BasePlayer" 29 | } 30 | 31 | func (self *BasePlayerImp) IsBind() bool { 32 | if self.session == nil { 33 | return false 34 | } else { 35 | return true 36 | } 37 | } 38 | 39 | func (self *BasePlayerImp) Bind(session gate.Session) BasePlayer { 40 | self.lastNewsDate = time.Now().Unix() 41 | self.session = session 42 | return self 43 | } 44 | 45 | func (self *BasePlayerImp) UnBind() error { 46 | self.session = nil 47 | return nil 48 | } 49 | 50 | /** 51 | 玩家主动发请求时间 52 | */ 53 | func (self *BasePlayerImp) OnRequest(session gate.Session) { 54 | self.session = session 55 | self.lastNewsDate = time.Now().Unix() 56 | } 57 | 58 | /** 59 | 服务器主动发送消息给客户端的时间 60 | */ 61 | func (self *BasePlayerImp) OnResponse(session gate.Session) { 62 | self.session = session 63 | self.lastNewsDate = time.Now().Unix() 64 | } 65 | 66 | func (self *BasePlayerImp) GetLastReqResDate() int64 { 67 | return self.lastNewsDate 68 | } 69 | func (self *BasePlayerImp) Body() interface{} { 70 | return self.body 71 | } 72 | 73 | func (self *BasePlayerImp) SetBody(body interface{}) { 74 | self.body = body 75 | } 76 | 77 | func (self *BasePlayerImp) Session() gate.Session { 78 | return self.session 79 | } 80 | -------------------------------------------------------------------------------- /component/rsync/rsync_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Julian Gutierrez Oschmann (github.com/julian-gutierrez-o). 2 | // All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | // Unit tests for core package 7 | package rsync 8 | 9 | import "testing" 10 | import ( 11 | "github.com/liangdas/mqant/log" 12 | "hash/crc32" 13 | "io/ioutil" 14 | ) 15 | 16 | type filePair struct { 17 | original string 18 | modified string 19 | } 20 | 21 | func Test_SyncModifiedContent(t *testing.T) { 22 | //,filePair{"golang-original.bmp", "golang-modified.bmp"} 23 | files := []filePair{filePair{"text-original.txt", "text-modified.txt"}} 24 | 25 | for _, filePair := range files { 26 | original, _ := ioutil.ReadFile("test-data/" + filePair.original) 27 | modified, _ := ioutil.ReadFile("test-data/" + filePair.modified) 28 | rs := &LRsync{ 29 | BlockSize: 16, 30 | } 31 | hashes := rs.CalculateBlockHashes(original) 32 | opsChannel := rs.CalculateDifferences(modified, hashes) 33 | log.Info("CalculateDifferences %v", filePair) 34 | //result := ApplyOps(original, opsChannel, len(modified)) 35 | ieee := crc32.NewIEEE() 36 | ieee.Write(modified) 37 | s := ieee.Sum32() 38 | log.Info("IEEE modified = 0x%x", s) 39 | delta := rs.CreateDelta(opsChannel, len(modified), s) 40 | result, err := rs.Patch(original, delta) 41 | if err != nil { 42 | log.Info("rsync did not work as expected for %v error %v", filePair, err) 43 | } 44 | log.TInfo(nil, "original %v opsChannel %v CreateDelta %v", len(original), len(opsChannel), len(delta)) 45 | if string(result) != string(modified) { 46 | t.Errorf("rsync did not work as expected for %v", filePair) 47 | } 48 | } 49 | } 50 | 51 | func Test_WeakHash(t *testing.T) { 52 | content := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 53 | expectedWeak := uint32(10813485) 54 | expectedA := uint32(45) 55 | expectedB := uint32(165) 56 | weak, a, b := weakHash(content) 57 | 58 | assertHash(t, "weak", content, expectedWeak, weak) 59 | assertHash(t, "a", content, expectedA, a) 60 | assertHash(t, "b", content, expectedB, b) 61 | } 62 | 63 | func assertHash(t *testing.T, name string, content []byte, expected uint32, found uint32) { 64 | if found != expected { 65 | t.Errorf("Incorrent "+name+" hash for %v - Expected %d - Found %d", content, expected, found) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sms/readme.md: -------------------------------------------------------------------------------- 1 | # 短信验证码模块 2 | 3 | 可以在mqant任何模块中方便的使用阿里云和sendcloud发送短信验证码 4 | 5 | # 依赖模块 6 | 7 | go get github.com/liangdas/mqant 8 | 9 | # 外部依赖 10 | 11 | 1. redis 12 | 2. http 13 | 14 | # 使用方法 15 | 16 | ### 1,导入项目 17 | 18 | go get github.com/liangdas/mqant-modules 19 | 20 | ### 2,将模块加入启动列表 21 | 22 | app.Run(true, //只有是在调试模式下才会在控制台打印日志, 非调试模式下只在日志文件中输出日志 23 | sms.Module(), 24 | 。。。。 25 | ) 26 | 27 | ### 3,配置文件中加入模块配置 28 | 29 | { 30 | //..... 31 | "Module":{ 32 | //..... 33 | "SMS":[ 34 | { 35 | "Id":"SMS001", 36 | "ProcessID":"development", 37 | "Settings":{ 38 | //用于限制每一个手机号码发送短信时间间隔(TTL) 39 | "RedisUrl": "redis://:[user]@[ip]:[port]/[db]", 40 | "TTL":60, 41 | //sendcloud后台申请参数 42 | "SendCloud":{ 43 | "SmsUser": "xxx", 44 | "SmsKey": "xxx", 45 | "TemplateId":"xxx" 46 | }, 47 | //阿里云后台申请参数 48 | "Ailyun":{ 49 | "AccessKeyId":"xxx", 50 | "AccessSecret":"xxx", 51 | "SignName":"xxx", 52 | "TemplateCode":"xxx" 53 | } 54 | }, 55 | //mqant rpc 通信配置 56 | "Redis":{ 57 | "Uri" :"redis://:[user]@[ip]:[port]/[db]", 58 | "Queue" :"SMS001" 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | 65 | ### 4,在mqant模块中通过rpc远程调用发送验证码 66 | 67 | extra:=map[string]interface{}{} 68 | m,err:=self.module.RpcInvoke("SMS","SendVerifiycode",phone,purpose,extra) 69 | if err!=""{ 70 | //验证码发送失败 71 | } 72 | 73 | 传入参数: 74 | 75 | | 参数 | 参数类型 | 是否必选 | 说明 | 76 | | :-------- |:--:| --------:| :--: | 77 | | phone | string|是 | 手机号码 | 78 | | purpose | string |是 | 验证码用途 | 79 | | extra | map |是 | 额外参数 | 80 | 81 | 82 | ### 5,获取验证码结果 83 | 84 | > 在登录/注册验证码校验时调用这个接口 85 | 86 | extra:=map[string]interface{}{} 87 | m,err:=self.module.RpcInvoke("SMS","GetCodeData",phone,smsCode,del) 88 | if err!=""{ 89 | //验证码发送失败 90 | } 91 | 92 | 传入参数: 93 | 94 | | 参数 | 参数类型 | 是否必选 | 说明 | 95 | | :-------- |:--:| --------:| :--: | 96 | | phone | string|是 | 手机号码 | 97 | | smsCode | int64 |是 | 验证码用途 | 98 | | del | bool |是 | 查询后将这个key删除(失效) | 99 | 100 | ### 6,只加入发送队列,不需要等待返回结果 101 | 102 | > 发送短信会进行http请求,这是一个耗时的io操作,因此我们可以只给sms一个发送短信任务,然后继续做自己的不用sms模块返回结果。 103 | > 如果短信发送失败,用户可能也会重新触发发送短信,因此等待这个返回结果意义不大 104 | 105 | extra:=map[string]interface{}{} 106 | err:=self.module.RpcInvokeNR("SMS","SendVerifiycode",phone,purpose,extra) 107 | 108 | 上面的调用就不是不需要SMS模块返回结果,SMS模块可以根据自身的处理性能异步执行发送短信任务。 -------------------------------------------------------------------------------- /room/options.go: -------------------------------------------------------------------------------- 1 | package room 2 | 3 | import ( 4 | "github.com/liangdas/mqant/log" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | type ProUpdateHandle func(ds time.Duration) 10 | 11 | type UpdateHandle func(ds time.Duration) 12 | 13 | type PostUpdateHandle func(ds time.Duration) 14 | 15 | type NoFoundHandle func(msg *QueueMsg) (reflect.Value, error) 16 | 17 | type ErrorHandle func(msg *QueueMsg, err error) 18 | 19 | type RecoverHandle func(msg *QueueMsg, err error) 20 | 21 | /** 22 | 当房间关闭是通知持有方注销 23 | */ 24 | type LifeCallback func(table BaseTable) error 25 | 26 | /* 27 | 获取可以路由到该房间的地址路径 28 | */ 29 | type Route func(TableId string) string 30 | 31 | func newOptions(opts ...Option) Options { 32 | opt := Options{ 33 | TimeOut: 60, 34 | Capaciity: 256, 35 | SendMsgCapaciity: 256, 36 | RunInterval: 100 * time.Millisecond, 37 | } 38 | 39 | for _, o := range opts { 40 | o(&opt) 41 | } 42 | return opt 43 | } 44 | 45 | type Option func(*Options) 46 | 47 | type Options struct { 48 | ProUpdate ProUpdateHandle 49 | Update UpdateHandle 50 | PostUpdate PostUpdateHandle 51 | NoFound NoFoundHandle 52 | ErrorHandle ErrorHandle 53 | RecoverHandle RecoverHandle 54 | DestroyCallbacks LifeCallback 55 | TableId string 56 | Router Route 57 | Trace log.TraceSpan 58 | TimeOut int64 //判断客户端超时时间单位秒 59 | Capaciity uint32 //消息队列容量,真实容量为 Capaciity*2 60 | SendMsgCapaciity uint32 //每帧发送消息容量 61 | RunInterval time.Duration //运行间隔 62 | } 63 | 64 | func ProUpdate(fn ProUpdateHandle) Option { 65 | return func(o *Options) { 66 | o.ProUpdate = fn 67 | } 68 | } 69 | 70 | func PostUpdate(fn PostUpdateHandle) Option { 71 | return func(o *Options) { 72 | o.PostUpdate = fn 73 | } 74 | } 75 | 76 | func Update(fn UpdateHandle) Option { 77 | return func(o *Options) { 78 | o.Update = fn 79 | } 80 | } 81 | 82 | func NoFound(fn NoFoundHandle) Option { 83 | return func(o *Options) { 84 | o.NoFound = fn 85 | } 86 | } 87 | 88 | func SetErrorHandle(fn ErrorHandle) Option { 89 | return func(o *Options) { 90 | o.ErrorHandle = fn 91 | } 92 | } 93 | 94 | func SetRecoverHandle(fn RecoverHandle) Option { 95 | return func(o *Options) { 96 | o.RecoverHandle = fn 97 | } 98 | } 99 | 100 | func TableId(v string) Option { 101 | return func(o *Options) { 102 | o.TableId = v 103 | } 104 | } 105 | 106 | func Router(v Route) Option { 107 | return func(o *Options) { 108 | o.Router = v 109 | } 110 | } 111 | 112 | func Trace(v log.TraceSpan) Option { 113 | return func(o *Options) { 114 | o.Trace = v 115 | } 116 | } 117 | 118 | func DestroyCallbacks(v LifeCallback) Option { 119 | return func(o *Options) { 120 | o.DestroyCallbacks = v 121 | } 122 | } 123 | 124 | func TimeOut(v int64) Option { 125 | return func(o *Options) { 126 | o.TimeOut = v 127 | } 128 | } 129 | 130 | func Capaciity(v uint32) Option { 131 | return func(o *Options) { 132 | o.Capaciity = v 133 | } 134 | } 135 | 136 | func SendMsgCapaciity(v uint32) Option { 137 | return func(o *Options) { 138 | o.SendMsgCapaciity = v 139 | } 140 | } 141 | 142 | func RunInterval(v time.Duration) Option { 143 | return func(o *Options) { 144 | o.RunInterval = v 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /sms/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 liangdas Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package sms 15 | 16 | import ( 17 | "crypto/hmac" 18 | "crypto/md5" 19 | "crypto/sha1" 20 | "encoding/base64" 21 | "encoding/hex" 22 | "github.com/liangdas/mqant/log" 23 | "github.com/liangdas/mqant/utils/uuid" 24 | "math/rand" 25 | "net/url" 26 | "sort" 27 | "strings" 28 | "time" 29 | ) 30 | 31 | func RandInt64(min, max int64) int64 { 32 | if min >= max { 33 | return max 34 | } 35 | return rand.Int63n(max-min) + min 36 | } 37 | func SendCloudSignature(smsKey string, param map[string]string) string { 38 | delete(param, "signature") 39 | sorted_keys := make([]string, 0) 40 | for k, _ := range param { 41 | sorted_keys = append(sorted_keys, k) 42 | } 43 | 44 | // sort 'string' key in increasing order 45 | sort.Strings(sorted_keys) 46 | 47 | param_strs := make([]string, 0) 48 | for _, k := range sorted_keys { 49 | param_strs = append(param_strs, k+"="+param[k]) 50 | } 51 | param_str := strings.Join(param_strs, "&") 52 | sign_str := smsKey + "&" + param_str + "&" + smsKey 53 | h := md5.New() 54 | h.Write([]byte(sign_str)) // 需要加密的字符串 55 | signature := h.Sum(nil) 56 | sign := hex.EncodeToString(signature) 57 | param["signature"] = sign 58 | return sign 59 | } 60 | 61 | func specialUrlEncode(value string) string { 62 | value = url.QueryEscape(value) 63 | value = strings.Replace(value, "+", "%20", -1) 64 | value = strings.Replace(value, "*", "%2A", -1) 65 | value = strings.Replace(value, "%7E", "~", -1) 66 | return value 67 | } 68 | 69 | func AliyunPOPSignature(HTTPMethod, accessKeyId string, accessSecret string, param map[string]string) string { 70 | delete(param, "Signature") 71 | local, err3 := time.LoadLocation("GMT") 72 | if err3 != nil { 73 | log.Error(err3.Error()) 74 | } 75 | // 1. 系统参数 76 | param["SignatureMethod"] = "HMAC-SHA1" 77 | param["SignatureNonce"] = uuid.Rand().Hex() 78 | param["AccessKeyId"] = accessKeyId 79 | param["SignatureVersion"] = "1.0" 80 | param["Timestamp"] = time.Now().In(local).Format("2006-01-02T15:04:05Z") //"yyyy-MM-dd'T'HH:mm:ss'Z' 这里一定要设置GMT时区" 81 | param["Format"] = "JSON" 82 | 83 | //log.Error(time.Now().Format("2006-01-02T15:04:05Z")) 84 | //log.Error(time.Now().In(local).Format("2006-01-02T15:04:05Z")) 85 | sorted_keys := make([]string, 0) 86 | for k, _ := range param { 87 | sorted_keys = append(sorted_keys, k) 88 | } 89 | 90 | // sort 'string' key in increasing order 91 | sort.Strings(sorted_keys) 92 | 93 | param_strs := make([]string, 0) 94 | for _, k := range sorted_keys { 95 | param_strs = append(param_strs, specialUrlEncode(k)+"="+specialUrlEncode(param[k])) 96 | } 97 | sortedQueryString := strings.Join(param_strs, "&") 98 | //log.Error(sortedQueryString) 99 | //HTTPMethod + “&” + specialUrlEncode(“/”) + ”&” + specialUrlEncode(sortedQueryString) 100 | stringToSign := HTTPMethod + "&" + specialUrlEncode("/") + "&" + specialUrlEncode(sortedQueryString) 101 | //log.Error(stringToSign) 102 | hash := hmac.New(sha1.New, []byte(accessSecret+"&")) 103 | hash.Write([]byte(stringToSign)) 104 | signature := base64.StdEncoding.EncodeToString(hash.Sum(nil)) 105 | //signature= specialUrlEncode(signature) 106 | //log.Error(signature) 107 | param["Signature"] = signature 108 | 109 | //for k, v := range param { 110 | // param[k] = specialUrlEncode(v) 111 | //} 112 | 113 | //return signature 114 | return signature 115 | } 116 | -------------------------------------------------------------------------------- /component/rsync/hton.go: -------------------------------------------------------------------------------- 1 | package rsync 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "unsafe" 7 | ) 8 | 9 | var ( 10 | localOrder binary.ByteOrder 11 | ) 12 | 13 | // Todo 2015-08-08: 14 | // 使用binary/encoding的BigEndian/LittleEndian来重写 15 | 16 | // host to network byte order transform 17 | 18 | // 判断是BigEndian还是LittleEndian 19 | // http://grokbase.com/t/gg/golang-nuts/129jhmdb3d/go-nuts-how-to-tell-endian-ness-of-machine 20 | // 21 | // x86,MOS Technology 6502,Z80,VAX,PDP-11等处理器为Little endian。 22 | // Motorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)等处理器为Big endian 23 | // ARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64的字节序是可配置的。 24 | // 网络传输一般采用大端序,也被称之为网络字节序,或网络序。IP协议中定义大端序为网络字节序。 25 | func init() { 26 | var x uint32 = 0x01020304 27 | switch *(*byte)(unsafe.Pointer(&x)) { 28 | case 0x01: 29 | localOrder = binary.BigEndian 30 | 31 | case 0x04: 32 | localOrder = binary.LittleEndian 33 | } 34 | } 35 | 36 | func Htons(s uint16) []byte { 37 | var buf [2]byte 38 | 39 | binary.BigEndian.PutUint16(buf[0:2], s) 40 | return buf[0:2] 41 | } 42 | 43 | func Htonl(l uint32) []byte { 44 | var buf [4]byte 45 | 46 | binary.BigEndian.PutUint32(buf[0:4], l) 47 | return buf[0:4] 48 | } 49 | 50 | func Hton8(ll uint64) []byte { 51 | var buf [8]byte 52 | 53 | binary.BigEndian.PutUint64(buf[0:8], ll) 54 | return buf[0:8] 55 | } 56 | 57 | func hton2(s uint16) (res []byte) { 58 | res = make([]byte, 2) 59 | res[1] = byte(s & 0xff) 60 | res[0] = byte(s >> 8) 61 | 62 | return 63 | } 64 | 65 | func hton4(l uint32) (res []byte) { 66 | res = make([]byte, 4) 67 | res[0] = byte(l >> 24) 68 | res[1] = byte(l >> 16) 69 | res[2] = byte(l >> 8) 70 | res[3] = byte(l & 0xff) 71 | 72 | return 73 | } 74 | 75 | // 变长 76 | func vhtonll(d uint64, bytes int8) []byte { 77 | buf := make([]byte, 8) 78 | for i := bytes - 1; i >= 0; i-- { 79 | buf[i] = uint8(d) /* truncated */ 80 | d >>= 8 81 | } 82 | return buf[0:bytes] 83 | } 84 | 85 | // rd中保存的字节序为网络字序 86 | // 根据参数l,从rd中读出l个字节,转换为uint64 87 | // l最大为8 88 | func vRead(rd io.Reader, l uint32) (i uint64, err error) { 89 | var buf [8]byte 90 | 91 | if l != 8 && l != 4 && l != 2 && l != 1 { 92 | panic("invalid param length") 93 | } 94 | _, err = io.ReadFull(rd, buf[0:l]) 95 | if err != nil { 96 | return 97 | } 98 | switch l { 99 | case 1: 100 | i = uint64(buf[0]) 101 | case 2: 102 | i = uint64(binary.BigEndian.Uint16(buf[0:2])) 103 | case 4: 104 | i = uint64(binary.BigEndian.Uint32(buf[0:4])) 105 | case 8: 106 | i = uint64(binary.BigEndian.Uint64(buf[0:8])) 107 | } 108 | 109 | return 110 | } 111 | 112 | // 从rd中读取8个字节 转换为uint64 113 | func ntoh8(rd io.Reader) (i uint64, err error) { 114 | var ( 115 | n int 116 | buf []byte = make([]byte, 8) 117 | ) 118 | 119 | n, err = io.ReadFull(rd, buf) 120 | if n != 8 { 121 | return 122 | } 123 | 124 | i = uint64(uint64(buf[0])<<56 + uint64(buf[1])<<48 + uint64(buf[2])<<40 + uint64(buf[3])<<32 + 125 | uint64(buf[4])<<24 + uint64(buf[5])<<16 + uint64(buf[6])<<8 + uint64(buf[7])) 126 | 127 | return 128 | } 129 | 130 | // 从rd中读取4个字节 转换为uint32 131 | func ntoh4(rd io.Reader) (i uint32, err error) { 132 | var ( 133 | n int 134 | buf []byte = make([]byte, 4) 135 | ) 136 | 137 | n, err = io.ReadFull(rd, buf) 138 | if n != 4 { 139 | return 140 | } 141 | 142 | i = uint32(uint32(buf[0])<<24 + uint32(buf[1])<<16 + uint32(buf[2])<<8 + uint32(buf[3])) 143 | 144 | return 145 | } 146 | 147 | // 从rd中读取2个字节,转换为int16 148 | func ntoh2(rd io.Reader) (i uint16, err error) { 149 | var ( 150 | n int 151 | buf []byte = make([]byte, 2) 152 | ) 153 | 154 | n, err = io.ReadFull(rd, buf) 155 | if n != 2 { 156 | return 157 | } 158 | 159 | i = uint16(uint16(buf[0])<<8 + uint16(buf[1])) 160 | 161 | return 162 | } 163 | 164 | // 读出一个字节 165 | func readByte(rd io.Reader) (i uint8, err error) { 166 | var buf [1]byte 167 | if _, err = rd.Read(buf[0:1]); err != nil { 168 | return 169 | } 170 | i = uint8(buf[0]) 171 | return 172 | } 173 | -------------------------------------------------------------------------------- /component/rsync/dart/rsync.dart: -------------------------------------------------------------------------------- 1 | import 'package:typed_data/typed_data.dart' as typed; 2 | import 'dart:typed_data'; 3 | import 'dart:convert'; 4 | import 'dart:convert' show utf8; 5 | 6 | import 'crclib.dart'; 7 | 8 | const int DeltaMagic = 0x7273; 9 | const int RS_OP_END =0x00; //结束 10 | const int RS_OP_BLOCK_N1 = 0x01; //匹配块index 1字节 0-256 个BLOCK内的数据有效 11 | const int RS_OP_BLOCK_N2 = 0x02; 12 | const int RS_OP_BLOCK_N4 = 0x03; 13 | const int RS_OP_DATA_N1 = 0x04; //未匹配块 数据长度 0-256 14 | const int RS_OP_DATA_N2 = 0x05; 15 | const int RS_OP_DATA_N4 = 0x06; 16 | 17 | void _copy( ByteData source,int srcPos, ByteData dest, int desPos,int length) { 18 | //逻辑代码; 19 | for(var i =0; i onStop()->onDestroy() 168 | func (this *BaseTableImp) OnDestroy() { 169 | panic("implement func OnDestroy()") 170 | } 171 | 172 | //在table超时是调用 173 | func (this *BaseTableImp) OnTimeOut() { 174 | this.Finish() 175 | } 176 | -------------------------------------------------------------------------------- /component/rsync/javascript/crc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [js-crc]{@link https://github.com/emn178/js-crc} 3 | * 4 | * @namespace crc 5 | * @version 0.2.0 6 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 7 | * @copyright Chen, Yi-Cyuan 2015-2017 8 | * @license MIT 9 | */ 10 | /*jslint bitwise: true */ 11 | (function () { 12 | 'use strict'; 13 | 14 | var root = typeof window === 'object' ? window : {}; 15 | var NODE_JS = !root.JS_CRC_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; 16 | if (NODE_JS) { 17 | root = global; 18 | } 19 | var COMMON_JS = !root.JS_CRC_NO_COMMON_JS && typeof module === 'object' && module.exports; 20 | var AMD = typeof define === 'function' && define.amd; 21 | var ARRAY_BUFFER = !root.JS_CRC_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; 22 | var HEX_CHARS = '0123456789abcdef'.split(''); 23 | 24 | var Modules = [ 25 | { 26 | name: 'crc32', 27 | polynom: 0xEDB88320, 28 | initValue: -1, 29 | bytes: 4, 30 | hex:false 31 | }, 32 | { 33 | name: 'crc16', 34 | polynom: 0xA001, 35 | initValue: 0, 36 | bytes: 2, 37 | hex:false 38 | }, 39 | { 40 | name: 'crc32hex', 41 | polynom: 0xEDB88320, 42 | initValue: -1, 43 | bytes: 4, 44 | hex:true 45 | }, 46 | { 47 | name: 'crc16hex', 48 | polynom: 0xA001, 49 | initValue: 0, 50 | bytes: 2, 51 | hex:true 52 | } 53 | ]; 54 | 55 | var i, j, k, b; 56 | for (i = 0; i < Modules.length; ++i) { 57 | var m = Modules[i]; 58 | m.method = (function (m) { 59 | return function (message) { 60 | return crc(message, m); 61 | }; 62 | })(m); 63 | m.table = []; 64 | for (j = 0; j < 256; ++j) { 65 | b = j; 66 | for (k = 0; k < 8; ++k) { 67 | b = b & 1 ? m.polynom ^ (b >>> 1) : b >>> 1; 68 | } 69 | m.table[j] = b >>> 0; 70 | } 71 | } 72 | 73 | var crc = function (message, module) { 74 | var notString = typeof message !== 'string'; 75 | if (notString && ARRAY_BUFFER && message instanceof ArrayBuffer) { 76 | message = new Uint8Array(message); 77 | } 78 | 79 | var crc = module.initValue, code, i, length = message.length, table = module.table; 80 | if (notString) { 81 | for (i = 0; i < length; ++i) { 82 | crc = table[(crc ^ message[i]) & 0xFF] ^ (crc >>> 8); 83 | } 84 | } else { 85 | for (i = 0; i < length; ++i) { 86 | code = message.charCodeAt(i); 87 | if (code < 0x80) { 88 | crc = table[(crc ^ code) & 0xFF] ^ (crc >>> 8); 89 | } else if (code < 0x800) { 90 | crc = table[(crc ^ (0xc0 | (code >> 6))) & 0xFF] ^ (crc >>> 8); 91 | crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); 92 | } else if (code < 0xd800 || code >= 0xe000) { 93 | crc = table[(crc ^ (0xe0 | (code >> 12))) & 0xFF] ^ (crc >>> 8); 94 | crc = table[(crc ^ (0x80 | ((code >> 6) & 0x3f))) & 0xFF] ^ (crc >>> 8); 95 | crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); 96 | } else { 97 | code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++i) & 0x3ff)); 98 | crc = table[(crc ^ (0xf0 | (code >> 18))) & 0xFF] ^ (crc >>> 8); 99 | crc = table[(crc ^ (0x80 | ((code >> 12) & 0x3f))) & 0xFF] ^ (crc >>> 8); 100 | crc = table[(crc ^ (0x80 | ((code >> 6) & 0x3f))) & 0xFF] ^ (crc >>> 8); 101 | crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); 102 | } 103 | } 104 | } 105 | crc ^= module.initValue; 106 | if(!module.hex){ 107 | return crc>>>0; 108 | } 109 | var hex = ''; 110 | if (module.bytes > 2) { 111 | hex += HEX_CHARS[(crc >> 28) & 0x0F] + HEX_CHARS[(crc >> 24) & 0x0F] + 112 | HEX_CHARS[(crc >> 20) & 0x0F] + HEX_CHARS[(crc >> 16) & 0x0F]; 113 | } 114 | hex += HEX_CHARS[(crc >> 12) & 0x0F] + HEX_CHARS[(crc >> 8) & 0x0F] + 115 | HEX_CHARS[(crc >> 4) & 0x0F] + HEX_CHARS[crc & 0x0F]; 116 | return hex; 117 | }; 118 | 119 | var exports = {}; 120 | for (i = 0;i < Modules.length;++i) { 121 | var m = Modules[i]; 122 | exports[m.name] = m.method; 123 | } 124 | if (COMMON_JS) { 125 | module.exports = exports; 126 | } else { 127 | for (i = 0;i < Modules.length;++i) { 128 | var m = Modules[i]; 129 | root[m.name] = m.method; 130 | } 131 | if (AMD) { 132 | define(function() { 133 | return exports; 134 | }); 135 | } 136 | } 137 | })(); -------------------------------------------------------------------------------- /component/rsync/javascript/rsync.js: -------------------------------------------------------------------------------- 1 | var DeltaMagic = 0x7273; 2 | var RS_OP_END =0x00; //结束 3 | var RS_OP_BLOCK_N1 = 0x01; //匹配块index 1字节 0-256 个BLOCK内的数据有效 4 | var RS_OP_BLOCK_N2 = 0x02; 5 | var RS_OP_BLOCK_N4 = 0x03; 6 | var RS_OP_DATA_N1 = 0x04; //未匹配块 数据长度 0-256 7 | var RS_OP_DATA_N2 = 0x05; 8 | var RS_OP_DATA_N4 = 0x06; 9 | /** 10 | * This is a function born of annoyance. You can't create a Uint32Array at a non 4 byte boundary. So this is necessary to read 11 | * a 32bit int from an arbitrary location. Lame. 12 | * 13 | * BTW: This assumes everything to be big endian. 14 | */ 15 | function readUint32(uint8View, offset) 16 | { 17 | return (uint8View[offset]<<24 | uint8View[++offset] << 16 | uint8View[++offset] << 8 | uint8View[++offset]) >>> 0; 18 | } 19 | function readUint16(uint8View, offset) 20 | { 21 | return (uint8View[offset] << 8 | uint8View[++offset]) >>> 0; 22 | } 23 | function readUint8(uint8View, offset) 24 | { 25 | //console.log("readUint8",uint8View[offset]); 26 | return (uint8View[offset]) >>> 0; 27 | } 28 | 29 | function copy( source,srcPos, dest, desPos,length) { 30 | //逻辑代码; 31 | for(var i =0; i 0 { 204 | return "", "操作过于频繁,请您稍后再试。" 205 | } 206 | 207 | smsCode := RandInt64(100000, 999999) 208 | if self.Ailyun != nil { 209 | errstr := self.aliyun(phone, smsCode) 210 | if errstr != "" { 211 | if self.SendCloud != nil { 212 | errstr := self.sendcloud(phone, smsCode) 213 | if errstr != "" { 214 | return "", errstr 215 | } 216 | } else { 217 | return "", errstr 218 | } 219 | } 220 | } else if self.SendCloud != nil { 221 | errstr := self.sendcloud(phone, smsCode) 222 | if errstr != "" { 223 | return "", errstr 224 | } 225 | } else { 226 | return "", "没有可用的短信通道。" 227 | } 228 | _, err = conn.Do("SET", fmt.Sprintf(MobileTTLFormat, phone), smsCode) 229 | if err != nil { 230 | return "", err.Error() 231 | } 232 | _, err = conn.Do("EXPIRE", fmt.Sprintf(MobileTTLFormat, phone), self.TTL) 233 | if err != nil { 234 | return "", err.Error() 235 | } 236 | 237 | savedatas := map[string]interface{}{ 238 | "purpose": purpose, 239 | "extra": extra, 240 | } 241 | savedatasBytes, err := json.Marshal(savedatas) 242 | if err != nil { 243 | return "", err.Error() 244 | } 245 | _, err = conn.Do("SET", fmt.Sprintf(MobileSmsCodeFormat, phone, smsCode), savedatasBytes) 246 | if err != nil { 247 | return "", err.Error() 248 | } 249 | _, err = conn.Do("EXPIRE", fmt.Sprintf(MobileSmsCodeFormat, phone, smsCode), self.TTL*5) 250 | if err != nil { 251 | return "", err.Error() 252 | } 253 | return "验证码发送成功", "" 254 | } 255 | 256 | /** 257 | 获取验证码参数 258 | 如果验证码已过期将返回失败 259 | */ 260 | func (self *SMS) getCodeData(session gate.Session, phone string, smsCode int64, del bool) (map[string]interface{}, string) { 261 | conn := tools.GetRedisFactory().GetPool(self.RedisUrl).Get() 262 | defer conn.Close() 263 | r, err := redis.Bytes(conn.Do("GET", fmt.Sprintf(MobileSmsCodeFormat, phone, smsCode))) 264 | if err != nil { 265 | return nil, err.Error() 266 | } 267 | if del { 268 | conn.Do("DEL", fmt.Sprintf(MobileSmsCodeFormat, phone, smsCode)) 269 | } 270 | savedatas := map[string]interface{}{} 271 | err = json.Unmarshal(r, &savedatas) 272 | if err != nil { 273 | return nil, err.Error() 274 | } 275 | return savedatas, "" 276 | } 277 | -------------------------------------------------------------------------------- /component/rsync/dart/crclib.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain 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, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Generic CRC calculations as Dart converters and some common algorithms. 16 | /// 17 | /// The easiest way to use this library is to call `convert` on the instance of 18 | /// the desired CRC routine. 19 | /// 20 | /// new Crc32Zlib().convert(UTF8.encode('123456789')) == 0xCBF43926 21 | /// 22 | /// Another supported use case is as stream transformers. 23 | /// 24 | /// new File(...).openRead().transform(new Crc32Zlib()) 25 | /// 26 | /// Instead of using predefined classes, it is also possible to construct a 27 | /// customized CRC function with [ParametricCrc] class. For a list of known 28 | /// CRC routines, check out http://reveng.sourceforge.net/crc-catalogue/all.htm. 29 | /// 30 | /// TODO: 31 | /// 1. inputReflected and outputReflected can be different, see CRC-12/UMTS. 32 | /// 2. Bit-level checksums (including non-multiple-of-8 checksums). 33 | 34 | import 'dart:convert' 35 | show ByteConversionSink, ByteConversionSinkBase, Converter; 36 | 37 | import 'package:meta/meta.dart' show visibleForTesting; 38 | import 'package:tuple/tuple.dart' show Tuple2; 39 | 40 | /// Ultimate sink that stores the final CRC value. 41 | class _FinalSink extends Sink { 42 | int _value; 43 | 44 | int get value { 45 | assert(_value != null); 46 | return _value; 47 | } 48 | 49 | @override 50 | void add(int data) { 51 | // Can only be called once. 52 | assert(_value == null); 53 | _value = data; 54 | } 55 | 56 | @override 57 | void close() { 58 | assert(_value != null); 59 | } 60 | } 61 | 62 | /// Intermediate sink that performs the actual CRC calculation. It outputs to 63 | /// [_FinalSink]. 64 | abstract class _CrcSink extends ByteConversionSinkBase { 65 | final List _table; 66 | final int _finalMask; 67 | final Sink _outputSink; 68 | CrcLoopFunction _loopFunction; 69 | int _value; 70 | bool _closed; 71 | 72 | _CrcSink( 73 | this._table, this._value, this._finalMask, this._outputSink, int width) 74 | : _closed = false { 75 | _loopFunction = _selectLoopFunction(width); 76 | } 77 | 78 | @override 79 | void add(List chunk) { 80 | addSlice(chunk, 0, chunk.length, false /* isLast */); 81 | } 82 | 83 | @override 84 | void addSlice(List chunk, int start, int end, bool isLast) { 85 | _loopFunction(chunk, start, end); 86 | if (isLast) { 87 | close(); 88 | } 89 | } 90 | 91 | @override 92 | void close() { 93 | if (!_closed) { 94 | _closed = true; 95 | _outputSink.add(_value ^ _finalMask); 96 | _outputSink.close(); 97 | } 98 | } 99 | 100 | CrcLoopFunction _selectLoopFunction(int width); 101 | } 102 | 103 | typedef void CrcLoopFunction(List chunk, int start, int end); 104 | 105 | /// "Normal" CRC routines where the high bits are shifted out to the left. 106 | /// 107 | /// The various [CrcLoopFunction] definitions are to optimize for different 108 | /// integer sizes in Dart VM. See "Optimally shifting to the left" in 109 | /// https://www.dartlang.org/articles/dart-vm/numeric-computation. 110 | /// 111 | // Note for maintainers: Try not to call any function in these loops. Function 112 | // calls require boxing and unboxing. 113 | class _NormalSink extends _CrcSink { 114 | _NormalSink(_table, _value, _finalMask, _outputSink, int width) 115 | : super(_table, _value, _finalMask, _outputSink, width); 116 | 117 | void _crc8Loop(List chunk, int start, int end) { 118 | for (int b in chunk.getRange(start, end)) { 119 | _value = _table[_value ^ b]; 120 | } 121 | } 122 | 123 | void _crc16Loop(List chunk, int start, int end) { 124 | for (int b in chunk.getRange(start, end)) { 125 | _value = _table[(_value >> 8) ^ b] ^ ((_value << 8) & 0xFFFF); 126 | } 127 | } 128 | 129 | void _crc24Loop(List chunk, int start, int end) { 130 | for (int b in chunk.getRange(start, end)) { 131 | _value = _table[(_value >> 16) ^ b] ^ ((_value << 8) & 0xFFFFFF); 132 | } 133 | } 134 | 135 | void _crc32Loop(List chunk, int start, int end) { 136 | for (int b in chunk.getRange(start, end)) { 137 | _value = _table[(_value >> 24) ^ b] ^ ((_value << 8) & 0xFFFFFFFF); 138 | } 139 | } 140 | 141 | 142 | @override 143 | CrcLoopFunction _selectLoopFunction(int width) { 144 | void _crcLoop(List chunk, int start, int end) { 145 | int shiftWidth = width - 8; 146 | int mask = (1 << width) - 1; 147 | for (int b in chunk.getRange(start, end)) { 148 | _value = _table[((_value >> shiftWidth) ^ b) & 0xFF] ^ 149 | ((_value << 8) & mask); 150 | } 151 | } 152 | 153 | switch (width) { 154 | case 32: 155 | return _crc32Loop; 156 | case 24: 157 | return _crc24Loop; 158 | case 16: 159 | return _crc16Loop; 160 | case 8: 161 | return _crc8Loop; 162 | default: 163 | return _crcLoop; 164 | } 165 | } 166 | } 167 | 168 | /// "Reflected" CRC routines. 169 | /// 170 | /// The specialized loop functions are meant to speed up calculations 171 | /// according to the width of the CRC value. 172 | class _ReflectedSink extends _CrcSink { 173 | _ReflectedSink(_table, _value, _finalMask, _outputSink, int width) 174 | : super(_table, _value, _finalMask, _outputSink, width); 175 | 176 | void _crc8Loop(List chunk, int start, int end) { 177 | for (int b in chunk.getRange(start, end)) { 178 | _value = (_table[_value ^ b]); 179 | } 180 | } 181 | 182 | void _crcLoop(List chunk, int start, int end) { 183 | for (int b in chunk.getRange(start, end)) { 184 | _value = 185 | (_table[(_value ^ b) & 0xFF] ^ ((_value >> 8) & 0xFFFFFFFF)); 186 | } 187 | } 188 | 189 | CrcLoopFunction _selectLoopFunction(int width) { 190 | return width <= 8 ? _crc8Loop : _crcLoop; 191 | } 192 | } 193 | 194 | /// Reflects the least [width] bits of input value [i]. 195 | /// 196 | /// For example: the value of `_reflect(0x80, 8)` is 0x01 because 0x80 is 197 | /// 10000000 in binary; its reflected binary value is 00000001, which is 0x01 in 198 | /// hexadecimal. And `_reflect(0x3e23, 3)` is 6 because the least significant 3 199 | /// bits are 011, when reflected is 110, which is 6 in decimal. 200 | @visibleForTesting 201 | int reflect(int i, int width) { 202 | int ret = 0; 203 | while (width-- > 0) { 204 | ret = (ret << 1) | (i & 1); 205 | i >>= 1; 206 | } 207 | return ret; 208 | } 209 | 210 | /// The base class of all CRC routines. The parameters are: 211 | /// 212 | /// * width: The bit count of the CRC value, eg 32, 16. 213 | /// * polynomial: The generator polynomial in integer form, eg if the 214 | /// polynomial is x^4 + x + 1, its integer form is 0x13 (0b10011). The 215 | /// highest bit of this value can be left out too, eg 0x03. 216 | /// * initialValue: The initial CRC value to start the calculation with. 217 | /// * finalMask: The bit mask to XOR the (possibly reflected) final CRC value. 218 | /// * inputReflected: Whether the input to CRC calculation should be 219 | /// reflected. 220 | /// * outputReflected: Whether the CRC value is reflected before being XOR'd 221 | /// with finalMask. 222 | class ParametricCrc extends Converter, int> { 223 | static Map, List> _generatedTables = 224 | new Map, List>(); 225 | 226 | List _table; 227 | final int _width; 228 | final int _polynomial; 229 | final int _initialValue; 230 | final int _finalMask; 231 | final bool _inputReflected; 232 | final bool _outputReflected; 233 | 234 | ParametricCrc( 235 | this._width, this._polynomial, this._initialValue, this._finalMask, 236 | {inputReflected = true, outputReflected = true}) 237 | : _inputReflected = inputReflected, 238 | _outputReflected = outputReflected { 239 | // TODO 240 | assert(_inputReflected == _outputReflected, 241 | "Different input and output reflection flag is not supported yet."); 242 | assert((_width % 8) == 0, "Bit level checksums not supported yet."); 243 | 244 | final key = new Tuple2(_polynomial, _inputReflected); 245 | _table = _generatedTables[key]; 246 | if (_table == null) { 247 | _table = _createByteLookupTable(_width, _polynomial, _inputReflected); 248 | _generatedTables[key] = _table; 249 | } 250 | } 251 | 252 | @override 253 | int convert(List input) { 254 | final outputSink = new _FinalSink(); 255 | final inputSink = startChunkedConversion(outputSink); 256 | inputSink.add(input); 257 | inputSink.close(); 258 | return outputSink.value; 259 | } 260 | 261 | @override 262 | ByteConversionSink startChunkedConversion(Sink outputSink) { 263 | ByteConversionSink ret; 264 | if (_inputReflected) { 265 | ret = new _ReflectedSink( 266 | _table, _initialValue, _finalMask, outputSink, _width); 267 | } else { 268 | ret = new _NormalSink( 269 | _table, _initialValue, _finalMask, outputSink, _width); 270 | } 271 | return ret; 272 | } 273 | } 274 | 275 | /// Creates new lookup table for ONE byte. 276 | /// 277 | /// The [poly] value will be truncated to [width] bits. If [reflected] is 278 | /// `true`, the entries in the table will be reflected. 279 | List _createByteLookupTable(int width, int poly, bool reflected) { 280 | List ret = new List.filled(256, 0); 281 | int widthMask = (1 << width) - 1; 282 | int truncatedPoly = poly & widthMask; 283 | int topMask = 1 << (width - 1); 284 | for (int i = 0; i < 256; ++i) { 285 | int crc; 286 | if (reflected) { 287 | crc = reflect(i, 8) << (width - 8); 288 | } else { 289 | crc = i << (width - 8); 290 | } 291 | for (int j = 0; j < 8; ++j) { 292 | if ((crc & topMask) != 0) { 293 | crc = ((crc << 1) ^ truncatedPoly); 294 | } else { 295 | crc <<= 1; 296 | } 297 | } 298 | if (reflected) { 299 | ret[i] = reflect(crc, width); 300 | } else { 301 | ret[i] = crc & widthMask; 302 | } 303 | } 304 | return ret; 305 | } 306 | 307 | // Below are well-known CRC models. 308 | // Naming convention: Crc where is the bit count, and 309 | // is a well-known use of the routine. Please specify both fields even if the 310 | // algorithm is only used by one thing. 311 | 312 | 313 | /// CRC-32 variant used in Zlib. 314 | class Crc32Zlib extends ParametricCrc { 315 | Crc32Zlib() : super(32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF); 316 | } 317 | 318 | /// CRC-32 variant used in Bzip2. 319 | class Crc32Bzip2 extends ParametricCrc { 320 | Crc32Bzip2() 321 | : super(32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 322 | inputReflected: false, outputReflected: false); 323 | } 324 | 325 | /// CRC-32 variant used in iSCSI, SSE4.2, ext4. 326 | class Crc32Iscsi extends ParametricCrc { 327 | Crc32Iscsi() : super(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF); 328 | } 329 | 330 | /// CRC-24 variant used in OpenPGP, RTCM. 331 | class Crc24OpenPgp extends ParametricCrc { 332 | Crc24OpenPgp() 333 | : super(24, 0x864CFB, 0xB704CE, 0, 334 | inputReflected: false, outputReflected: false); 335 | } 336 | 337 | /// CRC-16 variant used in X25. 338 | class Crc16X25 extends ParametricCrc { 339 | Crc16X25() : super(16, 0x1021, 0xFFFF, 0xFFFF); 340 | } 341 | 342 | /// CRC-16 variant used in USB. 343 | class Crc16Usb extends ParametricCrc { 344 | Crc16Usb() : super(16, 0x8005, 0xFFFF, 0xFFFF); 345 | } 346 | 347 | /// CRC-8 variant used in WCDMA (UMTS). 348 | class Crc8Wcdma extends ParametricCrc { 349 | Crc8Wcdma() : super(8, 0x9B, 0, 0); 350 | } 351 | 352 | /// CRC-8 variant used in ATM Header Error Control sequence. 353 | class Crc8Atm extends ParametricCrc { 354 | Crc8Atm() 355 | : super(8, 0x07, 0, 0x55, inputReflected: false, outputReflected: false); 356 | } 357 | 358 | /// CRC-8 variant used in Robust Header Compression (RFC3095). 359 | class Crc8Rohc extends ParametricCrc { 360 | Crc8Rohc() : super(8, 0x07, 0xFF, 0); 361 | } -------------------------------------------------------------------------------- /component/rsync/rsync.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Julian Gutierrez Oschmann (github.com/julian-gutierrez-o). 2 | // All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | // A Golang implementation of the rsync algorithm. 7 | // This package contains the algorithm for both client and server side. 8 | package rsync 9 | 10 | import ( 11 | "bufio" 12 | "bytes" 13 | "crypto/md5" 14 | "encoding/binary" 15 | "fmt" 16 | "github.com/liangdas/mqant/log" 17 | "github.com/pkg/errors" 18 | "io" 19 | "math" 20 | ) 21 | 22 | const ( 23 | M = 1 << 16 24 | DeltaMagic uint16 = 0x7273 25 | ) 26 | 27 | type LRsync struct { 28 | BlockSize int 29 | } 30 | 31 | type BlockHash struct { 32 | index []int 33 | strongHash []byte 34 | weakHash uint32 35 | } 36 | 37 | func (this *BlockHash) AddIndex(index int) { 38 | if this.index == nil { 39 | this.index = make([]int, 1) 40 | this.index[0] = index 41 | } else { 42 | for _, i := range this.index { 43 | if i == index { 44 | 45 | return 46 | } 47 | } 48 | this.index = append(this.index, index) 49 | } 50 | } 51 | 52 | /** 53 | hu获取index,尽量选取大于且接近pro的 54 | */ 55 | func (this *BlockHash) GetIndex(pro int) int { 56 | var max int = 0 57 | if len(this.index) > 0 { 58 | max = this.index[0] 59 | } 60 | for _, i := range this.index { 61 | if math.Abs(float64(i-pro)) <= math.Abs(float64(max-pro)) { 62 | max = i 63 | } 64 | } 65 | //log.Info("index=%v pro=%v max=%v",this.index,pro,max) 66 | return max 67 | } 68 | 69 | // There are two kind of operations: BLOCK and DATA. 70 | // If a block match is found on the server, a BLOCK operation is sent over the channel along with the block index. 71 | // Modified data between two block matches is sent like a DATA operation. 72 | const ( 73 | BLOCK = iota 74 | DATA 75 | ) 76 | 77 | // An rsync operation (typically to be sent across the network). It can be either a block of raw data or a block index. 78 | type RSyncOp struct { 79 | // Kind of operation: BLOCK | DATA. 80 | opCode int 81 | // The raw modificated (or misaligned) data. Iff opCode == DATA, nil otherwise. 82 | data []byte 83 | // The index of found block. Iff opCode == BLOCK. nil otherwise. 84 | offset int 85 | blockIndex int 86 | } 87 | 88 | // Returns weak and strong hashes for a given slice. 89 | func (this *LRsync) CalculateBlockHashes(content []byte) map[string]*BlockHash { 90 | //blockHashes := make([]BlockHash, getBlocksNumber(content)) 91 | //for i := range blockHashes { 92 | // initialByte := i * BlockSize 93 | // endingByte := min((i+1)*BlockSize, len(content)) 94 | // block := content[initialByte:endingByte] 95 | // weak, _, _ := weakHash(block) 96 | // blockHashes[i] = BlockHash{strongHash: strongHash(block), weakHash: weak} 97 | // blockHashes[i].AddIndex(int32(i)) 98 | //} 99 | //return blockHashes 100 | blockHashes := make(map[string]*BlockHash) 101 | num := this.getBlocksNumber(content) 102 | for i := 0; i < num; i++ { 103 | initialByte := i * this.BlockSize 104 | endingByte := min((i+1)*this.BlockSize, len(content)) 105 | block := content[initialByte:endingByte] 106 | weak, _, _ := weakHash(block) 107 | if b, ok := blockHashes[string(strongHash(block))]; ok { 108 | b.AddIndex(i) 109 | } else { 110 | bb := &BlockHash{strongHash: strongHash(block), weakHash: weak} 111 | bb.AddIndex(i) 112 | blockHashes[string(strongHash(block))] = bb 113 | } 114 | } 115 | return blockHashes 116 | } 117 | 118 | // Returns the number of blocks for a given slice of content. 119 | func (this *LRsync) getBlocksNumber(content []byte) int { 120 | blockNumber := (len(content) / this.BlockSize) 121 | if len(content)%this.BlockSize != 0 { 122 | blockNumber += 1 123 | } 124 | return blockNumber 125 | } 126 | 127 | // Applies operations from the channel to the original content. 128 | // Returns the modified content. 129 | func (this *LRsync) ApplyOps(content []byte, ops []RSyncOp, fileSize int) []byte { 130 | var offset int 131 | result := make([]byte, fileSize) 132 | for _, op := range ops { 133 | switch op.opCode { 134 | case BLOCK: 135 | copy(result[offset:offset+this.BlockSize], content[op.blockIndex*this.BlockSize:op.blockIndex*this.BlockSize+this.BlockSize]) 136 | offset += this.BlockSize 137 | case DATA: 138 | copy(result[offset:], op.data) 139 | offset += len(op.data) 140 | } 141 | } 142 | return result 143 | } 144 | 145 | // Computes all the operations needed to recreate content. 146 | // All these operations are sent through a channel of RSyncOp. 147 | func (this *LRsync) CalculateDifferences(content []byte, hashes map[string]*BlockHash) (opsChannel []RSyncOp) { 148 | 149 | hashesMap := make(map[uint32][]BlockHash) 150 | opsChannel = make([]RSyncOp, 0) 151 | 152 | for _, h := range hashes { 153 | key := h.weakHash 154 | hashesMap[key] = append(hashesMap[key], *h) 155 | } 156 | 157 | var offset, previousMatch int 158 | var aweak, bweak, weak uint32 159 | var pro int 160 | var dirty, isRolling bool 161 | 162 | for offset < len(content) { 163 | //log.Info("hashesMap offset=%v content=%v",offset,len(content)) 164 | endingByte := min(offset+this.BlockSize, len(content)-1) 165 | block := content[offset:endingByte] 166 | if !isRolling { 167 | weak, aweak, bweak = weakHash(block) 168 | isRolling = true 169 | } else { 170 | aweak = (aweak - uint32(content[offset-1]) + uint32(content[endingByte-1])) % M 171 | bweak = (bweak - (uint32(endingByte-offset) * uint32(content[offset-1])) + aweak) % M 172 | weak = aweak + (1 << 16 * bweak) 173 | } 174 | if l := hashesMap[weak]; l != nil { 175 | blockFound, blockHash := searchStrongHash(hashes, strongHash(block)) 176 | if blockFound { 177 | if dirty { 178 | pro++ 179 | opsChannel = append(opsChannel, RSyncOp{opCode: DATA, offset: pro, data: content[previousMatch:offset]}) 180 | dirty = false 181 | } 182 | pro++ 183 | opsChannel = append(opsChannel, RSyncOp{opCode: BLOCK, offset: pro, blockIndex: blockHash.GetIndex(pro)}) 184 | previousMatch = endingByte 185 | isRolling = false 186 | offset += this.BlockSize 187 | continue 188 | } 189 | } 190 | dirty = true 191 | offset++ 192 | } 193 | 194 | if dirty { 195 | pro++ 196 | opsChannel = append(opsChannel, RSyncOp{opCode: DATA, offset: pro, data: content[previousMatch:]}) 197 | } 198 | return opsChannel 199 | } 200 | 201 | func Int8ToBytes(x int8) []byte { 202 | bytesBuffer := bytes.NewBuffer([]byte{}) 203 | binary.Write(bytesBuffer, binary.BigEndian, x) 204 | return bytesBuffer.Bytes() 205 | } 206 | 207 | const ( 208 | RS_OP_END uint8 = 0x00 //结束 209 | RS_OP_BLOCK_N1 uint8 = 0x01 //匹配块index 1字节 0-256 个BLOCK内的数据有效 210 | RS_OP_BLOCK_N2 uint8 = 0x02 211 | RS_OP_BLOCK_N4 uint8 = 0x03 212 | RS_OP_DATA_N1 uint8 = 0x04 //未匹配块 数据长度 0-256 213 | RS_OP_DATA_N2 uint8 = 0x05 214 | RS_OP_DATA_N4 uint8 = 0x06 215 | ) 216 | 217 | /** 218 | 填充规则 219 | mode 1 字节 220 | 221 | BLOCK 222 | START_INDEX 223 | END_INDEX 224 | 225 | DATA 226 | LENGHT 227 | DATA[] 228 | 229 | */ 230 | func (this *LRsync) CreateDelta(opsChannel []RSyncOp, modifiedSize int, crc32 uint32) []byte { 231 | b := bytes.NewBuffer(make([]byte, 0)) 232 | writer := bufio.NewWriter(b) 233 | writer.Write(hton2(DeltaMagic)) 234 | writer.Write(hton4(crc32)) 235 | writer.Write(Htonl(uint32(this.BlockSize))) 236 | writer.Write(Htonl(uint32(modifiedSize))) 237 | totls := 0 238 | var opCode = -1 239 | var startblock int = 0 240 | var problock int = 0 241 | for _, op := range opsChannel { 242 | 243 | switch op.opCode { 244 | case BLOCK: 245 | totls++ 246 | if opCode == BLOCK { 247 | if (problock + 1) == op.blockIndex { 248 | //上一个op也是匹配块,则继续累加 249 | problock = op.blockIndex 250 | //log.TInfo(nil,"累加 %v offset %v" ,op.blockIndex,op.offset) 251 | } else { 252 | //应该是重头开始 253 | if len(opsChannel) < 256 { 254 | writer.Write([]byte{RS_OP_BLOCK_N1}) //写块的index 255 | writer.Write(Int8ToBytes(int8(startblock))) 256 | writer.Write(Int8ToBytes(int8(problock))) 257 | } else if 256 <= len(opsChannel) && len(opsChannel) < 65535 { 258 | writer.Write([]byte{RS_OP_BLOCK_N2}) //写块的index 259 | writer.Write(hton2(uint16(startblock))) 260 | writer.Write(hton2(uint16(problock))) 261 | } else { 262 | writer.Write([]byte{RS_OP_BLOCK_N4}) //写块的index 263 | writer.Write(Htonl(uint32(startblock))) 264 | writer.Write(Htonl(uint32(problock))) 265 | } 266 | //log.TInfo(nil,"应该填充数据了 opCode=%v startblock=%v problock=%v offset %v",opCode,startblock,problock,op.offset) 267 | startblock = op.blockIndex 268 | problock = op.blockIndex 269 | //log.TInfo(nil,"重头开始 startblock %v problock %v offset %v" ,startblock,problock,op.offset) 270 | } 271 | } else if opCode == DATA { 272 | //上一个是未匹配块 273 | startblock = op.blockIndex 274 | problock = op.blockIndex 275 | //log.TInfo(nil,"重头开始 startblock %v blockIndex %v offset %v" ,startblock,problock,op.offset) 276 | } else { 277 | //上一个op未知类型 278 | startblock = op.blockIndex 279 | problock = op.blockIndex 280 | 281 | //log.TInfo(nil,"重头开始 startblock %v blockIndex %v offset %v" ,startblock,problock,op.offset) 282 | } 283 | opCode = BLOCK 284 | //log.TInfo(nil,"blockIndex %v",op.blockIndex) 285 | //writer.Write(Int16ToBytes(int16(op.blockIndex))) //写块的index 286 | case DATA: 287 | totls++ 288 | if opCode == BLOCK { 289 | //log.TInfo(nil,"填充数据 opCode=%v startblock=%v blockIndex %v offset=%v",opCode,startblock,problock,op.offset) 290 | if len(opsChannel) < 256 { 291 | writer.Write([]byte{RS_OP_BLOCK_N1}) //写块的index 292 | writer.Write(Int8ToBytes(int8(startblock))) 293 | writer.Write(Int8ToBytes(int8(problock))) 294 | } else if 256 <= len(opsChannel) && len(opsChannel) < 65535 { 295 | writer.Write([]byte{RS_OP_BLOCK_N2}) //写块的index 296 | writer.Write(hton2(uint16(startblock))) 297 | writer.Write(hton2(uint16(problock))) 298 | } else { 299 | writer.Write([]byte{RS_OP_BLOCK_N4}) //写块的index 300 | writer.Write(Htonl(uint32(startblock))) 301 | writer.Write(Htonl(uint32(problock))) 302 | } 303 | } 304 | opCode = DATA 305 | if len(op.data) < 256 { 306 | writer.Write([]byte{RS_OP_DATA_N1}) 307 | writer.Write(Int8ToBytes(int8(len(op.data)))) //写块的数据长度 308 | } else if 256 <= len(opsChannel) && len(opsChannel) < 65025 { 309 | writer.Write([]byte{RS_OP_DATA_N2}) 310 | writer.Write(hton2(uint16(len(op.data)))) //写块的数据长度 311 | } else { 312 | writer.Write([]byte{RS_OP_DATA_N4}) 313 | writer.Write(Htonl(uint32(len(op.data)))) //写块的数据长度 314 | } 315 | writer.Write(op.data) //写块的数据长度 316 | //log.TInfo(nil,"填充未匹配数据 opCode=%v blockIndex %v len %v offset=%v",opCode,op.blockIndex,int16(len(op.data)),op.offset) 317 | default: 318 | log.Info("未知类型 %v", op) 319 | } 320 | } 321 | writer.Write([]byte{RS_OP_END}) 322 | //for _,op:=range opsChannel{ 323 | // switch op.opCode { 324 | // case DATA: 325 | // writer.Write(Int16ToBytes(int16(op.blockIndex))) //写块的index 326 | // writer.Write(Int16ToBytes(int16(len(op.data)))) //写块的数据长度 327 | // writer.Write(op.data) //写块的数据长度 328 | // } 329 | //} 330 | writer.Flush() 331 | return b.Bytes() 332 | } 333 | 334 | func (this *LRsync) Patch(content []byte, delta []byte) (result []byte, err error) { 335 | var ( 336 | offset int 337 | cmd uint8 338 | ) 339 | rd := bytes.NewReader(delta) 340 | //deltamagic 341 | if deltamagic, errr := ntoh2(rd); errr == io.EOF { 342 | return 343 | } else { 344 | if deltamagic != DeltaMagic { 345 | err = errors.New("DeltaMagic error") 346 | return 347 | } 348 | } 349 | //crc32 350 | if _, err = ntoh4(rd); err == io.EOF { 351 | return 352 | } 353 | var BlockSize int = 0 354 | if blocksize, errr := ntoh4(rd); errr == io.EOF { 355 | err = errr 356 | return 357 | } else { 358 | BlockSize = int(blocksize) 359 | } 360 | var modifiedSize uint32 361 | if modifiedSize, err = ntoh4(rd); err == io.EOF { 362 | return 363 | } 364 | result = make([]byte, modifiedSize) 365 | for { 366 | if cmd, err = readByte(rd); err == io.EOF { 367 | err = nil 368 | break 369 | } 370 | if cmd == 0 { // delta的结束命令 371 | break 372 | } 373 | switch uint8(cmd) { 374 | case RS_OP_BLOCK_N1: 375 | start_index, err := readByte(rd) 376 | if err != nil { 377 | return nil, err 378 | } 379 | end_index, err := readByte(rd) 380 | if err != nil { 381 | return nil, err 382 | } 383 | for i := start_index; i <= end_index; i++ { 384 | var cp = int(i) * int(BlockSize) 385 | var cc = int(i)*int(BlockSize) + int(BlockSize) 386 | //log.Info("start_index %v end_index %v i %v cp %v cc %v",start_index,end_index,i,cp,cc) 387 | copy(result[offset:offset+BlockSize], content[cp:cc]) 388 | offset += BlockSize 389 | } 390 | case RS_OP_BLOCK_N2: 391 | var start_index uint16 = 0 392 | var end_index uint16 = 0 393 | if start_index, err = ntoh2(rd); err != nil { 394 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 395 | return nil, err 396 | } 397 | if end_index, err = ntoh2(rd); err != nil { 398 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 399 | return nil, err 400 | } 401 | for i := start_index; i <= end_index; i++ { 402 | var cp = int(i) * int(BlockSize) 403 | var cc = int(i)*int(BlockSize) + int(BlockSize) 404 | copy(result[offset:offset+BlockSize], content[cp:cc]) 405 | offset += BlockSize 406 | } 407 | case RS_OP_BLOCK_N4: 408 | var start_index uint32 = 0 409 | var end_index uint32 = 0 410 | if start_index, err = ntoh4(rd); err != nil { 411 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 412 | return nil, err 413 | } 414 | if end_index, err = ntoh4(rd); err != nil { 415 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 416 | return nil, err 417 | } 418 | for i := start_index; i <= end_index; i++ { 419 | var cp = int(i) * int(BlockSize) 420 | var cc = int(i)*int(BlockSize) + int(BlockSize) 421 | copy(result[offset:offset+BlockSize], content[cp:cc]) 422 | offset += BlockSize 423 | } 424 | case RS_OP_DATA_N1: 425 | var lenght uint8 = 0 426 | if lenght, err = readByte(rd); err != nil { 427 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 428 | return nil, err 429 | } 430 | var ( 431 | n int 432 | buf []byte = make([]byte, lenght) 433 | ) 434 | n, err = io.ReadFull(rd, buf) 435 | if uint8(n) != lenght { 436 | err = fmt.Errorf("uint8(n) %v != lenght %v", uint8(n), lenght) 437 | return nil, err 438 | } 439 | copy(result[offset:], buf) 440 | offset += len(buf) 441 | case RS_OP_DATA_N2: 442 | var lenght uint16 = 0 443 | if lenght, err = ntoh2(rd); err != nil { 444 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 445 | return nil, err 446 | } 447 | var ( 448 | n int 449 | buf []byte = make([]byte, lenght) 450 | ) 451 | n, err = io.ReadFull(rd, buf) 452 | if uint16(n) != lenght { 453 | err = fmt.Errorf("int16(n) %v != lenght %v", uint8(n), lenght) 454 | return nil, err 455 | } 456 | copy(result[offset:], buf) 457 | offset += len(buf) 458 | case RS_OP_DATA_N4: 459 | var lenght uint32 = 0 460 | if lenght, err = ntoh4(rd); err != nil { 461 | err = fmt.Errorf("read signature maigin failed: %s", err.Error()) 462 | return nil, err 463 | } 464 | var ( 465 | n int 466 | buf []byte = make([]byte, lenght) 467 | ) 468 | n, err = io.ReadFull(rd, buf) 469 | if uint32(n) != lenght { 470 | err = fmt.Errorf("uint32(n) %v != lenght %v", uint8(n), lenght) 471 | return nil, err 472 | } 473 | copy(result[offset:], buf) 474 | offset += len(buf) 475 | } 476 | } 477 | 478 | return result, nil 479 | } 480 | 481 | // Searches for a given strong hash among all strong hashes in this bucket. 482 | func searchStrongHash(l map[string]*BlockHash, hashValue []byte) (bool, *BlockHash) { 483 | //for _, blockHash := range l { 484 | // if string(blockHash.strongHash) == string(hashValue) { 485 | // return true, &blockHash 486 | // } 487 | //} 488 | if blockHash, ok := l[string(hashValue)]; ok { 489 | return true, blockHash 490 | } 491 | return false, nil 492 | } 493 | 494 | // Returns a strong hash for a given block of data 495 | func strongHash(v []byte) []byte { 496 | h := md5.New() 497 | h.Write(v) 498 | return h.Sum(nil) 499 | } 500 | 501 | // Returns a weak hash for a given block of data. 502 | func weakHash(v []byte) (uint32, uint32, uint32) { 503 | var a, b uint32 504 | for i := range v { 505 | a += uint32(v[i]) 506 | b += (uint32(len(v)-1) - uint32(i) + 1) * uint32(v[i]) 507 | } 508 | return (a % M) + (1 << 16 * (b % M)), a % M, b % M 509 | } 510 | 511 | // Returns the smaller of a or b. 512 | func min(a, b int) int { 513 | if a < b { 514 | return a 515 | } 516 | return b 517 | } 518 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 4 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 5 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 6 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= 7 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 8 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 9 | github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY= 10 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 11 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 12 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 13 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 14 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 15 | github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 16 | github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 17 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 18 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 19 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 20 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 22 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 24 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 25 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 26 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 27 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 28 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 29 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 30 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 31 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 32 | github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= 33 | github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 34 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 35 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 36 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 37 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 39 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 40 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 41 | github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 42 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 43 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= 44 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 45 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 46 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 47 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 48 | github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= 49 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 50 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 51 | github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= 52 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 53 | github.com/grpc-ecosystem/grpc-gateway v1.11.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 54 | github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= 55 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 56 | github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= 57 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 58 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 59 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 60 | github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= 61 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 62 | github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= 63 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 64 | github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= 65 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 66 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 67 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 68 | github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= 69 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 70 | github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= 71 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 72 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 73 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 74 | github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 75 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 76 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 77 | github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 78 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 79 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 80 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 81 | github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= 82 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 83 | github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= 84 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 85 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 86 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 87 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 88 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 89 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 90 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 91 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 92 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 93 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 94 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 95 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 96 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 97 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 98 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 99 | github.com/liangdas/mqant v1.3.9 h1:4qYHixOSbqLwSLwLa+iiMoJkwLAUZcNAm9V3yGusKlQ= 100 | github.com/liangdas/mqant v1.3.9/go.mod h1:CVUjpJez2aFMVsbcP3NJdedxobY8RIXB9gaP41LJKto= 101 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 102 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 103 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 104 | github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= 105 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 106 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 107 | github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= 108 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 109 | github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= 110 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 111 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 112 | github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= 113 | github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= 114 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 115 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 116 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 117 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 118 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 119 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 120 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 121 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 122 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 123 | github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= 124 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 125 | github.com/nats-io/nats-server/v2 v2.1.0 h1:Yi0+ZhRPtPAGeIxFn5erIeJIV9wXA+JznfSxK621Fbk= 126 | github.com/nats-io/nats-server/v2 v2.1.0/go.mod h1:r5y0WgCag0dTj/qiHkHrXAcKQ/f5GMOZaEGdoxxnJ4I= 127 | github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= 128 | github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= 129 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 130 | github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= 131 | github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= 132 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 133 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 134 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 135 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 136 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= 137 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 138 | github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= 139 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 140 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 141 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 142 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 143 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 144 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 145 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 146 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 147 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 148 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 149 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 150 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 151 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 152 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 153 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 154 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 155 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 156 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 157 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 158 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= 159 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 160 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 161 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 162 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 163 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 164 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 165 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 166 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 167 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 168 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 169 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 170 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 171 | github.com/yireyun/go-queue v0.0.0-20180809062148-5e6897360dac h1:a4krU0HaACgZ1ai3epUH6U6Kji7WHiucldhzWv6oD2o= 172 | github.com/yireyun/go-queue v0.0.0-20180809062148-5e6897360dac/go.mod h1:NS8O3p7NiPwC1Yw9xTd9DnDBguxFJG/BL1VPTSfJ5Gw= 173 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 174 | go.etcd.io/etcd v3.3.15+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= 175 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 176 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 177 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 178 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 179 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 180 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 181 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 182 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= 183 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 184 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 185 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 186 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 187 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 188 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 189 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 190 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 191 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 192 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 193 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 194 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 195 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 196 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 197 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= 198 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 199 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 200 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 201 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 202 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 203 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= 204 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 205 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 206 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 207 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 208 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 209 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 210 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 211 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 212 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 213 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 214 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 215 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= 216 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 217 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 218 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 219 | golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 220 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 221 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 222 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 223 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 224 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 225 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 226 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 227 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 228 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 229 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 230 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 231 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 232 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 233 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 234 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 235 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 236 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 237 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 238 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 239 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 240 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 241 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 242 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 243 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 244 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 245 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 246 | --------------------------------------------------------------------------------