github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/message.go (about) 1 package p2p 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "math/big" 12 "net" 13 "sync" 14 "sync/atomic" 15 "time" 16 17 "github.com/jonasnick/go-ethereum/ethutil" 18 "github.com/jonasnick/go-ethereum/rlp" 19 ) 20 21 // parameters for frameRW 22 const ( 23 // maximum time allowed for reading a message header. 24 // this is effectively the amount of time a connection can be idle. 25 frameReadTimeout = 1 * time.Minute 26 27 // maximum time allowed for reading the payload data of a message. 28 // this is shorter than (and distinct from) frameReadTimeout because 29 // the connection is not considered idle while a message is transferred. 30 // this also limits the payload size of messages to how much the connection 31 // can transfer within the timeout. 32 payloadReadTimeout = 5 * time.Second 33 34 // maximum amount of time allowed for writing a complete message. 35 msgWriteTimeout = 5 * time.Second 36 37 // messages smaller than this many bytes will be read at 38 // once before passing them to a protocol. this increases 39 // concurrency in the processing. 40 wholePayloadSize = 64 * 1024 41 ) 42 43 // Msg defines the structure of a p2p message. 44 // 45 // Note that a Msg can only be sent once since the Payload reader is 46 // consumed during sending. It is not possible to create a Msg and 47 // send it any number of times. If you want to reuse an encoded 48 // structure, encode the payload into a byte array and create a 49 // separate Msg with a bytes.Reader as Payload for each send. 50 type Msg struct { 51 Code uint64 52 Size uint32 // size of the paylod 53 Payload io.Reader 54 } 55 56 // NewMsg creates an RLP-encoded message with the given code. 57 func NewMsg(code uint64, params ...interface{}) Msg { 58 buf := new(bytes.Buffer) 59 for _, p := range params { 60 buf.Write(ethutil.Encode(p)) 61 } 62 return Msg{Code: code, Size: uint32(buf.Len()), Payload: buf} 63 } 64 65 func encodePayload(params ...interface{}) []byte { 66 buf := new(bytes.Buffer) 67 for _, p := range params { 68 buf.Write(ethutil.Encode(p)) 69 } 70 return buf.Bytes() 71 } 72 73 // Decode parse the RLP content of a message into 74 // the given value, which must be a pointer. 75 // 76 // For the decoding rules, please see package rlp. 77 func (msg Msg) Decode(val interface{}) error { 78 s := rlp.NewListStream(msg.Payload, uint64(msg.Size)) 79 if err := s.Decode(val); err != nil { 80 return newPeerError(errInvalidMsg, "(code %#x) (size %d) %v", msg.Code, msg.Size, err) 81 } 82 return nil 83 } 84 85 func (msg Msg) String() string { 86 return fmt.Sprintf("msg #%v (%v bytes)", msg.Code, msg.Size) 87 } 88 89 // Discard reads any remaining payload data into a black hole. 90 func (msg Msg) Discard() error { 91 _, err := io.Copy(ioutil.Discard, msg.Payload) 92 return err 93 } 94 95 type MsgReader interface { 96 ReadMsg() (Msg, error) 97 } 98 99 type MsgWriter interface { 100 // WriteMsg sends a message. It will block until the message's 101 // Payload has been consumed by the other end. 102 // 103 // Note that messages can be sent only once because their 104 // payload reader is drained. 105 WriteMsg(Msg) error 106 } 107 108 // MsgReadWriter provides reading and writing of encoded messages. 109 // Implementations should ensure that ReadMsg and WriteMsg can be 110 // called simultaneously from multiple goroutines. 111 type MsgReadWriter interface { 112 MsgReader 113 MsgWriter 114 } 115 116 // EncodeMsg writes an RLP-encoded message with the given code and 117 // data elements. 118 func EncodeMsg(w MsgWriter, code uint64, data ...interface{}) error { 119 return w.WriteMsg(NewMsg(code, data...)) 120 } 121 122 // frameRW is a MsgReadWriter that reads and writes devp2p message frames. 123 // As required by the interface, ReadMsg and WriteMsg can be called from 124 // multiple goroutines. 125 type frameRW struct { 126 net.Conn // make Conn methods available. be careful. 127 bufconn *bufio.ReadWriter 128 129 // this channel is used to 'lend' bufconn to a caller of ReadMsg 130 // until the message payload has been consumed. the channel 131 // receives a value when EOF is reached on the payload, unblocking 132 // a pending call to ReadMsg. 133 rsync chan struct{} 134 135 // this mutex guards writes to bufconn. 136 writeMu sync.Mutex 137 } 138 139 func newFrameRW(conn net.Conn, timeout time.Duration) *frameRW { 140 rsync := make(chan struct{}, 1) 141 rsync <- struct{}{} 142 return &frameRW{ 143 Conn: conn, 144 bufconn: bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)), 145 rsync: rsync, 146 } 147 } 148 149 var magicToken = []byte{34, 64, 8, 145} 150 151 func (rw *frameRW) WriteMsg(msg Msg) error { 152 rw.writeMu.Lock() 153 defer rw.writeMu.Unlock() 154 rw.SetWriteDeadline(time.Now().Add(msgWriteTimeout)) 155 if err := writeMsg(rw.bufconn, msg); err != nil { 156 return err 157 } 158 return rw.bufconn.Flush() 159 } 160 161 func writeMsg(w io.Writer, msg Msg) error { 162 // TODO: handle case when Size + len(code) + len(listhdr) overflows uint32 163 code := ethutil.Encode(uint32(msg.Code)) 164 listhdr := makeListHeader(msg.Size + uint32(len(code))) 165 payloadLen := uint32(len(listhdr)) + uint32(len(code)) + msg.Size 166 167 start := make([]byte, 8) 168 copy(start, magicToken) 169 binary.BigEndian.PutUint32(start[4:], payloadLen) 170 171 for _, b := range [][]byte{start, listhdr, code} { 172 if _, err := w.Write(b); err != nil { 173 return err 174 } 175 } 176 _, err := io.CopyN(w, msg.Payload, int64(msg.Size)) 177 return err 178 } 179 180 func makeListHeader(length uint32) []byte { 181 if length < 56 { 182 return []byte{byte(length + 0xc0)} 183 } 184 enc := big.NewInt(int64(length)).Bytes() 185 lenb := byte(len(enc)) + 0xf7 186 return append([]byte{lenb}, enc...) 187 } 188 189 func (rw *frameRW) ReadMsg() (msg Msg, err error) { 190 <-rw.rsync // wait until bufconn is ours 191 192 rw.SetReadDeadline(time.Now().Add(frameReadTimeout)) 193 194 // read magic and payload size 195 start := make([]byte, 8) 196 if _, err = io.ReadFull(rw.bufconn, start); err != nil { 197 return msg, err 198 } 199 if !bytes.HasPrefix(start, magicToken) { 200 return msg, fmt.Errorf("bad magic token %x", start[:4], magicToken) 201 } 202 size := binary.BigEndian.Uint32(start[4:]) 203 204 // decode start of RLP message to get the message code 205 posr := &postrack{rw.bufconn, 0} 206 s := rlp.NewStream(posr) 207 if _, err := s.List(); err != nil { 208 return msg, err 209 } 210 msg.Code, err = s.Uint() 211 if err != nil { 212 return msg, err 213 } 214 msg.Size = size - posr.p 215 216 rw.SetReadDeadline(time.Now().Add(payloadReadTimeout)) 217 218 if msg.Size <= wholePayloadSize { 219 // msg is small, read all of it and move on to the next message. 220 pbuf := make([]byte, msg.Size) 221 if _, err := io.ReadFull(rw.bufconn, pbuf); err != nil { 222 return msg, err 223 } 224 rw.rsync <- struct{}{} // bufconn is available again 225 msg.Payload = bytes.NewReader(pbuf) 226 } else { 227 // lend bufconn to the caller until it has 228 // consumed the payload. eofSignal will send a value 229 // on rw.rsync when EOF is reached. 230 pr := &eofSignal{rw.bufconn, msg.Size, rw.rsync} 231 msg.Payload = pr 232 } 233 return msg, nil 234 } 235 236 // postrack wraps an rlp.ByteReader with a position counter. 237 type postrack struct { 238 r rlp.ByteReader 239 p uint32 240 } 241 242 func (r *postrack) Read(buf []byte) (int, error) { 243 n, err := r.r.Read(buf) 244 r.p += uint32(n) 245 return n, err 246 } 247 248 func (r *postrack) ReadByte() (byte, error) { 249 b, err := r.r.ReadByte() 250 if err == nil { 251 r.p++ 252 } 253 return b, err 254 } 255 256 // eofSignal wraps a reader with eof signaling. the eof channel is 257 // closed when the wrapped reader returns an error or when count bytes 258 // have been read. 259 type eofSignal struct { 260 wrapped io.Reader 261 count uint32 // number of bytes left 262 eof chan<- struct{} 263 } 264 265 // note: when using eofSignal to detect whether a message payload 266 // has been read, Read might not be called for zero sized messages. 267 func (r *eofSignal) Read(buf []byte) (int, error) { 268 if r.count == 0 { 269 if r.eof != nil { 270 r.eof <- struct{}{} 271 r.eof = nil 272 } 273 return 0, io.EOF 274 } 275 276 max := len(buf) 277 if int(r.count) < len(buf) { 278 max = int(r.count) 279 } 280 n, err := r.wrapped.Read(buf[:max]) 281 r.count -= uint32(n) 282 if (err != nil || r.count == 0) && r.eof != nil { 283 r.eof <- struct{}{} // tell Peer that msg has been consumed 284 r.eof = nil 285 } 286 return n, err 287 } 288 289 // MsgPipe creates a message pipe. Reads on one end are matched 290 // with writes on the other. The pipe is full-duplex, both ends 291 // implement MsgReadWriter. 292 func MsgPipe() (*MsgPipeRW, *MsgPipeRW) { 293 var ( 294 c1, c2 = make(chan Msg), make(chan Msg) 295 closing = make(chan struct{}) 296 closed = new(int32) 297 rw1 = &MsgPipeRW{c1, c2, closing, closed} 298 rw2 = &MsgPipeRW{c2, c1, closing, closed} 299 ) 300 return rw1, rw2 301 } 302 303 // ErrPipeClosed is returned from pipe operations after the 304 // pipe has been closed. 305 var ErrPipeClosed = errors.New("p2p: read or write on closed message pipe") 306 307 // MsgPipeRW is an endpoint of a MsgReadWriter pipe. 308 type MsgPipeRW struct { 309 w chan<- Msg 310 r <-chan Msg 311 closing chan struct{} 312 closed *int32 313 } 314 315 // WriteMsg sends a messsage on the pipe. 316 // It blocks until the receiver has consumed the message payload. 317 func (p *MsgPipeRW) WriteMsg(msg Msg) error { 318 if atomic.LoadInt32(p.closed) == 0 { 319 consumed := make(chan struct{}, 1) 320 msg.Payload = &eofSignal{msg.Payload, msg.Size, consumed} 321 select { 322 case p.w <- msg: 323 if msg.Size > 0 { 324 // wait for payload read or discard 325 <-consumed 326 } 327 return nil 328 case <-p.closing: 329 } 330 } 331 return ErrPipeClosed 332 } 333 334 // ReadMsg returns a message sent on the other end of the pipe. 335 func (p *MsgPipeRW) ReadMsg() (Msg, error) { 336 if atomic.LoadInt32(p.closed) == 0 { 337 select { 338 case msg := <-p.r: 339 return msg, nil 340 case <-p.closing: 341 } 342 } 343 return Msg{}, ErrPipeClosed 344 } 345 346 // Close unblocks any pending ReadMsg and WriteMsg calls on both ends 347 // of the pipe. They will return ErrPipeClosed. Note that Close does 348 // not interrupt any reads from a message payload. 349 func (p *MsgPipeRW) Close() error { 350 if atomic.AddInt32(p.closed, 1) != 1 { 351 // someone else is already closing 352 atomic.StoreInt32(p.closed, 1) // avoid overflow 353 return nil 354 } 355 close(p.closing) 356 return nil 357 }