//----------------------------------------------------------------------------- // 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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 // [....], 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)); } } } }