Files
rdp-proxy/agents/rap-node-agent/internal/fabricproto/session_frames.go
T

136 lines
4.2 KiB
Go

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...),
}
}