From 523c804b530e9dfaab0277fb7303c1ae7a315b43 Mon Sep 17 00:00:00 2001 From: lesismal Date: Wed, 17 Jan 2024 21:16:55 +0800 Subject: [PATCH 01/11] save --- stream.go | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 stream.go diff --git a/stream.go b/stream.go new file mode 100644 index 0000000..b93c477 --- /dev/null +++ b/stream.go @@ -0,0 +1,235 @@ +package arpc + +import ( + "encoding/binary" + "io" + "sync/atomic" + + "github.com/lesismal/arpc/util" +) + +// stream . +type Stream struct { + id uint64 + cli *Client + method string + chData chan *Message + stateDone int32 + stateClosed int32 + local bool +} + +func (s *Stream) isCreatedByLocal() bool { + return s.local +} + +func (s *Stream) onMessage(msg *Message) { + if msg != nil { + select { + case s.chData <- msg: + case <-s.cli.chClose: + } + } +} + +func (s *Stream) done() { + if atomic.CompareAndSwapInt32(&s.stateDone, 0, 1) { + select { + case s.chData <- nil: + case <-s.cli.chClose: + } + } +} + +func (s *Stream) Recv(v interface{}) error { + select { + case msg, ok := <-s.chData: + if !ok { + return io.EOF + } + data := msg.Data() + if len(data) == 0 { + s.cli.Handler.OnMessageDone(s.cli, msg) + return io.EOF + } + err := s.cli.Codec.Unmarshal(data, v) + s.cli.Handler.OnMessageDone(s.cli, msg) + return err + case <-s.cli.chClose: + return ErrClientStopped + } +} + +// func (s *Stream) RecvWith(ctx context.Context, v interface{}) error { +// return s.RecvContext(ctx, v) +// } + +// func (s *Stream) RecvContext(ctx context.Context, v interface{}) error { +// var ( +// ok bool +// data []byte +// ) +// select { +// case data, ok = <-s.chData: +// if !ok || len(data) == 0 { +// return io.EOF +// } +// case <-ctx.Done(): +// return ErrTimeout +// } +// return s.cli.Codec.Unmarshal(data, v) +// } + +func (s *Stream) newMessage(v interface{}, args ...interface{}) *Message { + if len(args) == 0 { + return newMessage(CmdStream, s.method, v, false, false, s.id, s.cli.Handler, s.cli.Codec, nil) + } + return newMessage(CmdStream, s.method, v, false, false, s.id, s.cli.Handler, s.cli.Codec, args[0].(map[interface{}]interface{})) +} + +func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { + c := s.cli + err := c.CheckState() + if err != nil { + return err + } + + data := util.ValueToBytes(c.Codec, v) + buf := c.Handler.Malloc(8 + len(data)) + binary.LittleEndian.PutUint64(buf, s.id) + copy(buf[8:], data) + msg := s.newMessage(buf, args...) + msg.SetStreamLocal(s.local) + msg.SetStreamDone(done) + c.Handler.Free(buf) + + if c.Handler.AsyncWrite() { + select { + case c.chSend <- msg: + case <-c.chClose: + // c.Handler.OnOverstock(c, msg) + c.Handler.OnMessageDone(c, msg) + return ErrClientStopped + } + } else { + if !c.reconnecting { + coders := c.Handler.Coders() + for j := 0; j < len(coders); j++ { + msg = coders[j].Encode(c, msg) + } + _, err := c.Handler.Send(c.Conn, msg.Buffer) + if err != nil { + c.Conn.Close() + } + c.Handler.OnMessageDone(c, msg) + return err + } else { + c.dropMessage(msg) + return ErrClientReconnecting + } + } + + return nil +} + +// func (s *Stream) SendWith(ctx context.Context, v interface{}, timeout time.Duration, args ...interface{}) error { +// return s.SendContext(ctx, v, args...) +// } + +// func (s *Stream) SendContext(ctx context.Context, v interface{}, args ...interface{}) error { +// c := s.cli +// err := c.CheckState() +// if err != nil { +// return err +// } + +// data := util.ValueToBytes(c.Codec, v) +// buf := c.Handler.Malloc(8 + len(data)) +// binary.LittleEndian.PutUint64(buf, s.id) +// copy(buf[8:], data) +// msg := c.newRequestMessage(CmdStream, s.method, buf, false, false, args...) +// c.Handler.Free(buf) + +// if c.Handler.AsyncWrite() { +// select { +// case c.chSend <- msg: +// case <-ctx.Done(): +// // c.Handler.OnOverstock(c, msg) +// c.Handler.OnMessageDone(c, msg) +// return ErrClientTimeout +// case <-c.chClose: +// // c.Handler.OnOverstock(c, msg) +// c.Handler.OnMessageDone(c, msg) +// return ErrClientStopped +// } +// } else { +// if !c.reconnecting { +// coders := c.Handler.Coders() +// for j := 0; j < len(coders); j++ { +// msg = coders[j].Encode(c, msg) +// } +// _, err := c.Handler.Send(c.Conn, msg.Buffer) +// if err != nil { +// c.Conn.Close() +// } +// c.Handler.OnMessageDone(c, msg) +// return err +// } else { +// c.dropMessage(msg) +// return ErrClientReconnecting +// } +// } + +// return err +// } + +func (s *Stream) Close(args ...interface{}) error { + var err error + if atomic.CompareAndSwapInt32(&s.stateClosed, 0, 1) { + defer s.done() + err = s.send([]byte{}, true, args...) + s.cli.deleteStream(s.id, s.local) + n := len(s.chData) + for i := 0; i < n; i++ { + select { + case msg := <-s.chData: + s.cli.Handler.OnMessageDone(s.cli, msg) + default: + goto Exit + } + } + } +Exit: + return err +} + +func (s *Stream) Send(v interface{}, args ...interface{}) error { + return s.send(v, false, args...) +} + +func (s *Stream) SendAndClose(v interface{}, args ...interface{}) error { + return s.send(v, true, args...) +} + +// NewStream creates a stream. +func (client *Client) NewStream(method string) *Stream { + return client.newStream(method, true) +} + +func (client *Client) newStream(method string, local bool) *Stream { + stream := &Stream{ + id: atomic.AddUint64(&client.seq, 1), + cli: client, + method: method, + chData: make(chan *Message, client.Handler.StreamQueueSize()), + local: local, + } + client.mux.Lock() + if local { + client.streamLocalMap[stream.id] = stream + } else { + client.streamRemoteMap[stream.id] = stream + } + client.mux.Unlock() + return stream +} From 9ea04a51f0b3ee882a900d76521d0e83b14e0a90 Mon Sep 17 00:00:00 2001 From: lesismal Date: Wed, 17 Jan 2024 21:17:09 +0800 Subject: [PATCH 02/11] save --- client.go | 67 +++++++++++++++++++++++++++++++ handler.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++------ proto.go | 56 +++++++++++++++++++++++++- 3 files changed, 223 insertions(+), 14 deletions(-) diff --git a/client.go b/client.go index 3663016..cdf83ad 100644 --- a/client.go +++ b/client.go @@ -60,6 +60,8 @@ type Client struct { mux sync.Mutex sessionMap map[uint64]*rpcSession asyncHandlerMap map[uint64]*asyncHandler + streamLocalMap map[uint64]*Stream + streamRemoteMap map[uint64]*Stream chSend chan *Message chClose chan util.Empty @@ -483,6 +485,8 @@ func (c *Client) Restart() error { c.chClose = make(chan util.Empty) c.sessionMap = make(map[uint64]*rpcSession) c.asyncHandlerMap = make(map[uint64]*asyncHandler) + c.streamLocalMap = make(map[uint64]*Stream) + c.streamRemoteMap = make(map[uint64]*Stream) c.values = map[interface{}]interface{}{} c.initReader() @@ -521,6 +525,7 @@ func (c *Client) closeAndClean() { if c.onStop != nil { c.onStop(c) } + c.Handler.OnDisconnected(c) } @@ -737,6 +742,67 @@ func (c *Client) clearAsyncHandler() { } } +func (c *Client) addStream(id uint64, local bool, stream *Stream) { + c.mux.Lock() + if c.running { + var streamMap map[uint64]*Stream + if local { + streamMap = c.streamLocalMap + } else { + streamMap = c.streamRemoteMap + } + streamMap[id] = stream + } + c.mux.Unlock() +} + +func (c *Client) deleteStream(id uint64, local bool) { + c.mux.Lock() + if c.running { + var streamMap map[uint64]*Stream + if local { + streamMap = c.streamLocalMap + } else { + streamMap = c.streamRemoteMap + } + delete(streamMap, id) + } + c.mux.Unlock() +} + +func (c *Client) getAndPushMsg(id uint64, local, done bool, msg *Message) (stream *Stream, ok bool) { + c.mux.Lock() + if c.running { + var streamMap map[uint64]*Stream + if local { + streamMap = c.streamLocalMap + } else { + streamMap = c.streamRemoteMap + } + stream, ok = streamMap[id] + if ok && done { + delete(streamMap, id) + } + } + c.mux.Unlock() + return stream, ok +} + +func (c *Client) clearStream() { + c.mux.Lock() + streamLocalMap := c.streamLocalMap + streamRemoteMap := c.streamRemoteMap + c.streamLocalMap = make(map[uint64]*Stream) + c.streamRemoteMap = make(map[uint64]*Stream) + c.mux.Unlock() + for _, stream := range streamLocalMap { + stream.Close() + } + for _, stream := range streamRemoteMap { + stream.Close() + } +} + func (c *Client) run() { c.mux.Lock() defer c.mux.Unlock() @@ -797,6 +863,7 @@ func (c *Client) recvLoop() { c.Conn.Close() c.clearSession() c.clearAsyncHandler() + c.clearStream() // if c.running { // log.Info("%v\t%v\tReconnect Start", c.Handler.LogTag(), addr) diff --git a/handler.go b/handler.go index 6a11823..92648f4 100644 --- a/handler.go +++ b/handler.go @@ -21,9 +21,12 @@ import ( // DefaultHandler is the default Handler used by arpc var DefaultHandler Handler = NewHandler() -// HandlerFunc defines message handler of arpc middleware and method/router. +// HandlerFunc defines message handler. type HandlerFunc func(*Context) +// StreamHandlerFunc defines stream handler. +type StreamHandlerFunc func(*Stream) + // AsyncHandlerFunc defines callback of Client.CallAsync. type AsyncHandlerFunc func(*Context, error) @@ -61,6 +64,14 @@ type routerHandler struct { handlers []HandlerFunc } +// streamHandler saves all stream handler and middleware funcs. +// for every method by register order, +// all the funcs will be called one by one for every message. +type streamHandler struct { + async bool + handler StreamHandlerFunc +} + // Handler defines net message handler interface. type Handler interface { // Clone returns a copy of Handler. @@ -172,6 +183,11 @@ type Handler interface { // SetSendQueueSize sets client's send queue channel capacity. SetSendQueueSize(size int) + // StreamQueueSize returns stream queue channel capacity. + StreamQueueSize() int + // SetStreamQueueSize sets stream queue channel capacity. + SetStreamQueueSize(size int) + // MaxBodyLen returns max body length of a message. MaxBodyLen() int // SetMaxBodyLen sets max body length of a message. @@ -198,6 +214,9 @@ type Handler interface { // It will be called when mothod/router is not found. HandleNotFound(h HandlerFunc) + // HandleStream registers method/router stream handler. + HandleStream(m string, h StreamHandlerFunc, args ...interface{}) + // OnMessage finds method/router middlewares and handler, then call them one by one. OnMessage(c *Client, m *Message) @@ -248,6 +267,7 @@ type handler struct { readTimeout time.Duration writeTimeout time.Duration sendQueueSize int + streamQueueSize int maxBodyLen int maxReconnectTimes int @@ -267,10 +287,12 @@ type handler struct { wrapReader func(conn net.Conn) io.Reader - middles []HandlerFunc - msgCoders []MessageCoder + routes map[string]*routerHandler + streams map[string]*streamHandler - routes map[string]*routerHandler + middles []HandlerFunc + streamMiddles []StreamHandlerFunc + msgCoders []MessageCoder ctx context.Context cancel context.CancelFunc @@ -505,6 +527,14 @@ func (h *handler) SetSendQueueSize(size int) { h.sendQueueSize = size } +func (h *handler) StreamQueueSize() int { + return h.streamQueueSize +} + +func (h *handler) SetStreamQueueSize(size int) { + h.streamQueueSize = size +} + func (h *handler) MaxBodyLen() int { return h.maxBodyLen } @@ -597,6 +627,31 @@ func (h *handler) handle(method string, cb HandlerFunc, args ...interface{}) { h.routes[method] = rh } +func (h *handler) HandleStream(method string, cb StreamHandlerFunc, args ...interface{}) { + if h.streams == nil { + h.streams = map[string]*streamHandler{} + } + if len(method) > MaxMethodLen { + panic(fmt.Errorf("invalid method length %v(> MaxMethodLen %v)", len(method), MaxMethodLen)) + } + + if _, ok := h.streams[method]; ok && method != "" { + panic(fmt.Errorf("stream handler exist for method %v ", method)) + } + + async := h.AsyncResponse() + if len(args) > 0 { + if bv, ok := args[0].(bool); ok { + async = bv + } + } + rh := &streamHandler{ + async: async, + handler: cb, + } + h.streams[method] = rh +} + func (h *handler) Recv(c *Client) (*Message, error) { var ( err error @@ -731,6 +786,30 @@ func (h *handler) OnMessage(c *Client, msg *Message) { log.Warn("%v OnMessage: async handler not exist or expired", h.LogTag()) } } + case CmdStream: + f := func() { + id := msg.Seq() + local := !msg.IsStreamLocal() + done := !msg.IsStreamDone() + stream, ok := c.getAndPushMsg(id, local, done, msg) + if !ok && !local { + stream = c.newStream(msg.method(), false) + ok = true + } + if ok { + stream.onMessage(msg) + if done { + stream.done() + } + } else { + h.onMessageDone(c, msg) + } + } + if msg.IsAsync() { + h.AsyncExecute(f) + } else { + f() + } default: log.Warn("%v OnMessage: invalid cmd [%v]", h.LogTag(), msg.Cmd()) go c.Stop() @@ -836,14 +915,15 @@ func (h *handler) AsyncExecute(f func()) { // NewHandler returns a default Handler implementation. func NewHandler() Handler { h := &handler{ - logtag: "[ARPC CLI]", - batchRecv: true, - batchSend: true, - asyncWrite: true, - asyncResponse: true, - recvBufferSize: 8192, - sendQueueSize: 4096, - maxBodyLen: DefaultMaxBodyLen, + logtag: "[ARPC CLI]", + batchRecv: true, + batchSend: true, + asyncWrite: true, + asyncResponse: true, + recvBufferSize: 8192, + sendQueueSize: 4096, + streamQueueSize: 4, + maxBodyLen: DefaultMaxBodyLen, } h.wrapReader = func(conn net.Conn) io.Reader { return bufio.NewReaderSize(conn, h.recvBufferSize) @@ -984,6 +1064,16 @@ func SetSendQueueSize(size int) { DefaultHandler.SetSendQueueSize(size) } +// StreamQueueSize returns default stream queue channel capacity. +func StreamQueueSize() int { + return DefaultHandler.StreamQueueSize() +} + +// SetStreamQueueSize sets default stream queue channel capacity. +func SetStreamQueueSize(size int) { + DefaultHandler.SetStreamQueueSize(size) +} + func MaxBodyLen() int { return DefaultHandler.MaxBodyLen() } diff --git a/proto.go b/proto.go index 235c333..5a9c252 100644 --- a/proto.go +++ b/proto.go @@ -33,6 +33,9 @@ const ( // CmdPong . CmdPong byte = 5 + + // CmdStream . + CmdStream byte = 6 ) const ( @@ -56,6 +59,13 @@ const ( HeaderFlagMaskError byte = 0x01 // HeaderFlagMaskAsync . HeaderFlagMaskAsync byte = 0x02 + + HeaderStreamLocalBitIndex = 7 + HeaderStreamDoneBitIndex = 6 + HeaderStreamLocalBit = byte(0x1) << HeaderStreamLocalBitIndex + HeaderStreamDoneBit = byte(0x1) << HeaderStreamDoneBitIndex + HeaderStreamFlagBitMask = HeaderStreamLocalBit | HeaderStreamDoneBit + HeaderCmdBitMask = ^HeaderStreamFlagBitMask ) const ( @@ -158,12 +168,54 @@ func (m *Message) Len() int { // Cmd returns cmd. func (m *Message) Cmd() byte { - return m.Buffer[HeaderIndexCmd] + return m.Buffer[HeaderIndexCmd] & HeaderCmdBitMask } // SetCmd sets cmd. func (m *Message) SetCmd(cmd byte) { - m.Buffer[HeaderIndexCmd] = cmd + m.Buffer[HeaderIndexCmd] = (m.Buffer[HeaderIndexCmd] & HeaderStreamFlagBitMask) | cmd +} + +// // IsStream represents whether it's a stream message. +// func (m *Message) IsStream() bool { +// return m.Buffer[HeaderIndexCmd]&HeaderStreamBit > 0 +// } + +// // SetStream sets the flag for a stream message. +// func (m *Message) SetStream(isStream bool) { +// if isStream { +// m.Buffer[HeaderIndexCmd] |= HeaderStreamBit +// } else { +// m.Buffer[HeaderIndexCmd] &= (^HeaderStreamBit) +// } +// } + +// IsStream represents whether it's a stream message. +func (m *Message) IsStreamLocal() bool { + return m.Buffer[HeaderIndexCmd]&HeaderStreamLocalBit > 0 +} + +// SetStream sets the flag for a stream message. +func (m *Message) SetStreamLocal(local bool) { + if local { + m.Buffer[HeaderIndexCmd] |= HeaderStreamLocalBit + } else { + m.Buffer[HeaderIndexCmd] &= (^HeaderStreamLocalBit) + } +} + +// IsStream represents whether it's a stream's last message and the stream is done and closed. +func (m *Message) IsStreamDone() bool { + return m.Buffer[HeaderIndexCmd]&HeaderStreamDoneBit > 0 +} + +// SetStream sets the flag for a stream's last message and mark the stream is done and closed. +func (m *Message) SetStreamDone(done bool) { + if done { + m.Buffer[HeaderIndexCmd] |= HeaderStreamDoneBit + } else { + m.Buffer[HeaderIndexCmd] &= (^HeaderStreamDoneBit) + } } // IsError returns error flag. From 0ede9bbe58d3fc5d8b811ccb2eedf36b255b9dfe Mon Sep 17 00:00:00 2001 From: lesismal Date: Thu, 18 Jan 2024 01:58:30 +0800 Subject: [PATCH 03/11] save --- stream.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/stream.go b/stream.go index b93c477..5bf45dc 100644 --- a/stream.go +++ b/stream.go @@ -1,7 +1,6 @@ package arpc import ( - "encoding/binary" "io" "sync/atomic" @@ -95,13 +94,13 @@ func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { } data := util.ValueToBytes(c.Codec, v) - buf := c.Handler.Malloc(8 + len(data)) - binary.LittleEndian.PutUint64(buf, s.id) - copy(buf[8:], data) - msg := s.newMessage(buf, args...) + // buf := c.Handler.Malloc(8 + len(data)) + // binary.LittleEndian.PutUint64(buf, s.id) + // copy(buf[8:], data) + msg := s.newMessage(data, args...) msg.SetStreamLocal(s.local) msg.SetStreamDone(done) - c.Handler.Free(buf) + // c.Handler.Free(buf) if c.Handler.AsyncWrite() { select { From 49ffd6d5ee166a3a874e8b7e675fbf70d8066eda Mon Sep 17 00:00:00 2001 From: lesismal Date: Thu, 18 Jan 2024 03:13:27 +0800 Subject: [PATCH 04/11] save --- client.go | 48 ++-- examples/stream/client/client.go | 105 +++++++ examples/stream/server/server.go | 96 +++++++ go.mod | 64 ++++- go.sum | 463 +++++++++++++++++++++++++++++++ handler.go | 47 ++-- stream.go | 19 +- util/util.go | 25 ++ 8 files changed, 824 insertions(+), 43 deletions(-) create mode 100644 examples/stream/client/client.go create mode 100644 examples/stream/server/server.go create mode 100644 go.sum diff --git a/client.go b/client.go index cdf83ad..e13bd16 100644 --- a/client.go +++ b/client.go @@ -1001,16 +1001,20 @@ func (c *Client) batchSendLoop() { func newClientWithConn(conn net.Conn, codec codec.Codec, handler Handler, onStop func(*Client)) *Client { log.Info("%v\t%v\tConnected", handler.LogTag(), conn.RemoteAddr()) - c := &Client{} - c.Conn = conn - c.Codec = codec - c.Handler = handler - c.Head = make([]byte, 4) - c.chSend = make(chan *Message, c.Handler.SendQueueSize()) - c.chClose = make(chan util.Empty) - c.sessionMap = make(map[uint64]*rpcSession) - c.asyncHandlerMap = make(map[uint64]*asyncHandler) - c.onStop = onStop + c := &Client{ + seq: 1, + Conn: conn, + Codec: codec, + Handler: handler, + Head: make([]byte, 4), + chSend: make(chan *Message, handler.SendQueueSize()), + chClose: make(chan util.Empty), + sessionMap: make(map[uint64]*rpcSession), + asyncHandlerMap: make(map[uint64]*asyncHandler), + streamLocalMap: make(map[uint64]*Stream), + streamRemoteMap: make(map[uint64]*Stream), + onStop: onStop, + } c.run() @@ -1034,16 +1038,20 @@ func NewClient(dialer DialerFunc, args ...interface{}) (*Client, error) { handler = DefaultHandler.Clone() } - c := &Client{} - c.Conn = conn - c.Codec = codec.DefaultCodec - c.Handler = handler - c.Dialer = dialer - c.Head = make([]byte, 4) - c.chSend = make(chan *Message, c.Handler.SendQueueSize()) - c.chClose = make(chan util.Empty) - c.sessionMap = make(map[uint64]*rpcSession) - c.asyncHandlerMap = make(map[uint64]*asyncHandler) + c := &Client{ + seq: 1, + Conn: conn, + Codec: codec.DefaultCodec, + Handler: handler, + Dialer: dialer, + Head: make([]byte, 4), + chSend: make(chan *Message, handler.SendQueueSize()), + chClose: make(chan util.Empty), + sessionMap: make(map[uint64]*rpcSession), + asyncHandlerMap: make(map[uint64]*asyncHandler), + streamLocalMap: make(map[uint64]*Stream), + streamRemoteMap: make(map[uint64]*Stream), + } c.run() diff --git a/examples/stream/client/client.go b/examples/stream/client/client.go new file mode 100644 index 0000000..2e0ca67 --- /dev/null +++ b/examples/stream/client/client.go @@ -0,0 +1,105 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "io" + "log" + "net" + "sync" + "time" + + "github.com/lesismal/arpc" +) + +var ( + addr = "localhost:8888" + + method = "Hello" +) + +// HelloReq . +type HelloReq struct { + Msg string +} + +// HelloRsp . +type HelloRsp struct { + Msg string +} + +func dialer() (net.Conn, error) { + return net.DialTimeout("tcp", addr, time.Second*3) +} + +func main() { + arpc.EnablePool(true) + + client, err := arpc.NewClient(dialer) + if err != nil { + log.Println("NewClient failed:", err) + return + } + + wg := &sync.WaitGroup{} + wg.Add(1) + client.Handler.HandleStream("/stream_server_to_client", func(stream *arpc.Stream) { + defer wg.Done() + defer stream.Close() + str := "" + for { + err := stream.Recv(&str) + if err == io.EOF { + err = stream.Close() + if err != nil { + panic(err) + } + log.Println("[client] stream_server_to_client closed with:", str) + break + } + if err != nil { + panic(err) + } + log.Println("[client] stream_server_to_client:", str) + str := "" + err = stream.Send(&str) + if err != nil { + panic(err) + } + } + }) + + stream := client.NewStream("/stream_client_to_server") + defer stream.Close() + for i := 0; i < 3; i++ { + err := stream.Send(fmt.Sprintf("stream data %v", i)) + if err != nil { + panic(err) + } + str := "" + err = stream.Recv(&str) + if err != nil { + panic(err) + } + } + err = stream.SendAndClose(fmt.Sprintf("stream data %v", 3)) + if err != nil { + panic(err) + } + + defer client.Stop() + + data := make([]byte, 10) + rand.Read(data) + req := &HelloReq{Msg: base64.RawStdEncoding.EncodeToString(data)} + rsp := &HelloRsp{} + err = client.Call(method, req, rsp, time.Second*5) + if err != nil { + log.Printf("Call failed: %v", err) + } else if rsp.Msg != req.Msg { + log.Fatal("Call failed: not equal") + } + + wg.Wait() +} diff --git a/examples/stream/server/server.go b/examples/stream/server/server.go new file mode 100644 index 0000000..775e99e --- /dev/null +++ b/examples/stream/server/server.go @@ -0,0 +1,96 @@ +package main + +import ( + "fmt" + "io" + "log" + "net" + + "github.com/lesismal/arpc" +) + +const ( + addr = "localhost:8888" +) + +// HelloReq . +type HelloReq struct { + Msg string +} + +// HelloRsp . +type HelloRsp struct { + Msg string +} + +// OnHello . +func OnHello(ctx *arpc.Context) { + req := &HelloReq{} + ctx.Bind(req) + ctx.Write(&HelloRsp{Msg: req.Msg}) + + stream := ctx.Client.NewStream("/stream_server_to_client") + go func() { + for i := 0; i < 3; i++ { + err := stream.Send(fmt.Sprintf("stream data %v", i)) + if err != nil { + panic(err) + } + } + err := stream.SendAndClose(fmt.Sprintf("stream data %v", 3)) + if err != nil { + panic(err) + } + }() + + for { + str := "" + err := stream.Recv(&str) + if err == io.EOF { + log.Println("[server] stream_server_to_client closed with:", stream.Id(), str) + break + } + if err != nil { + panic(err) + } + log.Println("[server] stream_server_to_client:", stream.Id(), str) + } +} + +func OnStream(stream *arpc.Stream) { + defer stream.Close() + for { + str := "" + err := stream.Recv(&str) + if err == io.EOF { + err = stream.Close() + if err != nil { + panic(err) + } + log.Println("[server] stream_client_to_server closed with:", str) + break + } + if err != nil { + panic(err) + } + log.Println("[server] stream_client_to_server:", str) + err = stream.Send(&str) + if err != nil { + panic(err) + } + } +} + +func main() { + ln, err := net.Listen("tcp", addr) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + svr := arpc.NewServer() + svr.Handler.EnablePool(true) + svr.Handler.SetAsyncResponse(true) + svr.Handler.Handle("Hello", OnHello) + svr.Handler.HandleStream("/stream_client_to_server", OnStream) + svr.Serve(ln) +} diff --git a/go.mod b/go.mod index 5c85057..f419224 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,65 @@ module github.com/lesismal/arpc -go 1.16 +go 1.21 + +toolchain go1.21.5 + +require ( + github.com/anacrolix/utp v0.2.0 + github.com/go-redis/redis/v8 v8.11.5 + github.com/gogo/protobuf v1.3.2 + github.com/gorilla/websocket v1.5.1 + github.com/lesismal/nbio v1.3.21 + github.com/opentracing/basictracer-go v1.1.0 + github.com/opentracing/opentracing-go v1.2.0 + github.com/quic-go/quic-go v0.41.0 + github.com/vmihailenco/msgpack v4.0.4+incompatible + github.com/xtaci/kcp-go v5.4.20+incompatible + go.etcd.io/etcd/client/v3 v3.5.11 + golang.org/x/crypto v0.18.0 +) + +require ( + github.com/anacrolix/missinggo v1.3.0 // indirect + github.com/anacrolix/missinggo/perf v1.0.0 // indirect + github.com/anacrolix/missinggo/v2 v2.5.1 // indirect + github.com/anacrolix/sync v0.4.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/huandu/xstrings v1.3.1 // indirect + github.com/klauspost/cpuid/v2 v2.1.1 // indirect + github.com/klauspost/reedsolomon v1.12.0 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/lesismal/llib v1.1.12 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect + github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect + github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect + go.etcd.io/etcd/api/v3 v3.5.11 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.11 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/mock v0.3.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.10.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fc27fb4 --- /dev/null +++ b/go.sum @@ -0,0 +1,463 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= +crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= +github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= +github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= +github.com/anacrolix/envpprof v1.3.0 h1:WJt9bpuT7A/CDCxPOv/eeZqHWlle/Y0keJUvc6tcJDk= +github.com/anacrolix/envpprof v1.3.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0= +github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= +github.com/anacrolix/log v0.13.1 h1:BmVwTdxHd5VcNrLylgKwph4P4wf+5VvPgOK4yi91fTY= +github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= +github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw= +github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc= +github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw= +github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= +github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= +github.com/anacrolix/missinggo/v2 v2.5.1 h1:aCQcBYPdUaABfXolqKyHMIh7K/xuBUnunxCfS4CeDzE= +github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= +github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= +github.com/anacrolix/sync v0.4.0 h1:T+MdO/u87ir/ijWsTFsPYw5jVm0SMm4kVpg8t4KF38o= +github.com/anacrolix/sync v0.4.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= +github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= +github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI= +github.com/anacrolix/utp v0.2.0/go.mod h1:HGk4GYQw1O/3T1+yhqT/F6EcBd+AAwlo9dYErNy7mj8= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= +github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno= +github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lesismal/llib v1.1.12 h1:KJFB8bL02V+QGIvILEw/w7s6bKj9Ps9Px97MZP2EOk0= +github.com/lesismal/llib v1.1.12/go.mod h1:70tFXXe7P1FZ02AU9l8LgSOK7d7sRrpnkUr3rd3gKSg= +github.com/lesismal/nbio v1.3.21 h1:Dqlj9SVe9P4/tni0aj7wFmjqAnyMa7ceoJ09peiyKwM= +github.com/lesismal/nbio v1.3.21/go.mod h1:l+unWf/Sj0ken8GdLNgvclSRK5GGD+g78oALs7x21L8= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0= +github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= +github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= +github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/etcd/api/v3 v3.5.11 h1:B54KwXbWDHyD3XYAwprxNzTe7vlhR69LuBgZnMVvS7E= +go.etcd.io/etcd/api/v3 v3.5.11/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= +go.etcd.io/etcd/client/pkg/v3 v3.5.11 h1:bT2xVspdiCj2910T0V+/KHcVKjkUrCZVtk8J2JF2z1A= +go.etcd.io/etcd/client/pkg/v3 v3.5.11/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= +go.etcd.io/etcd/client/v3 v3.5.11 h1:ajWtgoNSZJ1gmS8k+icvPtqsqEav+iUorF7b0qozgUU= +go.etcd.io/etcd/client/v3 v3.5.11/go.mod h1:a6xQUEqFJ8vztO1agJh/KQKOMfFI8og52ZconzcDJwE= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513122933-cd7d49e622d5/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/handler.go b/handler.go index 92648f4..f26419e 100644 --- a/handler.go +++ b/handler.go @@ -787,28 +787,43 @@ func (h *handler) OnMessage(c *Client, msg *Message) { } } case CmdStream: - f := func() { - id := msg.Seq() - local := !msg.IsStreamLocal() - done := !msg.IsStreamDone() - stream, ok := c.getAndPushMsg(id, local, done, msg) - if !ok && !local { - stream = c.newStream(msg.method(), false) - ok = true - } - if ok { + id := msg.Seq() + local := !msg.IsStreamLocal() + done := msg.IsStreamDone() + method := msg.method() + + sh, ok := h.streams[method] + if ok { + var stream *Stream + stream, ok = c.getAndPushMsg(id, local, done, msg) + if !ok { + if !local { + stream = c.newStream(msg.method(), id, false) + stream.onMessage(msg) + f := func() { + sh.handler(stream) + if done { + stream.done() + } + } + if !sh.async { + f() + } else { + h.AsyncExecute(f) + } + } else { + h.onMessageDone(c, msg) + log.Warn("%v OnMessage: invalid Stream with method: [%v], no handler", h.LogTag(), method) + } + } else { stream.onMessage(msg) if done { stream.done() } - } else { - h.onMessageDone(c, msg) } - } - if msg.IsAsync() { - h.AsyncExecute(f) } else { - f() + h.onMessageDone(c, msg) + log.Warn("%v OnMessage: invalid Stream with method: [%v], no handler", h.LogTag(), method) } default: log.Warn("%v OnMessage: invalid cmd [%v]", h.LogTag(), msg.Cmd()) diff --git a/stream.go b/stream.go index 5bf45dc..6c05c05 100644 --- a/stream.go +++ b/stream.go @@ -18,6 +18,10 @@ type Stream struct { local bool } +func (s *Stream) Id() uint64 { + return s.id +} + func (s *Stream) isCreatedByLocal() bool { return s.local } @@ -43,7 +47,7 @@ func (s *Stream) done() { func (s *Stream) Recv(v interface{}) error { select { case msg, ok := <-s.chData: - if !ok { + if !ok || msg == nil { return io.EOF } data := msg.Data() @@ -51,7 +55,7 @@ func (s *Stream) Recv(v interface{}) error { s.cli.Handler.OnMessageDone(s.cli, msg) return io.EOF } - err := s.cli.Codec.Unmarshal(data, v) + err := util.BytesToValue(s.cli.Codec, data, v) s.cli.Handler.OnMessageDone(s.cli, msg) return err case <-s.cli.chClose: @@ -185,7 +189,7 @@ func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { func (s *Stream) Close(args ...interface{}) error { var err error if atomic.CompareAndSwapInt32(&s.stateClosed, 0, 1) { - defer s.done() + // defer s.done() err = s.send([]byte{}, true, args...) s.cli.deleteStream(s.id, s.local) n := len(s.chData) @@ -212,12 +216,15 @@ func (s *Stream) SendAndClose(v interface{}, args ...interface{}) error { // NewStream creates a stream. func (client *Client) NewStream(method string) *Stream { - return client.newStream(method, true) + return client.newStream(method, 0, true) } -func (client *Client) newStream(method string, local bool) *Stream { +func (client *Client) newStream(method string, id uint64, local bool) *Stream { + if id == 0 { + id = atomic.AddUint64(&client.seq, 1) + } stream := &Stream{ - id: atomic.AddUint64(&client.seq, 1), + id: id, cli: client, method: method, chData: make(chan *Message, client.Handler.StreamQueueSize()), diff --git a/util/util.go b/util/util.go index 520b0a4..38ad989 100644 --- a/util/util.go +++ b/util/util.go @@ -5,6 +5,7 @@ package util import ( + "errors" "runtime" "unsafe" @@ -77,3 +78,27 @@ func ValueToBytes(codec acodec.Codec, v interface{}) []byte { return data } + +// BytesToValue converts []byte to values +func BytesToValue(codec acodec.Codec, data []byte, v interface{}) error { + var err error + if v != nil { + switch vt := v.(type) { + case *[]byte: + *vt = data + case *string: + *vt = string(data) + case *error: + *vt = errors.New(string(data)) + default: + if codec == nil { + codec = acodec.DefaultCodec + } + err = codec.Unmarshal(data, vt) + if err != nil { + log.Error("ValueToBytes: %v", err) + } + } + } + return err +} From 20e712ed7f99babde287e4bf6786227d7140ae09 Mon Sep 17 00:00:00 2001 From: lesismal Date: Thu, 18 Jan 2024 03:38:06 +0800 Subject: [PATCH 05/11] save --- examples/stream/client/client.go | 57 ++++++++++++++++++-------------- examples/stream/server/server.go | 8 ++--- handler.go | 47 +++++++++++--------------- 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/examples/stream/client/client.go b/examples/stream/client/client.go index 2e0ca67..2ce9534 100644 --- a/examples/stream/client/client.go +++ b/examples/stream/client/client.go @@ -41,28 +41,28 @@ func main() { log.Println("NewClient failed:", err) return } + defer client.Stop() wg := &sync.WaitGroup{} wg.Add(1) client.Handler.HandleStream("/stream_server_to_client", func(stream *arpc.Stream) { defer wg.Done() defer stream.Close() - str := "" for { + str := "" err := stream.Recv(&str) if err == io.EOF { err = stream.Close() if err != nil { panic(err) } - log.Println("[client] stream_server_to_client closed with:", str) + log.Printf("[client] [stream id: %v] stream_server_to_client closed", stream.Id()) break } if err != nil { panic(err) } - log.Println("[client] stream_server_to_client:", str) - str := "" + log.Printf("[client] [stream id: %v] stream_server_to_client: %v", stream.Id(), str) err = stream.Send(&str) if err != nil { panic(err) @@ -70,26 +70,6 @@ func main() { } }) - stream := client.NewStream("/stream_client_to_server") - defer stream.Close() - for i := 0; i < 3; i++ { - err := stream.Send(fmt.Sprintf("stream data %v", i)) - if err != nil { - panic(err) - } - str := "" - err = stream.Recv(&str) - if err != nil { - panic(err) - } - } - err = stream.SendAndClose(fmt.Sprintf("stream data %v", 3)) - if err != nil { - panic(err) - } - - defer client.Stop() - data := make([]byte, 10) rand.Read(data) req := &HelloReq{Msg: base64.RawStdEncoding.EncodeToString(data)} @@ -102,4 +82,33 @@ func main() { } wg.Wait() + time.Sleep(time.Second) + + stream := client.NewStream("/stream_client_to_server") + defer stream.Close() + go func() { + for i := 0; i < 3; i++ { + err := stream.Send(fmt.Sprintf("stream data %v", i)) + if err != nil { + panic(err) + } + } + err = stream.SendAndClose(fmt.Sprintf("stream data %v", 3)) + if err != nil { + panic(err) + } + }() + + for { + str := "" + err = stream.Recv(&str) + if err == io.EOF { + log.Printf("[client] [stream id: %v] stream_client_to_server closed", stream.Id()) + break + } + if err != nil { + panic(err) + } + log.Printf("[client] [stream id: %v] stream_client_to_server: %v", stream.Id(), str) + } } diff --git a/examples/stream/server/server.go b/examples/stream/server/server.go index 775e99e..726af4d 100644 --- a/examples/stream/server/server.go +++ b/examples/stream/server/server.go @@ -47,13 +47,13 @@ func OnHello(ctx *arpc.Context) { str := "" err := stream.Recv(&str) if err == io.EOF { - log.Println("[server] stream_server_to_client closed with:", stream.Id(), str) + log.Printf("[server] [stream id: %v] stream_server_to_client closed", stream.Id()) break } if err != nil { panic(err) } - log.Println("[server] stream_server_to_client:", stream.Id(), str) + log.Printf("[server] [stream id: %v] stream_server_to_client: %v", stream.Id(), str) } } @@ -67,13 +67,13 @@ func OnStream(stream *arpc.Stream) { if err != nil { panic(err) } - log.Println("[server] stream_client_to_server closed with:", str) + log.Printf("[server] [stream id: %v] stream_client_to_server closed", stream.Id()) break } if err != nil { panic(err) } - log.Println("[server] stream_client_to_server:", str) + log.Printf("[server] [stream id: %v] stream_client_to_server: [%v]", stream.Id(), str) err = stream.Send(&str) if err != nil { panic(err) diff --git a/handler.go b/handler.go index f26419e..6c84509 100644 --- a/handler.go +++ b/handler.go @@ -791,39 +791,32 @@ func (h *handler) OnMessage(c *Client, msg *Message) { local := !msg.IsStreamLocal() done := msg.IsStreamDone() method := msg.method() - - sh, ok := h.streams[method] - if ok { - var stream *Stream - stream, ok = c.getAndPushMsg(id, local, done, msg) - if !ok { - if !local { - stream = c.newStream(msg.method(), id, false) - stream.onMessage(msg) - f := func() { - sh.handler(stream) - if done { - stream.done() - } - } - if !sh.async { - f() - } else { - h.AsyncExecute(f) + stream, ok := c.getAndPushMsg(id, local, done, msg) + if !ok { + sh, ok := h.streams[method] + if ok && !local { + stream = c.newStream(msg.method(), id, false) + stream.onMessage(msg) + f := func() { + sh.handler(stream) + if done { + stream.done() } + } + if !sh.async { + f() } else { - h.onMessageDone(c, msg) - log.Warn("%v OnMessage: invalid Stream with method: [%v], no handler", h.LogTag(), method) + h.AsyncExecute(f) } } else { - stream.onMessage(msg) - if done { - stream.done() - } + h.onMessageDone(c, msg) + log.Warn("%v OnMessage: invalid Stream with method: [%v], no handler", h.LogTag(), method) } } else { - h.onMessageDone(c, msg) - log.Warn("%v OnMessage: invalid Stream with method: [%v], no handler", h.LogTag(), method) + stream.onMessage(msg) + if done { + stream.done() + } } default: log.Warn("%v OnMessage: invalid cmd [%v]", h.LogTag(), msg.Cmd()) From 4d827460167021feaf0a252185d260aad9a612b7 Mon Sep 17 00:00:00 2001 From: lesismal Date: Fri, 19 Jan 2024 02:21:27 +0800 Subject: [PATCH 06/11] save --- client.go | 6 +- error.go | 6 + examples/stream/client/client.go | 9 +- examples/stream/server/server.go | 8 +- handler.go | 19 ++- proto.go | 22 +-- stream.go | 224 +++++++++++++++---------------- util/util.go | 3 +- 8 files changed, 143 insertions(+), 154 deletions(-) diff --git a/client.go b/client.go index e13bd16..475f3b1 100644 --- a/client.go +++ b/client.go @@ -796,10 +796,12 @@ func (c *Client) clearStream() { c.streamRemoteMap = make(map[uint64]*Stream) c.mux.Unlock() for _, stream := range streamLocalMap { - stream.Close() + stream.CloseSend() + stream.CloseRecv() } for _, stream := range streamRemoteMap { - stream.Close() + stream.CloseSend() + stream.CloseRecv() } } diff --git a/error.go b/error.go index d9aa1a2..515a180 100644 --- a/error.go +++ b/error.go @@ -54,6 +54,12 @@ var ( ErrContextResponseToNotify = errors.New("should not response to a context with notify message") ) +// stream errors +var ( + // ErrStreamClosedSend represents an error of stream closed send. + ErrStreamClosedSend = errors.New("stream has closed send") +) + // general errors var ( // ErrTimeout represents an error of timeout. diff --git a/examples/stream/client/client.go b/examples/stream/client/client.go index 2ce9534..ed03d90 100644 --- a/examples/stream/client/client.go +++ b/examples/stream/client/client.go @@ -47,15 +47,12 @@ func main() { wg.Add(1) client.Handler.HandleStream("/stream_server_to_client", func(stream *arpc.Stream) { defer wg.Done() - defer stream.Close() + defer stream.CloseRecv() for { str := "" err := stream.Recv(&str) if err == io.EOF { - err = stream.Close() - if err != nil { - panic(err) - } + stream.CloseSend() log.Printf("[client] [stream id: %v] stream_server_to_client closed", stream.Id()) break } @@ -85,7 +82,7 @@ func main() { time.Sleep(time.Second) stream := client.NewStream("/stream_client_to_server") - defer stream.Close() + defer stream.CloseRecv() go func() { for i := 0; i < 3; i++ { err := stream.Send(fmt.Sprintf("stream data %v", i)) diff --git a/examples/stream/server/server.go b/examples/stream/server/server.go index 726af4d..ede6c43 100644 --- a/examples/stream/server/server.go +++ b/examples/stream/server/server.go @@ -30,6 +30,7 @@ func OnHello(ctx *arpc.Context) { ctx.Write(&HelloRsp{Msg: req.Msg}) stream := ctx.Client.NewStream("/stream_server_to_client") + defer stream.CloseRecv() go func() { for i := 0; i < 3; i++ { err := stream.Send(fmt.Sprintf("stream data %v", i)) @@ -58,15 +59,12 @@ func OnHello(ctx *arpc.Context) { } func OnStream(stream *arpc.Stream) { - defer stream.Close() + defer stream.CloseRecv() for { str := "" err := stream.Recv(&str) if err == io.EOF { - err = stream.Close() - if err != nil { - panic(err) - } + stream.CloseSend() log.Printf("[server] [stream id: %v] stream_client_to_server closed", stream.Id()) break } diff --git a/handler.go b/handler.go index 6c84509..91d003c 100644 --- a/handler.go +++ b/handler.go @@ -789,24 +789,21 @@ func (h *handler) OnMessage(c *Client, msg *Message) { case CmdStream: id := msg.Seq() local := !msg.IsStreamLocal() - done := msg.IsStreamDone() + eof := msg.IsStreamEOF() method := msg.method() - stream, ok := c.getAndPushMsg(id, local, done, msg) + stream, ok := c.getAndPushMsg(id, local, eof, msg) if !ok { sh, ok := h.streams[method] if ok && !local { stream = c.newStream(msg.method(), id, false) stream.onMessage(msg) - f := func() { - sh.handler(stream) - if done { - stream.done() - } + if eof { + stream.CloseRecv() } if !sh.async { - f() + sh.handler(stream) } else { - h.AsyncExecute(f) + h.AsyncExecute(func() { sh.handler(stream) }) } } else { h.onMessageDone(c, msg) @@ -814,8 +811,8 @@ func (h *handler) OnMessage(c *Client, msg *Message) { } } else { stream.onMessage(msg) - if done { - stream.done() + if eof { + stream.CloseRecv() } } default: diff --git a/proto.go b/proto.go index 5a9c252..2df77c3 100644 --- a/proto.go +++ b/proto.go @@ -61,10 +61,10 @@ const ( HeaderFlagMaskAsync byte = 0x02 HeaderStreamLocalBitIndex = 7 - HeaderStreamDoneBitIndex = 6 + HeaderStreamEOFBitIndex = 6 HeaderStreamLocalBit = byte(0x1) << HeaderStreamLocalBitIndex - HeaderStreamDoneBit = byte(0x1) << HeaderStreamDoneBitIndex - HeaderStreamFlagBitMask = HeaderStreamLocalBit | HeaderStreamDoneBit + HeaderStreamEOFBit = byte(0x1) << HeaderStreamEOFBitIndex + HeaderStreamFlagBitMask = HeaderStreamLocalBit | HeaderStreamEOFBit HeaderCmdBitMask = ^HeaderStreamFlagBitMask ) @@ -204,17 +204,17 @@ func (m *Message) SetStreamLocal(local bool) { } } -// IsStream represents whether it's a stream's last message and the stream is done and closed. -func (m *Message) IsStreamDone() bool { - return m.Buffer[HeaderIndexCmd]&HeaderStreamDoneBit > 0 +// IsStream represents whether it's a stream's last message and the stream is EOF and closed. +func (m *Message) IsStreamEOF() bool { + return m.Buffer[HeaderIndexCmd]&HeaderStreamEOFBit > 0 } -// SetStream sets the flag for a stream's last message and mark the stream is done and closed. -func (m *Message) SetStreamDone(done bool) { - if done { - m.Buffer[HeaderIndexCmd] |= HeaderStreamDoneBit +// SetStream sets the flag for a stream's last message and mark the stream is EOF and closed. +func (m *Message) SetStreamEOF(eof bool) { + if eof { + m.Buffer[HeaderIndexCmd] |= HeaderStreamEOFBit } else { - m.Buffer[HeaderIndexCmd] &= (^HeaderStreamDoneBit) + m.Buffer[HeaderIndexCmd] &= (^HeaderStreamEOFBit) } } diff --git a/stream.go b/stream.go index 6c05c05..f1646fb 100644 --- a/stream.go +++ b/stream.go @@ -1,21 +1,23 @@ package arpc import ( + "context" "io" "sync/atomic" "github.com/lesismal/arpc/util" ) -// stream . +// Stream . type Stream struct { - id uint64 - cli *Client - method string - chData chan *Message - stateDone int32 - stateClosed int32 - local bool + id uint64 + cli *Client + method string + local bool + chData chan *Message + stateRecv int32 + stateSend int32 + stateCloseCnt int32 } func (s *Stream) Id() uint64 { @@ -27,6 +29,13 @@ func (s *Stream) isCreatedByLocal() bool { } func (s *Stream) onMessage(msg *Message) { + if len(msg.Data()) == 0 { + return + } + if atomic.LoadInt32(&s.stateRecv) == 1 { + s.cli.Handler.OnMessageDone(s.cli, msg) + return + } if msg != nil { select { case s.chData <- msg: @@ -35,26 +44,35 @@ func (s *Stream) onMessage(msg *Message) { } } -func (s *Stream) done() { - if atomic.CompareAndSwapInt32(&s.stateDone, 0, 1) { - select { - case s.chData <- nil: - case <-s.cli.chClose: - } +func (s *Stream) CloseRecv() { + if atomic.CompareAndSwapInt32(&s.stateRecv, 0, 1) { + close(s.chData) + s.close() + } +} + +func (s *Stream) CloseSend() { + go s.CloseSendContext(context.Background()) +} + +func (s *Stream) CloseSendContext(ctx context.Context) { + if atomic.CompareAndSwapInt32(&s.stateSend, 0, 1) { + eof := true + s.send(ctx, []byte{}, eof) + s.close() } } func (s *Stream) Recv(v interface{}) error { + if atomic.LoadInt32(&s.stateRecv) == 1 && len(s.chData) == 0 { + return io.EOF + } select { case msg, ok := <-s.chData: - if !ok || msg == nil { + if !ok { return io.EOF } data := msg.Data() - if len(data) == 0 { - s.cli.Handler.OnMessageDone(s.cli, msg) - return io.EOF - } err := util.BytesToValue(s.cli.Codec, data, v) s.cli.Handler.OnMessageDone(s.cli, msg) return err @@ -63,25 +81,23 @@ func (s *Stream) Recv(v interface{}) error { } } -// func (s *Stream) RecvWith(ctx context.Context, v interface{}) error { -// return s.RecvContext(ctx, v) -// } +func (s *Stream) RecvContext(ctx context.Context, v interface{}) error { + select { + case msg := <-s.chData: + data := msg.Data() + err := util.BytesToValue(s.cli.Codec, data, v) + s.cli.Handler.OnMessageDone(s.cli, msg) + return err + case <-ctx.Done(): + return ErrTimeout + case <-s.cli.chClose: + return ErrClientStopped + } +} -// func (s *Stream) RecvContext(ctx context.Context, v interface{}) error { -// var ( -// ok bool -// data []byte -// ) -// select { -// case data, ok = <-s.chData: -// if !ok || len(data) == 0 { -// return io.EOF -// } -// case <-ctx.Done(): -// return ErrTimeout -// } -// return s.cli.Codec.Unmarshal(data, v) -// } +func (s *Stream) RecvWith(ctx context.Context, v interface{}) error { + return s.RecvContext(ctx, v) +} func (s *Stream) newMessage(v interface{}, args ...interface{}) *Message { if len(args) == 0 { @@ -90,7 +106,40 @@ func (s *Stream) newMessage(v interface{}, args ...interface{}) *Message { return newMessage(CmdStream, s.method, v, false, false, s.id, s.cli.Handler, s.cli.Codec, args[0].(map[interface{}]interface{})) } -func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { +func (s *Stream) Send(v interface{}, args ...interface{}) error { + return s.SendContext(context.Background(), v, args...) +} + +func (s *Stream) SendContext(ctx context.Context, v interface{}, args ...interface{}) error { + eof := false + return s.checkStateAndSend(ctx, v, eof, args...) +} + +func (s *Stream) SendWith(ctx context.Context, v interface{}, args ...interface{}) error { + return s.SendContext(ctx, v, args...) +} + +func (s *Stream) SendAndClose(v interface{}, args ...interface{}) error { + return s.SendAndCloseContext(context.Background(), v, args...) +} + +func (s *Stream) SendAndCloseContext(ctx context.Context, v interface{}, args ...interface{}) error { + eof := true + return s.checkStateAndSend(ctx, v, eof, args...) +} + +func (s *Stream) SendAndCloseWith(ctx context.Context, v interface{}, args ...interface{}) error { + return s.SendAndCloseContext(ctx, v, args...) +} + +func (s *Stream) checkStateAndSend(ctx context.Context, v interface{}, eof bool, args ...interface{}) error { + if atomic.LoadInt32(&s.stateSend) == 1 { + return ErrStreamClosedSend + } + return s.send(ctx, v, eof, args...) +} + +func (s *Stream) send(ctx context.Context, v interface{}, eof bool, args ...interface{}) error { c := s.cli err := c.CheckState() if err != nil { @@ -98,13 +147,13 @@ func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { } data := util.ValueToBytes(c.Codec, v) - // buf := c.Handler.Malloc(8 + len(data)) - // binary.LittleEndian.PutUint64(buf, s.id) - // copy(buf[8:], data) msg := s.newMessage(data, args...) msg.SetStreamLocal(s.local) - msg.SetStreamDone(done) - // c.Handler.Free(buf) + msg.SetStreamEOF(eof) + + if eof && atomic.CompareAndSwapInt32(&s.stateSend, 0, 1) { + s.close() + } if c.Handler.AsyncWrite() { select { @@ -113,6 +162,8 @@ func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { // c.Handler.OnOverstock(c, msg) c.Handler.OnMessageDone(c, msg) return ErrClientStopped + case <-ctx.Done(): + return ErrTimeout } } else { if !c.reconnecting { @@ -135,83 +186,20 @@ func (s *Stream) send(v interface{}, done bool, args ...interface{}) error { return nil } -// func (s *Stream) SendWith(ctx context.Context, v interface{}, timeout time.Duration, args ...interface{}) error { -// return s.SendContext(ctx, v, args...) -// } - -// func (s *Stream) SendContext(ctx context.Context, v interface{}, args ...interface{}) error { -// c := s.cli -// err := c.CheckState() -// if err != nil { -// return err -// } - -// data := util.ValueToBytes(c.Codec, v) -// buf := c.Handler.Malloc(8 + len(data)) -// binary.LittleEndian.PutUint64(buf, s.id) -// copy(buf[8:], data) -// msg := c.newRequestMessage(CmdStream, s.method, buf, false, false, args...) -// c.Handler.Free(buf) - -// if c.Handler.AsyncWrite() { -// select { -// case c.chSend <- msg: -// case <-ctx.Done(): -// // c.Handler.OnOverstock(c, msg) -// c.Handler.OnMessageDone(c, msg) -// return ErrClientTimeout -// case <-c.chClose: -// // c.Handler.OnOverstock(c, msg) -// c.Handler.OnMessageDone(c, msg) -// return ErrClientStopped -// } -// } else { -// if !c.reconnecting { -// coders := c.Handler.Coders() -// for j := 0; j < len(coders); j++ { -// msg = coders[j].Encode(c, msg) -// } -// _, err := c.Handler.Send(c.Conn, msg.Buffer) -// if err != nil { -// c.Conn.Close() -// } -// c.Handler.OnMessageDone(c, msg) -// return err -// } else { -// c.dropMessage(msg) -// return ErrClientReconnecting -// } -// } - -// return err -// } - -func (s *Stream) Close(args ...interface{}) error { - var err error - if atomic.CompareAndSwapInt32(&s.stateClosed, 0, 1) { - // defer s.done() - err = s.send([]byte{}, true, args...) +func (s *Stream) close() { + // When cnt == 2, both recv and send are closed, then need to clear this stream + if atomic.AddInt32(&s.stateCloseCnt, 1) == 2 { s.cli.deleteStream(s.id, s.local) - n := len(s.chData) - for i := 0; i < n; i++ { - select { - case msg := <-s.chData: - s.cli.Handler.OnMessageDone(s.cli, msg) - default: - goto Exit - } - } + // n := len(s.chData) + // for i := 0; i < n; i++ { + // select { + // case msg := <-s.chData: + // s.cli.Handler.OnMessageDone(s.cli, msg) + // default: + // return + // } + // } } -Exit: - return err -} - -func (s *Stream) Send(v interface{}, args ...interface{}) error { - return s.send(v, false, args...) -} - -func (s *Stream) SendAndClose(v interface{}, args ...interface{}) error { - return s.send(v, true, args...) } // NewStream creates a stream. diff --git a/util/util.go b/util/util.go index 38ad989..b2ea75e 100644 --- a/util/util.go +++ b/util/util.go @@ -85,7 +85,8 @@ func BytesToValue(codec acodec.Codec, data []byte, v interface{}) error { if v != nil { switch vt := v.(type) { case *[]byte: - *vt = data + *vt = make([]byte, len(data)) + copy(*vt, data) case *string: *vt = string(data) case *error: From 07f4c41cfc58c52cd466f87527a8f363762bbb59 Mon Sep 17 00:00:00 2001 From: lesismal Date: Sat, 20 Jan 2024 00:28:49 +0800 Subject: [PATCH 07/11] save --- stream.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stream.go b/stream.go index f1646fb..513a41f 100644 --- a/stream.go +++ b/stream.go @@ -51,6 +51,10 @@ func (s *Stream) CloseRecv() { } } +func (s *Stream) CloseRecvContext(ctx context.Context) { + s.CloseRecv() +} + func (s *Stream) CloseSend() { go s.CloseSendContext(context.Background()) } From 88a54902f5661843a38137db69fb68b50af3f1b2 Mon Sep 17 00:00:00 2001 From: lesismal Date: Sat, 20 Jan 2024 00:29:36 +0800 Subject: [PATCH 08/11] save --- stream.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/stream.go b/stream.go index 513a41f..e94e35d 100644 --- a/stream.go +++ b/stream.go @@ -47,7 +47,7 @@ func (s *Stream) onMessage(msg *Message) { func (s *Stream) CloseRecv() { if atomic.CompareAndSwapInt32(&s.stateRecv, 0, 1) { close(s.chData) - s.close() + s.halfClose() } } @@ -63,7 +63,7 @@ func (s *Stream) CloseSendContext(ctx context.Context) { if atomic.CompareAndSwapInt32(&s.stateSend, 0, 1) { eof := true s.send(ctx, []byte{}, eof) - s.close() + s.halfClose() } } @@ -156,7 +156,7 @@ func (s *Stream) send(ctx context.Context, v interface{}, eof bool, args ...inte msg.SetStreamEOF(eof) if eof && atomic.CompareAndSwapInt32(&s.stateSend, 0, 1) { - s.close() + s.halfClose() } if c.Handler.AsyncWrite() { @@ -190,19 +190,9 @@ func (s *Stream) send(ctx context.Context, v interface{}, eof bool, args ...inte return nil } -func (s *Stream) close() { - // When cnt == 2, both recv and send are closed, then need to clear this stream +func (s *Stream) halfClose() { if atomic.AddInt32(&s.stateCloseCnt, 1) == 2 { s.cli.deleteStream(s.id, s.local) - // n := len(s.chData) - // for i := 0; i < n; i++ { - // select { - // case msg := <-s.chData: - // s.cli.Handler.OnMessageDone(s.cli, msg) - // default: - // return - // } - // } } } From 02c2ff12d76f71cc83f2a1727e77264a8a462e78 Mon Sep 17 00:00:00 2001 From: lesismal Date: Sat, 20 Jan 2024 14:31:11 +0800 Subject: [PATCH 09/11] save --- client.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 475f3b1..47d27dc 100644 --- a/client.go +++ b/client.go @@ -491,6 +491,7 @@ func (c *Client) Restart() error { c.initReader() if c.Handler.AsyncWrite() { + c.chSend = make(chan *Message, c.Handler.SendQueueSize()) go util.Safe(c.sendLoop) } go util.Safe(c.recvLoop) @@ -1009,7 +1010,6 @@ func newClientWithConn(conn net.Conn, codec codec.Codec, handler Handler, onStop Codec: codec, Handler: handler, Head: make([]byte, 4), - chSend: make(chan *Message, handler.SendQueueSize()), chClose: make(chan util.Empty), sessionMap: make(map[uint64]*rpcSession), asyncHandlerMap: make(map[uint64]*asyncHandler), @@ -1017,6 +1017,9 @@ func newClientWithConn(conn net.Conn, codec codec.Codec, handler Handler, onStop streamRemoteMap: make(map[uint64]*Stream), onStop: onStop, } + if c.Handler.AsyncWrite() { + c.chSend = make(chan *Message, handler.SendQueueSize()) + } c.run() @@ -1047,13 +1050,15 @@ func NewClient(dialer DialerFunc, args ...interface{}) (*Client, error) { Handler: handler, Dialer: dialer, Head: make([]byte, 4), - chSend: make(chan *Message, handler.SendQueueSize()), chClose: make(chan util.Empty), sessionMap: make(map[uint64]*rpcSession), asyncHandlerMap: make(map[uint64]*asyncHandler), streamLocalMap: make(map[uint64]*Stream), streamRemoteMap: make(map[uint64]*Stream), } + if c.Handler.AsyncWrite() { + c.chSend = make(chan *Message, handler.SendQueueSize()) + } c.run() From ed7e44362abab60763344d05c4ee86d04b73f21d Mon Sep 17 00:00:00 2001 From: lesismal Date: Sat, 2 Mar 2024 16:11:33 +0800 Subject: [PATCH 10/11] fix sync write timeout for calling --- client.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client.go b/client.go index 47d27dc..5ea8b87 100644 --- a/client.go +++ b/client.go @@ -182,11 +182,11 @@ func (c *Client) Call(method string, req interface{}, rsp interface{}, timeout t msg = coders[j].Encode(c, msg) } _, err := c.Handler.Send(c.Conn, msg.Buffer) + c.Handler.OnMessageDone(c, msg) if err != nil { c.Conn.Close() + return err } - c.Handler.OnMessageDone(c, msg) - return err } else { c.dropMessage(msg) return ErrClientReconnecting @@ -244,11 +244,11 @@ func (c *Client) CallContext(ctx context.Context, method string, req interface{} msg = coders[j].Encode(c, msg) } _, err := c.Handler.Send(c.Conn, msg.Buffer) + c.Handler.OnMessageDone(c, msg) if err != nil { c.Conn.Close() + return err } - c.Handler.OnMessageDone(c, msg) - return err } else { c.dropMessage(msg) return ErrClientReconnecting From 34c6d63e104118c6839775a34fda79805b5dfaab Mon Sep 17 00:00:00 2001 From: lesismal Date: Sat, 2 Mar 2024 18:22:08 +0800 Subject: [PATCH 11/11] tidy --- client.go | 2 +- handler.go | 2 +- stream.go | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 5ea8b87..4c712b7 100644 --- a/client.go +++ b/client.go @@ -771,7 +771,7 @@ func (c *Client) deleteStream(id uint64, local bool) { c.mux.Unlock() } -func (c *Client) getAndPushMsg(id uint64, local, done bool, msg *Message) (stream *Stream, ok bool) { +func (c *Client) getStreamAndPushMsg(id uint64, local, done bool) (stream *Stream, ok bool) { c.mux.Lock() if c.running { var streamMap map[uint64]*Stream diff --git a/handler.go b/handler.go index 91d003c..69c460b 100644 --- a/handler.go +++ b/handler.go @@ -791,7 +791,7 @@ func (h *handler) OnMessage(c *Client, msg *Message) { local := !msg.IsStreamLocal() eof := msg.IsStreamEOF() method := msg.method() - stream, ok := c.getAndPushMsg(id, local, eof, msg) + stream, ok := c.getStreamAndPushMsg(id, local, eof) if !ok { sh, ok := h.streams[method] if ok && !local { diff --git a/stream.go b/stream.go index e94e35d..f1af279 100644 --- a/stream.go +++ b/stream.go @@ -24,10 +24,6 @@ func (s *Stream) Id() uint64 { return s.id } -func (s *Stream) isCreatedByLocal() bool { - return s.local -} - func (s *Stream) onMessage(msg *Message) { if len(msg.Data()) == 0 { return