trpc.group/trpc-go/trpc-go@v1.0.3/client/stream.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package client 15 16 import ( 17 "context" 18 19 "trpc.group/trpc-go/trpc-go/codec" 20 "trpc.group/trpc-go/trpc-go/errs" 21 icodec "trpc.group/trpc-go/trpc-go/internal/codec" 22 "trpc.group/trpc-go/trpc-go/internal/report" 23 "trpc.group/trpc-go/trpc-go/transport" 24 ) 25 26 // Stream is the interface that performs streaming RPCs. 27 type Stream interface { 28 // Send sends stream messages. 29 Send(ctx context.Context, m interface{}) error 30 // Recv receives stream messages. 31 Recv(ctx context.Context) ([]byte, error) 32 // Init initiates all stream related options. 33 Init(ctx context.Context, opt ...Option) (*Options, error) 34 // Invoke initiates the lower layer connection to build the stream. 35 Invoke(ctx context.Context) error 36 // Close closes the stream. 37 Close(ctx context.Context) error 38 } 39 40 // DefaultStream is the default client Stream. 41 var DefaultStream = NewStream() 42 43 // NewStream is the function that returns a Stream. 44 var NewStream = func() Stream { 45 return &stream{} 46 } 47 48 // stream is an implementation of Stream. 49 type stream struct { 50 opts *Options 51 client 52 } 53 54 // SendControl is the interface used for sender's flow control. 55 type SendControl interface { 56 GetWindow(uint32) error 57 UpdateWindow(uint32) 58 } 59 60 // RecvControl is the interface used for receiver's flow control. 61 type RecvControl interface { 62 OnRecv(n uint32) error 63 } 64 65 // Send implements Stream. 66 // It serializes the message and sends it to server through stream transport. 67 // It's safe to call Recv and Send in different goroutines concurrently, but calling 68 // Send in different goroutines concurrently is not thread-safe. 69 func (s *stream) Send(ctx context.Context, m interface{}) (err error) { 70 defer func() { 71 if err != nil { 72 s.opts.StreamTransport.Close(ctx) 73 } 74 }() 75 76 msg := codec.Message(ctx) 77 reqBodyBuf, err := serializeAndCompress(ctx, msg, m, s.opts) 78 if err != nil { 79 return err 80 } 81 82 // if m != nil, m is Data frame and sender flow control is needed. 83 if m != nil && s.opts.SControl != nil { 84 if err := s.opts.SControl.GetWindow(uint32(len(reqBodyBuf))); err != nil { 85 return err 86 } 87 } 88 // encode reqBodyBuf 89 reqBuf, err := s.opts.Codec.Encode(msg, reqBodyBuf) 90 if err != nil { 91 return errs.NewFrameError(errs.RetClientEncodeFail, "client codec Encode: "+err.Error()) 92 } 93 94 if err := s.opts.StreamTransport.Send(ctx, reqBuf); err != nil { 95 return err 96 } 97 return nil 98 } 99 100 // Recv implements Stream. 101 // It decodes and decompresses the message and leaves serialization to upper layer. 102 // It's safe to call Recv and Send in different goroutines concurrently, but calling 103 // Send in different goroutines concurrently is not thread-safe. 104 func (s *stream) Recv(ctx context.Context) (buf []byte, err error) { 105 defer func() { 106 if err != nil { 107 s.opts.StreamTransport.Close(ctx) 108 } 109 }() 110 rspBuf, err := s.opts.StreamTransport.Recv(ctx) 111 if err != nil { 112 return nil, err 113 } 114 msg := codec.Message(ctx) 115 rspBodyBuf, err := s.opts.Codec.Decode(msg, rspBuf) 116 if err != nil { 117 return nil, errs.NewFrameError(errs.RetClientDecodeFail, "client codec Decode: "+err.Error()) 118 } 119 if err := msg.ClientRspErr(); err != nil { 120 return nil, err 121 } 122 if len(rspBodyBuf) > 0 { 123 compressType := msg.CompressType() 124 if icodec.IsValidCompressType(s.opts.CurrentCompressType) { 125 compressType = s.opts.CurrentCompressType 126 } 127 // decompress 128 if icodec.IsValidCompressType(compressType) && compressType != codec.CompressTypeNoop { 129 rspBodyBuf, err = codec.Decompress(compressType, rspBodyBuf) 130 if err != nil { 131 return nil, errs.NewFrameError(errs.RetClientDecodeFail, "client codec Decompress: "+err.Error()) 132 } 133 } 134 } 135 return rspBodyBuf, nil 136 } 137 138 // Close implements Stream. 139 func (s *stream) Close(ctx context.Context) error { 140 // Send Close message. 141 return s.Send(ctx, nil) 142 } 143 144 // Init implements Stream. 145 func (s *stream) Init(ctx context.Context, opt ...Option) (*Options, error) { 146 // The generic message structure data of the current request is retrieved from the context, 147 // and each backend call uses a new msg generated by the client stub code. 148 msg := codec.Message(ctx) 149 150 // Get options. 151 opts, err := s.getOptions(msg, opt...) 152 if err != nil { 153 return nil, err 154 } 155 156 // Update msg. 157 s.updateMsg(msg, opts) 158 159 // Select a node of backend service. 160 node, err := selectNode(ctx, msg, opts) 161 if err != nil { 162 report.SelectNodeFail.Incr() 163 return nil, err 164 } 165 ensureMsgRemoteAddr(msg, findFirstNonEmpty(node.Network, opts.Network), node.Address, node.ParseAddr) 166 const invalidCost = -1 167 opts.Node.set(node, node.Address, invalidCost) 168 if opts.Codec == nil { 169 report.ClientCodecEmpty.Incr() 170 return nil, errs.NewFrameError(errs.RetClientEncodeFail, "client: codec empty") 171 } 172 opts.CallOptions = append(opts.CallOptions, transport.WithMsg(msg)) 173 s.opts = opts 174 return s.opts, nil 175 } 176 177 func findFirstNonEmpty(ss ...string) string { 178 for _, s := range ss { 179 if s != "" { 180 return s 181 } 182 } 183 return "" 184 } 185 186 // Invoke implements Stream. 187 func (s *stream) Invoke(ctx context.Context) error { 188 return s.opts.StreamTransport.Init(ctx, s.opts.CallOptions...) 189 }