internal static ClassifyState TransitionExpectEpoxyHeaders(
            ClassifyState state, Frame frame, ref EpoxyHeaders headers, ref ProtocolErrorCode?errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectEpoxyHeaders);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyHeaders)
            {
                return(ClassifyState.InternalStateError);
            }

            var framelet         = frame.Framelets[0];
            var inputBuffer      = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader <InputBuffer>(inputBuffer, version: 1);

            switch (headersDeserializer.TryDeserialize(fastBinaryReader, out headers))
            {
            case Deserialize.Result.Success:
                break;

            default:
                logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyHeaders));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            logger.Site().Debug("Deserialized {0} with conversation ID {1} and message type {2}.",
                                nameof(EpoxyHeaders), headers.conversation_id, headers.message_type);
            return(ClassifyState.ExpectOptionalLayerData);
        }
        internal static ClassifyState TransitionExpectOptionalLayerData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ref ArraySegment <byte> layerData,
            ref ProtocolErrorCode?errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectOptionalLayerData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            if (frame.Count < 2)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            var framelet = frame.Framelets[1];

            if (framelet.Type == FrameletType.LayerData)
            {
                layerData = framelet.Contents;
                logger.Site().Debug("Extracted {0}-byte layer data in conversation ID {1}.",
                                    layerData.Count, headers.conversation_id);
            }

            return(ClassifyState.ExpectMessageData);
        }
示例#3
0
        internal static ClassifyState TransitionValidFrame(
            ClassifyState state, EpoxyHeaders headers, ref FrameDisposition disposition)
        {
            if (state != ClassifyState.ValidFrame || headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            switch (headers.payload_type)
            {
            case PayloadType.Request:
                disposition = FrameDisposition.DeliverRequestToService;
                return(ClassifyState.ClassifiedValidFrame);

            case PayloadType.Response:
                disposition = FrameDisposition.DeliverResponseToProxy;
                return(ClassifyState.ClassifiedValidFrame);

            case PayloadType.Event:
                disposition = FrameDisposition.DeliverEventToService;
                return(ClassifyState.ClassifiedValidFrame);

            default:
                return(ClassifyState.InternalStateError);
            }
        }
        internal static ClassifyState TransitionExpectFirstFramelet(
            ClassifyState state, Frame frame, ref ProtocolErrorCode?errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectFirstFramelet);
            Debug.Assert(frame != null);

            if (frame.Framelets.Count == 0)
            {
                logger.Site().Error("Frame was empty.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            switch (frame.Framelets[0].Type)
            {
            case FrameletType.EpoxyHeaders:
                return(ClassifyState.ExpectEpoxyHeaders);

            case FrameletType.EpoxyConfig:
                return(ClassifyState.ExpectConfig);

            case FrameletType.ProtocolError:
                return(ClassifyState.ExpectProtocolError);

            default:
                logger.Site().Error("Frame began with invalid FrameletType {0}.", frame.Framelets[0].Type);
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }
        }
示例#5
0
        internal static ClassifyState TransitionExpectPayload(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ArraySegment <byte> layerData,
            ref ArraySegment <byte> payload, ref ProtocolErrorCode?errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectPayload);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            int payloadDataIndex = (layerData.Array == null ? 1 : 2);

            if (payloadDataIndex >= frame.Count)
            {
                logger.Site().Error("Frame had headers but no payload.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            var framelet = frame.Framelets[payloadDataIndex];

            if (framelet.Type != FrameletType.PayloadData)
            {
                logger.Site().Error("Frame had headers but no payload.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            payload = framelet.Contents;
            logger.Site().Debug("Extracted {0}-byte payload in conversation ID {1}.",
                                payload.Count, headers.conversation_id);
            return(ClassifyState.ExpectEndOfFrame);
        }
        internal static ClassifyState TransitionExpectProtocolError(
            ClassifyState state, Frame frame, ref ProtocolError error, ref FrameDisposition disposition, Logger logger)
        {
            if (state != ClassifyState.ExpectProtocolError || frame == null || frame.Count == 0 ||
                frame.Framelets[0].Type != FrameletType.ProtocolError)
            {
                return(ClassifyState.InternalStateError);
            }

            if (frame.Count > 1)
            {
                logger.Site().Error("Protocol error frame had trailing framelets.");
                return(ClassifyState.ErrorInErrorFrame);
            }

            var framelet = frame.Framelets[0];

            var inputBuffer      = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader <InputBuffer>(inputBuffer, version: 1);

            switch (errorDeserializer.TryDeserialize(fastBinaryReader, out error))
            {
            case Deserialize.Result.Success:
                break;

            default:
                logger.Site().Error("Didn't get a valid {0}.", nameof(ProtocolError));
                return(ClassifyState.ErrorInErrorFrame);
            }

            logger.Site().Debug("Deserialized {0} with code {1}.", nameof(ProtocolError), error.error_code);
            disposition = FrameDisposition.HandleProtocolError;
            return(ClassifyState.ClassifiedValidFrame);
        }
        internal static ClassifyState TransitionValidFrame(
            ClassifyState state, EpoxyHeaders headers, ref FrameDisposition disposition)
        {
            if (state != ClassifyState.ValidFrame || headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            switch (headers.message_type)
            {
            case EpoxyMessageType.REQUEST:
                disposition = FrameDisposition.DeliverRequestToService;
                return(ClassifyState.ClassifiedValidFrame);

            case EpoxyMessageType.RESPONSE:
                disposition = FrameDisposition.DeliverResponseToProxy;
                return(ClassifyState.ClassifiedValidFrame);

            case EpoxyMessageType.EVENT:
                disposition = FrameDisposition.DeliverEventToService;
                return(ClassifyState.ClassifiedValidFrame);

            default:
                return(ClassifyState.InternalStateError);
            }
        }
        internal static ClassifyState TransitionExpectConfig(
            ClassifyState state,
            Frame frame,
            ref ProtocolErrorCode?errorCode,
            ref FrameDisposition disposition,
            Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectConfig);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyConfig)
            {
                return(ClassifyState.InternalStateError);
            }

            if (frame.Count != 1)
            {
                logger.Site().Error("Config frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            var framelet = frame.Framelets[0];

            var inputBuffer      = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader <InputBuffer>(inputBuffer, 1);

            // We don't currently do anything with the config aside from try to deserialize it.
            EpoxyConfig config;

            switch (configDeserializer.TryDeserialize(fastBinaryReader, out config))
            {
            case Deserialize.Result.Success:
                break;

            default:
                logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyConfig));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            disposition = FrameDisposition.ProcessConfig;
            return(ClassifyState.ClassifiedValidFrame);
        }
        internal static ClassifyState TransitionExpectMessageData(
            ClassifyState state,
            Frame frame,
            EpoxyHeaders headers,
            ArraySegment <byte> layerData,
            ref MessageData messageData,
            ref ProtocolErrorCode?errorCode,
            Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectMessageData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            int messageDataIndex = (layerData.Array == null ? 1 : 2);

            if (messageDataIndex >= frame.Count)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            var framelet = frame.Framelets[messageDataIndex];

            if (framelet.Type != FrameletType.PayloadData && framelet.Type != FrameletType.ErrorData)
            {
                logger.Site().Error("Frame had headers but no message data. Unexpected framelet type {0}", (int)framelet.Type);
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            messageData = new MessageData(framelet.Type == FrameletType.ErrorData, framelet.Contents);

            logger.Site().Debug(
                "Extracted {0}-byte message in conversation ID {1}.",
                messageData.Data.Count,
                headers.conversation_id);
            return(ClassifyState.ExpectEndOfFrame);
        }
示例#10
0
        internal static ClassifyState TransitionFrameComplete(
            ClassifyState state, EpoxyHeaders headers, ref ProtocolErrorCode?errorCode, Logger logger)
        {
            if (state != ClassifyState.FrameComplete || headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            switch (headers.payload_type)
            {
            case PayloadType.Request:
            case PayloadType.Response:
            case PayloadType.Event:
                return(ClassifyState.ValidFrame);

            default:
                logger.Site().Warning("Received unrecognized payload type {0}.", headers.payload_type);
                errorCode = ProtocolErrorCode.NOT_SUPPORTED;
                return(ClassifyState.MalformedFrame);
            }
        }
        internal static ClassifyState TransitionFrameComplete(
            ClassifyState state, EpoxyHeaders headers, ref ProtocolErrorCode?errorCode, Logger logger)
        {
            if (state != ClassifyState.FrameComplete || headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            switch (headers.message_type)
            {
            case EpoxyMessageType.REQUEST:
            case EpoxyMessageType.RESPONSE:
            case EpoxyMessageType.EVENT:
                return(ClassifyState.ValidFrame);

            default:
                logger.Site().Warning("Received unrecognized message type {0}.", headers.message_type);
                errorCode = ProtocolErrorCode.NOT_SUPPORTED;
                return(ClassifyState.MalformedFrame);
            }
        }
示例#12
0
        internal static ClassifyState TransitionExpectOptionalLayerData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ref ArraySegment <byte> layerData,
            ref ProtocolErrorCode?errorCode)
        {
            Debug.Assert(state == ClassifyState.ExpectOptionalLayerData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            if (frame.Count < 2)
            {
                Log.Error("{0}.{1}: Frame did not continue with LayerData or PayloadData.",
                          nameof(EpoxyProtocol), nameof(TransitionExpectEpoxyHeaders));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            if (frame.Framelets[1].Type == FrameletType.PayloadData)
            {
                return(ClassifyState.ExpectPayload);
            }

            var framelet = frame.Framelets[1];

            if (framelet.Type != FrameletType.LayerData)
            {
                Log.Error("{0}.{1}: Frame did not continue with LayerData or PayloadData.",
                          nameof(EpoxyProtocol), nameof(TransitionExpectOptionalLayerData));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            layerData = framelet.Contents;
            Log.Debug("{0}.{1}: Extracted {2}-byte layer data in conversation ID {3}.",
                      nameof(EpoxyProtocol), nameof(TransitionExpectOptionalLayerData), layerData.Count, headers.conversation_id);
            return(ClassifyState.ExpectPayload);
        }
示例#13
0
        internal static ClassifyState TransitionExpectEndOfFrame(
            ClassifyState state, Frame frame, ArraySegment <byte> layerData, ref ProtocolErrorCode?errorCode,
            Logger logger)
        {
            if (state != ClassifyState.ExpectEndOfFrame || frame == null)
            {
                return(ClassifyState.InternalStateError);
            }

            int validFrameSize = (layerData.Array == null ? 2 : 3);

            if (frame.Count == validFrameSize)
            {
                return(ClassifyState.FrameComplete);
            }
            else
            {
                logger.Site().Error("Frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }
        }
示例#14
0
        internal static ClassifyState TransitionExpectPayload(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ArraySegment <byte> layerData,
            ref ArraySegment <byte> payload, ref ProtocolErrorCode?errorCode)
        {
            Debug.Assert(state == ClassifyState.ExpectPayload);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return(ClassifyState.InternalStateError);
            }

            int payloadDataIndex = (layerData.Array == null ? 1 : 2);

            if (payloadDataIndex >= frame.Count)
            {
                Log.Error("{0}.{1}: Frame did not continue with PayloadData.",
                          nameof(EpoxyProtocol), nameof(TransitionExpectEpoxyHeaders));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            var framelet = frame.Framelets[payloadDataIndex];

            if (framelet.Type != FrameletType.PayloadData)
            {
                Log.Error("{0}.{1}: Frame did not continue with PayloadData.",
                          nameof(EpoxyProtocol), nameof(TransitionExpectEpoxyHeaders));
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return(ClassifyState.MalformedFrame);
            }

            payload = framelet.Contents;
            Log.Debug("{0}.{1}: Extracted {2}-byte payload in conversation ID {3}.",
                      nameof(EpoxyProtocol), nameof(TransitionExpectPayload), payload.Count, headers.conversation_id);
            return(ClassifyState.ExpectEndOfFrame);
        }
示例#15
0
        internal static ClassifyState TransitionExpectProtocolError(
            ClassifyState state, Frame frame, ref ProtocolError error, ref FrameDisposition disposition, Logger logger)
        {
            if (state != ClassifyState.ExpectProtocolError || frame == null || frame.Count == 0
                || frame.Framelets[0].Type != FrameletType.ProtocolError)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count > 1)
            {
                logger.Site().Error("Protocol error frame had trailing framelets.");
                return ClassifyState.ErrorInErrorFrame;
            }

            var framelet = frame.Framelets[0];

            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);
            switch (errorDeserializer.TryDeserialize(fastBinaryReader, out error))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(ProtocolError));
                    return ClassifyState.ErrorInErrorFrame;
            }

            logger.Site().Debug("Deserialized {0} with code {1}.", nameof(ProtocolError), error.error_code);
            disposition = FrameDisposition.HandleProtocolError;
            return ClassifyState.ClassifiedValidFrame;
        }
示例#16
0
        internal static ClassifyState TransitionExpectConfig(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, ref FrameDisposition disposition,
            Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectConfig);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyConfig)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count != 1)
            {
                logger.Site().Error("Config frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[0];

            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);

            // We don't currently do anything with the config aside from try to deserialize it.
            EpoxyConfig config;
            switch (configDeserializer.TryDeserialize(fastBinaryReader, out config))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyConfig));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            disposition = FrameDisposition.ProcessConfig;
            return ClassifyState.ClassifiedValidFrame;
        }
示例#17
0
        internal static ClassifyState TransitionValidFrame(
            ClassifyState state, EpoxyHeaders headers, ref FrameDisposition disposition)
        {
            if (state != ClassifyState.ValidFrame || headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            switch (headers.message_type)
            {
                case EpoxyMessageType.REQUEST:
                    disposition = FrameDisposition.DeliverRequestToService;
                    return ClassifyState.ClassifiedValidFrame;

                case EpoxyMessageType.RESPONSE:
                    disposition = FrameDisposition.DeliverResponseToProxy;
                    return ClassifyState.ClassifiedValidFrame;

                case EpoxyMessageType.EVENT:
                    disposition = FrameDisposition.DeliverEventToService;
                    return ClassifyState.ClassifiedValidFrame;

                default:
                    return ClassifyState.InternalStateError;
            }
        }
示例#18
0
        internal static ClassifyState TransitionFrameComplete(
            ClassifyState state, EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            if (state != ClassifyState.FrameComplete || headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            switch (headers.message_type)
            {
                case EpoxyMessageType.REQUEST:
                case EpoxyMessageType.RESPONSE:
                case EpoxyMessageType.EVENT:
                    return ClassifyState.ValidFrame;

                default:
                    logger.Site().Warning("Received unrecognized message type {0}.", headers.message_type);
                    errorCode = ProtocolErrorCode.NOT_SUPPORTED;
                    return ClassifyState.MalformedFrame;
            }
        }
示例#19
0
        internal static ClassifyState TransitionExpectEndOfFrame(
            ClassifyState state, Frame frame, ArraySegment<byte> layerData, ref ProtocolErrorCode? errorCode,
            Logger logger)
        {
            // FIXME: Change all of these to asserts.
            if (state != ClassifyState.ExpectEndOfFrame || frame == null)
            {
                return ClassifyState.InternalStateError;
            }

            var validFrameSize = (layerData.Array == null ? 2 : 3);
            if (frame.Count == validFrameSize)
            {
                return ClassifyState.FrameComplete;
            }
            else
            {
                logger.Site().Error("Frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }
        }
示例#20
0
        internal static ClassifyState TransitionExpectMessageData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ArraySegment<byte> layerData,
            ref MessageData messageData, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectMessageData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            int messageDataIndex = (layerData.Array == null ? 1 : 2);
            if (messageDataIndex >= frame.Count)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[messageDataIndex];
            if (framelet.Type != FrameletType.PayloadData && framelet.Type != FrameletType.ErrorData)
            {
                logger.Site().Error("Frame had headers but no message data. Unexpected framelet type {0}", (int)framelet.Type);
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            messageData = new MessageData(
                isError: framelet.Type == FrameletType.ErrorData,
                data: framelet.Contents);

            logger.Site().Debug("Extracted {0}-byte message in conversation ID {1}.",
                messageData.Data.Count, headers.conversation_id);
            return ClassifyState.ExpectEndOfFrame;
        }
示例#21
0
        internal static ClassifyState TransitionExpectOptionalLayerData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ref ArraySegment<byte> layerData,
            ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectOptionalLayerData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count < 2)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[1];
            if (framelet.Type == FrameletType.LayerData)
            {
                layerData = framelet.Contents;
                logger.Site().Debug("Extracted {0}-byte layer data in conversation ID {1}.",
                    layerData.Count, headers.conversation_id);
            }

            return ClassifyState.ExpectMessageData;
        }
示例#22
0
        internal static ClassifyState TransitionExpectEpoxyHeaders(
            ClassifyState state, Frame frame, ref EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectEpoxyHeaders);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyHeaders)
            {
                return ClassifyState.InternalStateError;
            }

            var framelet = frame.Framelets[0];
            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);
            switch (headersDeserializer.TryDeserialize(fastBinaryReader, out headers))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyHeaders));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            logger.Site().Debug("Deserialized {0} with conversation ID {1} and message type {2}.",
                nameof(EpoxyHeaders), headers.conversation_id, headers.message_type);
            return ClassifyState.ExpectOptionalLayerData;
        }
示例#23
0
        internal static ClassifyState TransitionExpectFirstFramelet(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectFirstFramelet);
            Debug.Assert(frame != null);

            if (frame.Framelets.Count == 0)
            {
                logger.Site().Error("Frame was empty.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            switch (frame.Framelets[0].Type)
            {
                case FrameletType.EpoxyHeaders:
                    return ClassifyState.ExpectEpoxyHeaders;

                case FrameletType.EpoxyConfig:
                    return ClassifyState.ExpectConfig;

                case FrameletType.ProtocolError:
                    return ClassifyState.ExpectProtocolError;

                default:
                    logger.Site().Error("Frame began with invalid FrameletType {0}.", frame.Framelets[0].Type);
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }
        }