github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/conn/connection.go (about)

     1  package conn
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"math"
     9  	"net"
    10  	"reflect"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	amino "github.com/evdatsion/go-amino"
    16  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    17  	flow "github.com/evdatsion/aphelion-dpos-bft/libs/flowrate"
    18  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    19  )
    20  
    21  const (
    22  	defaultMaxPacketMsgPayloadSize = 1024
    23  
    24  	numBatchPacketMsgs = 10
    25  	minReadBufferSize  = 1024
    26  	minWriteBufferSize = 65536
    27  	updateStats        = 2 * time.Second
    28  
    29  	// some of these defaults are written in the user config
    30  	// flushThrottle, sendRate, recvRate
    31  	// TODO: remove values present in config
    32  	defaultFlushThrottle = 100 * time.Millisecond
    33  
    34  	defaultSendQueueCapacity   = 1
    35  	defaultRecvBufferCapacity  = 4096
    36  	defaultRecvMessageCapacity = 22020096      // 21MB
    37  	defaultSendRate            = int64(512000) // 500KB/s
    38  	defaultRecvRate            = int64(512000) // 500KB/s
    39  	defaultSendTimeout         = 10 * time.Second
    40  	defaultPingInterval        = 60 * time.Second
    41  	defaultPongTimeout         = 45 * time.Second
    42  )
    43  
    44  type receiveCbFunc func(chID byte, msgBytes []byte)
    45  type errorCbFunc func(interface{})
    46  
    47  /*
    48  Each peer has one `MConnection` (multiplex connection) instance.
    49  
    50  __multiplex__ *noun* a system or signal involving simultaneous transmission of
    51  several messages along a single channel of communication.
    52  
    53  Each `MConnection` handles message transmission on multiple abstract communication
    54  `Channel`s.  Each channel has a globally unique byte id.
    55  The byte id and the relative priorities of each `Channel` are configured upon
    56  initialization of the connection.
    57  
    58  There are two methods for sending messages:
    59  	func (m MConnection) Send(chID byte, msgBytes []byte) bool {}
    60  	func (m MConnection) TrySend(chID byte, msgBytes []byte}) bool {}
    61  
    62  `Send(chID, msgBytes)` is a blocking call that waits until `msg` is
    63  successfully queued for the channel with the given id byte `chID`, or until the
    64  request times out.  The message `msg` is serialized using Go-Amino.
    65  
    66  `TrySend(chID, msgBytes)` is a nonblocking call that returns false if the
    67  channel's queue is full.
    68  
    69  Inbound message bytes are handled with an onReceive callback function.
    70  */
    71  type MConnection struct {
    72  	cmn.BaseService
    73  
    74  	conn          net.Conn
    75  	bufConnReader *bufio.Reader
    76  	bufConnWriter *bufio.Writer
    77  	sendMonitor   *flow.Monitor
    78  	recvMonitor   *flow.Monitor
    79  	send          chan struct{}
    80  	pong          chan struct{}
    81  	channels      []*Channel
    82  	channelsIdx   map[byte]*Channel
    83  	onReceive     receiveCbFunc
    84  	onError       errorCbFunc
    85  	errored       uint32
    86  	config        MConnConfig
    87  
    88  	// Closing quitSendRoutine will cause the sendRoutine to eventually quit.
    89  	// doneSendRoutine is closed when the sendRoutine actually quits.
    90  	quitSendRoutine chan struct{}
    91  	doneSendRoutine chan struct{}
    92  
    93  	// used to ensure FlushStop and OnStop
    94  	// are safe to call concurrently.
    95  	stopMtx sync.Mutex
    96  
    97  	flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled.
    98  	pingTimer  *time.Ticker       // send pings periodically
    99  
   100  	// close conn if pong is not received in pongTimeout
   101  	pongTimer     *time.Timer
   102  	pongTimeoutCh chan bool // true - timeout, false - peer sent pong
   103  
   104  	chStatsTimer *time.Ticker // update channel stats periodically
   105  
   106  	created time.Time // time of creation
   107  
   108  	_maxPacketMsgSize int
   109  }
   110  
   111  // MConnConfig is a MConnection configuration.
   112  type MConnConfig struct {
   113  	SendRate int64 `mapstructure:"send_rate"`
   114  	RecvRate int64 `mapstructure:"recv_rate"`
   115  
   116  	// Maximum payload size
   117  	MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"`
   118  
   119  	// Interval to flush writes (throttled)
   120  	FlushThrottle time.Duration `mapstructure:"flush_throttle"`
   121  
   122  	// Interval to send pings
   123  	PingInterval time.Duration `mapstructure:"ping_interval"`
   124  
   125  	// Maximum wait time for pongs
   126  	PongTimeout time.Duration `mapstructure:"pong_timeout"`
   127  }
   128  
   129  // DefaultMConnConfig returns the default config.
   130  func DefaultMConnConfig() MConnConfig {
   131  	return MConnConfig{
   132  		SendRate:                defaultSendRate,
   133  		RecvRate:                defaultRecvRate,
   134  		MaxPacketMsgPayloadSize: defaultMaxPacketMsgPayloadSize,
   135  		FlushThrottle:           defaultFlushThrottle,
   136  		PingInterval:            defaultPingInterval,
   137  		PongTimeout:             defaultPongTimeout,
   138  	}
   139  }
   140  
   141  // NewMConnection wraps net.Conn and creates multiplex connection
   142  func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc) *MConnection {
   143  	return NewMConnectionWithConfig(
   144  		conn,
   145  		chDescs,
   146  		onReceive,
   147  		onError,
   148  		DefaultMConnConfig())
   149  }
   150  
   151  // NewMConnectionWithConfig wraps net.Conn and creates multiplex connection with a config
   152  func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc, config MConnConfig) *MConnection {
   153  	if config.PongTimeout >= config.PingInterval {
   154  		panic("pongTimeout must be less than pingInterval (otherwise, next ping will reset pong timer)")
   155  	}
   156  
   157  	mconn := &MConnection{
   158  		conn:          conn,
   159  		bufConnReader: bufio.NewReaderSize(conn, minReadBufferSize),
   160  		bufConnWriter: bufio.NewWriterSize(conn, minWriteBufferSize),
   161  		sendMonitor:   flow.New(0, 0),
   162  		recvMonitor:   flow.New(0, 0),
   163  		send:          make(chan struct{}, 1),
   164  		pong:          make(chan struct{}, 1),
   165  		onReceive:     onReceive,
   166  		onError:       onError,
   167  		config:        config,
   168  		created:       time.Now(),
   169  	}
   170  
   171  	// Create channels
   172  	var channelsIdx = map[byte]*Channel{}
   173  	var channels = []*Channel{}
   174  
   175  	for _, desc := range chDescs {
   176  		channel := newChannel(mconn, *desc)
   177  		channelsIdx[channel.desc.ID] = channel
   178  		channels = append(channels, channel)
   179  	}
   180  	mconn.channels = channels
   181  	mconn.channelsIdx = channelsIdx
   182  
   183  	mconn.BaseService = *cmn.NewBaseService(nil, "MConnection", mconn)
   184  
   185  	// maxPacketMsgSize() is a bit heavy, so call just once
   186  	mconn._maxPacketMsgSize = mconn.maxPacketMsgSize()
   187  
   188  	return mconn
   189  }
   190  
   191  func (c *MConnection) SetLogger(l log.Logger) {
   192  	c.BaseService.SetLogger(l)
   193  	for _, ch := range c.channels {
   194  		ch.SetLogger(l)
   195  	}
   196  }
   197  
   198  // OnStart implements BaseService
   199  func (c *MConnection) OnStart() error {
   200  	if err := c.BaseService.OnStart(); err != nil {
   201  		return err
   202  	}
   203  	c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle)
   204  	c.pingTimer = time.NewTicker(c.config.PingInterval)
   205  	c.pongTimeoutCh = make(chan bool, 1)
   206  	c.chStatsTimer = time.NewTicker(updateStats)
   207  	c.quitSendRoutine = make(chan struct{})
   208  	c.doneSendRoutine = make(chan struct{})
   209  	go c.sendRoutine()
   210  	go c.recvRoutine()
   211  	return nil
   212  }
   213  
   214  // stopServices stops the BaseService and timers and closes the quitSendRoutine.
   215  // if the quitSendRoutine was already closed, it returns true, otherwise it returns false.
   216  // It uses the stopMtx to ensure only one of FlushStop and OnStop can do this at a time.
   217  func (c *MConnection) stopServices() (alreadyStopped bool) {
   218  	c.stopMtx.Lock()
   219  	defer c.stopMtx.Unlock()
   220  
   221  	select {
   222  	case <-c.quitSendRoutine:
   223  		// already quit via FlushStop or OnStop
   224  		return true
   225  	default:
   226  	}
   227  
   228  	c.BaseService.OnStop()
   229  	c.flushTimer.Stop()
   230  	c.pingTimer.Stop()
   231  	c.chStatsTimer.Stop()
   232  
   233  	close(c.quitSendRoutine)
   234  	return false
   235  }
   236  
   237  // FlushStop replicates the logic of OnStop.
   238  // It additionally ensures that all successful
   239  // .Send() calls will get flushed before closing
   240  // the connection.
   241  func (c *MConnection) FlushStop() {
   242  	if c.stopServices() {
   243  		return
   244  	}
   245  
   246  	// this block is unique to FlushStop
   247  	{
   248  		// wait until the sendRoutine exits
   249  		// so we dont race on calling sendSomePacketMsgs
   250  		<-c.doneSendRoutine
   251  
   252  		// Send and flush all pending msgs.
   253  		// By now, IsRunning == false,
   254  		// so any concurrent attempts to send will fail.
   255  		// Since sendRoutine has exited, we can call this
   256  		// safely
   257  		eof := c.sendSomePacketMsgs()
   258  		for !eof {
   259  			eof = c.sendSomePacketMsgs()
   260  		}
   261  		c.flush()
   262  
   263  		// Now we can close the connection
   264  	}
   265  
   266  	c.conn.Close() // nolint: errcheck
   267  
   268  	// We can't close pong safely here because
   269  	// recvRoutine may write to it after we've stopped.
   270  	// Though it doesn't need to get closed at all,
   271  	// we close it @ recvRoutine.
   272  
   273  	// c.Stop()
   274  }
   275  
   276  // OnStop implements BaseService
   277  func (c *MConnection) OnStop() {
   278  	if c.stopServices() {
   279  		return
   280  	}
   281  
   282  	c.conn.Close() // nolint: errcheck
   283  
   284  	// We can't close pong safely here because
   285  	// recvRoutine may write to it after we've stopped.
   286  	// Though it doesn't need to get closed at all,
   287  	// we close it @ recvRoutine.
   288  }
   289  
   290  func (c *MConnection) String() string {
   291  	return fmt.Sprintf("MConn{%v}", c.conn.RemoteAddr())
   292  }
   293  
   294  func (c *MConnection) flush() {
   295  	c.Logger.Debug("Flush", "conn", c)
   296  	err := c.bufConnWriter.Flush()
   297  	if err != nil {
   298  		c.Logger.Error("MConnection flush failed", "err", err)
   299  	}
   300  }
   301  
   302  // Catch panics, usually caused by remote disconnects.
   303  func (c *MConnection) _recover() {
   304  	if r := recover(); r != nil {
   305  		err := cmn.ErrorWrap(r, "recovered panic in MConnection")
   306  		c.stopForError(err)
   307  	}
   308  }
   309  
   310  func (c *MConnection) stopForError(r interface{}) {
   311  	c.Stop()
   312  	if atomic.CompareAndSwapUint32(&c.errored, 0, 1) {
   313  		if c.onError != nil {
   314  			c.onError(r)
   315  		}
   316  	}
   317  }
   318  
   319  // Queues a message to be sent to channel.
   320  func (c *MConnection) Send(chID byte, msgBytes []byte) bool {
   321  	if !c.IsRunning() {
   322  		return false
   323  	}
   324  
   325  	c.Logger.Debug("Send", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes))
   326  
   327  	// Send message to channel.
   328  	channel, ok := c.channelsIdx[chID]
   329  	if !ok {
   330  		c.Logger.Error(fmt.Sprintf("Cannot send bytes, unknown channel %X", chID))
   331  		return false
   332  	}
   333  
   334  	success := channel.sendBytes(msgBytes)
   335  	if success {
   336  		// Wake up sendRoutine if necessary
   337  		select {
   338  		case c.send <- struct{}{}:
   339  		default:
   340  		}
   341  	} else {
   342  		c.Logger.Debug("Send failed", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes))
   343  	}
   344  	return success
   345  }
   346  
   347  // Queues a message to be sent to channel.
   348  // Nonblocking, returns true if successful.
   349  func (c *MConnection) TrySend(chID byte, msgBytes []byte) bool {
   350  	if !c.IsRunning() {
   351  		return false
   352  	}
   353  
   354  	c.Logger.Debug("TrySend", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes))
   355  
   356  	// Send message to channel.
   357  	channel, ok := c.channelsIdx[chID]
   358  	if !ok {
   359  		c.Logger.Error(fmt.Sprintf("Cannot send bytes, unknown channel %X", chID))
   360  		return false
   361  	}
   362  
   363  	ok = channel.trySendBytes(msgBytes)
   364  	if ok {
   365  		// Wake up sendRoutine if necessary
   366  		select {
   367  		case c.send <- struct{}{}:
   368  		default:
   369  		}
   370  	}
   371  
   372  	return ok
   373  }
   374  
   375  // CanSend returns true if you can send more data onto the chID, false
   376  // otherwise. Use only as a heuristic.
   377  func (c *MConnection) CanSend(chID byte) bool {
   378  	if !c.IsRunning() {
   379  		return false
   380  	}
   381  
   382  	channel, ok := c.channelsIdx[chID]
   383  	if !ok {
   384  		c.Logger.Error(fmt.Sprintf("Unknown channel %X", chID))
   385  		return false
   386  	}
   387  	return channel.canSend()
   388  }
   389  
   390  // sendRoutine polls for packets to send from channels.
   391  func (c *MConnection) sendRoutine() {
   392  	defer c._recover()
   393  
   394  FOR_LOOP:
   395  	for {
   396  		var _n int64
   397  		var err error
   398  	SELECTION:
   399  		select {
   400  		case <-c.flushTimer.Ch:
   401  			// NOTE: flushTimer.Set() must be called every time
   402  			// something is written to .bufConnWriter.
   403  			c.flush()
   404  		case <-c.chStatsTimer.C:
   405  			for _, channel := range c.channels {
   406  				channel.updateStats()
   407  			}
   408  		case <-c.pingTimer.C:
   409  			c.Logger.Debug("Send Ping")
   410  			_n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPing{})
   411  			if err != nil {
   412  				break SELECTION
   413  			}
   414  			c.sendMonitor.Update(int(_n))
   415  			c.Logger.Debug("Starting pong timer", "dur", c.config.PongTimeout)
   416  			c.pongTimer = time.AfterFunc(c.config.PongTimeout, func() {
   417  				select {
   418  				case c.pongTimeoutCh <- true:
   419  				default:
   420  				}
   421  			})
   422  			c.flush()
   423  		case timeout := <-c.pongTimeoutCh:
   424  			if timeout {
   425  				c.Logger.Debug("Pong timeout")
   426  				err = errors.New("pong timeout")
   427  			} else {
   428  				c.stopPongTimer()
   429  			}
   430  		case <-c.pong:
   431  			c.Logger.Debug("Send Pong")
   432  			_n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPong{})
   433  			if err != nil {
   434  				break SELECTION
   435  			}
   436  			c.sendMonitor.Update(int(_n))
   437  			c.flush()
   438  		case <-c.quitSendRoutine:
   439  			break FOR_LOOP
   440  		case <-c.send:
   441  			// Send some PacketMsgs
   442  			eof := c.sendSomePacketMsgs()
   443  			if !eof {
   444  				// Keep sendRoutine awake.
   445  				select {
   446  				case c.send <- struct{}{}:
   447  				default:
   448  				}
   449  			}
   450  		}
   451  
   452  		if !c.IsRunning() {
   453  			break FOR_LOOP
   454  		}
   455  		if err != nil {
   456  			c.Logger.Error("Connection failed @ sendRoutine", "conn", c, "err", err)
   457  			c.stopForError(err)
   458  			break FOR_LOOP
   459  		}
   460  	}
   461  
   462  	// Cleanup
   463  	c.stopPongTimer()
   464  	close(c.doneSendRoutine)
   465  }
   466  
   467  // Returns true if messages from channels were exhausted.
   468  // Blocks in accordance to .sendMonitor throttling.
   469  func (c *MConnection) sendSomePacketMsgs() bool {
   470  	// Block until .sendMonitor says we can write.
   471  	// Once we're ready we send more than we asked for,
   472  	// but amortized it should even out.
   473  	c.sendMonitor.Limit(c._maxPacketMsgSize, atomic.LoadInt64(&c.config.SendRate), true)
   474  
   475  	// Now send some PacketMsgs.
   476  	for i := 0; i < numBatchPacketMsgs; i++ {
   477  		if c.sendPacketMsg() {
   478  			return true
   479  		}
   480  	}
   481  	return false
   482  }
   483  
   484  // Returns true if messages from channels were exhausted.
   485  func (c *MConnection) sendPacketMsg() bool {
   486  	// Choose a channel to create a PacketMsg from.
   487  	// The chosen channel will be the one whose recentlySent/priority is the least.
   488  	var leastRatio float32 = math.MaxFloat32
   489  	var leastChannel *Channel
   490  	for _, channel := range c.channels {
   491  		// If nothing to send, skip this channel
   492  		if !channel.isSendPending() {
   493  			continue
   494  		}
   495  		// Get ratio, and keep track of lowest ratio.
   496  		ratio := float32(channel.recentlySent) / float32(channel.desc.Priority)
   497  		if ratio < leastRatio {
   498  			leastRatio = ratio
   499  			leastChannel = channel
   500  		}
   501  	}
   502  
   503  	// Nothing to send?
   504  	if leastChannel == nil {
   505  		return true
   506  	}
   507  	// c.Logger.Info("Found a msgPacket to send")
   508  
   509  	// Make & send a PacketMsg from this channel
   510  	_n, err := leastChannel.writePacketMsgTo(c.bufConnWriter)
   511  	if err != nil {
   512  		c.Logger.Error("Failed to write PacketMsg", "err", err)
   513  		c.stopForError(err)
   514  		return true
   515  	}
   516  	c.sendMonitor.Update(int(_n))
   517  	c.flushTimer.Set()
   518  	return false
   519  }
   520  
   521  // recvRoutine reads PacketMsgs and reconstructs the message using the channels' "recving" buffer.
   522  // After a whole message has been assembled, it's pushed to onReceive().
   523  // Blocks depending on how the connection is throttled.
   524  // Otherwise, it never blocks.
   525  func (c *MConnection) recvRoutine() {
   526  	defer c._recover()
   527  
   528  FOR_LOOP:
   529  	for {
   530  		// Block until .recvMonitor says we can read.
   531  		c.recvMonitor.Limit(c._maxPacketMsgSize, atomic.LoadInt64(&c.config.RecvRate), true)
   532  
   533  		// Peek into bufConnReader for debugging
   534  		/*
   535  			if numBytes := c.bufConnReader.Buffered(); numBytes > 0 {
   536  				bz, err := c.bufConnReader.Peek(cmn.MinInt(numBytes, 100))
   537  				if err == nil {
   538  					// return
   539  				} else {
   540  					c.Logger.Debug("Error peeking connection buffer", "err", err)
   541  					// return nil
   542  				}
   543  				c.Logger.Info("Peek connection buffer", "numBytes", numBytes, "bz", bz)
   544  			}
   545  		*/
   546  
   547  		// Read packet type
   548  		var packet Packet
   549  		var _n int64
   550  		var err error
   551  		_n, err = cdc.UnmarshalBinaryLengthPrefixedReader(c.bufConnReader, &packet, int64(c._maxPacketMsgSize))
   552  		c.recvMonitor.Update(int(_n))
   553  		if err != nil {
   554  			if c.IsRunning() {
   555  				c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "err", err)
   556  				c.stopForError(err)
   557  			}
   558  			break FOR_LOOP
   559  		}
   560  
   561  		// Read more depending on packet type.
   562  		switch pkt := packet.(type) {
   563  		case PacketPing:
   564  			// TODO: prevent abuse, as they cause flush()'s.
   565  			// https://github.com/evdatsion/aphelion-dpos-bft/issues/1190
   566  			c.Logger.Debug("Receive Ping")
   567  			select {
   568  			case c.pong <- struct{}{}:
   569  			default:
   570  				// never block
   571  			}
   572  		case PacketPong:
   573  			c.Logger.Debug("Receive Pong")
   574  			select {
   575  			case c.pongTimeoutCh <- false:
   576  			default:
   577  				// never block
   578  			}
   579  		case PacketMsg:
   580  			channel, ok := c.channelsIdx[pkt.ChannelID]
   581  			if !ok || channel == nil {
   582  				err := fmt.Errorf("Unknown channel %X", pkt.ChannelID)
   583  				c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err)
   584  				c.stopForError(err)
   585  				break FOR_LOOP
   586  			}
   587  
   588  			msgBytes, err := channel.recvPacketMsg(pkt)
   589  			if err != nil {
   590  				if c.IsRunning() {
   591  					c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err)
   592  					c.stopForError(err)
   593  				}
   594  				break FOR_LOOP
   595  			}
   596  			if msgBytes != nil {
   597  				c.Logger.Debug("Received bytes", "chID", pkt.ChannelID, "msgBytes", fmt.Sprintf("%X", msgBytes))
   598  				// NOTE: This means the reactor.Receive runs in the same thread as the p2p recv routine
   599  				c.onReceive(pkt.ChannelID, msgBytes)
   600  			}
   601  		default:
   602  			err := fmt.Errorf("Unknown message type %v", reflect.TypeOf(packet))
   603  			c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err)
   604  			c.stopForError(err)
   605  			break FOR_LOOP
   606  		}
   607  	}
   608  
   609  	// Cleanup
   610  	close(c.pong)
   611  	for range c.pong {
   612  		// Drain
   613  	}
   614  }
   615  
   616  // not goroutine-safe
   617  func (c *MConnection) stopPongTimer() {
   618  	if c.pongTimer != nil {
   619  		_ = c.pongTimer.Stop()
   620  		c.pongTimer = nil
   621  	}
   622  }
   623  
   624  // maxPacketMsgSize returns a maximum size of PacketMsg, including the overhead
   625  // of amino encoding.
   626  func (c *MConnection) maxPacketMsgSize() int {
   627  	return len(cdc.MustMarshalBinaryLengthPrefixed(PacketMsg{
   628  		ChannelID: 0x01,
   629  		EOF:       1,
   630  		Bytes:     make([]byte, c.config.MaxPacketMsgPayloadSize),
   631  	})) + 10 // leave room for changes in amino
   632  }
   633  
   634  type ConnectionStatus struct {
   635  	Duration    time.Duration
   636  	SendMonitor flow.Status
   637  	RecvMonitor flow.Status
   638  	Channels    []ChannelStatus
   639  }
   640  
   641  type ChannelStatus struct {
   642  	ID                byte
   643  	SendQueueCapacity int
   644  	SendQueueSize     int
   645  	Priority          int
   646  	RecentlySent      int64
   647  }
   648  
   649  func (c *MConnection) Status() ConnectionStatus {
   650  	var status ConnectionStatus
   651  	status.Duration = time.Since(c.created)
   652  	status.SendMonitor = c.sendMonitor.Status()
   653  	status.RecvMonitor = c.recvMonitor.Status()
   654  	status.Channels = make([]ChannelStatus, len(c.channels))
   655  	for i, channel := range c.channels {
   656  		status.Channels[i] = ChannelStatus{
   657  			ID:                channel.desc.ID,
   658  			SendQueueCapacity: cap(channel.sendQueue),
   659  			SendQueueSize:     int(atomic.LoadInt32(&channel.sendQueueSize)),
   660  			Priority:          channel.desc.Priority,
   661  			RecentlySent:      atomic.LoadInt64(&channel.recentlySent),
   662  		}
   663  	}
   664  	return status
   665  }
   666  
   667  //-----------------------------------------------------------------------------
   668  
   669  type ChannelDescriptor struct {
   670  	ID                  byte
   671  	Priority            int
   672  	SendQueueCapacity   int
   673  	RecvBufferCapacity  int
   674  	RecvMessageCapacity int
   675  }
   676  
   677  func (chDesc ChannelDescriptor) FillDefaults() (filled ChannelDescriptor) {
   678  	if chDesc.SendQueueCapacity == 0 {
   679  		chDesc.SendQueueCapacity = defaultSendQueueCapacity
   680  	}
   681  	if chDesc.RecvBufferCapacity == 0 {
   682  		chDesc.RecvBufferCapacity = defaultRecvBufferCapacity
   683  	}
   684  	if chDesc.RecvMessageCapacity == 0 {
   685  		chDesc.RecvMessageCapacity = defaultRecvMessageCapacity
   686  	}
   687  	filled = chDesc
   688  	return
   689  }
   690  
   691  // TODO: lowercase.
   692  // NOTE: not goroutine-safe.
   693  type Channel struct {
   694  	conn          *MConnection
   695  	desc          ChannelDescriptor
   696  	sendQueue     chan []byte
   697  	sendQueueSize int32 // atomic.
   698  	recving       []byte
   699  	sending       []byte
   700  	recentlySent  int64 // exponential moving average
   701  
   702  	maxPacketMsgPayloadSize int
   703  
   704  	Logger log.Logger
   705  }
   706  
   707  func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel {
   708  	desc = desc.FillDefaults()
   709  	if desc.Priority <= 0 {
   710  		panic("Channel default priority must be a positive integer")
   711  	}
   712  	return &Channel{
   713  		conn:                    conn,
   714  		desc:                    desc,
   715  		sendQueue:               make(chan []byte, desc.SendQueueCapacity),
   716  		recving:                 make([]byte, 0, desc.RecvBufferCapacity),
   717  		maxPacketMsgPayloadSize: conn.config.MaxPacketMsgPayloadSize,
   718  	}
   719  }
   720  
   721  func (ch *Channel) SetLogger(l log.Logger) {
   722  	ch.Logger = l
   723  }
   724  
   725  // Queues message to send to this channel.
   726  // Goroutine-safe
   727  // Times out (and returns false) after defaultSendTimeout
   728  func (ch *Channel) sendBytes(bytes []byte) bool {
   729  	select {
   730  	case ch.sendQueue <- bytes:
   731  		atomic.AddInt32(&ch.sendQueueSize, 1)
   732  		return true
   733  	case <-time.After(defaultSendTimeout):
   734  		return false
   735  	}
   736  }
   737  
   738  // Queues message to send to this channel.
   739  // Nonblocking, returns true if successful.
   740  // Goroutine-safe
   741  func (ch *Channel) trySendBytes(bytes []byte) bool {
   742  	select {
   743  	case ch.sendQueue <- bytes:
   744  		atomic.AddInt32(&ch.sendQueueSize, 1)
   745  		return true
   746  	default:
   747  		return false
   748  	}
   749  }
   750  
   751  // Goroutine-safe
   752  func (ch *Channel) loadSendQueueSize() (size int) {
   753  	return int(atomic.LoadInt32(&ch.sendQueueSize))
   754  }
   755  
   756  // Goroutine-safe
   757  // Use only as a heuristic.
   758  func (ch *Channel) canSend() bool {
   759  	return ch.loadSendQueueSize() < defaultSendQueueCapacity
   760  }
   761  
   762  // Returns true if any PacketMsgs are pending to be sent.
   763  // Call before calling nextPacketMsg()
   764  // Goroutine-safe
   765  func (ch *Channel) isSendPending() bool {
   766  	if len(ch.sending) == 0 {
   767  		if len(ch.sendQueue) == 0 {
   768  			return false
   769  		}
   770  		ch.sending = <-ch.sendQueue
   771  	}
   772  	return true
   773  }
   774  
   775  // Creates a new PacketMsg to send.
   776  // Not goroutine-safe
   777  func (ch *Channel) nextPacketMsg() PacketMsg {
   778  	packet := PacketMsg{}
   779  	packet.ChannelID = byte(ch.desc.ID)
   780  	maxSize := ch.maxPacketMsgPayloadSize
   781  	packet.Bytes = ch.sending[:cmn.MinInt(maxSize, len(ch.sending))]
   782  	if len(ch.sending) <= maxSize {
   783  		packet.EOF = byte(0x01)
   784  		ch.sending = nil
   785  		atomic.AddInt32(&ch.sendQueueSize, -1) // decrement sendQueueSize
   786  	} else {
   787  		packet.EOF = byte(0x00)
   788  		ch.sending = ch.sending[cmn.MinInt(maxSize, len(ch.sending)):]
   789  	}
   790  	return packet
   791  }
   792  
   793  // Writes next PacketMsg to w and updates c.recentlySent.
   794  // Not goroutine-safe
   795  func (ch *Channel) writePacketMsgTo(w io.Writer) (n int64, err error) {
   796  	var packet = ch.nextPacketMsg()
   797  	n, err = cdc.MarshalBinaryLengthPrefixedWriter(w, packet)
   798  	atomic.AddInt64(&ch.recentlySent, n)
   799  	return
   800  }
   801  
   802  // Handles incoming PacketMsgs. It returns a message bytes if message is
   803  // complete. NOTE message bytes may change on next call to recvPacketMsg.
   804  // Not goroutine-safe
   805  func (ch *Channel) recvPacketMsg(packet PacketMsg) ([]byte, error) {
   806  	ch.Logger.Debug("Read PacketMsg", "conn", ch.conn, "packet", packet)
   807  	var recvCap, recvReceived = ch.desc.RecvMessageCapacity, len(ch.recving) + len(packet.Bytes)
   808  	if recvCap < recvReceived {
   809  		return nil, fmt.Errorf("Received message exceeds available capacity: %v < %v", recvCap, recvReceived)
   810  	}
   811  	ch.recving = append(ch.recving, packet.Bytes...)
   812  	if packet.EOF == byte(0x01) {
   813  		msgBytes := ch.recving
   814  
   815  		// clear the slice without re-allocating.
   816  		// http://stackoverflow.com/questions/16971741/how-do-you-clear-a-slice-in-go
   817  		//   suggests this could be a memory leak, but we might as well keep the memory for the channel until it closes,
   818  		//	at which point the recving slice stops being used and should be garbage collected
   819  		ch.recving = ch.recving[:0] // make([]byte, 0, ch.desc.RecvBufferCapacity)
   820  		return msgBytes, nil
   821  	}
   822  	return nil, nil
   823  }
   824  
   825  // Call this periodically to update stats for throttling purposes.
   826  // Not goroutine-safe
   827  func (ch *Channel) updateStats() {
   828  	// Exponential decay of stats.
   829  	// TODO: optimize.
   830  	atomic.StoreInt64(&ch.recentlySent, int64(float64(atomic.LoadInt64(&ch.recentlySent))*0.8))
   831  }
   832  
   833  //----------------------------------------
   834  // Packet
   835  
   836  type Packet interface {
   837  	AssertIsPacket()
   838  }
   839  
   840  func RegisterPacket(cdc *amino.Codec) {
   841  	cdc.RegisterInterface((*Packet)(nil), nil)
   842  	cdc.RegisterConcrete(PacketPing{}, "tendermint/p2p/PacketPing", nil)
   843  	cdc.RegisterConcrete(PacketPong{}, "tendermint/p2p/PacketPong", nil)
   844  	cdc.RegisterConcrete(PacketMsg{}, "tendermint/p2p/PacketMsg", nil)
   845  }
   846  
   847  func (_ PacketPing) AssertIsPacket() {}
   848  func (_ PacketPong) AssertIsPacket() {}
   849  func (_ PacketMsg) AssertIsPacket()  {}
   850  
   851  type PacketPing struct {
   852  }
   853  
   854  type PacketPong struct {
   855  }
   856  
   857  type PacketMsg struct {
   858  	ChannelID byte
   859  	EOF       byte // 1 means message ends here.
   860  	Bytes     []byte
   861  }
   862  
   863  func (mp PacketMsg) String() string {
   864  	return fmt.Sprintf("PacketMsg{%X:%X T:%X}", mp.ChannelID, mp.Bytes, mp.EOF)
   865  }