145 lines
4.4 KiB
Go
145 lines
4.4 KiB
Go
package fabricproto
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"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,
|
|
Payload: DataAckPayload(frame.Payload),
|
|
}}, 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 DataAckPayload(payload []byte) []byte {
|
|
sum := sha256.Sum256(payload)
|
|
return sum[:]
|
|
}
|
|
|
|
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...),
|
|
}
|
|
}
|