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