github.com/zhouxv/fabric@v2.1.1+incompatible/core/chaincode/mock/mockccstream.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package mock
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  	"time"
    13  
    14  	pb "github.com/hyperledger/fabric-protos-go/peer"
    15  )
    16  
    17  //MockResponseSet is used for processing CC to Peer comm
    18  //such as GET/PUT/DEL state. The MockResponse contains the
    19  //response to be returned for each input received.from the
    20  //CC. Every stub call will generate a response
    21  type MockResponseSet struct {
    22  	//DoneFunc is invoked when all I/O is done for this
    23  	//response set
    24  	DoneFunc func(int, error)
    25  
    26  	//ErrorFunc is invoked at any step when the input does not
    27  	//match the received message
    28  	ErrorFunc func(int, error)
    29  
    30  	//Responses contained the expected received message (optional)
    31  	//and response to send (optional)
    32  	Responses []*MockResponse
    33  }
    34  
    35  //MockResponse contains the expected received message (optional)
    36  //and response to send (optional)
    37  type MockResponse struct {
    38  	RecvMsg *pb.ChaincodeMessage
    39  	RespMsg interface{}
    40  }
    41  
    42  // MockCCComm implements the mock communication between chaincode and peer
    43  // We'd need two MockCCComm for communication. The receiver and sender will
    44  // be switched between the two.
    45  type MockCCComm struct {
    46  	name        string
    47  	bailOnError bool
    48  	keepAlive   *pb.ChaincodeMessage
    49  	recvStream  chan *pb.ChaincodeMessage
    50  	sendStream  chan *pb.ChaincodeMessage
    51  	respIndex   int
    52  	respLock    sync.Mutex
    53  	respSet     *MockResponseSet
    54  	pong        bool
    55  	skipClose   bool
    56  }
    57  
    58  func (s *MockCCComm) SetName(newname string) {
    59  	s.name = newname
    60  }
    61  
    62  //Send sends a message
    63  func (s *MockCCComm) Send(msg *pb.ChaincodeMessage) error {
    64  	s.sendStream <- msg
    65  	return nil
    66  }
    67  
    68  //Recv receives a message
    69  func (s *MockCCComm) Recv() (*pb.ChaincodeMessage, error) {
    70  	msg := <-s.recvStream
    71  	return msg, nil
    72  }
    73  
    74  //CloseSend closes send
    75  func (s *MockCCComm) CloseSend() error {
    76  	return nil
    77  }
    78  
    79  //GetRecvStream returns the recvStream
    80  func (s *MockCCComm) GetRecvStream() chan *pb.ChaincodeMessage {
    81  	return s.recvStream
    82  }
    83  
    84  //GetSendStream returns the sendStream
    85  func (s *MockCCComm) GetSendStream() chan *pb.ChaincodeMessage {
    86  	return s.sendStream
    87  }
    88  
    89  //Quit closes the channels...
    90  func (s *MockCCComm) Quit() {
    91  	if !s.skipClose {
    92  		close(s.recvStream)
    93  		close(s.sendStream)
    94  	}
    95  }
    96  
    97  //SetBailOnError will cause Run to return on any error
    98  func (s *MockCCComm) SetBailOnError(b bool) {
    99  	s.bailOnError = b
   100  }
   101  
   102  //SetPong pongs received keepalive. This mut be done on the chaincode only
   103  func (s *MockCCComm) SetPong(val bool) {
   104  	s.pong = val
   105  }
   106  
   107  //SetKeepAlive sets keepalive. This mut be done on the server only
   108  func (s *MockCCComm) SetKeepAlive(ka *pb.ChaincodeMessage) {
   109  	s.keepAlive = ka
   110  }
   111  
   112  //SetResponses sets responses for an Init or Invoke
   113  func (s *MockCCComm) SetResponses(respSet *MockResponseSet) {
   114  	s.respLock.Lock()
   115  	s.respSet = respSet
   116  	s.respIndex = 0
   117  	s.respLock.Unlock()
   118  }
   119  
   120  //keepAlive
   121  func (s *MockCCComm) ka(done <-chan struct{}) {
   122  	for {
   123  		if s.keepAlive == nil {
   124  			return
   125  		}
   126  		s.Send(s.keepAlive)
   127  		select {
   128  		case <-time.After(10 * time.Millisecond):
   129  		case <-done:
   130  			return
   131  		}
   132  	}
   133  }
   134  
   135  //Run receives and sends indefinitely
   136  func (s *MockCCComm) Run(done <-chan struct{}) error {
   137  	//start the keepalive
   138  	go s.ka(done)
   139  	defer s.Quit()
   140  
   141  	for {
   142  		msg, err := s.Recv()
   143  
   144  		//stream could just be closed
   145  		if msg == nil {
   146  			return err
   147  		}
   148  
   149  		if err != nil {
   150  			return err
   151  		}
   152  
   153  		if err = s.respond(msg); err != nil {
   154  			if s.bailOnError {
   155  				return err
   156  			}
   157  		}
   158  	}
   159  }
   160  
   161  func (s *MockCCComm) respond(msg *pb.ChaincodeMessage) error {
   162  	if msg != nil && msg.Type == pb.ChaincodeMessage_KEEPALIVE {
   163  		//if ping should be ponged, pong
   164  		if s.pong {
   165  			return s.Send(msg)
   166  		}
   167  		return nil
   168  	}
   169  
   170  	s.respLock.Lock()
   171  	defer s.respLock.Unlock()
   172  
   173  	var err error
   174  	if s.respIndex < len(s.respSet.Responses) {
   175  		mockResp := s.respSet.Responses[s.respIndex]
   176  		if mockResp.RecvMsg != nil {
   177  			if msg.Type != mockResp.RecvMsg.Type {
   178  				if s.respSet.ErrorFunc != nil {
   179  					s.respSet.ErrorFunc(s.respIndex, fmt.Errorf("Invalid message expected %d received %d", int32(mockResp.RecvMsg.Type), int32(msg.Type)))
   180  					s.respIndex = s.respIndex + 1
   181  					return nil
   182  				}
   183  			}
   184  		}
   185  
   186  		if mockResp.RespMsg != nil {
   187  			var ccMsg *pb.ChaincodeMessage
   188  			if ccMsg, _ = mockResp.RespMsg.(*pb.ChaincodeMessage); ccMsg == nil {
   189  				if ccMsgFunc, ok := mockResp.RespMsg.(func(*pb.ChaincodeMessage) *pb.ChaincodeMessage); ok && ccMsgFunc != nil {
   190  					ccMsg = ccMsgFunc(msg)
   191  				}
   192  			}
   193  
   194  			if ccMsg == nil {
   195  				panic("----no pb.ChaincodeMessage---")
   196  			}
   197  			err = s.Send(ccMsg)
   198  		}
   199  
   200  		s.respIndex = s.respIndex + 1
   201  
   202  		if s.respIndex == len(s.respSet.Responses) {
   203  			if s.respSet.DoneFunc != nil {
   204  				s.respSet.DoneFunc(s.respIndex, nil)
   205  			}
   206  		}
   207  	}
   208  	return err
   209  }