public void Frame_Add_FrameletsCanBeRetreived() { var AnyOtherContents = new ArraySegment<byte>(new[] {(byte)0x00}); var expectedFramelets = new[] { new Framelet(FrameletType.EpoxyConfig, AnyContents), new Framelet(FrameletType.EpoxyConfig, AnyOtherContents), new Framelet(FrameletType.LayerData, AnyContents), new Framelet(FrameletType.EpoxyConfig, AnyContents), }; var frame = new Frame(4, LoggerTests.BlackHole); frame.Add(new Framelet(FrameletType.EpoxyConfig, AnyContents)); frame.Add(new Framelet(FrameletType.EpoxyConfig, AnyOtherContents)); frame.Add(new Framelet(FrameletType.LayerData, AnyContents)); frame.Add(new Framelet(FrameletType.EpoxyConfig, AnyContents)); Assert.AreEqual(4, frame.Count); Assert.AreEqual(frame.Framelets.Count, frame.Count); CollectionAssert.AreEqual(expectedFramelets, frame.Framelets); }
public async Task Frame_RoundTrip_Works() { var expectedFramelets = new[] { new Framelet(FrameletType.EpoxyConfig, AnyContents), new Framelet(FrameletType.LayerData, AnyContents), new Framelet(FrameletType.EpoxyConfig, AnyContents), }; var frame = new Frame(LoggerTests.BlackHole); foreach (var framelet in expectedFramelets) { frame.Add(framelet); } var memStream = new MemoryStream(); await frame.WriteAsync(memStream); memStream.Seek(0, SeekOrigin.Begin); var resultFrame = await Frame.ReadAsync(memStream, CancellationToken.None, LoggerTests.BlackHole); CollectionAssert.AreEqual(expectedFramelets, resultFrame.Framelets, DeepFrameletComparer.Instance); }
public void Frame_default_ctor_DoesntThrow() { var frame = new Frame(LoggerTests.BlackHole); Assert.AreEqual(0, frame.Count); CollectionAssert.IsEmpty(frame.Framelets); }
public void Frame_WriteAsync_EmptyFrame_Throws() { var frame = new Frame(LoggerTests.BlackHole); Assert.Throws<InvalidOperationException>(async () => await frame.WriteAsync(new MemoryStream())); }
public async Task Frame_WriteAsync_OneFramelet_ContentsExpected() { var frame = new Frame(LoggerTests.BlackHole); frame.Add(new Framelet(FrameletType.EpoxyConfig, AnyContents)); var memStream = new MemoryStream(); await frame.WriteAsync(memStream); var expectedBytes = new[] { 0x01, 0x00, // frame count 0x43, 0x47, // EpoxyConfig framelet type 0x04, 0x00, 0x00, 0x00, // framelet length 0x62, 0x6F, 0x6E, 0x64 // AnyContents bytes }; CollectionAssert.AreEqual(expectedBytes, memStream.ToArray()); }
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; } }
public void Frame_Add_AddMoreThanInt32TotalSize_Throws() { var largeContents = new ArraySegment<byte>(new byte[2 * 65535]); int numFramesToAdd = Int32.MaxValue/largeContents.Count; var frame = new Frame(numFramesToAdd, LoggerTests.BlackHole); for (int i = 0; i < numFramesToAdd - 1; ++i) { frame.Add(new Framelet(FrameletType.LayerData, largeContents)); } Assert.That( () => frame.Add(new Framelet(FrameletType.LayerData, largeContents)), Throws.InvalidOperationException.With.Message.ContainsSubstring("Exceeded maximum size of frame")); }
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; }
public void Frame_Add_TotalCount_Updated() { var frame = new Frame(2, LoggerTests.BlackHole); int expectedSize = 2; Assert.AreEqual(expectedSize, frame.TotalSize); frame.Add(new Framelet(FrameletType.LayerData, AnyContents)); expectedSize += 2 + 4 + AnyContents.Count; Assert.AreEqual(expectedSize, frame.TotalSize); frame.Add(new Framelet(FrameletType.ProtocolError, AnyContents)); expectedSize += 2 + 4 + AnyContents.Count; Assert.AreEqual(expectedSize, frame.TotalSize); }
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; } }
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; }
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; }
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; }
public static async Task<Frame> ReadAsync(Stream stream, CancellationToken ct, Logger logger) { try { var frameletCount = await ReadUInt16Async(stream, ct); if (frameletCount == 0) { throw new EpoxyProtocolErrorException("Zero framelets"); } var frame = new Frame(frameletCount, logger); while (frameletCount > 0) { if (ct.IsCancellationRequested) { return null; } var frameletType = await ReadUInt16Async(stream, ct); if (!Framelet.IsKnownType(frameletType)) { throw new EpoxyProtocolErrorException("Unknown framelet type: " + frameletType); } var frameletLength = await ReadUInt32Async(stream, ct); if (frameletLength > int.MaxValue) { throw new EpoxyProtocolErrorException("Framelet too big: " + frameletLength); } byte[] frameletContents = await ReadBufferAsync(stream, unchecked((int)frameletLength), ct); frame.Add(new Framelet((FrameletType)frameletType, new ArraySegment<byte>(frameletContents))); --frameletCount; } return frame; } catch (Exception ex) when (ex is OperationCanceledException || ex is EndOfStreamException || ex is ObjectDisposedException) { return null; } }
public void Frame_Add_AddMoreThanUInt16Framelets_Throws() { var frame = new Frame((int)UInt16.MaxValue + 1, LoggerTests.BlackHole); for (int i = 0; i < UInt16.MaxValue; ++i) { frame.Add(new Framelet(FrameletType.LayerData, AnyContents)); } Assert.That( () => frame.Add(new Framelet(FrameletType.PayloadData, AnyContents)), Throws.InvalidOperationException.With.Message.ContainsSubstring("Exceeded maximum allowed count of framelets")); }
public static void CreateFrames() { // Set up the non-empty layer data we'll use. Bond.IBonded goodLayerObject = new Bond.Bonded<Dummy>(dummyObject); var outputBuffer = new OutputBuffer(); var compactWriter = new CompactBinaryWriter<OutputBuffer>(outputBuffer); compactWriter.WriteVersion(); goodLayerObject.Serialize(compactWriter); goodLayerData = outputBuffer.Data; // Good frames, from which we can pull good framelets to build bad frames. goodRequestFrame = EpoxyConnection.MessageToFrame( GoodRequestId, GoodService, GoodMethod, EpoxyMessageType.REQUEST, meaninglessPayload, null, LoggerTests.BlackHole); goodRequestLayerDataFrame = EpoxyConnection.MessageToFrame( GoodRequestId, GoodService, GoodMethod, EpoxyMessageType.REQUEST, meaninglessPayload, goodLayerObject, LoggerTests.BlackHole); goodResponseFrame = EpoxyConnection.MessageToFrame( GoodResponseId, GoodService, GoodMethod, EpoxyMessageType.RESPONSE, meaninglessPayload, null, LoggerTests.BlackHole); goodErrorResponseFrame = EpoxyConnection.MessageToFrame( GoodResponseId, GoodService, GoodMethod, EpoxyMessageType.RESPONSE, meaninglessError, null, LoggerTests.BlackHole); goodEventFrame = EpoxyConnection.MessageToFrame( GoodRequestId, GoodService, GoodMethod, EpoxyMessageType.EVENT, meaninglessPayload, null, LoggerTests.BlackHole); configFrame = EpoxyConnection.MakeConfigFrame(LoggerTests.BlackHole); protocolErrorFrame = EpoxyConnection.MakeProtocolErrorFrame(MeaninglessErrorCode, null, LoggerTests.BlackHole); var goodFrameletCount = goodRequestFrame.Count; // Bad frames made of good framelets. shortRequestFrame = new Frame(goodFrameletCount - 1, LoggerTests.BlackHole); for (var i = 0; i < goodFrameletCount - 1; i++) { shortRequestFrame.Add(goodRequestFrame.Framelets[i]); } doubleHeadersRequestFrame = new Frame(goodFrameletCount + 1, LoggerTests.BlackHole); doubleHeadersRequestFrame.Add(goodRequestFrame.Framelets[0]); for (var i = 0; i < goodFrameletCount; i++) { doubleHeadersRequestFrame.Add(goodRequestFrame.Framelets[i]); } headersConfigRequestFrame = new Frame(2, LoggerTests.BlackHole); headersConfigRequestFrame.Add(goodRequestFrame.Framelets[0]); headersConfigRequestFrame.Add(configFrame.Framelets[0]); doublePayloadRequestFrame = new Frame(goodFrameletCount + 1, LoggerTests.BlackHole); for (var i = 0; i < goodFrameletCount; i++) { doublePayloadRequestFrame.Add(goodRequestFrame.Framelets[i]); } doublePayloadRequestFrame.Add(goodRequestFrame.Framelets[goodFrameletCount - 1]); backwardsRequestFrame = new Frame(goodFrameletCount, LoggerTests.BlackHole); foreach (var framelet in goodRequestFrame.Framelets.Reverse()) { backwardsRequestFrame.Add(framelet); } doubleProtocolErrorFrame = EpoxyConnection.MakeProtocolErrorFrame(MeaninglessErrorCode, null, LoggerTests.BlackHole); doubleProtocolErrorFrame.Add(doubleProtocolErrorFrame.Framelets[0]); configFrameExtra = EpoxyConnection.MakeConfigFrame(LoggerTests.BlackHole); configFrameExtra.Add(goodRequestFrame.Framelets[0]); // Bad frames made of bad framelets. var invalidConfigData = new ArraySegment<byte>(new byte[] { 0x01 }); configFrameBadConfigData = new Frame(1, LoggerTests.BlackHole); configFrameBadConfigData.Add(new Framelet(FrameletType.EpoxyConfig, invalidConfigData)); }
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 }; } } }