github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/abci/client/socket_client.go (about)

     1  package abciclient
     2  
     3  import (
     4  	"bufio"
     5  	"container/list"
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/ari-anchor/sei-tendermint/abci/types"
    15  	"github.com/ari-anchor/sei-tendermint/libs/log"
    16  	tmnet "github.com/ari-anchor/sei-tendermint/libs/net"
    17  	"github.com/ari-anchor/sei-tendermint/libs/service"
    18  )
    19  
    20  // This is goroutine-safe, but users should beware that the application in
    21  // general is not meant to be interfaced with concurrent callers.
    22  type socketClient struct {
    23  	service.BaseService
    24  	logger log.Logger
    25  
    26  	addr        string
    27  	mustConnect bool
    28  	conn        net.Conn
    29  
    30  	reqQueue chan *requestAndResponse
    31  
    32  	mtx     sync.Mutex
    33  	err     error
    34  	reqSent *list.List // list of requests sent, waiting for response
    35  }
    36  
    37  var _ Client = (*socketClient)(nil)
    38  
    39  // NewSocketClient creates a new socket client, which connects to a given
    40  // address. If mustConnect is true, the client will return an error upon start
    41  // if it fails to connect.
    42  func NewSocketClient(logger log.Logger, addr string, mustConnect bool) Client {
    43  	cli := &socketClient{
    44  		logger:      logger,
    45  		reqQueue:    make(chan *requestAndResponse),
    46  		mustConnect: mustConnect,
    47  		addr:        addr,
    48  		reqSent:     list.New(),
    49  	}
    50  	cli.BaseService = *service.NewBaseService(logger, "socketClient", cli)
    51  	return cli
    52  }
    53  
    54  // OnStart implements Service by connecting to the server and spawning reading
    55  // and writing goroutines.
    56  func (cli *socketClient) OnStart(ctx context.Context) error {
    57  	var (
    58  		err  error
    59  		conn net.Conn
    60  	)
    61  	timer := time.NewTimer(0)
    62  	defer timer.Stop()
    63  
    64  	for {
    65  		conn, err = tmnet.Connect(cli.addr)
    66  		if err != nil {
    67  			if cli.mustConnect {
    68  				return err
    69  			}
    70  			cli.logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v.  Retrying after %vs...",
    71  				cli.addr, dialRetryIntervalSeconds), "err", err)
    72  
    73  			timer.Reset(time.Second * dialRetryIntervalSeconds)
    74  			select {
    75  			case <-ctx.Done():
    76  				return ctx.Err()
    77  			case <-timer.C:
    78  				continue
    79  			}
    80  
    81  		}
    82  		cli.conn = conn
    83  
    84  		go cli.sendRequestsRoutine(ctx, conn)
    85  		go cli.recvResponseRoutine(ctx, conn)
    86  
    87  		return nil
    88  	}
    89  }
    90  
    91  // OnStop implements Service by closing connection and flushing all queues.
    92  func (cli *socketClient) OnStop() {
    93  	if cli.conn != nil {
    94  		cli.conn.Close()
    95  	}
    96  	cli.drainQueue()
    97  }
    98  
    99  // Error returns an error if the client was stopped abruptly.
   100  func (cli *socketClient) Error() error {
   101  	cli.mtx.Lock()
   102  	defer cli.mtx.Unlock()
   103  	return cli.err
   104  }
   105  
   106  //----------------------------------------
   107  
   108  func (cli *socketClient) sendRequestsRoutine(ctx context.Context, conn io.Writer) {
   109  	bw := bufio.NewWriter(conn)
   110  	for {
   111  		select {
   112  		case <-ctx.Done():
   113  			return
   114  		case reqres := <-cli.reqQueue:
   115  			// N.B. We must enqueue before sending out the request, otherwise the
   116  			// server may reply before we do it, and the receiver will fail for an
   117  			// unsolicited reply.
   118  			cli.trackRequest(reqres)
   119  
   120  			if err := types.WriteMessage(reqres.Request, bw); err != nil {
   121  				cli.stopForError(fmt.Errorf("write to buffer: %w", err))
   122  				return
   123  			}
   124  
   125  			if err := bw.Flush(); err != nil {
   126  				cli.stopForError(fmt.Errorf("flush buffer: %w", err))
   127  				return
   128  			}
   129  		}
   130  	}
   131  }
   132  
   133  func (cli *socketClient) recvResponseRoutine(ctx context.Context, conn io.Reader) {
   134  	r := bufio.NewReader(conn)
   135  	for {
   136  		if ctx.Err() != nil {
   137  			return
   138  		}
   139  		res := &types.Response{}
   140  
   141  		if err := types.ReadMessage(r, res); err != nil {
   142  			cli.stopForError(fmt.Errorf("read message: %w", err))
   143  			return
   144  		}
   145  
   146  		switch r := res.Value.(type) {
   147  		case *types.Response_Exception: // app responded with error
   148  			// XXX After setting cli.err, release waiters (e.g. reqres.Done())
   149  			cli.stopForError(errors.New(r.Exception.Error))
   150  			return
   151  		default:
   152  			if err := cli.didRecvResponse(res); err != nil {
   153  				cli.stopForError(err)
   154  				return
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  func (cli *socketClient) trackRequest(reqres *requestAndResponse) {
   161  	// N.B. We must NOT hold the client state lock while checking this, or we
   162  	// may deadlock with shutdown.
   163  	if !cli.IsRunning() {
   164  		return
   165  	}
   166  
   167  	cli.mtx.Lock()
   168  	defer cli.mtx.Unlock()
   169  	cli.reqSent.PushBack(reqres)
   170  }
   171  
   172  func (cli *socketClient) didRecvResponse(res *types.Response) error {
   173  	cli.mtx.Lock()
   174  	defer cli.mtx.Unlock()
   175  
   176  	// Get the first ReqRes.
   177  	next := cli.reqSent.Front()
   178  	if next == nil {
   179  		return fmt.Errorf("unexpected %T when nothing expected", res.Value)
   180  	}
   181  
   182  	reqres := next.Value.(*requestAndResponse)
   183  	if !resMatchesReq(reqres.Request, res) {
   184  		return fmt.Errorf("unexpected %T when response to %T expected", res.Value, reqres.Request.Value)
   185  	}
   186  
   187  	reqres.Response = res
   188  	reqres.markDone()        // release waiters
   189  	cli.reqSent.Remove(next) // pop first item from linked list
   190  
   191  	return nil
   192  }
   193  
   194  //----------------------------------------
   195  
   196  func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*types.Response, error) {
   197  	if !cli.IsRunning() {
   198  		return nil, errors.New("client has stopped")
   199  	}
   200  
   201  	reqres := makeReqRes(req)
   202  
   203  	select {
   204  	case cli.reqQueue <- reqres:
   205  	case <-ctx.Done():
   206  		return nil, fmt.Errorf("can't queue req: %w", ctx.Err())
   207  	}
   208  
   209  	select {
   210  	case <-reqres.signal:
   211  		if err := cli.Error(); err != nil {
   212  			return nil, err
   213  		}
   214  
   215  		return reqres.Response, nil
   216  	case <-ctx.Done():
   217  		return nil, ctx.Err()
   218  	}
   219  }
   220  
   221  // drainQueue marks as complete and discards all remaining pending requests
   222  // from the queue.
   223  func (cli *socketClient) drainQueue() {
   224  	cli.mtx.Lock()
   225  	defer cli.mtx.Unlock()
   226  
   227  	// mark all in-flight messages as resolved (they will get cli.Error())
   228  	for req := cli.reqSent.Front(); req != nil; req = req.Next() {
   229  		reqres := req.Value.(*requestAndResponse)
   230  		reqres.markDone()
   231  	}
   232  }
   233  
   234  //----------------------------------------
   235  
   236  func (cli *socketClient) Flush(ctx context.Context) error {
   237  	_, err := cli.doRequest(ctx, types.ToRequestFlush())
   238  	if err != nil {
   239  		return err
   240  	}
   241  	return nil
   242  }
   243  
   244  func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
   245  	res, err := cli.doRequest(ctx, types.ToRequestEcho(msg))
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	return res.GetEcho(), nil
   250  }
   251  
   252  func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
   253  	res, err := cli.doRequest(ctx, types.ToRequestInfo(req))
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	return res.GetInfo(), nil
   258  }
   259  
   260  func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
   261  	res, err := cli.doRequest(ctx, types.ToRequestCheckTx(req))
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	return res.GetCheckTx(), nil
   266  }
   267  
   268  func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
   269  	res, err := cli.doRequest(ctx, types.ToRequestQuery(req))
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	return res.GetQuery(), nil
   274  }
   275  
   276  func (cli *socketClient) Commit(ctx context.Context) (*types.ResponseCommit, error) {
   277  	res, err := cli.doRequest(ctx, types.ToRequestCommit())
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	return res.GetCommit(), nil
   282  }
   283  
   284  func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
   285  	res, err := cli.doRequest(ctx, types.ToRequestInitChain(req))
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	return res.GetInitChain(), nil
   290  }
   291  
   292  func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
   293  	res, err := cli.doRequest(ctx, types.ToRequestListSnapshots(req))
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  	return res.GetListSnapshots(), nil
   298  }
   299  
   300  func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
   301  	res, err := cli.doRequest(ctx, types.ToRequestOfferSnapshot(req))
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	return res.GetOfferSnapshot(), nil
   306  }
   307  
   308  func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
   309  	res, err := cli.doRequest(ctx, types.ToRequestLoadSnapshotChunk(req))
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  	return res.GetLoadSnapshotChunk(), nil
   314  }
   315  
   316  func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
   317  	res, err := cli.doRequest(ctx, types.ToRequestApplySnapshotChunk(req))
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	return res.GetApplySnapshotChunk(), nil
   322  }
   323  
   324  func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
   325  	res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req))
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return res.GetPrepareProposal(), nil
   330  }
   331  
   332  func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
   333  	res, err := cli.doRequest(ctx, types.ToRequestProcessProposal(req))
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	return res.GetProcessProposal(), nil
   338  }
   339  
   340  func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
   341  	res, err := cli.doRequest(ctx, types.ToRequestExtendVote(req))
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	return res.GetExtendVote(), nil
   346  }
   347  
   348  func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
   349  	res, err := cli.doRequest(ctx, types.ToRequestVerifyVoteExtension(req))
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  	return res.GetVerifyVoteExtension(), nil
   354  }
   355  
   356  func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
   357  	res, err := cli.doRequest(ctx, types.ToRequestFinalizeBlock(req))
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  	return res.GetFinalizeBlock(), nil
   362  }
   363  
   364  func (cli *socketClient) LoadLatest(ctx context.Context, req *types.RequestLoadLatest) (*types.ResponseLoadLatest, error) {
   365  	res, err := cli.doRequest(ctx, types.ToRequestLoadLatest(req))
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  	return res.GetLoadLatest(), nil
   370  }
   371  
   372  //----------------------------------------
   373  
   374  func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
   375  	switch req.Value.(type) {
   376  	case *types.Request_Echo:
   377  		_, ok = res.Value.(*types.Response_Echo)
   378  	case *types.Request_Flush:
   379  		_, ok = res.Value.(*types.Response_Flush)
   380  	case *types.Request_Info:
   381  		_, ok = res.Value.(*types.Response_Info)
   382  	case *types.Request_CheckTx:
   383  		_, ok = res.Value.(*types.Response_CheckTx)
   384  	case *types.Request_Commit:
   385  		_, ok = res.Value.(*types.Response_Commit)
   386  	case *types.Request_Query:
   387  		_, ok = res.Value.(*types.Response_Query)
   388  	case *types.Request_InitChain:
   389  		_, ok = res.Value.(*types.Response_InitChain)
   390  	case *types.Request_ProcessProposal:
   391  		_, ok = res.Value.(*types.Response_ProcessProposal)
   392  	case *types.Request_PrepareProposal:
   393  		_, ok = res.Value.(*types.Response_PrepareProposal)
   394  	case *types.Request_ExtendVote:
   395  		_, ok = res.Value.(*types.Response_ExtendVote)
   396  	case *types.Request_VerifyVoteExtension:
   397  		_, ok = res.Value.(*types.Response_VerifyVoteExtension)
   398  	case *types.Request_ApplySnapshotChunk:
   399  		_, ok = res.Value.(*types.Response_ApplySnapshotChunk)
   400  	case *types.Request_LoadSnapshotChunk:
   401  		_, ok = res.Value.(*types.Response_LoadSnapshotChunk)
   402  	case *types.Request_ListSnapshots:
   403  		_, ok = res.Value.(*types.Response_ListSnapshots)
   404  	case *types.Request_OfferSnapshot:
   405  		_, ok = res.Value.(*types.Response_OfferSnapshot)
   406  	case *types.Request_FinalizeBlock:
   407  		_, ok = res.Value.(*types.Response_FinalizeBlock)
   408  	}
   409  	return ok
   410  }
   411  
   412  func (cli *socketClient) stopForError(err error) {
   413  	if !cli.IsRunning() {
   414  		return
   415  	}
   416  
   417  	cli.mtx.Lock()
   418  	cli.err = err
   419  	cli.mtx.Unlock()
   420  
   421  	cli.logger.Info("Stopping abci.socketClient", "reason", err)
   422  	cli.Stop()
   423  }