Handle fabric session protocol frames
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package fabricproto
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrUnsupportedSessionFrame = errors.New("unsupported fabric session frame")
|
||||
ErrTrafficClassMismatch = errors.New("fabric stream traffic class mismatch")
|
||||
)
|
||||
|
||||
type SessionEventType string
|
||||
|
||||
const (
|
||||
SessionEventNone SessionEventType = ""
|
||||
SessionEventHello SessionEventType = "hello"
|
||||
SessionEventAuth SessionEventType = "auth"
|
||||
SessionEventReady SessionEventType = "session_ready"
|
||||
SessionEventStreamOpened SessionEventType = "stream_opened"
|
||||
SessionEventData SessionEventType = "data"
|
||||
SessionEventAck SessionEventType = "ack"
|
||||
SessionEventCredit SessionEventType = "credit"
|
||||
SessionEventPing SessionEventType = "ping"
|
||||
SessionEventPong SessionEventType = "pong"
|
||||
SessionEventRouteUpdate SessionEventType = "route_update"
|
||||
SessionEventPressure SessionEventType = "node_pressure"
|
||||
SessionEventStreamClosed SessionEventType = "stream_closed"
|
||||
SessionEventStreamReset SessionEventType = "stream_reset"
|
||||
SessionEventGoAway SessionEventType = "goaway"
|
||||
)
|
||||
|
||||
type SessionEvent struct {
|
||||
Type SessionEventType
|
||||
StreamID uint64
|
||||
Sequence uint64
|
||||
TrafficClass TrafficClass
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (s *Session) HandleFrame(frame Frame) (SessionEvent, []Frame, error) {
|
||||
if err := ValidateFrame(frame, DefaultMaxPayload); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
|
||||
switch frame.Type {
|
||||
case FrameHello:
|
||||
return sessionEvent(frame, SessionEventHello), nil, nil
|
||||
case FrameAuth:
|
||||
return sessionEvent(frame, SessionEventAuth), nil, nil
|
||||
case FrameSessionReady:
|
||||
return sessionEvent(frame, SessionEventReady), nil, nil
|
||||
case FrameOpenStream:
|
||||
if err := s.OpenStream(frame.StreamID, frame.TrafficClass); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return sessionEvent(frame, SessionEventStreamOpened), nil, nil
|
||||
case FrameData:
|
||||
event, err := s.handleDataFrame(frame)
|
||||
if err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return event, []Frame{{
|
||||
Type: FrameAck,
|
||||
TrafficClass: frame.TrafficClass,
|
||||
StreamID: frame.StreamID,
|
||||
Sequence: frame.Sequence,
|
||||
}}, nil
|
||||
case FrameAck:
|
||||
if err := s.Ack(frame.StreamID, frame.Sequence); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return sessionEvent(frame, SessionEventAck), nil, nil
|
||||
case FramePing:
|
||||
return sessionEvent(frame, SessionEventPing), []Frame{{
|
||||
Type: FramePong,
|
||||
Sequence: frame.Sequence,
|
||||
Payload: append([]byte(nil), frame.Payload...),
|
||||
}}, nil
|
||||
case FramePong:
|
||||
return sessionEvent(frame, SessionEventPong), nil, nil
|
||||
case FrameRouteUpdate:
|
||||
return sessionEvent(frame, SessionEventRouteUpdate), nil, nil
|
||||
case FrameStreamCredit:
|
||||
if err := s.AddCredit(frame.StreamID, int(frame.Sequence)); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return sessionEvent(frame, SessionEventCredit), nil, nil
|
||||
case FrameNodePressure:
|
||||
return sessionEvent(frame, SessionEventPressure), nil, nil
|
||||
case FrameCloseStream:
|
||||
if err := s.CloseStream(frame.StreamID); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return sessionEvent(frame, SessionEventStreamClosed), nil, nil
|
||||
case FrameResetStream:
|
||||
if err := s.ResetStream(frame.StreamID); err != nil {
|
||||
return SessionEvent{}, nil, err
|
||||
}
|
||||
return sessionEvent(frame, SessionEventStreamReset), nil, nil
|
||||
case FrameGoAway:
|
||||
s.Close()
|
||||
return sessionEvent(frame, SessionEventGoAway), nil, nil
|
||||
default:
|
||||
return SessionEvent{}, nil, ErrUnsupportedSessionFrame
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) handleDataFrame(frame Frame) (SessionEvent, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.closed {
|
||||
return SessionEvent{}, ErrSessionClosed
|
||||
}
|
||||
st, err := s.openStreamLocked(frame.StreamID)
|
||||
if err != nil {
|
||||
return SessionEvent{}, err
|
||||
}
|
||||
if frame.TrafficClass == 0 {
|
||||
frame.TrafficClass = st.trafficClass
|
||||
}
|
||||
if frame.TrafficClass != st.trafficClass {
|
||||
return SessionEvent{}, ErrTrafficClassMismatch
|
||||
}
|
||||
st.metrics.Received++
|
||||
s.metrics.FramesReceived++
|
||||
return sessionEvent(frame, SessionEventData), nil
|
||||
}
|
||||
|
||||
func sessionEvent(frame Frame, eventType SessionEventType) SessionEvent {
|
||||
return SessionEvent{
|
||||
Type: eventType,
|
||||
StreamID: frame.StreamID,
|
||||
Sequence: frame.Sequence,
|
||||
TrafficClass: frame.TrafficClass,
|
||||
Payload: append([]byte(nil), frame.Payload...),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user