1653 lines
64 KiB
C#
1653 lines
64 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// 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));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|