private void DispatchEvent(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData) { if (headers.error_code != (int)ErrorCode.OK) { Log.Error("{0}.{1}: Received event with a non-zero error code. Conversation ID: {2}", this, nameof(DispatchEvent), headers.conversation_id); return; } IMessage request = Message.FromPayload(Unmarshal.From(payload)); var receiveContext = new EpoxyReceiveContext(this); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); Error layerError = LayerStackUtils.ProcessOnReceive(parentTransport.LayerStack, MessageType.Event, receiveContext, bondedLayerData); if (layerError != null) { Log.Error("{0}.{1}: Receiving event {2}/{3} failed due to layer error (Code: {4}, Message: {5}).", this, nameof(DispatchEvent), headers.conversation_id, headers.method_name, layerError.error_code, layerError.message); return; } Task.Run(async() => { await serviceHost.DispatchEvent(headers.method_name, receiveContext, request, connectionMetrics); }); }
// These end-to-end tests cover states that don't fit in functions. private static void AssertHeadersEqual(EpoxyHeaders expected, EpoxyHeaders actual) { Assert.AreEqual(expected.error_code, actual.error_code); Assert.AreEqual(expected.method_name, actual.method_name); Assert.AreEqual(expected.payload_type, actual.payload_type); Assert.AreEqual(expected.conversation_id, actual.conversation_id); }
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 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); }
// These end-to-end tests cover states that don't fit in functions. private static void AssertHeadersEqual(EpoxyHeaders expected, EpoxyHeaders actual) { Assert.AreEqual(expected.conversation_id, actual.conversation_id); Assert.AreEqual(expected.message_type, actual.message_type); Assert.AreEqual(expected.service_name, actual.service_name); Assert.AreEqual(expected.method_name, actual.method_name); }
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); }
private void DispatchResponse(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData) { IMessage response; if (headers.error_code != (int)ErrorCode.OK) { response = Message.FromError(Unmarshal <Error> .From(payload)); } else { response = Message.FromPayload(Unmarshal.From(payload)); } var receiveContext = new EpoxyReceiveContext(this); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); Error layerError = LayerStackUtils.ProcessOnReceive(parentTransport.LayerStack, MessageType.Response, receiveContext, bondedLayerData); if (layerError != null) { Log.Error("{0}.{1}: Receiving response {2}/{3} failed due to layer error (Code: {4}, Message: {5}).", this, nameof(DispatchResponse), headers.conversation_id, headers.method_name, layerError.error_code, layerError.message); response = Message.FromError(layerError); } if (!responseMap.Complete(headers.conversation_id, response)) { Log.Error("{0}.{1}: Response for unmatched request. Conversation ID: {2}", this, nameof(DispatchResponse), headers.conversation_id); } }
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 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); } }
public void TransitionExpectEpoxyHeaders_Valid() { EpoxyHeaders headers = null; ProtocolErrorCode?errorCode = null; var after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, goodRequestFrame, ref headers, ref errorCode, LoggerTests.BlackHole); Assert.AreEqual(EpoxyProtocol.ClassifyState.ExpectOptionalLayerData, after); Assert.NotNull(headers); Assert.AreEqual(GoodRequestId, headers.conversation_id); Assert.AreEqual(GoodService, headers.service_name); Assert.AreEqual(GoodMethod, headers.method_name); Assert.AreEqual(EpoxyMessageType.REQUEST, headers.message_type); Assert.Null(errorCode); after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, goodRequestLayerDataFrame, ref headers, ref errorCode, LoggerTests.BlackHole); Assert.AreEqual(EpoxyProtocol.ClassifyState.ExpectOptionalLayerData, after); Assert.NotNull(headers); Assert.AreEqual(GoodRequestId, headers.conversation_id); Assert.AreEqual(GoodService, headers.service_name); Assert.AreEqual(GoodMethod, headers.method_name); Assert.AreEqual(EpoxyMessageType.REQUEST, headers.message_type); Assert.Null(errorCode); }
public void TransitionExpectEpoxyHeaders_Valid() { EpoxyHeaders headers = null; ProtocolErrorCode?errorCode = null; var after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, goodRequestFrame, ref headers, ref errorCode); Assert.AreEqual(EpoxyProtocol.ClassifyState.ExpectOptionalLayerData, after); Assert.NotNull(headers); Assert.AreEqual(GoodRequestId, headers.conversation_id); Assert.AreEqual(0, headers.error_code); Assert.AreEqual(GoodMethod, headers.method_name); Assert.AreEqual(PayloadType.Request, headers.payload_type); Assert.Null(errorCode); after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, goodRequestLayerDataFrame, ref headers, ref errorCode); Assert.AreEqual(EpoxyProtocol.ClassifyState.ExpectOptionalLayerData, after); Assert.NotNull(headers); Assert.AreEqual(GoodRequestId, headers.conversation_id); Assert.AreEqual(0, headers.error_code); Assert.AreEqual(GoodMethod, headers.method_name); Assert.AreEqual(PayloadType.Request, headers.payload_type); Assert.Null(errorCode); }
private State?DispatchRequest(EpoxyHeaders headers, EpoxyProtocol.MessageData messageData, ArraySegment <byte> layerData) { Task.Run(async() => { var totalTime = Stopwatch.StartNew(); var requestMetrics = Metrics.StartRequestMetrics(ConnectionMetrics); var receiveContext = new EpoxyReceiveContext(this, ConnectionMetrics, requestMetrics); ILayerStack layerStack = null; IMessage result; if (messageData.IsError) { logger.Site().Error("{0} Received request with an error message. Only payload messages are allowed. Conversation ID: {1}", this, headers.conversation_id); result = Message.FromError(new Error { error_code = (int)ErrorCode.INVALID_INVOCATION, message = "Received request with an error message" }); } else { IMessage request = Message.FromPayload(Unmarshal.From(messageData.Data)); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); Error layerError = parentTransport.GetLayerStack(requestMetrics.request_id, out layerStack); if (layerError == null) { layerError = LayerStackUtils.ProcessOnReceive( layerStack, MessageType.REQUEST, receiveContext, bondedLayerData, logger); } if (layerError == null) { result = await serviceHost.DispatchRequest(headers.service_name, headers.method_name, receiveContext, request); } else { logger.Site().Error("{0} Receiving request {1}/{2}.{3} failed due to layer error (Code: {4}, Message: {5}).", this, headers.conversation_id, headers.service_name, headers.method_name, layerError.error_code, layerError.message); // Set layer error as result of this Bond method call and do not dispatch to method. // Since this error will be returned to client, cleanse out internal server error details, if any. result = Message.FromError(Errors.CleanseInternalServerError(layerError)); } } await SendReplyAsync(headers.conversation_id, result, layerStack, requestMetrics); Metrics.FinishRequestMetrics(requestMetrics, totalTime); metrics.Emit(requestMetrics); }); // no state change needed return(null); }
internal static Frame MessageToFrame(ulong conversationId, string methodName, PayloadType type, IMessage payload, IBonded layerData, Logger logger) { var frame = new Frame(logger); { var headers = new EpoxyHeaders { conversation_id = conversationId, payload_type = type, method_name = methodName ?? string.Empty, // method_name is not nullable }; if (payload.IsError) { headers.error_code = payload.Error.Deserialize <Error>().error_code; } else { headers.error_code = (int)ErrorCode.OK; } const int initialHeaderBufferSize = 150; var outputBuffer = new OutputBuffer(initialHeaderBufferSize); var fastWriter = new FastBinaryWriter <OutputBuffer>(outputBuffer); Serialize.To(fastWriter, headers); frame.Add(new Framelet(FrameletType.EpoxyHeaders, outputBuffer.Data)); } if (layerData != null) { const int initialLayerDataBufferSize = 150; var outputBuffer = new OutputBuffer(initialLayerDataBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); // TODO: See TODO below about issues with IBonded Marshal.TO(...) compactWriter.WriteVersion(); layerData.Serialize(compactWriter); frame.Add(new Framelet(FrameletType.LayerData, outputBuffer.Data)); } { var userData = payload.IsError ? (IBonded)payload.Error : (IBonded)payload.RawPayload; const int initialPayloadBufferSize = 1024; var outputBuffer = new OutputBuffer(initialPayloadBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); // TODO: marshal dies on IBonded Marshal.To(compactWriter, request) // understand more deeply why and consider fixing compactWriter.WriteVersion(); userData.Serialize(compactWriter); frame.Add(new Framelet(FrameletType.PayloadData, outputBuffer.Data)); } return(frame); }
internal static Frame MessageToFrame( ulong conversationId, string serviceName, string methodName, EpoxyMessageType type, IMessage message, IBonded layerData, Logger logger) { var frame = new Frame(logger); { var headers = new EpoxyHeaders { conversation_id = conversationId, message_type = type, service_name = serviceName ?? string.Empty, // service_name is not nullable method_name = methodName ?? string.Empty // method_name is not nullable }; const int initialHeaderBufferSize = 150; var outputBuffer = new OutputBuffer(initialHeaderBufferSize); var fastWriter = new FastBinaryWriter <OutputBuffer>(outputBuffer); Serialize.To(fastWriter, headers); frame.Add(new Framelet(FrameletType.EpoxyHeaders, outputBuffer.Data)); } if (layerData != null) { const int initialLayerDataBufferSize = 150; var outputBuffer = new OutputBuffer(initialLayerDataBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); compactWriter.WriteVersion(); layerData.Serialize(compactWriter); frame.Add(new Framelet(FrameletType.LayerData, outputBuffer.Data)); } { FrameletType frameletType = message.IsError ? FrameletType.ErrorData : FrameletType.PayloadData; IBonded userData = message.IsError ? message.Error : message.RawPayload; const int initialMessageBufferSize = 1024; var outputBuffer = new OutputBuffer(initialMessageBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); compactWriter.WriteVersion(); userData.Serialize(compactWriter); frame.Add(new Framelet(frameletType, outputBuffer.Data)); } return(frame); }
private State?DispatchRequest(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData) { if (headers.error_code != (int)ErrorCode.OK) { logger.Site().Error("{0} Received request with a non-zero error code. Conversation ID: {1}", this, headers.conversation_id); protocolError = ProtocolErrorCode.PROTOCOL_VIOLATED; return(State.SendProtocolError); } Task.Run(async() => { var totalTime = Stopwatch.StartNew(); var requestMetrics = Metrics.StartRequestMetrics(ConnectionMetrics); var receiveContext = new EpoxyReceiveContext(this, ConnectionMetrics, requestMetrics); IMessage request = Message.FromPayload(Unmarshal.From(payload)); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); ILayerStack layerStack; Error layerError = parentTransport.GetLayerStack(requestMetrics.request_id, out layerStack); if (layerError == null) { layerError = LayerStackUtils.ProcessOnReceive( layerStack, MessageType.Request, receiveContext, bondedLayerData, logger); } IMessage result; if (layerError == null) { result = await serviceHost.DispatchRequest(headers.method_name, receiveContext, request); } else { logger.Site().Error("{0} Receiving request {1}/{2} failed due to layer error (Code: {3}, Message: {4}).", this, headers.conversation_id, headers.method_name, layerError.error_code, layerError.message); // Set layer error as result of this Bond method call and do not dispatch to method. // Since this error will be returned to client, cleanse out internal server error details, if any. result = Message.FromError(Errors.CleanseInternalServerError(layerError)); } await SendReplyAsync(headers.conversation_id, result, layerStack, requestMetrics); Metrics.FinishRequestMetrics(requestMetrics, totalTime); metrics.Emit(requestMetrics); }); // no state change needed return(null); }
internal static Frame MessageToFrame(ulong conversationId, string methodName, PayloadType type, IMessage message, IBonded layerData, Logger logger) { var frame = new Frame(logger); { var headers = new EpoxyHeaders { conversation_id = conversationId, payload_type = type, method_name = methodName ?? string.Empty, // method_name is not nullable }; if (message.IsError) { headers.error_code = message.Error.Deserialize <Error>().error_code; } else { headers.error_code = (int)ErrorCode.OK; } const int initialHeaderBufferSize = 150; var outputBuffer = new OutputBuffer(initialHeaderBufferSize); var fastWriter = new FastBinaryWriter <OutputBuffer>(outputBuffer); Serialize.To(fastWriter, headers); frame.Add(new Framelet(FrameletType.EpoxyHeaders, outputBuffer.Data)); } if (layerData != null) { const int initialLayerDataBufferSize = 150; var outputBuffer = new OutputBuffer(initialLayerDataBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); compactWriter.WriteVersion(); layerData.Serialize(compactWriter); frame.Add(new Framelet(FrameletType.LayerData, outputBuffer.Data)); } { var userData = message.IsError ? (IBonded)message.Error : (IBonded)message.RawPayload; const int initialPayloadBufferSize = 1024; var outputBuffer = new OutputBuffer(initialPayloadBufferSize); var compactWriter = new CompactBinaryWriter <OutputBuffer>(outputBuffer); compactWriter.WriteVersion(); userData.Serialize(compactWriter); frame.Add(new Framelet(FrameletType.PayloadData, outputBuffer.Data)); } return(frame); }
void DispatchEvent(EpoxyHeaders headers, EpoxyProtocol.MessageData messageData, ArraySegment <byte> layerData) { if (messageData.IsError) { logger.Site().Error( "{0} Received event with an error message. Only payload messages are allowed. Conversation ID: {1}", this, headers.conversation_id); return; } Task.Run( async() => { IMessage request = Message.FromPayload(Unmarshal.From(messageData.Data)); var totalTime = Stopwatch.StartNew(); var requestMetrics = Metrics.StartRequestMetrics(ConnectionMetrics); var receiveContext = new RelayEpoxyReceiveContext(this, ConnectionMetrics, requestMetrics); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); ILayerStack layerStack; Error layerError = parentTransport.GetLayerStack(requestMetrics.request_id, out layerStack); if (layerError == null) { layerError = LayerStackUtils.ProcessOnReceive( layerStack, MessageType.EVENT, receiveContext, bondedLayerData, logger); } if (layerError != null) { logger.Site().Error( "{0}: Receiving event {1}/{2}.{3} failed due to layer error (Code: {4}, Message: {5}).", this, headers.conversation_id, headers.service_name, headers.method_name, layerError.error_code, layerError.message); return; } await serviceHost.DispatchEvent(headers.service_name, headers.method_name, receiveContext, request); Metrics.FinishRequestMetrics(requestMetrics, totalTime); metrics.Emit(requestMetrics); }); }
void DispatchResponse(EpoxyHeaders headers, EpoxyProtocol.MessageData messageData, ArraySegment <byte> layerData) { IMessage response = messageData.IsError ? Message.FromError(Unmarshal <Error> .From(messageData.Data)) : Message.FromPayload(Unmarshal.From(messageData.Data)); TaskCompletionSource <IMessage> tcs = responseMap.TakeTaskCompletionSource(headers.conversation_id); if (tcs == null) { logger.Site().Error( "{0} Response for unmatched request. Conversation ID: {1}", this, headers.conversation_id); return; } Task.Run( () => { var totalTime = Stopwatch.StartNew(); var requestMetrics = Metrics.StartRequestMetrics(ConnectionMetrics); var receiveContext = new RelayEpoxyReceiveContext(this, ConnectionMetrics, requestMetrics); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); ILayerStack layerStack = tcs.Task.AsyncState as ILayerStack; Error layerError = LayerStackUtils.ProcessOnReceive(layerStack, MessageType.RESPONSE, receiveContext, bondedLayerData, logger); if (layerError != null) { logger.Site().Error( "{0} Receiving response {1}/{2}.{3} failed due to layer error (Code: {4}, Message: {5}).", this, headers.conversation_id, headers.service_name, headers.method_name, layerError.error_code, layerError.message); response = Message.FromError(layerError); } tcs.SetResult(response); Metrics.FinishRequestMetrics(requestMetrics, totalTime); metrics.Emit(requestMetrics); }); }
public void TransitionExpectEpoxyHeaders_InvalidPreconditions() { EpoxyHeaders headers = null; ProtocolErrorCode?errorCode = null; var after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, emptyFrame, ref headers, ref errorCode, LoggerTests.BlackHole); Assert.AreEqual(EpoxyProtocol.ClassifyState.InternalStateError, after); Assert.Null(headers); Assert.Null(errorCode); after = EpoxyProtocol.TransitionExpectEpoxyHeaders( EpoxyProtocol.ClassifyState.ExpectEpoxyHeaders, backwardsRequestFrame, ref headers, ref errorCode, LoggerTests.BlackHole); Assert.AreEqual(EpoxyProtocol.ClassifyState.InternalStateError, after); Assert.Null(headers); Assert.Null(errorCode); }
private void DispatchResponse(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData) { IMessage response; if (headers.error_code != (int)ErrorCode.OK) { response = Message.FromError(Unmarshal <Error> .From(payload)); } else { response = Message.FromPayload(Unmarshal.From(payload)); } TaskCompletionSource <IMessage> tcs = responseMap.TakeTaskCompletionSource(headers.conversation_id); if (tcs == null) { logger.Site().Error("{0} Response for unmatched request. Conversation ID: {1}", this, headers.conversation_id); return; } Task.Run(() => { var receiveContext = new EpoxyReceiveContext(this); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); ILayerStack layerStack = tcs.Task.AsyncState as ILayerStack; Error layerError = LayerStackUtils.ProcessOnReceive(layerStack, MessageType.Response, receiveContext, bondedLayerData, logger); if (layerError != null) { logger.Site().Error("{0} Receiving response {1}/{2} failed due to layer error (Code: {3}, Message: {4}).", this, headers.conversation_id, headers.method_name, layerError.error_code, layerError.message); response = Message.FromError(layerError); } tcs.SetResult(response); }); }
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); }
private State?DispatchRequest(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData) { if (headers.error_code != (int)ErrorCode.OK) { Log.Error("{0}.{1}: Received request with a non-zero error code. Conversation ID: {2}", this, nameof(DispatchRequest), headers.conversation_id); protocolError = ProtocolErrorCode.PROTOCOL_VIOLATED; return(State.SendProtocolError); } IMessage request = Message.FromPayload(Unmarshal.From(payload)); var receiveContext = new EpoxyReceiveContext(this); IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData); Error layerError = LayerStackUtils.ProcessOnReceive(parentTransport.LayerStack, MessageType.Request, receiveContext, bondedLayerData); Task.Run(async() => { IMessage result; if (layerError == null) { result = await serviceHost.DispatchRequest(headers.method_name, receiveContext, request, connectionMetrics); } else { Log.Error("{0}.{1}: Receiving request {2}/{3} failed due to layer error (Code: {4}, Message: {5}).", this, nameof(DispatchRequest), headers.conversation_id, headers.method_name, layerError.error_code, layerError.message); result = Message.FromError(layerError); } await SendReplyAsync(headers.conversation_id, result); }); // no state change needed return(null); }
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); } }
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 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); }
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); }
internal static ClassifyResult Classify(Frame frame, Logger logger) { if (frame == null) { return(new ClassifyResult { Disposition = FrameDisposition.Indeterminate }); } logger.Site().Debug("Processing {0} framelets.", frame.Count); var state = ClassifyState.ExpectFirstFramelet; var disposition = FrameDisposition.Indeterminate; EpoxyHeaders headers = null; var layerData = new ArraySegment <byte>(); var messageData = default(MessageData); ProtocolError error = null; ProtocolErrorCode?errorCode = null; uint transitions = 0; while (true) { // If it looks like we have a bug and are looping forever, bail out of the state machine. if (transitions++ > maximumTransitions) { return(new ClassifyResult { Disposition = FrameDisposition.Indeterminate }); } switch (state) { case ClassifyState.ExpectFirstFramelet: state = TransitionExpectFirstFramelet(state, frame, ref errorCode, logger); continue; case ClassifyState.ExpectEpoxyHeaders: state = TransitionExpectEpoxyHeaders(state, frame, ref headers, ref errorCode, logger); continue; case ClassifyState.ExpectOptionalLayerData: state = TransitionExpectOptionalLayerData(state, frame, headers, ref layerData, ref errorCode, logger); continue; case ClassifyState.ExpectMessageData: state = TransitionExpectMessageData(state, frame, headers, layerData, ref messageData, ref errorCode, logger); continue; case ClassifyState.ExpectEndOfFrame: state = TransitionExpectEndOfFrame(state, frame, layerData, ref errorCode, logger); continue; case ClassifyState.FrameComplete: state = TransitionFrameComplete(state, headers, ref errorCode, logger); continue; case ClassifyState.ValidFrame: state = TransitionValidFrame(state, headers, ref disposition); continue; case ClassifyState.ExpectConfig: state = TransitionExpectConfig(state, frame, ref errorCode, ref disposition, logger); continue; case ClassifyState.ExpectProtocolError: state = TransitionExpectProtocolError(state, frame, ref error, ref disposition, logger); continue; case ClassifyState.ClassifiedValidFrame: if (disposition == FrameDisposition.Indeterminate) { state = ClassifyState.InternalStateError; continue; } return(new ClassifyResult { Disposition = disposition, Headers = headers, LayerData = layerData, MessageData = messageData, Error = error }); case ClassifyState.MalformedFrame: if (errorCode == null) { state = ClassifyState.InternalStateError; continue; } return(new ClassifyResult { Disposition = FrameDisposition.SendProtocolError, ErrorCode = errorCode }); case ClassifyState.ErrorInErrorFrame: return(new ClassifyResult { Disposition = FrameDisposition.HangUp, Error = new ProtocolError { error_code = ProtocolErrorCode.ERROR_IN_ERROR } }); case ClassifyState.InternalStateError: return(new ClassifyResult { Disposition = FrameDisposition.Indeterminate }); default: logger.Site().Error("Unhandled state {0}. Dropping frame.", state); return(new ClassifyResult { Disposition = FrameDisposition.Indeterminate }); } } }
// These end-to-end tests cover states that don't fit in functions. private static void AssertHeadersEqual(EpoxyHeaders expected, EpoxyHeaders actual) { Assert.AreEqual(expected.conversation_id, actual.conversation_id); Assert.AreEqual(expected.message_type, actual.message_type); Assert.AreEqual(expected.service_name, actual.service_name); Assert.AreEqual(expected.method_name, actual.method_name); }
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 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 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; } }
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; }
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; }