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 }