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  }