github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/vntp2p/message.go (about)

     1  // Copyright 2019 The go-vnt Authors
     2  // This file is part of the go-vnt library.
     3  //
     4  // The go-vnt library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-vnt library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-vnt library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package vntp2p
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"io/ioutil"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	inet "github.com/libp2p/go-libp2p-net"
    30  	"github.com/vntchain/go-vnt/log"
    31  	"github.com/vntchain/go-vnt/rlp"
    32  )
    33  
    34  type MsgReadWriter interface {
    35  	MsgReader
    36  	MsgWriter
    37  }
    38  
    39  type MsgReader interface {
    40  	ReadMsg() (Msg, error)
    41  }
    42  
    43  type MsgWriter interface {
    44  	WriteMsg(Msg) error
    45  }
    46  
    47  // MessageHeaderLength define message header length
    48  const MessageHeaderLength = 5
    49  
    50  // MessageType define vnt p2p protocol message type
    51  type MessageType uint64
    52  
    53  const (
    54  	// GoodMorning say good morning protocol
    55  	GoodMorning MessageType = iota
    56  	// GoodAfternoon say good afternoon protocol
    57  	GoodAfternoon
    58  	// GoodNight say good night protocol
    59  	GoodNight
    60  )
    61  
    62  // Msg message struct
    63  type Msg struct {
    64  	Header MsgHeader
    65  	Body   MsgBody
    66  }
    67  
    68  // MsgHeader store the size of MsgBody
    69  type MsgHeader [MessageHeaderLength]byte
    70  
    71  // MsgBody message body
    72  type MsgBody struct {
    73  	ProtocolID  string //Protocol name
    74  	Type        MessageType
    75  	ReceivedAt  time.Time
    76  	PayloadSize uint32
    77  	Payload     io.Reader
    78  }
    79  
    80  // GoodMorningMsg message for goodmorning protocol
    81  type GoodMorningMsg struct {
    82  	Greet     string
    83  	Timestamp string
    84  }
    85  
    86  // HandleMessage implement VNTMessage interface
    87  func (gmm *GoodMorningMsg) HandleMessage() error {
    88  	fmt.Printf("Receive Message: greet = %s, at %s\n", gmm.Greet, gmm.Timestamp)
    89  	return nil
    90  }
    91  
    92  // Send is used to send message payload with specific messge type
    93  func Send(w MsgWriter, protocolID string, msgType MessageType, data interface{}) error {
    94  	// 还是要使用rlp进行序列化,因为类型多变,rlp已经有完整的支持
    95  	log.Info("Send message", "type", msgType)
    96  	size, r, err := rlp.EncodeToReader(data)
    97  	if err != nil {
    98  		log.Error("Send()", "rlp encode error", err)
    99  		return err
   100  	}
   101  
   102  	msgBody := MsgBody{
   103  		ProtocolID:  protocolID,
   104  		Type:        msgType,
   105  		PayloadSize: uint32(size),
   106  		Payload:     r,
   107  	}
   108  	msgBodyByte, err := json.Marshal(msgBody)
   109  	if err != nil {
   110  		log.Error("Send()", "marshal msgBody error", err)
   111  		return err
   112  	}
   113  	msgBodySize := len(msgBodyByte)
   114  	msgHeaderByte := make([]byte, MessageHeaderLength)
   115  	binary.LittleEndian.PutUint32(msgHeaderByte, uint32(msgBodySize))
   116  
   117  	var msgHeader MsgHeader
   118  	copy(msgHeader[:], msgHeaderByte)
   119  
   120  	msg := Msg{
   121  		Header: msgHeader,
   122  		Body:   msgBody,
   123  	}
   124  
   125  	return w.WriteMsg(msg)
   126  }
   127  
   128  // SendItems can send many payload in one function call
   129  func SendItems(w MsgWriter, protocolID string, msgType MessageType, elems ...interface{}) error {
   130  	return Send(w, protocolID, msgType, elems)
   131  }
   132  
   133  // Decode using json unmarshal decode msg payload
   134  func (msg Msg) Decode(val interface{}) error {
   135  	s := rlp.NewStream(msg.Body.Payload, uint64(msg.Body.PayloadSize))
   136  	err := s.Decode(val)
   137  	if err != nil {
   138  		log.Error("Decode()", "err", err, "message type", msg.Body.Type, "payload size", msg.Body.PayloadSize)
   139  		return err
   140  	}
   141  	return nil
   142  }
   143  
   144  // GetBodySize get message body size in uint32
   145  func (msg *Msg) GetBodySize() uint32 {
   146  	header := msg.Header
   147  	bodySize := binary.LittleEndian.Uint32(header[:])
   148  	return bodySize
   149  }
   150  
   151  // VNTMsger vnt chain message readwriter
   152  type VNTMsger struct {
   153  	protocol Protocol
   154  	in       chan Msg
   155  	err      chan error
   156  	w        inet.Stream
   157  	peer     *Peer
   158  }
   159  
   160  // WriteMsg implement MsgReadWriter interface
   161  func (rw *VNTMsger) WriteMsg(msg Msg) (err error) {
   162  	msgHeaderByte := msg.Header[:]
   163  	msgBodyByte, err := json.Marshal(msg.Body)
   164  	if err != nil {
   165  		rw.peer.log.Warn("Write message", "marshal msgbody error", err)
   166  		return err
   167  	}
   168  	m := append(msgHeaderByte, msgBodyByte...)
   169  
   170  	_, err = rw.w.Write(m)
   171  	if err != nil {
   172  		rw.peer.log.Warn("Write message", "write msg error", err)
   173  		if atomic.LoadInt32(&rw.peer.reseted) == 0 {
   174  			rw.peer.log.Info("Write message", "underlay will close this connection which remotePID", rw.peer.RemoteID())
   175  			rw.peer.sendError(err)
   176  		}
   177  		rw.peer.log.Trace("Write message exit", "peer", rw.peer.RemoteID())
   178  		return err
   179  	}
   180  	return nil
   181  }
   182  
   183  // ReadMsg implement MsgReadWriter interface
   184  func (rw *VNTMsger) ReadMsg() (Msg, error) {
   185  	select {
   186  	case msg := <-rw.in:
   187  		return msg, nil
   188  	case err := <-rw.err:
   189  		return Msg{}, err
   190  	case <-rw.peer.server.quit:
   191  		rw.peer.log.Info("P2P server is being closed, no longer read message...")
   192  		return Msg{}, errServerStopped
   193  	}
   194  }
   195  
   196  // ExpectMsg COMMENT: this function is just for _test.go files
   197  // ExpectMsg reads a message from r and verifies that its
   198  // code and encoded RLP content match the provided values.
   199  // If content is nil, the payload is discarded and not verified.
   200  func ExpectMsg(r MsgReader, code MessageType, content interface{}) error {
   201  	msg, err := r.ReadMsg()
   202  	if err != nil {
   203  		return err
   204  	}
   205  	if msg.Body.Type != code {
   206  		return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Body.Type, code)
   207  	}
   208  	if content == nil {
   209  		return nil
   210  	}
   211  	contentEnc, err := rlp.EncodeToBytes(content)
   212  	if err != nil {
   213  		panic("content encode error: " + err.Error())
   214  	}
   215  	if int(msg.Body.PayloadSize) != len(contentEnc) {
   216  		return fmt.Errorf("message size mismatch: got %d, want %d", msg.Body.PayloadSize, len(contentEnc))
   217  	}
   218  	actualContent, err := ioutil.ReadAll(msg.Body.Payload)
   219  	if err != nil {
   220  		return err
   221  	}
   222  	if !bytes.Equal(actualContent, contentEnc) {
   223  		return fmt.Errorf("message payload mismatch:\ngot:  %x\nwant: %x", actualContent, contentEnc)
   224  	}
   225  	return nil
   226  }