@@ -9,15 +9,22 @@ import (
99 "sync"
1010)
1111
12+ const (
13+ // This is completely arbitrary, but the line had to be drawn somewhere
14+ MAX_MESSAGES_PER_SEGMENT = 20
15+ )
16+
1217type Protocol struct {
13- config ProtocolConfig
14- sendChan chan * muxer.Segment
15- recvChan chan * muxer.Segment
16- state State
17- stateMutex sync.Mutex
18- recvBuffer * bytes.Buffer
19- recvReadyChan chan bool
20- sendReadyChan chan bool
18+ config ProtocolConfig
19+ muxerSendChan chan * muxer.Segment
20+ muxerRecvChan chan * muxer.Segment
21+ state State
22+ stateMutex sync.Mutex
23+ recvBuffer * bytes.Buffer
24+ sendQueueChan chan Message
25+ sendStateQueueChan chan Message
26+ recvReadyChan chan bool
27+ sendReadyChan chan bool
2128}
2229
2330type ProtocolConfig struct {
@@ -62,19 +69,22 @@ type MessageHandlerFunc func(Message, bool) error
6269type MessageFromCborFunc func (uint , []byte ) (Message , error )
6370
6471func New (config ProtocolConfig ) * Protocol {
65- sendChan , recvChan := config .Muxer .RegisterProtocol (config .ProtocolId )
72+ muxerSendChan , muxerRecvChan := config .Muxer .RegisterProtocol (config .ProtocolId )
6673 p := & Protocol {
67- config : config ,
68- sendChan : sendChan ,
69- recvChan : recvChan ,
70- recvBuffer : bytes .NewBuffer (nil ),
71- recvReadyChan : make (chan bool , 1 ),
72- sendReadyChan : make (chan bool , 1 ),
74+ config : config ,
75+ muxerSendChan : muxerSendChan ,
76+ muxerRecvChan : muxerRecvChan ,
77+ recvBuffer : bytes .NewBuffer (nil ),
78+ sendQueueChan : make (chan Message , 50 ),
79+ sendStateQueueChan : make (chan Message , 50 ),
80+ recvReadyChan : make (chan bool , 1 ),
81+ sendReadyChan : make (chan bool , 1 ),
7382 }
7483 // Set initial state
7584 p .setState (config .InitialState )
76- // Start our receiver Goroutine
85+ // Start our send and receive Goroutines
7786 go p .recvLoop ()
87+ go p .sendLoop ()
7888 return p
7989}
8090
@@ -86,56 +96,114 @@ func (p *Protocol) Role() ProtocolRole {
8696 return p .config .Role
8797}
8898
89- func (p * Protocol ) SendMessage (msg Message , isResponse bool ) error {
90- // Wait until ready to send based on state map
91- <- p .sendReadyChan
92- // Lock the state to prevent collisions
93- p .stateMutex .Lock ()
94- if err := p .checkCurrentState (); err != nil {
95- return fmt .Errorf ("%s: error sending message: %s" , p .config .Name , err )
96- }
97- newState , err := p .getNewState (msg )
98- if err != nil {
99- return fmt .Errorf ("%s: error sending message: %s" , p .config .Name , err )
100- }
101- // Get raw CBOR from message
102- data := msg .Cbor ()
103- // If message has no raw CBOR, encode the message
104- if data == nil {
105- var err error
106- data , err = utils .CborEncode (msg )
107- if err != nil {
108- return err
109- }
110- }
111- // Send message in multiple segments (if needed)
112- for {
113- // Determine segment payload length
114- segmentPayloadLength := len (data )
115- if segmentPayloadLength > muxer .SEGMENT_MAX_PAYLOAD_LENGTH {
116- segmentPayloadLength = muxer .SEGMENT_MAX_PAYLOAD_LENGTH
117- }
118- // Send current segment
119- segmentPayload := data [:segmentPayloadLength ]
120- segment := muxer .NewSegment (p .config .ProtocolId , segmentPayload , isResponse )
121- p .sendChan <- segment
122- // Remove current segment's data from buffer
123- if len (data ) > segmentPayloadLength {
124- data = data [segmentPayloadLength :]
125- } else {
126- break
127- }
128- }
129- // Set new state and unlock
130- p .setState (newState )
131- p .stateMutex .Unlock ()
99+ func (p * Protocol ) SendMessage (msg Message ) error {
100+ p .sendQueueChan <- msg
132101 return nil
133102}
134103
135104func (p * Protocol ) SendError (err error ) {
136105 p .config .ErrorChan <- err
137106}
138107
108+ func (p * Protocol ) sendLoop () {
109+ var setNewState bool
110+ var newState State
111+ var err error
112+ for {
113+ // Wait until ready to send based on state map
114+ <- p .sendReadyChan
115+ // Lock the state to prevent collisions
116+ p .stateMutex .Lock ()
117+ // Check for queued state changes from previous pipelined sends
118+ setNewState = false
119+ if len (p .sendStateQueueChan ) > 0 {
120+ msg := <- p .sendStateQueueChan
121+ newState , err = p .getNewState (msg )
122+ if err != nil {
123+ p .SendError (fmt .Errorf ("%s: error sending message: %s" , p .config .Name , err ))
124+ }
125+ setNewState = true
126+ // If there are no queued messages, set the new state now
127+ if len (p .sendQueueChan ) == 0 {
128+ p .setState (newState )
129+ p .stateMutex .Unlock ()
130+ continue
131+ }
132+ }
133+ // Read queued messages and write into buffer
134+ payloadBuf := bytes .NewBuffer (nil )
135+ msgCount := 0
136+ for {
137+ // Get next message from send queue
138+ msg := <- p .sendQueueChan
139+ msgCount = msgCount + 1
140+ // Write the message into the send state queue if we already have a new state
141+ if setNewState {
142+ p .sendStateQueueChan <- msg
143+ }
144+ // Get raw CBOR from message
145+ data := msg .Cbor ()
146+ // If message has no raw CBOR, encode the message
147+ if data == nil {
148+ var err error
149+ data , err = utils .CborEncode (msg )
150+ if err != nil {
151+ p .SendError (err )
152+ }
153+ }
154+ payloadBuf .Write (data )
155+ if ! setNewState {
156+ newState , err = p .getNewState (msg )
157+ if err != nil {
158+ p .SendError (fmt .Errorf ("%s: error sending message: %s" , p .config .Name , err ))
159+ }
160+ setNewState = true
161+ }
162+ // We don't want more than MAX_MESSAGES_PER_SEGMENT messages in a segment
163+ if msgCount >= MAX_MESSAGES_PER_SEGMENT {
164+ break
165+ }
166+ // We don't want to add more messages once we spill over into a second segment
167+ if payloadBuf .Len () > muxer .SEGMENT_MAX_PAYLOAD_LENGTH {
168+ break
169+ }
170+ // Check if there are any more queued messages
171+ if len (p .sendQueueChan ) == 0 {
172+ break
173+ }
174+ // We don't want to block on writes to the send state queue
175+ if len (p .sendStateQueueChan ) == cap (p .sendStateQueueChan ) {
176+ break
177+ }
178+ }
179+ // Send messages in multiple segments (if needed)
180+ for {
181+ // Determine segment payload length
182+ segmentPayloadLength := payloadBuf .Len ()
183+ if segmentPayloadLength > muxer .SEGMENT_MAX_PAYLOAD_LENGTH {
184+ segmentPayloadLength = muxer .SEGMENT_MAX_PAYLOAD_LENGTH
185+ }
186+ // Send current segment
187+ segmentPayload := payloadBuf .Bytes ()[:segmentPayloadLength ]
188+ isResponse := false
189+ if p .Role () == ProtocolRoleServer {
190+ isResponse = true
191+ }
192+ segment := muxer .NewSegment (p .config .ProtocolId , segmentPayload , isResponse )
193+ p .muxerSendChan <- segment
194+ // Remove current segment's data from buffer
195+ if payloadBuf .Len () > segmentPayloadLength {
196+ payloadBuf = bytes .NewBuffer (payloadBuf .Bytes ()[segmentPayloadLength :])
197+ } else {
198+ break
199+ }
200+ }
201+ // Set new state and unlock
202+ p .setState (newState )
203+ p .stateMutex .Unlock ()
204+ }
205+ }
206+
139207func (p * Protocol ) recvLoop () {
140208 leftoverData := false
141209 isResponse := false
@@ -144,7 +212,7 @@ func (p *Protocol) recvLoop() {
144212 // Don't grab the next segment from the muxer if we still have data in the buffer
145213 if ! leftoverData {
146214 // Wait for segment
147- segment := <- p .recvChan
215+ segment := <- p .muxerRecvChan
148216 // Add segment payload to buffer
149217 p .recvBuffer .Write (segment .Payload )
150218 // Save whether it's a response
@@ -192,18 +260,6 @@ func (p *Protocol) recvLoop() {
192260 }
193261}
194262
195- func (p * Protocol ) checkCurrentState () error {
196- if currentStateMapEntry , ok := p .config .StateMap [p .state ]; ok {
197- if currentStateMapEntry .Agency == AGENCY_NONE {
198- return fmt .Errorf ("protocol is in state with no agency" )
199- }
200- // TODO: check client/server agency
201- } else {
202- return fmt .Errorf ("protocol in unknown state" )
203- }
204- return nil
205- }
206-
207263func (p * Protocol ) getNewState (msg Message ) (State , error ) {
208264 var newState State
209265 matchFound := false
@@ -251,9 +307,6 @@ func (p *Protocol) setState(state State) {
251307func (p * Protocol ) handleMessage (msg Message , isResponse bool ) error {
252308 // Lock the state to prevent collisions
253309 p .stateMutex .Lock ()
254- if err := p .checkCurrentState (); err != nil {
255- return fmt .Errorf ("%s: error handling message: %s" , p .config .Name , err )
256- }
257310 newState , err := p .getNewState (msg )
258311 if err != nil {
259312 return fmt .Errorf ("%s: error handling message: %s" , p .config .Name , err )
0 commit comments