github.com/contiv/libOpenflow@v0.0.0-20210609050114-d967b14cc688/util/stream.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"net"
     7  	"strings"
     8  
     9  	log "github.com/sirupsen/logrus"
    10  )
    11  
    12  const numParserGoroutines = 25
    13  
    14  type BufferPool struct {
    15  	Empty chan *bytes.Buffer
    16  	Full  chan *bytes.Buffer
    17  }
    18  
    19  func NewBufferPool() *BufferPool {
    20  	m := new(BufferPool)
    21  	m.Empty = make(chan *bytes.Buffer, 50)
    22  	m.Full = make(chan *bytes.Buffer, 50)
    23  
    24  	for i := 0; i < 50; i++ {
    25  		m.Empty <- bytes.NewBuffer(make([]byte, 0, 2048))
    26  	}
    27  	return m
    28  }
    29  
    30  // Parser interface
    31  type Parser interface {
    32  	Parse(b []byte) (message Message, err error)
    33  }
    34  
    35  type MessageStream struct {
    36  	conn net.Conn
    37  	pool *BufferPool
    38  	// Message parser
    39  	parser Parser
    40  	// Channel to shut down the parser goroutine
    41  	parserShutdown chan bool
    42  	// OpenFlow Version
    43  	Version uint8
    44  	// Channel on which to publish connection errors
    45  	Error chan error
    46  	// Channel on which to publish inbound messages
    47  	Inbound chan Message
    48  	// Channel on which to receive outbound messages
    49  	Outbound chan Message
    50  	// Channel on which to receive a shutdown command
    51  	Shutdown chan bool
    52  }
    53  
    54  // Returns a pointer to a new MessageStream. Used to parse
    55  // OpenFlow messages from conn.
    56  func NewMessageStream(conn net.Conn, parser Parser) *MessageStream {
    57  	m := &MessageStream{
    58  		conn,
    59  		NewBufferPool(),
    60  		parser,
    61  		make(chan bool, 1), // parserShutdown
    62  		0,
    63  		make(chan error, 1),   // Error
    64  		make(chan Message, 1), // Inbound
    65  		make(chan Message, 1), // Outbound
    66  		make(chan bool, 1),    // Shutdown
    67  	}
    68  
    69  	go m.outbound()
    70  	go m.inbound()
    71  	for i := 0; i < numParserGoroutines; i++ {
    72  		go m.parse()
    73  	}
    74  
    75  	return m
    76  }
    77  
    78  func (m *MessageStream) GetAddr() net.Addr {
    79  	return m.conn.RemoteAddr()
    80  }
    81  
    82  // Listen for a Shutdown signal or Outbound messages.
    83  func (m *MessageStream) outbound() {
    84  	for {
    85  		select {
    86  		case <-m.Shutdown:
    87  			log.Infof("Closing OpenFlow message stream.")
    88  			m.conn.Close()
    89  			for i := 0; i < numParserGoroutines; i++ {
    90  				m.parserShutdown <- true
    91  			}
    92  			return
    93  		case msg := <-m.Outbound:
    94  			// Forward outbound messages to conn
    95  			data, _ := msg.MarshalBinary()
    96  			if _, err := m.conn.Write(data); err != nil {
    97  				log.Warnln("OutboundError:", err)
    98  				m.Error <- err
    99  				m.Shutdown <- true
   100  			}
   101  
   102  			log.Debugf("Sent(%d): %v", len(data), data)
   103  		}
   104  	}
   105  }
   106  
   107  // Handle inbound messages
   108  func (m *MessageStream) inbound() {
   109  	msg := 0
   110  	hdr := 0
   111  	hdrBuf := make([]byte, 4)
   112  
   113  	tmp := make([]byte, 2048)
   114  	buf := <-m.pool.Empty
   115  	for {
   116  		n, err := m.conn.Read(tmp)
   117  		if err != nil {
   118  			// Handle explicitly disconnecting by closing connection
   119  			if strings.Contains(err.Error(), "use of closed network connection") {
   120  				return
   121  			}
   122  			log.Warnln("InboundError", err)
   123  			m.Error <- err
   124  			m.Shutdown <- true
   125  			return
   126  		}
   127  
   128  		for i := 0; i < n; i++ {
   129  			if hdr < 4 {
   130  				hdrBuf[hdr] = tmp[i]
   131  				buf.WriteByte(tmp[i])
   132  				hdr += 1
   133  				if hdr >= 4 {
   134  					msg = int(binary.BigEndian.Uint16(hdrBuf[2:])) - 4
   135  				}
   136  				continue
   137  			}
   138  			if msg > 0 {
   139  				buf.WriteByte(tmp[i])
   140  				msg = msg - 1
   141  				if msg == 0 {
   142  					hdr = 0
   143  					m.pool.Full <- buf
   144  					buf = <-m.pool.Empty
   145  				}
   146  				continue
   147  			}
   148  		}
   149  	}
   150  }
   151  
   152  // Parse incoming message
   153  func (m *MessageStream) parse() {
   154  	errMessage := "received: %v and encountered error: %v"
   155  	for {
   156  		select {
   157  		case b := <-m.pool.Full:
   158  			msg, err := m.parser.Parse(b.Bytes())
   159  			// Log all message parsing errors.
   160  			if err != nil {
   161  				log.Errorf(errMessage, b.Bytes(), err)
   162  			}
   163  
   164  			m.Inbound <- msg
   165  			b.Reset()
   166  			m.pool.Empty <- b
   167  		case <-m.parserShutdown:
   168  			return
   169  		}
   170  	}
   171  }