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 }