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