File: System\ServiceModel\Channels\FramingDecoders.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System.Globalization;
    using System.ServiceModel;
    using System.IO;
    using System.Text;
 
    static class DecoderHelper
    {
        public static void ValidateSize(int size)
        {
            if (size <= 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.GetString(
                    SR.ValueMustBePositive)));
            }
        }
    }
 
    struct IntDecoder
    {
        int value;
        short index;
        bool isValueDecoded;
        const int LastIndex = 4;
 
        public int Value
        {
            get
            {
                if (!isValueDecoded)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return value;
            }
        }
 
        public bool IsValueDecoded
        {
            get { return isValueDecoded; }
        }
 
        public void Reset()
        {
            index = 0;
            value = 0;
            isValueDecoded = false;
        }
 
        public int Decode(byte[] buffer, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
            if (isValueDecoded)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
            }
            int bytesConsumed = 0;
            while (bytesConsumed < size)
            {
                int next = buffer[offset];
                value |= (next & 0x7F) << (index * 7);
                bytesConsumed++;
                if (index == LastIndex && (next & 0xF8) != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.FramingSizeTooLarge)));
                }
                index++;
                if ((next & 0x80) == 0)
                {
                    isValueDecoded = true;
                    break;
                }
                offset++;
            }
            return bytesConsumed;
        }
    }
 
    abstract class StringDecoder
    {
        int encodedSize;
        byte[] encodedBytes;
        int bytesNeeded;
        string value;
        State currentState;
        IntDecoder sizeDecoder;
        int sizeQuota;
        int valueLengthInBytes;
 
        public StringDecoder(int sizeQuota)
        {
            this.sizeQuota = sizeQuota;
            this.sizeDecoder = new IntDecoder();
            this.currentState = State.ReadingSize;
            Reset();
        }
 
        public bool IsValueDecoded
        {
            get { return currentState == State.Done; }
        }
 
        public string Value
        {
            get
            {
                if (currentState != State.Done)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return value;
            }
        }
 
        public int Decode(byte[] buffer, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            int bytesConsumed;
            switch (currentState)
            {
                case State.ReadingSize:
                    bytesConsumed = sizeDecoder.Decode(buffer, offset, size);
                    if (sizeDecoder.IsValueDecoded)
                    {
                        encodedSize = sizeDecoder.Value;
                        if (encodedSize > sizeQuota)
                        {
                            Exception quotaExceeded = OnSizeQuotaExceeded(encodedSize);
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(quotaExceeded);
                        }
                        if (encodedBytes == null || encodedBytes.Length < encodedSize)
                        {
                            encodedBytes = DiagnosticUtility.Utility.AllocateByteArray(encodedSize);
                            value = null;
                        }
                        currentState = State.ReadingBytes;
                        bytesNeeded = encodedSize;
                    }
                    break;
                case State.ReadingBytes:
                    if (value != null && valueLengthInBytes == encodedSize && bytesNeeded == encodedSize &&
                        size >= encodedSize && CompareBuffers(encodedBytes, buffer, offset))
                    {
                        bytesConsumed = bytesNeeded;
                        OnComplete(value);
                    }
                    else
                    {
                        bytesConsumed = bytesNeeded;
                        if (size < bytesNeeded)
                            bytesConsumed = size;
                        Buffer.BlockCopy(buffer, offset, encodedBytes, encodedSize - bytesNeeded, bytesConsumed);
                        bytesNeeded -= bytesConsumed;
                        if (bytesNeeded == 0)
                        {
                            value = Encoding.UTF8.GetString(encodedBytes, 0, encodedSize);
                            valueLengthInBytes = encodedSize;
                            OnComplete(value);
                        }
                    }
                    break;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine)));
            }
 
            return bytesConsumed;
        }
 
        protected virtual void OnComplete(string value)
        {
            this.currentState = State.Done;
        }
 
        static bool CompareBuffers(byte[] buffer1, byte[] buffer2, int offset)
        {
            for (int i = 0; i < buffer1.Length; i++)
            {
                if (buffer1[i] != buffer2[i + offset])
                {
                    return false;
                }
            }
            return true;
        }
 
        protected abstract Exception OnSizeQuotaExceeded(int size);
 
        public void Reset()
        {
            currentState = State.ReadingSize;
            sizeDecoder.Reset();
        }
 
        enum State
        {
            ReadingSize,
            ReadingBytes,
            Done,
        }
    }
 
    class ViaStringDecoder : StringDecoder
    {
        Uri via;
 
        public ViaStringDecoder(int sizeQuota)
            : base(sizeQuota)
        {
        }
 
        protected override Exception OnSizeQuotaExceeded(int size)
        {
            Exception result = new InvalidDataException(SR.GetString(SR.FramingViaTooLong, size));
            FramingEncodingString.AddFaultString(result, FramingEncodingString.ViaTooLongFault);
            return result;
        }
 
        protected override void OnComplete(string value)
        {
            try
            {
                via = new Uri(value);
                base.OnComplete(value);
            }
            catch (UriFormatException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.FramingViaNotUri, value), exception));
            }
        }
 
        public Uri ValueAsUri
        {
            get
            {
                if (!IsValueDecoded)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return via;
            }
        }
    }
 
    class FaultStringDecoder : StringDecoder
    {
        internal const int FaultSizeQuota = 256;
 
        public FaultStringDecoder()
            : base(FaultSizeQuota)
        {
        }
 
        protected override Exception OnSizeQuotaExceeded(int size)
        {
            return new InvalidDataException(SR.GetString(SR.FramingFaultTooLong, size));
        }
 
        public static Exception GetFaultException(string faultString, string via, string contentType)
        {
            if (faultString == FramingEncodingString.EndpointNotFoundFault)
            {
                return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, via));
            }
            else if (faultString == FramingEncodingString.ContentTypeInvalidFault)
            {
                return new ProtocolException(SR.GetString(SR.FramingContentTypeMismatch, contentType, via));
            }
            else if (faultString == FramingEncodingString.ServiceActivationFailedFault)
            {
                return new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, via));
            }
            else if (faultString == FramingEncodingString.ConnectionDispatchFailedFault)
            {
                return new CommunicationException(SR.GetString(SR.Sharing_ConnectionDispatchFailed, via));
            }
            else if (faultString == FramingEncodingString.EndpointUnavailableFault)
            {
                return new EndpointNotFoundException(SR.GetString(SR.Sharing_EndpointUnavailable, via));
            }
            else if (faultString == FramingEncodingString.MaxMessageSizeExceededFault)
            {
                Exception inner = new QuotaExceededException(SR.GetString(SR.FramingMaxMessageSizeExceeded));
                return new CommunicationException(inner.Message, inner);
            }
            else if (faultString == FramingEncodingString.UnsupportedModeFault)
            {
                return new ProtocolException(SR.GetString(SR.FramingModeNotSupportedFault, via));
            }
            else if (faultString == FramingEncodingString.UnsupportedVersionFault)
            {
                return new ProtocolException(SR.GetString(SR.FramingVersionNotSupportedFault, via));
            }
            else if (faultString == FramingEncodingString.ContentTypeTooLongFault)
            {
                Exception inner = new QuotaExceededException(SR.GetString(SR.FramingContentTypeTooLongFault, contentType));
                return new CommunicationException(inner.Message, inner);
            }
            else if (faultString == FramingEncodingString.ViaTooLongFault)
            {
                Exception inner = new QuotaExceededException(SR.GetString(SR.FramingViaTooLongFault, via));
                return new CommunicationException(inner.Message, inner);
            }
            else if (faultString == FramingEncodingString.ServerTooBusyFault)
            {
                return new ServerTooBusyException(SR.GetString(SR.ServerTooBusy, via));
            }
            else if (faultString == FramingEncodingString.UpgradeInvalidFault)
            {
                return new ProtocolException(SR.GetString(SR.FramingUpgradeInvalid, via));
            }
            else
            {
                return new ProtocolException(SR.GetString(SR.FramingFaultUnrecognized, faultString));
            }
        }
    }
 
    class ContentTypeStringDecoder : StringDecoder
    {
        public ContentTypeStringDecoder(int sizeQuota)
            : base(sizeQuota)
        {
        }
 
        protected override Exception OnSizeQuotaExceeded(int size)
        {
            Exception result = new InvalidDataException(SR.GetString(SR.FramingContentTypeTooLong, size));
            FramingEncodingString.AddFaultString(result, FramingEncodingString.ContentTypeTooLongFault);
            return result;
        }
 
        public static string GetString(FramingEncodingType type)
        {
            switch (type)
            {
                case FramingEncodingType.Soap11Utf8:
                    return FramingEncodingString.Soap11Utf8;
                case FramingEncodingType.Soap11Utf16:
                    return FramingEncodingString.Soap11Utf16;
                case FramingEncodingType.Soap11Utf16FFFE:
                    return FramingEncodingString.Soap11Utf16FFFE;
                case FramingEncodingType.Soap12Utf8:
                    return FramingEncodingString.Soap12Utf8;
                case FramingEncodingType.Soap12Utf16:
                    return FramingEncodingString.Soap12Utf16;
                case FramingEncodingType.Soap12Utf16FFFE:
                    return FramingEncodingString.Soap12Utf16FFFE;
                case FramingEncodingType.MTOM:
                    return FramingEncodingString.MTOM;
                case FramingEncodingType.Binary:
                    return FramingEncodingString.Binary;
                case FramingEncodingType.BinarySession:
                    return FramingEncodingString.BinarySession;
                default:
                    return "unknown" + ((int)type).ToString(CultureInfo.InvariantCulture);
            }
        }
    }
 
    abstract class FramingDecoder
    {
        long streamPosition;
 
        protected FramingDecoder()
        {
        }
 
        protected FramingDecoder(long streamPosition)
        {
            this.streamPosition = streamPosition;
        }
 
        protected abstract string CurrentStateAsString { get; }
 
        public long StreamPosition
        {
            get { return streamPosition; }
            set { streamPosition = value; }
        }
 
        protected void ValidateFramingMode(FramingMode mode)
        {
            switch (mode)
            {
                case FramingMode.Singleton:
                case FramingMode.Duplex:
                case FramingMode.Simplex:
                case FramingMode.SingletonSized:
                    break;
                default:
                    {
                        Exception exception = CreateException(new InvalidDataException(SR.GetString(
                            SR.FramingModeNotSupported, mode.ToString())), FramingEncodingString.UnsupportedModeFault);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception);
                    }
            }
        }
 
        protected void ValidateRecordType(FramingRecordType expectedType, FramingRecordType foundType)
        {
            if (foundType != expectedType)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidRecordTypeException(expectedType, foundType));
            }
        }
 
        // special validation for Preamble Ack for usability purposes (MB#39593)
        protected void ValidatePreambleAck(FramingRecordType foundType)
        {
            if (foundType != FramingRecordType.PreambleAck)
            {
                Exception inner = CreateInvalidRecordTypeException(FramingRecordType.PreambleAck, foundType);
                string exceptionString;
                if (((byte)foundType == 'h') || ((byte)foundType == 'H'))
                {
                    exceptionString = SR.GetString(SR.PreambleAckIncorrectMaybeHttp);
                }
                else
                {
                    exceptionString = SR.GetString(SR.PreambleAckIncorrect);
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(exceptionString, inner));
            }
        }
 
        Exception CreateInvalidRecordTypeException(FramingRecordType expectedType, FramingRecordType foundType)
        {
            return new InvalidDataException(SR.GetString(SR.FramingRecordTypeMismatch, expectedType.ToString(), foundType.ToString()));
        }
 
        protected void ValidateMajorVersion(int majorVersion)
        {
            if (majorVersion != FramingVersion.Major)
            {
                Exception exception = CreateException(new InvalidDataException(SR.GetString(
                    SR.FramingVersionNotSupported, majorVersion)), FramingEncodingString.UnsupportedVersionFault);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception);
            }
        }
 
        public Exception CreatePrematureEOFException()
        {
            return CreateException(new InvalidDataException(SR.GetString(SR.FramingPrematureEOF)));
        }
 
        protected Exception CreateException(InvalidDataException innerException, string framingFault)
        {
            Exception result = CreateException(innerException);
            FramingEncodingString.AddFaultString(result, framingFault);
            return result;
        }
 
        protected Exception CreateException(InvalidDataException innerException)
        {
            return new ProtocolException(SR.GetString(SR.FramingError, StreamPosition, CurrentStateAsString),
                innerException);
        }
    }
 
    // Pattern: 
    //   Done
    class ServerModeDecoder : FramingDecoder
    {
        State currentState;
        int majorVersion;
        int minorVersion;
        FramingMode mode;
 
        public ServerModeDecoder()
        {
            currentState = State.ReadingVersionRecord;
        }
 
        public int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                switch (currentState)
                {
                    case State.ReadingVersionRecord:
                        ValidateRecordType(FramingRecordType.Version, (FramingRecordType)bytes[offset]);
                        currentState = State.ReadingMajorVersion;
                        bytesConsumed = 1;
                        break;
                    case State.ReadingMajorVersion:
                        majorVersion = bytes[offset];
                        ValidateMajorVersion(majorVersion);
                        currentState = State.ReadingMinorVersion;
                        bytesConsumed = 1;
                        break;
                    case State.ReadingMinorVersion:
                        minorVersion = bytes[offset];
                        currentState = State.ReadingModeRecord;
                        bytesConsumed = 1;
                        break;
                    case State.ReadingModeRecord:
                        ValidateRecordType(FramingRecordType.Mode, (FramingRecordType)bytes[offset]);
                        currentState = State.ReadingModeValue;
                        bytesConsumed = 1;
                        break;
                    case State.ReadingModeValue:
                        mode = (FramingMode)bytes[offset];
                        ValidateFramingMode(mode);
                        currentState = State.Done;
                        bytesConsumed = 1;
                        break;
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
 
        public void Reset()
        {
            StreamPosition = 0;
            currentState = State.ReadingVersionRecord;
        }
 
        public State CurrentState
        {
            get { return currentState; }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public FramingMode Mode
        {
            get
            {
                if (currentState != State.Done)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return mode;
            }
        }
 
        public int MajorVersion
        {
            get
            {
                if (currentState != State.Done)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return majorVersion;
            }
        }
 
        public int MinorVersion
        {
            get
            {
                if (currentState != State.Done)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return minorVersion;
            }
        }
 
        public enum State
        {
            ReadingVersionRecord,
            ReadingMajorVersion,
            ReadingMinorVersion,
            ReadingModeRecord,
            ReadingModeValue,
            Done,
        }
    }
 
    // Used for Duplex/Simplex
    // Pattern: 
    //   Start, 
    //   (UpgradeRequest, upgrade-content-type)*, 
    //   (EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd)*, 
    //   End
    class ServerSessionDecoder : FramingDecoder
    {
        ViaStringDecoder viaDecoder;
        StringDecoder contentTypeDecoder;
        IntDecoder sizeDecoder;
        State currentState;
        string contentType;
        int envelopeBytesNeeded;
        int envelopeSize;
        string upgrade;
 
        public ServerSessionDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength)
            : base(streamPosition)
        {
            this.viaDecoder = new ViaStringDecoder(maxViaLength);
            this.contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength);
            this.sizeDecoder = new IntDecoder();
            this.currentState = State.ReadingViaRecord;
        }
 
        public State CurrentState
        {
            get { return currentState; }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public string ContentType
        {
            get
            {
                if (currentState < State.PreUpgradeStart)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return contentType;
            }
        }
 
        public Uri Via
        {
            get
            {
                if (currentState < State.ReadingContentTypeRecord)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return viaDecoder.ValueAsUri;
            }
        }
 
        public void Reset(long streamPosition)
        {
            this.StreamPosition = streamPosition;
            this.currentState = State.ReadingViaRecord;
        }
 
        public string Upgrade
        {
            get
            {
                if (currentState != State.UpgradeRequest)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return upgrade;
            }
        }
 
        public int EnvelopeSize
        {
            get
            {
                if (currentState < State.EnvelopeStart)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return envelopeSize;
            }
        }
 
        public int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                FramingRecordType recordType;
                switch (currentState)
                {
                    case State.ReadingViaRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.Via, recordType);
                        bytesConsumed = 1;
                        viaDecoder.Reset();
                        currentState = State.ReadingViaString;
                        break;
                    case State.ReadingViaString:
                        bytesConsumed = viaDecoder.Decode(bytes, offset, size);
                        if (viaDecoder.IsValueDecoded)
                        {
                            currentState = State.ReadingContentTypeRecord;
                        }
                        break;
                    case State.ReadingContentTypeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.KnownEncoding)
                        {
                            bytesConsumed = 1;
                            currentState = State.ReadingContentTypeByte;
                        }
                        else
                        {
                            ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType);
                            bytesConsumed = 1;
                            contentTypeDecoder.Reset();
                            currentState = State.ReadingContentTypeString;
                        }
                        break;
                    case State.ReadingContentTypeByte:
                        contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.PreUpgradeStart;
                        break;
                    case State.ReadingContentTypeString:
                        bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size);
                        if (contentTypeDecoder.IsValueDecoded)
                        {
                            currentState = State.PreUpgradeStart;
                            contentType = contentTypeDecoder.Value;
                        }
                        break;
                    case State.PreUpgradeStart:
                        bytesConsumed = 0;
                        currentState = State.ReadingUpgradeRecord;
                        break;
                    case State.ReadingUpgradeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.UpgradeRequest)
                        {
                            bytesConsumed = 1;
                            contentTypeDecoder.Reset();
                            currentState = State.ReadingUpgradeString;
                        }
                        else
                        {
                            bytesConsumed = 0;
                            currentState = State.ReadingPreambleEndRecord;
                        }
                        break;
                    case State.ReadingUpgradeString:
                        bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size);
                        if (contentTypeDecoder.IsValueDecoded)
                        {
                            currentState = State.UpgradeRequest;
                            upgrade = contentTypeDecoder.Value;
                        }
                        break;
                    case State.UpgradeRequest:
                        bytesConsumed = 0;
                        currentState = State.ReadingUpgradeRecord;
                        break;
                    case State.ReadingPreambleEndRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.PreambleEnd, recordType);
                        bytesConsumed = 1;
                        currentState = State.Start;
                        break;
                    case State.Start:
                        bytesConsumed = 0;
                        currentState = State.ReadingEndRecord;
                        break;
                    case State.ReadingEndRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.End)
                        {
                            bytesConsumed = 1;
                            currentState = State.End;
                        }
                        else
                        {
                            bytesConsumed = 0;
                            currentState = State.ReadingEnvelopeRecord;
                        }
                        break;
                    case State.ReadingEnvelopeRecord:
                        ValidateRecordType(FramingRecordType.SizedEnvelope, (FramingRecordType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.ReadingEnvelopeSize;
                        sizeDecoder.Reset();
                        break;
                    case State.ReadingEnvelopeSize:
                        bytesConsumed = sizeDecoder.Decode(bytes, offset, size);
                        if (sizeDecoder.IsValueDecoded)
                        {
                            currentState = State.EnvelopeStart;
                            envelopeSize = sizeDecoder.Value;
                            envelopeBytesNeeded = envelopeSize;
                        }
                        break;
                    case State.EnvelopeStart:
                        bytesConsumed = 0;
                        currentState = State.ReadingEnvelopeBytes;
                        break;
                    case State.ReadingEnvelopeBytes:
                        bytesConsumed = size;
                        if (bytesConsumed > envelopeBytesNeeded)
                            bytesConsumed = envelopeBytesNeeded;
                        envelopeBytesNeeded -= bytesConsumed;
                        if (envelopeBytesNeeded == 0)
                            currentState = State.EnvelopeEnd;
                        break;
                    case State.EnvelopeEnd:
                        bytesConsumed = 0;
                        currentState = State.ReadingEndRecord;
                        break;
                    case State.End:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
 
        public enum State
        {
            ReadingViaRecord,
            ReadingViaString,
            ReadingContentTypeRecord,
            ReadingContentTypeString,
            ReadingContentTypeByte,
            PreUpgradeStart,
            ReadingUpgradeRecord,
            ReadingUpgradeString,
            UpgradeRequest,
            ReadingPreambleEndRecord,
            Start,
            ReadingEnvelopeRecord,
            ReadingEnvelopeSize,
            EnvelopeStart,
            ReadingEnvelopeBytes,
            EnvelopeEnd,
            ReadingEndRecord,
            End,
        }
    }
 
    class SingletonMessageDecoder : FramingDecoder
    {
        IntDecoder sizeDecoder;
        int chunkBytesNeeded;
        int chunkSize;
        State currentState;
 
        public SingletonMessageDecoder(long streamPosition)
            : base(streamPosition)
        {
            this.sizeDecoder = new IntDecoder();
            this.currentState = State.ChunkStart;
        }
 
        public void Reset()
        {
            this.currentState = State.ChunkStart;
        }
 
        public State CurrentState
        {
            get { return currentState; }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public int ChunkSize
        {
            get
            {
                if (currentState < State.ChunkStart)
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                }
 
                return this.chunkSize;
            }
        }
 
        public int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                switch (currentState)
                {
                    case State.ReadingEnvelopeChunkSize:
                        bytesConsumed = sizeDecoder.Decode(bytes, offset, size);
                        if (sizeDecoder.IsValueDecoded)
                        {
                            this.chunkSize = sizeDecoder.Value;
                            sizeDecoder.Reset();
 
                            if (this.chunkSize == 0)
                            {
                                currentState = State.EnvelopeEnd;
                            }
                            else
                            {
                                currentState = State.ChunkStart;
                                chunkBytesNeeded = chunkSize;
                            }
                        }
                        break;
                    case State.ChunkStart:
                        bytesConsumed = 0;
                        currentState = State.ReadingEnvelopeBytes;
                        break;
                    case State.ReadingEnvelopeBytes:
                        bytesConsumed = size;
                        if (bytesConsumed > chunkBytesNeeded)
                        {
                            bytesConsumed = chunkBytesNeeded;
                        }
                        chunkBytesNeeded -= bytesConsumed;
                        if (chunkBytesNeeded == 0)
                        {
                            currentState = State.ChunkEnd;
                        }
                        break;
                    case State.ChunkEnd:
                        bytesConsumed = 0;
                        currentState = State.ReadingEnvelopeChunkSize;
                        break;
                    case State.EnvelopeEnd:
                        ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.End;
                        break;
                    case State.End:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
 
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
 
        public enum State
        {
            ReadingEnvelopeChunkSize,
            ChunkStart,
            ReadingEnvelopeBytes,
            ChunkEnd,
            EnvelopeEnd,
            End,
        }
    }
 
    // Pattern: 
    //   Start, 
    //   (UpgradeRequest, upgrade-bytes)*, 
    //   EnvelopeStart,
    class ServerSingletonDecoder : FramingDecoder
    {
        ViaStringDecoder viaDecoder;
        ContentTypeStringDecoder contentTypeDecoder;
        State currentState;
        string contentType;
        string upgrade;
 
        public ServerSingletonDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength)
            : base(streamPosition)
        {
            this.viaDecoder = new ViaStringDecoder(maxViaLength);
            this.contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength);
            this.currentState = State.ReadingViaRecord;
        }
 
        public void Reset()
        {
            this.currentState = State.ReadingViaRecord;
        }
 
        public State CurrentState
        {
            get { return currentState; }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public Uri Via
        {
            get
            {
                if (currentState < State.ReadingContentTypeRecord)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return viaDecoder.ValueAsUri;
            }
        }
 
        public string ContentType
        {
            get
            {
                if (currentState < State.PreUpgradeStart)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return contentType;
            }
        }
 
        public string Upgrade
        {
            get
            {
                if (currentState != State.UpgradeRequest)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return upgrade;
            }
        }
 
        public int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                FramingRecordType recordType;
                switch (currentState)
                {
                    case State.ReadingViaRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.Via, recordType);
                        bytesConsumed = 1;
                        viaDecoder.Reset();
                        currentState = State.ReadingViaString;
                        break;
                    case State.ReadingViaString:
                        bytesConsumed = viaDecoder.Decode(bytes, offset, size);
                        if (viaDecoder.IsValueDecoded)
                        {
                            currentState = State.ReadingContentTypeRecord;
                        }
                        break;
                    case State.ReadingContentTypeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.KnownEncoding)
                        {
                            bytesConsumed = 1;
                            currentState = State.ReadingContentTypeByte;
                        }
                        else
                        {
                            ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType);
                            bytesConsumed = 1;
                            contentTypeDecoder.Reset();
                            currentState = State.ReadingContentTypeString;
                        }
                        break;
                    case State.ReadingContentTypeByte:
                        contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.PreUpgradeStart;
                        break;
                    case State.ReadingContentTypeString:
                        bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size);
                        if (contentTypeDecoder.IsValueDecoded)
                        {
                            currentState = State.PreUpgradeStart;
                            contentType = contentTypeDecoder.Value;
                        }
                        break;
                    case State.PreUpgradeStart:
                        bytesConsumed = 0;
                        currentState = State.ReadingUpgradeRecord;
                        break;
                    case State.ReadingUpgradeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.UpgradeRequest)
                        {
                            bytesConsumed = 1;
                            contentTypeDecoder.Reset();
                            currentState = State.ReadingUpgradeString;
                        }
                        else
                        {
                            bytesConsumed = 0;
                            currentState = State.ReadingPreambleEndRecord;
                        }
                        break;
                    case State.ReadingUpgradeString:
                        bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size);
                        if (contentTypeDecoder.IsValueDecoded)
                        {
                            currentState = State.UpgradeRequest;
                            upgrade = contentTypeDecoder.Value;
                        }
                        break;
                    case State.UpgradeRequest:
                        bytesConsumed = 0;
                        currentState = State.ReadingUpgradeRecord;
                        break;
                    case State.ReadingPreambleEndRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.PreambleEnd, recordType);
                        bytesConsumed = 1;
                        currentState = State.Start;
                        break;
                    case State.Start:
                        bytesConsumed = 0;
                        currentState = State.ReadingEnvelopeRecord;
                        break;
                    case State.ReadingEnvelopeRecord:
                        ValidateRecordType(FramingRecordType.UnsizedEnvelope, (FramingRecordType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.EnvelopeStart;
                        break;
                    case State.EnvelopeStart:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
 
        public enum State
        {
            ReadingViaRecord,
            ReadingViaString,
            ReadingContentTypeRecord,
            ReadingContentTypeString,
            ReadingContentTypeByte,
            PreUpgradeStart,
            ReadingUpgradeRecord,
            ReadingUpgradeString,
            UpgradeRequest,
            ReadingPreambleEndRecord,
            Start,
            ReadingEnvelopeRecord,
            EnvelopeStart,
            ReadingEnvelopeChunkSize,
            ChunkStart,
            ReadingEnvelopeChunk,
            ChunkEnd,
            End,
        }
    }
 
    // Pattern: 
    //   Start, 
    //   EnvelopeStart,
    class ServerSingletonSizedDecoder : FramingDecoder
    {
        ViaStringDecoder viaDecoder;
        ContentTypeStringDecoder contentTypeDecoder;
        State currentState;
        string contentType;
 
        public ServerSingletonSizedDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength)
            : base(streamPosition)
        {
            this.viaDecoder = new ViaStringDecoder(maxViaLength);
            this.contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength);
            this.currentState = State.ReadingViaRecord;
        }
 
        public int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                FramingRecordType recordType;
                switch (currentState)
                {
                    case State.ReadingViaRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.Via, recordType);
                        bytesConsumed = 1;
                        viaDecoder.Reset();
                        currentState = State.ReadingViaString;
                        break;
                    case State.ReadingViaString:
                        bytesConsumed = viaDecoder.Decode(bytes, offset, size);
                        if (viaDecoder.IsValueDecoded)
                            currentState = State.ReadingContentTypeRecord;
                        break;
                    case State.ReadingContentTypeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.KnownEncoding)
                        {
                            bytesConsumed = 1;
                            currentState = State.ReadingContentTypeByte;
                        }
                        else
                        {
                            ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType);
                            bytesConsumed = 1;
                            contentTypeDecoder.Reset();
                            currentState = State.ReadingContentTypeString;
                        }
                        break;
                    case State.ReadingContentTypeByte:
                        contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]);
                        bytesConsumed = 1;
                        currentState = State.Start;
                        break;
                    case State.ReadingContentTypeString:
                        bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size);
                        if (contentTypeDecoder.IsValueDecoded)
                        {
                            currentState = State.Start;
                            contentType = contentTypeDecoder.Value;
                        }
                        break;
                    case State.Start:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
 
        public void Reset(long streamPosition)
        {
            this.StreamPosition = streamPosition;
            this.currentState = State.ReadingViaRecord;
        }
 
        public State CurrentState
        {
            get { return currentState; }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public Uri Via
        {
            get
            {
                if (currentState < State.ReadingContentTypeRecord)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return viaDecoder.ValueAsUri;
            }
        }
 
        public string ContentType
        {
            get
            {
                if (currentState < State.Start)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return contentType;
            }
        }
 
        public enum State
        {
            ReadingViaRecord,
            ReadingViaString,
            ReadingContentTypeRecord,
            ReadingContentTypeString,
            ReadingContentTypeByte,
            Start,
        }
    }
 
    // common set of states used on the client-side.
    enum ClientFramingDecoderState
    {
        ReadingUpgradeRecord,
        ReadingUpgradeMode,
        UpgradeResponse,
        ReadingAckRecord,
        Start,
        ReadingFault,
        ReadingFaultString,
        Fault,
        ReadingEnvelopeRecord,
        ReadingEnvelopeSize,
        EnvelopeStart,
        ReadingEnvelopeBytes,
        EnvelopeEnd,
        ReadingEndRecord,
        End,
    }
 
    abstract class ClientFramingDecoder : FramingDecoder
    {
        ClientFramingDecoderState currentState;
 
        protected ClientFramingDecoder(long streamPosition)
            : base(streamPosition)
        {
            this.currentState = ClientFramingDecoderState.ReadingUpgradeRecord;
        }
 
        public ClientFramingDecoderState CurrentState
        {
            get
            {
                return this.currentState;
            }
 
            protected set
            {
                this.currentState = value;
            }
        }
 
        protected override string CurrentStateAsString
        {
            get { return currentState.ToString(); }
        }
 
        public abstract string Fault
        {
            get;
        }
 
        public abstract int Decode(byte[] bytes, int offset, int size);
    }
 
    // Pattern: 
    //   (UpgradeResponse, upgrade-bytes)*, (Ack | Fault),
    //   ((EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd) | Fault)*, 
    //   End
    class ClientDuplexDecoder : ClientFramingDecoder
    {
        IntDecoder sizeDecoder;
        FaultStringDecoder faultDecoder;
        int envelopeBytesNeeded;
        int envelopeSize;
 
        public ClientDuplexDecoder(long streamPosition)
            : base(streamPosition)
        {
            sizeDecoder = new IntDecoder();
        }
 
        public int EnvelopeSize
        {
            get
            {
                if (CurrentState < ClientFramingDecoderState.EnvelopeStart)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return envelopeSize;
            }
        }
 
        public override string Fault
        {
            get
            {
                if (CurrentState < ClientFramingDecoderState.Fault)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return faultDecoder.Value;
            }
        }
 
        public override int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                FramingRecordType recordType;
                switch (CurrentState)
                {
                    case ClientFramingDecoderState.ReadingUpgradeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.UpgradeResponse)
                        {
                            bytesConsumed = 1;
                            base.CurrentState = ClientFramingDecoderState.UpgradeResponse;
                        }
                        else
                        {
                            bytesConsumed = 0;
                            base.CurrentState = ClientFramingDecoderState.ReadingAckRecord;
                        }
                        break;
                    case ClientFramingDecoderState.UpgradeResponse:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord;
                        break;
                    case ClientFramingDecoderState.ReadingAckRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.Fault)
                        {
                            bytesConsumed = 1;
                            faultDecoder = new FaultStringDecoder();
                            base.CurrentState = ClientFramingDecoderState.ReadingFaultString;
                            break;
                        }
                        ValidatePreambleAck(recordType);
                        bytesConsumed = 1;
                        base.CurrentState = ClientFramingDecoderState.Start;
                        break;
                    case ClientFramingDecoderState.Start:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord;
                        break;
                    case ClientFramingDecoderState.ReadingEnvelopeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.End)
                        {
                            bytesConsumed = 1;
                            base.CurrentState = ClientFramingDecoderState.End;
                            break;
                        }
                        else if (recordType == FramingRecordType.Fault)
                        {
                            bytesConsumed = 1;
                            faultDecoder = new FaultStringDecoder();
                            base.CurrentState = ClientFramingDecoderState.ReadingFaultString;
                            break;
                        }
                        ValidateRecordType(FramingRecordType.SizedEnvelope, recordType);
                        bytesConsumed = 1;
                        base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeSize;
                        sizeDecoder.Reset();
                        break;
                    case ClientFramingDecoderState.ReadingEnvelopeSize:
                        bytesConsumed = sizeDecoder.Decode(bytes, offset, size);
                        if (sizeDecoder.IsValueDecoded)
                        {
                            base.CurrentState = ClientFramingDecoderState.EnvelopeStart;
                            envelopeSize = sizeDecoder.Value;
                            envelopeBytesNeeded = envelopeSize;
                        }
                        break;
                    case ClientFramingDecoderState.EnvelopeStart:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeBytes;
                        break;
                    case ClientFramingDecoderState.ReadingEnvelopeBytes:
                        bytesConsumed = size;
                        if (bytesConsumed > envelopeBytesNeeded)
                            bytesConsumed = envelopeBytesNeeded;
                        envelopeBytesNeeded -= bytesConsumed;
                        if (envelopeBytesNeeded == 0)
                            base.CurrentState = ClientFramingDecoderState.EnvelopeEnd;
                        break;
                    case ClientFramingDecoderState.EnvelopeEnd:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord;
                        break;
                    case ClientFramingDecoderState.ReadingFaultString:
                        bytesConsumed = faultDecoder.Decode(bytes, offset, size);
                        if (faultDecoder.IsValueDecoded)
                        {
                            base.CurrentState = ClientFramingDecoderState.Fault;
                        }
                        break;
                    case ClientFramingDecoderState.Fault:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingEndRecord;
                        break;
                    case ClientFramingDecoderState.ReadingEndRecord:
                        ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]);
                        bytesConsumed = 1;
                        base.CurrentState = ClientFramingDecoderState.End;
                        break;
                    case ClientFramingDecoderState.End:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
    }
 
    // Pattern: 
    //   (UpgradeResponse, upgrade-bytes)*, (Ack | Fault),
    //   End
    class ClientSingletonDecoder : ClientFramingDecoder
    {
        FaultStringDecoder faultDecoder;
 
        public ClientSingletonDecoder(long streamPosition)
            : base(streamPosition)
        {
        }
 
        public override string Fault
        {
            get
            {
                if (CurrentState < ClientFramingDecoderState.Fault)
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FramingValueNotAvailable)));
                return faultDecoder.Value;
            }
        }
 
        public override int Decode(byte[] bytes, int offset, int size)
        {
            DecoderHelper.ValidateSize(size);
 
            try
            {
                int bytesConsumed;
                FramingRecordType recordType;
                switch (CurrentState)
                {
                    case ClientFramingDecoderState.ReadingUpgradeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.UpgradeResponse)
                        {
                            bytesConsumed = 1;
                            base.CurrentState = ClientFramingDecoderState.UpgradeResponse;
                        }
                        else
                        {
                            bytesConsumed = 0;
                            base.CurrentState = ClientFramingDecoderState.ReadingAckRecord;
                        }
                        break;
                    case ClientFramingDecoderState.UpgradeResponse:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord;
                        break;
                    case ClientFramingDecoderState.ReadingAckRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.Fault)
                        {
                            bytesConsumed = 1;
                            faultDecoder = new FaultStringDecoder();
                            base.CurrentState = ClientFramingDecoderState.ReadingFaultString;
                            break;
                        }
                        ValidatePreambleAck(recordType);
                        bytesConsumed = 1;
                        base.CurrentState = ClientFramingDecoderState.Start;
                        break;
 
                    case ClientFramingDecoderState.Start:
                        bytesConsumed = 0;
                        base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord;
                        break;
 
                    case ClientFramingDecoderState.ReadingEnvelopeRecord:
                        recordType = (FramingRecordType)bytes[offset];
                        if (recordType == FramingRecordType.End)
                        {
                            bytesConsumed = 1;
                            base.CurrentState = ClientFramingDecoderState.End;
                            break;
                        }
                        else if (recordType == FramingRecordType.Fault)
                        {
                            bytesConsumed = 0;
                            base.CurrentState = ClientFramingDecoderState.ReadingFault;
                            break;
                        }
                        ValidateRecordType(FramingRecordType.UnsizedEnvelope, recordType);
                        bytesConsumed = 1;
                        base.CurrentState = ClientFramingDecoderState.EnvelopeStart;
                        break;
 
                    case ClientFramingDecoderState.EnvelopeStart:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
 
                    case ClientFramingDecoderState.ReadingFault:
                        recordType = (FramingRecordType)bytes[offset];
                        ValidateRecordType(FramingRecordType.Fault, recordType);
                        bytesConsumed = 1;
                        faultDecoder = new FaultStringDecoder();
                        base.CurrentState = ClientFramingDecoderState.ReadingFaultString;
                        break;
                    case ClientFramingDecoderState.ReadingFaultString:
                        bytesConsumed = faultDecoder.Decode(bytes, offset, size);
                        if (faultDecoder.IsValueDecoded)
                        {
                            base.CurrentState = ClientFramingDecoderState.Fault;
                        }
                        break;
                    case ClientFramingDecoderState.Fault:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.FramingAtEnd))));
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            CreateException(new InvalidDataException(SR.GetString(SR.InvalidDecoderStateMachine))));
                }
 
                StreamPosition += bytesConsumed;
                return bytesConsumed;
            }
            catch (InvalidDataException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e));
            }
        }
    }
}