Пример #1
0
            public void Test_Constructor()
            {
                var dev = new CaptureFileReaderDevice("../../CaptureFiles/80211_ack_frame.pcap");

                dev.Open();
                var rawCapture = dev.GetNextPacket();

                dev.Close();

                Packet   p     = Packet.ParsePacket(rawCapture.LinkLayerType, rawCapture.Data);
                AckFrame frame = (AckFrame)p.PayloadPacket;

                Assert.AreEqual(0, frame.FrameControl.ProtocolVersion);
                Assert.AreEqual(FrameControlField.FrameSubTypes.ControlACK, frame.FrameControl.SubType);
                Assert.IsFalse(frame.FrameControl.ToDS);
                Assert.IsFalse(frame.FrameControl.FromDS);
                Assert.IsFalse(frame.FrameControl.MoreFragments);
                Assert.IsFalse(frame.FrameControl.Retry);
                Assert.IsFalse(frame.FrameControl.PowerManagement);
                Assert.IsFalse(frame.FrameControl.MoreData);
                Assert.IsFalse(frame.FrameControl.Protected);
                Assert.IsFalse(frame.FrameControl.Order);
                Assert.AreEqual(0, frame.Duration.Field);  //this need expanding on in the future
                Assert.AreEqual("F8DB7F491342", frame.ReceiverAddress.ToString().ToUpper());
                Assert.AreEqual(0xD2F5BE07, frame.FrameCheckSequence);
                Assert.AreEqual(10, frame.FrameSize);
            }
Пример #2
0
            public void Test_Constructor_ConstructWithValues()
            {
                AckFrame frame = new AckFrame(PhysicalAddress.Parse("111111111111"));

                frame.FrameControl.ToDS          = false;
                frame.FrameControl.FromDS        = true;
                frame.FrameControl.MoreFragments = true;

                frame.Duration.Field = 0x1234;

                frame.UpdateFrameCheckSequence();
                UInt32 fcs = frame.FrameCheckSequence;

                //serialize the frame into a byte buffer
                var bytes = frame.Bytes;
                var bas   = new ByteArraySegment(bytes);

                //create a new frame that should be identical to the original
                AckFrame recreatedFrame = MacFrame.ParsePacket(bas) as AckFrame;

                recreatedFrame.UpdateFrameCheckSequence();

                Assert.AreEqual(FrameControlField.FrameSubTypes.ControlACK, recreatedFrame.FrameControl.SubType);
                Assert.IsFalse(recreatedFrame.FrameControl.ToDS);
                Assert.IsTrue(recreatedFrame.FrameControl.FromDS);
                Assert.IsTrue(recreatedFrame.FrameControl.MoreFragments);

                Assert.AreEqual("111111111111", recreatedFrame.ReceiverAddress.ToString().ToUpper());

                Assert.AreEqual(fcs, recreatedFrame.FrameCheckSequence);
            }
Пример #3
0
            public void Test_Constructor_ConstructWithValues ()
            {
                AckFrame frame = new AckFrame (PhysicalAddress.Parse ("111111111111"));
                
                frame.FrameControl.ToDS = false;
                frame.FrameControl.FromDS = true;
                frame.FrameControl.MoreFragments = true;
                
                frame.Duration.Field = 0x1234;
                
                frame.UpdateFrameCheckSequence ();
                UInt32 fcs = frame.FrameCheckSequence;
                
                //serialize the frame into a byte buffer
                var bytes = frame.Bytes;
                var bas = new ByteArraySegment (bytes);

                //create a new frame that should be identical to the original
                AckFrame recreatedFrame = MacFrame.ParsePacket (bas) as AckFrame;
                recreatedFrame.UpdateFrameCheckSequence ();
                
                Assert.AreEqual (FrameControlField.FrameSubTypes.ControlACK, recreatedFrame.FrameControl.SubType);
                Assert.IsFalse (recreatedFrame.FrameControl.ToDS);
                Assert.IsTrue (recreatedFrame.FrameControl.FromDS);
                Assert.IsTrue (recreatedFrame.FrameControl.MoreFragments);
                
                Assert.AreEqual ("111111111111", recreatedFrame.ReceiverAddress.ToString ().ToUpper ());
                
                Assert.AreEqual (fcs, recreatedFrame.FrameCheckSequence);
            }
Пример #4
0
        public void TestAckFrameEmptyBody()
        {
            Dictionary <string, string> headers = new Dictionary <string, string>();

            headers.Add(StompHeaders.ID, "id");

            //test for null body
            AckFrame ackFrame = new AckFrame(null, headers);

            StringAssert.AreEqualIgnoringCase(
                string.Format(MESSAGE_TEMPLATE,
                              "ACK",
                              "id:id",
                              "\0"),
                ackFrame.AsStompMessage());

            //test for empty body
            AckFrame ackFrameEmptyBody = new AckFrame("", headers);

            StringAssert.AreEqualIgnoringCase(string.Format(MESSAGE_TEMPLATE,
                                                            "ACK",
                                                            "id:id",
                                                            "\0"),
                                              ackFrameEmptyBody.AsStompMessage());
        }
Пример #5
0
        /// <summary>
        /// Process a ack frame to remove packets that were acknowledged from the history
        /// </summary>
        /// <param name="frame">The AckFrame to process</param>
        /// <returns>Number of packet ack</returns>
        public UInt32 ProcessAckFrame(AckFrame frame)
        {
            UInt32 ack        = 0;
            UInt32 endOfRange = (UInt32)(frame.LargestAcknowledged.Value - frame.FirstAckRange.Value);

            HistoryMutex.WaitOne();

            for (UInt32 i = (UInt32)frame.LargestAcknowledged.Value; i > endOfRange; i--)
            {
                History.Remove(i);
                ack++;
            }

            foreach ((VariableLengthInteger, VariableLengthInteger)tuple in frame.AckRanges)
            {
                endOfRange -= (UInt32)tuple.Item1.Value;
                for (UInt32 j = 0; j < (UInt32)tuple.Item2.Value; j++)
                {
                    History.Remove(endOfRange);
                    endOfRange--;
                    ack++;
                }
            }
            HistoryMutex.ReleaseMutex();

            return(ack);
        }
Пример #6
0
        public void TestEncodeDecode()
        {
            AckFrame af = new AckFrame
            {
                LargestAcknowledged = new VariableLengthInteger(32767),
                Delay         = new VariableLengthInteger(500),
                AckRangeCount = new VariableLengthInteger(1),
                FirstAckRange = new VariableLengthInteger(10),
                AckRanges     = new List <(VariableLengthInteger, VariableLengthInteger)> {
                    (new VariableLengthInteger(3), new VariableLengthInteger(40))
                },
                ECT0   = new VariableLengthInteger(1345),
                ECT1   = new VariableLengthInteger(1234),
                ECN_CE = new VariableLengthInteger(84),
            };

            byte[] b = af.Encode();

            AckFrame afDecoded = new AckFrame();

            Assert.AreEqual(b.Length * 8, afDecoded.Decode(b, 0));

            Assert.AreEqual((UInt64)32767, afDecoded.LargestAcknowledged.Value);
            Assert.AreEqual((UInt64)500, afDecoded.Delay.Value);
            Assert.AreEqual((UInt64)1, afDecoded.AckRangeCount.Value);
            Assert.AreEqual((UInt64)10, afDecoded.FirstAckRange.Value);
            Assert.AreEqual((UInt64)1345, afDecoded.ECT0.Value);
            Assert.AreEqual((UInt64)1234, afDecoded.ECT1.Value);
            Assert.AreEqual((UInt64)84, afDecoded.ECN_CE.Value);

            Assert.AreEqual(1, afDecoded.AckRanges.Count);
            Assert.AreEqual((UInt64)3, afDecoded.AckRanges[0].Item1.Value);
            Assert.AreEqual((UInt64)40, afDecoded.AckRanges[0].Item2.Value);
        }
Пример #7
0
            public void Test_ConstructorWithCorruptBuffer()
            {
                //buffer is way too short for frame. We are just checking it doesn't throw
                Byte[]   corruptBuffer = new Byte[] { 0x01 };
                AckFrame frame         = new AckFrame(new ByteArraySegment(corruptBuffer));

                Assert.IsFalse(frame.FCSValid);
            }
Пример #8
0
        /// <summary>
        /// Read a received packet and process its frames.
        /// </summary>
        /// <param name="packet">The received packet</param>
        public void ReadPacket(Packet packet)
        {
            // Process every new packet
            if (!_packetManager.IsPacketOld(packet))
            {
                packet.DecodeFrames();


                foreach (Frame frame in packet.Frames)
                {
                    if (frame is StreamFrame)
                    {
                        StreamFrame sf = frame as StreamFrame;
                        Logger.Write($"Received StreamFrame in packet number {packet.PacketNumber} with message: {System.Text.Encoding.UTF8.GetString(sf.Data)}");
                        QuicStream stream;
                        try
                        {
                            stream = GetStream(sf.StreamID.Value);
                        }
                        catch (ArgumentException)
                        {
                            stream = CreateStream(0x00);
                        }
                        stream.AddFrameToRead(sf);
                    }
                    if (frame is AckFrame)
                    {
                        AckFrame af = frame as AckFrame;
                        Logger.Write($"Received AckFrame in packet #{packet.PacketNumber}");
                        _packetManager.ProcessAckFrame(af);
                    }
                }

                // Store received PacketNumber for further implementation of acknowledgement procedure
                _received.Add(packet.PacketNumber);
            }
            else
            {
                // The packet was sent again so we send another ack for it
                packet.IsAckEliciting = true;
            }

            // Generate a new Ack Frame and send it directly
            // Even if the packet is old, we send a new ack for this ; the ack packet may not have been received
            if (packet.IsAckEliciting)
            {
                AckFrame ack = new AckFrame(new List <UInt32>()
                {
                    packet.PacketNumber
                }, 100);
                AddFrame(ack);
                SendCurrentPacket();
                Logger.Write($"Acked packet #{packet.PacketNumber}");
            }
        }
Пример #9
0
        public void TestConstructor()
        {
            List <UInt32> l = new List <UInt32>()
            {
                1, 2, 4, 6, 5, 10, 11, 12, 13
            };
            AckFrame af = new AckFrame(l, 500);

            Assert.AreEqual((UInt64)2, af.AckRangeCount.Value);
            Assert.AreEqual((UInt64)13, af.LargestAcknowledged.Value);
            Assert.AreEqual((UInt64)4, af.FirstAckRange.Value);
            Assert.AreEqual((UInt64)500, af.Delay.Value);

            Assert.AreEqual((UInt64)0, af.ECT0.Value);
            Assert.AreEqual((UInt64)0, af.ECT1.Value);
            Assert.AreEqual((UInt64)0, af.ECN_CE.Value);
        }
Пример #10
0
			public void Test_ConstructorWithCorruptBuffer ()
			{
				//buffer is way too short for frame. We are just checking it doesn't throw
				byte[] corruptBuffer = new byte[]{0x01};
				AckFrame frame = new AckFrame(new ByteArraySegment(corruptBuffer));
				Assert.IsFalse(frame.FCSValid);
			}
Пример #11
0
            /// <summary>
            /// Parses the <see cref="Kavprot.Packets.Utils.ByteArraySegment"/> into a MacFrame.
            /// </summary>
            /// <returns>
            /// The parsed MacFrame or null if it could not be parsed.
            /// </returns>
            /// <param name='bas'>
            /// The bytes of the packet. bas.Offset should point to the first byte in the mac frame.
            /// </param>
            /// <remarks>If the provided bytes contain the FCS then call <see cref="MacFrame.ParsePacketWithFcs"/> instead. The presence of the
            /// FCS is usually determined by configuration of the device used to capture the packets.</remarks>
            public static MacFrame ParsePacket(ByteArraySegment bas)
            {
                if (bas.Length < MacFields.FrameControlLength)
                {
                    //there isn't enough data to even try and work out what type of packet it is
                    return(null);
                }
                //this is a bit ugly as we will end up parsing the framecontrol field twice, once here and once
                //inside the packet constructor. Could create the framecontrol and pass it to the packet but I think that is equally ugly
                FrameControlField frameControl = new FrameControlField(
                    EndianBitConverter.Big.ToUInt16(bas.Bytes, bas.Offset));

                MacFrame macFrame = null;

                switch (frameControl.SubType)
                {
                case FrameControlField.FrameSubTypes.ManagementAssociationRequest:
                {
                    macFrame = new AssociationRequestFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementAssociationResponse:
                {
                    macFrame = new AssociationResponseFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementReassociationRequest:
                {
                    macFrame = new ReassociationRequestFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementReassociationResponse:
                {
                    macFrame = new AssociationResponseFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementProbeRequest:
                {
                    macFrame = new ProbeRequestFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementProbeResponse:
                {
                    macFrame = new ProbeResponseFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementReserved0:
                    break; //TODO

                case FrameControlField.FrameSubTypes.ManagementReserved1:
                    break; //TODO

                case FrameControlField.FrameSubTypes.ManagementBeacon:
                {
                    macFrame = new BeaconFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementATIM:
                    break; //TODO

                case FrameControlField.FrameSubTypes.ManagementDisassociation:
                {
                    macFrame = new DisassociationFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementAuthentication:
                {
                    macFrame = new AuthenticationFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementDeauthentication:
                {
                    macFrame = new DeauthenticationFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementAction:
                {
                    macFrame = new ActionFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ManagementReserved3:
                    break; //TODO

                case FrameControlField.FrameSubTypes.ControlBlockAcknowledgmentRequest:
                {
                    macFrame = new BlockAcknowledgmentRequestFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlBlockAcknowledgment:
                {
                    macFrame = new BlockAcknowledgmentFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlPSPoll:
                    break; //TODO

                case FrameControlField.FrameSubTypes.ControlRTS:
                {
                    macFrame = new RtsFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlCTS:
                {
                    macFrame = new CtsFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlACK:
                {
                    macFrame = new AckFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlCFEnd:
                {
                    macFrame = new ContentionFreeEndFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.ControlCFEndCFACK:
                    break; //TODO

                case FrameControlField.FrameSubTypes.Data:
                case FrameControlField.FrameSubTypes.DataCFACK:
                case FrameControlField.FrameSubTypes.DataCFPoll:
                case FrameControlField.FrameSubTypes.DataCFAckCFPoll:
                {
                    macFrame = new DataDataFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.DataNullFunctionNoData:
                case FrameControlField.FrameSubTypes.DataCFAckNoData:
                case FrameControlField.FrameSubTypes.DataCFPollNoData:
                case FrameControlField.FrameSubTypes.DataCFAckCFPollNoData:
                {
                    macFrame = new NullDataFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.QosData:
                case FrameControlField.FrameSubTypes.QosDataAndCFAck:
                case FrameControlField.FrameSubTypes.QosDataAndCFPoll:
                case FrameControlField.FrameSubTypes.QosDataAndCFAckAndCFPoll:
                {
                    macFrame = new QosDataFrame(bas);
                    break;
                }

                case FrameControlField.FrameSubTypes.QosNullData:
                case FrameControlField.FrameSubTypes.QosCFAck:
                case FrameControlField.FrameSubTypes.QosCFPoll:
                case FrameControlField.FrameSubTypes.QosCFAckAndCFPoll:
                {
                    macFrame = new QosNullDataFrame(bas);
                    break;
                }

                default:
                    //this is an unsupported (and unknown) packet type
                    break;
                }

                return(macFrame);
            }
        public INetworkPacket Deserialize(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            INetworkPacket packet = null;

            var start1 = reader.ReadByte();

            switch (start1)
            {
            case Constants.MBUS_FRAME_ACK_START:
            {
                packet = new AckFrame();
            }
            break;

            case Constants.MBUS_FRAME_SHORT_START:
            {
                //if (stream.Length != 5)
                //    return false;

                if (start1 != Constants.MBUS_FRAME_SHORT_START)
                {
                    return(packet);
                }

                var control = reader.ReadByte();
                var address = reader.ReadByte();
                var crc     = reader.ReadByte();

                if (crc != new byte[] { control, address }.CheckSum())
                {
                    return(packet);
                }

                var stop = reader.ReadByte();

                if (stop != Constants.MBUS_FRAME_STOP)
                {
                    return(packet);
                }

                packet = new ShortFrame(control, address);
            }
            break;

            case Constants.MBUS_FRAME_LONG_START:
            {
                const int MBUS_FRAME_FIXED_SIZE_LONG = 6;

                var length1 = reader.ReadByte();
                var length2 = reader.ReadByte();

                if (length1 < 3)
                {
                    return(packet);
                }

                if (length1 != length2)
                {
                    return(packet);
                }

                //if (stream.Length < length1 + MBUS_FRAME_FIXED_SIZE_LONG)
                //    return false;

                var start2 = reader.ReadByte();

                if (start2 != Constants.MBUS_FRAME_LONG_START)
                {
                    return(packet);
                }

                var control            = reader.ReadByte();
                var address            = reader.ReadByte();
                var controlInformation = reader.ReadByte();

                var data = reader.ReadBytes(length1 - 3);

                if (data.Length != length1 - 3)
                {
                    return(packet);
                }

                var crc  = reader.ReadByte();
                var stop = reader.ReadByte();

                if (crc != new byte[] { control, address, controlInformation }.Merge(data).CheckSum())
                {
                    return(packet);
                }

                if (length1 - 3 == 0)
                {
                    packet = new ControlFrame(control, controlInformation, address);
                }
                else if ((ControlInformation)controlInformation == ControlInformation.RESP_VARIABLE)
                {
                    packet = new VariableDataLongFrame(control, controlInformation, address, data, length1);
                }
                else if ((ControlInformation)controlInformation == ControlInformation.RESP_FIXED)
                {
                    packet = new FixedDataLongFrame(control, controlInformation, address, data, length1);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            break;
            }

            return(packet);
        }
        private static int Serialize(AckFrame frame, BinaryWriter writer)
        {
            writer.Write(Constants.MBUS_FRAME_ACK_START);

            return(1);
        }
Пример #14
0
        public Frame GetFrame()
        {
            Frame result;
            var   frameType = _array.PeekByte();

            switch (frameType)
            {
            case 0x00:
                result = new PaddingFrame();
                break;

            case 0x01:
                result = new PingFrame();
                break;

            case 0x02:
                result = new AckFrame();
                break;

            case 0x03:
                result = new AckFrame();
                break;

            case 0x04:
                result = new ResetStreamFrame();
                break;

            case 0x05:
                result = new StopSendingFrame();
                break;

            case 0x06:
                result = new CryptoFrame();
                break;

            case 0x07:
                result = new NewTokenFrame();
                break;

            case 0x08:
                result = new StreamFrame();
                break;

            case 0x09:
                result = new StreamFrame();
                break;

            case 0x0a:
                result = new StreamFrame();
                break;

            case 0x0b:
                result = new StreamFrame();
                break;

            case 0x0c:
                result = new StreamFrame();
                break;

            case 0x0d:
                result = new StreamFrame();
                break;

            case 0x0e:
                result = new StreamFrame();
                break;

            case 0x0f:
                result = new StreamFrame();
                break;

            case 0x10:
                result = new MaxDataFrame();
                break;

            case 0x11:
                result = new MaxStreamDataFrame();
                break;

            case 0x12:
                result = new MaxStreamsFrame();
                break;

            case 0x13:
                result = new MaxStreamsFrame();
                break;

            case 0x14:
                result = new DataBlockedFrame();
                break;

            case 0x15:
                result = new StreamDataBlockedFrame();
                break;

            case 0x16:
                result = new StreamsBlockedFrame();
                break;

            case 0x17:
                result = new StreamsBlockedFrame();
                break;

            case 0x18:
                result = new NewConnectionIdFrame();
                break;

            case 0x19:
                result = new RetireConnectionIdFrame();
                break;

            case 0x1a:
                result = new PathChallengeFrame();
                break;

            case 0x1b:
                result = new PathResponseFrame();
                break;

            case 0x1c:
                result = new ConnectionCloseFrame();
                break;

            case 0x1d:
                result = new ConnectionCloseFrame();
                break;

            default:
                result = null;
                break;
            }

            if (result != null)
            {
                result.Decode(_array);
            }

            return(result);
        }
        public void AckFrame(MessageFrame frame)
        {
            var ack = new AckFrame(_ackExtensionId, frame.SequenceNumber);

            _connection.Send(ack);
        }
Пример #16
0
        /// <summary>
        /// Handles receiving and sending frames on the given stream.
        /// </summary>
        /// <param name="stream">The stream to receive and send frames on</param>
        /// <param name="notifyHandler">Function to invoke when a NOTIFY frame is received</param>
        public void HandleStream(Stream stream, Func <NotifyFrame, IList <SpoeAction> > notifyHandler)
        {
            if (stream == null)
            {
                throw new ApplicationException("Start method requires 'stream' parameter.");
            }

            if (notifyHandler == null)
            {
                throw new ApplicationException("Start method requires 'notifyHandler' parameter.");
            }

            var fragments = new FragmentCatalogue();

            string[] haproxyCapabilities = new string[0];

            while (true)
            {
                bool   sendAgentDisconnect = false;
                string disconnectReason    = string.Empty;
                Status disconnectStatus    = Status.Normal;
                bool   closeConnection     = false;
                Frame  frame          = null;
                var    responseFrames = new ConcurrentQueue <Frame>();

                try
                {
                    byte[] frameBytes = GetBytesForNextFrame(stream);
                    frame = ParseFrame(frameBytes);

                    if (this.EnableLogging)
                    {
                        if (frame != null)
                        {
                            this.LogFunc(frame.ToString());
                        }
                    }

                    switch (frame.Type)
                    {
                    case FrameType.HaproxyHello:
                        var agentHelloFrame = HandleHandshake(frame);
                        responseFrames.Enqueue(agentHelloFrame);

                        // if this is only a SPOP health check (option spop-check), then close the connection
                        if (((KeyValueListPayload)frame.Payload).KeyValueItems.Any(item => item.Key == "healthcheck" && (bool)item.Value.Value))
                        {
                            closeConnection = true;
                        }
                        else
                        {
                            string haproxyCapabilitiesString =
                                (string)((KeyValueListPayload)frame.Payload).KeyValueItems.First(item => item.Key == "capabilities").Value.Value;
                            haproxyCapabilities = haproxyCapabilitiesString.Split(',');
                        }
                        break;

                    case FrameType.HaproxyDisconnect:
                        sendAgentDisconnect = true;
                        disconnectStatus    = Status.Normal;
                        disconnectReason    = "HAProxy disconnected";
                        break;

                    case FrameType.Notify:
                        if (frame.Metadata.Flags.Fin)
                        {
                            var actions = notifyHandler((NotifyFrame)frame);

                            var ackFrame = new AckFrame(
                                frame.Metadata.StreamId.Value,
                                frame.Metadata.FrameId.Value,
                                actions ?? new List <SpoeAction>());

                            // fragment if necessary
                            if (haproxyCapabilities.Contains("fragmentation"))
                            {
                                List <Frame> fragmentedFrames = ((AckFrame)ackFrame).FragmentFrame(this.MaxFrameSize);

                                foreach (var f in fragmentedFrames)
                                {
                                    responseFrames.Enqueue(f);
                                }
                            }
                            else
                            {
                                responseFrames.Enqueue(ackFrame);
                            }
                        }
                        else
                        {
                            fragments.Push(
                                frame.Metadata.StreamId.Value,
                                frame.Metadata.FrameId.Value,
                                frameBytes);
                        }
                        break;

                    case FrameType.Unset:
                        if (frame.Metadata.Flags.Abort)
                        {
                            fragments.Discard(frame.Metadata.StreamId.Value, frame.Metadata.FrameId.Value);
                        }
                        else
                        {
                            // The Unset frame continues the data, but we only need
                            // to append its payload, not its metadata
                            fragments.Push(
                                frame.Metadata.StreamId.Value,
                                frame.Metadata.FrameId.Value,
                                frame.Payload.Bytes);

                            if (frame.Metadata.Flags.Fin)
                            {
                                byte[] data           = fragments.Pop(frame.Metadata.StreamId.Value, frame.Metadata.FrameId.Value);
                                Frame  newNotifyFrame = ParseFrame(data);
                                newNotifyFrame.Metadata.Flags.Fin = true;

                                if (this.EnableLogging)
                                {
                                    this.LogFunc(newNotifyFrame.ToString());
                                }

                                var actions = notifyHandler((NotifyFrame)newNotifyFrame);

                                var ackFrame = new AckFrame(
                                    frame.Metadata.StreamId.Value,
                                    frame.Metadata.FrameId.Value,
                                    actions ?? new List <SpoeAction>());

                                // fragment if necessary
                                if (haproxyCapabilities.Contains("fragmentation"))
                                {
                                    var fragmentedFrames = ((AckFrame)ackFrame).FragmentFrame(this.MaxFrameSize);

                                    foreach (var f in fragmentedFrames)
                                    {
                                        responseFrames.Enqueue(f);
                                    }
                                }
                                else
                                {
                                    responseFrames.Enqueue(ackFrame);
                                }
                            }
                        }
                        break;
                    }
                }
                catch (Exception ex)
                {
                    this.LogFunc(ex.ToString());
                    sendAgentDisconnect = true;
                    disconnectStatus    = Status.UnknownError;
                    disconnectReason    = ex.Message;
                    closeConnection     = true;
                }

                if (this.EnableLogging)
                {
                    if (responseFrames.Any())
                    {
                        foreach (var rf in responseFrames)
                        {
                            this.LogFunc(rf.ToString());
                        }
                    }
                }

                if (responseFrames.Any())
                {
                    Frame dequeuedFrame;

                    while (responseFrames.TryDequeue(out dequeuedFrame))
                    {
                        stream.Write(dequeuedFrame.Bytes, 0, dequeuedFrame.Bytes.Length);
                    }
                }

                if (sendAgentDisconnect)
                {
                    Frame disconnectFrame = Disconnect(disconnectStatus, disconnectReason);

                    if (this.EnableLogging)
                    {
                        this.LogFunc(disconnectFrame.ToString());
                    }

                    stream.Write(disconnectFrame.Bytes, 0, disconnectFrame.Bytes.Length);
                    closeConnection = true;
                }

                if (closeConnection)
                {
                    if (this.EnableLogging)
                    {
                        this.LogFunc("Closing connection.");
                    }

                    stream.Close();
                    break;
                }
            }
        }