//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Channels { using System.Collections.Generic; using System.IO; using System.Runtime; using System.Runtime.Diagnostics; using System.ServiceModel; using System.ServiceModel.Diagnostics; using System.ServiceModel.Diagnostics.Application; using System.Text; using System.Xml; class BinaryMessageEncoderFactory : MessageEncoderFactory { const int maxPooledXmlReaderPerMessage = 2; BinaryMessageEncoder messageEncoder; MessageVersion messageVersion; int maxReadPoolSize; int maxWritePoolSize; CompressionFormat compressionFormat; // Double-checked locking pattern requires volatile for read/write synchronization volatile SynchronizedPool streamedWriterPool; volatile SynchronizedPool streamedReaderPool; volatile SynchronizedPool bufferedDataPool; volatile SynchronizedPool bufferedWriterPool; volatile SynchronizedPool recycledStatePool; object thisLock; int maxSessionSize; OnXmlDictionaryReaderClose onStreamedReaderClose; XmlDictionaryReaderQuotas readerQuotas; XmlDictionaryReaderQuotas bufferedReadReaderQuotas; BinaryVersion binaryVersion; public BinaryMessageEncoderFactory(MessageVersion messageVersion, int maxReadPoolSize, int maxWritePoolSize, int maxSessionSize, XmlDictionaryReaderQuotas readerQuotas, long maxReceivedMessageSize, BinaryVersion version, CompressionFormat compressionFormat) { this.messageVersion = messageVersion; this.maxReadPoolSize = maxReadPoolSize; this.maxWritePoolSize = maxWritePoolSize; this.maxSessionSize = maxSessionSize; this.thisLock = new object(); this.onStreamedReaderClose = new OnXmlDictionaryReaderClose(ReturnStreamedReader); this.readerQuotas = new XmlDictionaryReaderQuotas(); if (readerQuotas != null) { readerQuotas.CopyTo(this.readerQuotas); } this.bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(this.readerQuotas); this.MaxReceivedMessageSize = maxReceivedMessageSize; this.binaryVersion = version; this.compressionFormat = compressionFormat; this.messageEncoder = new BinaryMessageEncoder(this, false, 0); } public static IXmlDictionary XmlDictionary { get { return XD.Dictionary; } } public override MessageEncoder Encoder { get { return messageEncoder; } } public override MessageVersion MessageVersion { get { return messageVersion; } } public int MaxWritePoolSize { get { return maxWritePoolSize; } } public XmlDictionaryReaderQuotas ReaderQuotas { get { return readerQuotas; } } public int MaxReadPoolSize { get { return maxReadPoolSize; } } public int MaxSessionSize { get { return maxSessionSize; } } public CompressionFormat CompressionFormat { get { return this.compressionFormat; } } long MaxReceivedMessageSize { get; set; } object ThisLock { get { return thisLock; } } SynchronizedPool RecycledStatePool { get { if (recycledStatePool == null) { lock (ThisLock) { if (recycledStatePool == null) { //running = true; recycledStatePool = new SynchronizedPool(maxReadPoolSize); } } } return recycledStatePool; } } public override MessageEncoder CreateSessionEncoder() { return new BinaryMessageEncoder(this, true, maxSessionSize); } XmlDictionaryWriter TakeStreamedWriter(Stream stream) { if (streamedWriterPool == null) { lock (ThisLock) { if (streamedWriterPool == null) { //running = true; streamedWriterPool = new SynchronizedPool(maxWritePoolSize); } } } XmlDictionaryWriter xmlWriter = streamedWriterPool.Take(); if (xmlWriter == null) { xmlWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, binaryVersion.Dictionary, null, false); if (TD.WritePoolMissIsEnabled()) { TD.WritePoolMiss(xmlWriter.GetType().Name); } } else { ((IXmlBinaryWriterInitializer)xmlWriter).SetOutput(stream, binaryVersion.Dictionary, null, false); } return xmlWriter; } void ReturnStreamedWriter(XmlDictionaryWriter xmlWriter) { xmlWriter.Close(); streamedWriterPool.Return(xmlWriter); } BinaryBufferedMessageWriter TakeBufferedWriter() { if (bufferedWriterPool == null) { lock (ThisLock) { if (bufferedWriterPool == null) { //running = true; bufferedWriterPool = new SynchronizedPool(maxWritePoolSize); } } } BinaryBufferedMessageWriter messageWriter = bufferedWriterPool.Take(); if (messageWriter == null) { messageWriter = new BinaryBufferedMessageWriter(binaryVersion.Dictionary); if (TD.WritePoolMissIsEnabled()) { TD.WritePoolMiss(messageWriter.GetType().Name); } } return messageWriter; } void ReturnMessageWriter(BinaryBufferedMessageWriter messageWriter) { bufferedWriterPool.Return(messageWriter); } XmlDictionaryReader TakeStreamedReader(Stream stream) { if (streamedReaderPool == null) { lock (ThisLock) { if (streamedReaderPool == null) { //running = true; streamedReaderPool = new SynchronizedPool(maxReadPoolSize); } } } XmlDictionaryReader xmlReader = streamedReaderPool.Take(); if (xmlReader == null) { xmlReader = XmlDictionaryReader.CreateBinaryReader(stream, binaryVersion.Dictionary, readerQuotas, null, onStreamedReaderClose); if (TD.ReadPoolMissIsEnabled()) { TD.ReadPoolMiss(xmlReader.GetType().Name); } } else { ((IXmlBinaryReaderInitializer)xmlReader).SetInput(stream, binaryVersion.Dictionary, readerQuotas, null, onStreamedReaderClose); } return xmlReader; } void ReturnStreamedReader(XmlDictionaryReader xmlReader) { streamedReaderPool.Return(xmlReader); } BinaryBufferedMessageData TakeBufferedData(BinaryMessageEncoder messageEncoder) { if (bufferedDataPool == null) { lock (ThisLock) { if (bufferedDataPool == null) { //running = true; bufferedDataPool = new SynchronizedPool(maxReadPoolSize); } } } BinaryBufferedMessageData messageData = bufferedDataPool.Take(); if (messageData == null) { messageData = new BinaryBufferedMessageData(this, maxPooledXmlReaderPerMessage); if (TD.ReadPoolMissIsEnabled()) { TD.ReadPoolMiss(messageData.GetType().Name); } } messageData.SetMessageEncoder(messageEncoder); return messageData; } void ReturnBufferedData(BinaryBufferedMessageData messageData) { messageData.SetMessageEncoder(null); bufferedDataPool.Return(messageData); } class BinaryBufferedMessageData : BufferedMessageData { BinaryMessageEncoderFactory factory; BinaryMessageEncoder messageEncoder; Pool readerPool; OnXmlDictionaryReaderClose onClose; public BinaryBufferedMessageData(BinaryMessageEncoderFactory factory, int maxPoolSize) : base(factory.RecycledStatePool) { this.factory = factory; readerPool = new Pool(maxPoolSize); onClose = new OnXmlDictionaryReaderClose(OnXmlReaderClosed); } public override MessageEncoder MessageEncoder { get { return messageEncoder; } } public override XmlDictionaryReaderQuotas Quotas { get { return factory.readerQuotas; } } public void SetMessageEncoder(BinaryMessageEncoder messageEncoder) { this.messageEncoder = messageEncoder; } protected override XmlDictionaryReader TakeXmlReader() { ArraySegment buffer = this.Buffer; XmlDictionaryReader xmlReader = readerPool.Take(); if (xmlReader != null) { ((IXmlBinaryReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, factory.binaryVersion.Dictionary, factory.bufferedReadReaderQuotas, messageEncoder.ReaderSession, onClose); } else { xmlReader = XmlDictionaryReader.CreateBinaryReader(buffer.Array, buffer.Offset, buffer.Count, factory.binaryVersion.Dictionary, factory.bufferedReadReaderQuotas, messageEncoder.ReaderSession, onClose); if (TD.ReadPoolMissIsEnabled()) { TD.ReadPoolMiss(xmlReader.GetType().Name); } } return xmlReader; } protected override void ReturnXmlReader(XmlDictionaryReader reader) { readerPool.Return(reader); } protected override void OnClosed() { factory.ReturnBufferedData(this); } } class BinaryBufferedMessageWriter : BufferedMessageWriter { XmlDictionaryWriter writer; IXmlDictionary dictionary; XmlBinaryWriterSession session; public BinaryBufferedMessageWriter(IXmlDictionary dictionary) { this.dictionary = dictionary; } public BinaryBufferedMessageWriter(IXmlDictionary dictionary, XmlBinaryWriterSession session) { this.dictionary = dictionary; this.session = session; } protected override XmlDictionaryWriter TakeXmlWriter(Stream stream) { XmlDictionaryWriter returnedWriter = writer; if (returnedWriter == null) { returnedWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session, false); } else { writer = null; ((IXmlBinaryWriterInitializer)returnedWriter).SetOutput(stream, dictionary, session, false); } return returnedWriter; } protected override void ReturnXmlWriter(XmlDictionaryWriter writer) { writer.Close(); if (this.writer == null) { this.writer = writer; } } } class BinaryMessageEncoder : MessageEncoder, ICompressedMessageEncoder, ITraceSourceStringProvider { const string SupportedCompressionTypesMessageProperty = "BinaryMessageEncoder.SupportedCompressionTypes"; BinaryMessageEncoderFactory factory; bool isSession; XmlBinaryWriterSessionWithQuota writerSession; BinaryBufferedMessageWriter sessionMessageWriter; XmlBinaryReaderSession readerSession; XmlBinaryReaderSession readerSessionForLogging; bool readerSessionForLoggingIsInvalid = false; int writeIdCounter; int idCounter; int maxSessionSize; int remainingReaderSessionSize; bool isReaderSessionInvalid; MessagePatterns messagePatterns; string contentType; string normalContentType; string gzipCompressedContentType; string deflateCompressedContentType; CompressionFormat sessionCompressionFormat; readonly long maxReceivedMessageSize; public BinaryMessageEncoder(BinaryMessageEncoderFactory factory, bool isSession, int maxSessionSize) { this.factory = factory; this.isSession = isSession; this.maxSessionSize = maxSessionSize; this.remainingReaderSessionSize = maxSessionSize; this.normalContentType = isSession ? factory.binaryVersion.SessionContentType : factory.binaryVersion.ContentType; this.gzipCompressedContentType = isSession ? BinaryVersion.GZipVersion1.SessionContentType : BinaryVersion.GZipVersion1.ContentType; this.deflateCompressedContentType = isSession ? BinaryVersion.DeflateVersion1.SessionContentType : BinaryVersion.DeflateVersion1.ContentType; this.sessionCompressionFormat = this.factory.CompressionFormat; this.maxReceivedMessageSize = this.factory.MaxReceivedMessageSize; switch (this.factory.CompressionFormat) { case CompressionFormat.Deflate: this.contentType = this.deflateCompressedContentType; break; case CompressionFormat.GZip: this.contentType = this.gzipCompressedContentType; break; default: this.contentType = this.normalContentType; break; } } public override string ContentType { get { return this.contentType; } } public override MessageVersion MessageVersion { get { return factory.messageVersion; } } public override string MediaType { get { return this.contentType; } } public XmlBinaryReaderSession ReaderSession { get { return readerSession; } } public bool CompressionEnabled { get { return this.factory.CompressionFormat != CompressionFormat.None; } } ArraySegment AddSessionInformationToMessage(ArraySegment messageData, BufferManager bufferManager, int maxMessageSize) { int dictionarySize = 0; byte[] buffer = messageData.Array; if (writerSession.HasNewStrings) { IList newStrings = writerSession.GetNewStrings(); for (int i = 0; i < newStrings.Count; i++) { int utf8ValueSize = Encoding.UTF8.GetByteCount(newStrings[i].Value); dictionarySize += IntEncoder.GetEncodedSize(utf8ValueSize) + utf8ValueSize; } int messageSize = messageData.Offset + messageData.Count; int remainingMessageSize = maxMessageSize - messageSize; if (remainingMessageSize - dictionarySize < 0) { string excMsg = SR.GetString(SR.MaxSentMessageSizeExceeded, maxMessageSize); if (TD.MaxSentMessageSizeExceededIsEnabled()) { TD.MaxSentMessageSizeExceeded(excMsg); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QuotaExceededException(excMsg)); } int requiredBufferSize = messageData.Offset + messageData.Count + dictionarySize; if (buffer.Length < requiredBufferSize) { byte[] newBuffer = bufferManager.TakeBuffer(requiredBufferSize); Buffer.BlockCopy(buffer, messageData.Offset, newBuffer, messageData.Offset, messageData.Count); bufferManager.ReturnBuffer(buffer); buffer = newBuffer; } Buffer.BlockCopy(buffer, messageData.Offset, buffer, messageData.Offset + dictionarySize, messageData.Count); int offset = messageData.Offset; for (int i = 0; i < newStrings.Count; i++) { string newString = newStrings[i].Value; int utf8ValueSize = Encoding.UTF8.GetByteCount(newString); offset += IntEncoder.Encode(utf8ValueSize, buffer, offset); offset += Encoding.UTF8.GetBytes(newString, 0, newString.Length, buffer, offset); } writerSession.ClearNewStrings(); } int headerSize = IntEncoder.GetEncodedSize(dictionarySize); int newOffset = messageData.Offset - headerSize; int newSize = headerSize + messageData.Count + dictionarySize; IntEncoder.Encode(dictionarySize, buffer, newOffset); return new ArraySegment(buffer, newOffset, newSize); } ArraySegment ExtractSessionInformationFromMessage(ArraySegment messageData) { if (isReaderSessionInvalid) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.BinaryEncoderSessionInvalid))); } byte[] buffer = messageData.Array; int dictionarySize; int headerSize; int newOffset; int newSize; bool throwing = true; try { IntDecoder decoder = new IntDecoder(); headerSize = decoder.Decode(buffer, messageData.Offset, messageData.Count); dictionarySize = decoder.Value; if (dictionarySize > messageData.Count) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.BinaryEncoderSessionMalformed))); } newOffset = messageData.Offset + headerSize + dictionarySize; newSize = messageData.Count - headerSize - dictionarySize; if (newSize < 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.BinaryEncoderSessionMalformed))); } if (dictionarySize > 0) { if (dictionarySize > remainingReaderSessionSize) { string message = SR.GetString(SR.BinaryEncoderSessionTooLarge, this.maxSessionSize); if (TD.MaxSessionSizeReachedIsEnabled()) { TD.MaxSessionSizeReached(message); } Exception inner = new QuotaExceededException(message); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner)); } else { remainingReaderSessionSize -= dictionarySize; } int size = dictionarySize; int offset = messageData.Offset + headerSize; while (size > 0) { decoder.Reset(); int bytesDecoded = decoder.Decode(buffer, offset, size); int utf8ValueSize = decoder.Value; offset += bytesDecoded; size -= bytesDecoded; if (utf8ValueSize > size) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.GetString(SR.BinaryEncoderSessionMalformed))); } string value = Encoding.UTF8.GetString(buffer, offset, utf8ValueSize); offset += utf8ValueSize; size -= utf8ValueSize; readerSession.Add(idCounter, value); idCounter++; } } throwing = false; } finally { if (throwing) { isReaderSessionInvalid = true; } } return new ArraySegment(buffer, newOffset, newSize); } public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) { if (bufferManager == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bufferManager"); } CompressionFormat compressionFormat = this.CheckContentType(contentType); if (TD.BinaryMessageDecodingStartIsEnabled()) { TD.BinaryMessageDecodingStart(); } if (compressionFormat != CompressionFormat.None) { MessageEncoderCompressionHandler.DecompressBuffer(ref buffer, bufferManager, compressionFormat, this.maxReceivedMessageSize); } if (isSession) { if (readerSession == null) { readerSession = new XmlBinaryReaderSession(); messagePatterns = new MessagePatterns(factory.binaryVersion.Dictionary, readerSession, this.MessageVersion); } try { buffer = ExtractSessionInformationFromMessage(buffer); } catch (InvalidDataException) { MessageLogger.LogMessage(buffer, MessageLoggingSource.Malformed); throw; } } BinaryBufferedMessageData messageData = factory.TakeBufferedData(this); Message message; if (messagePatterns != null) { message = messagePatterns.TryCreateMessage(buffer.Array, buffer.Offset, buffer.Count, bufferManager, messageData); } else { message = null; } if (message == null) { messageData.Open(buffer, bufferManager); RecycledMessageState messageState = messageData.TakeMessageState(); if (messageState == null) { messageState = new RecycledMessageState(); } message = new BufferedMessage(messageData, messageState); } message.Properties.Encoder = this; if (TD.MessageReadByEncoderIsEnabled() && buffer != null) { TD.MessageReadByEncoder( EventTraceActivityHelper.TryExtractActivity(message, true), buffer.Count, this); } if (MessageLogger.LogMessagesAtTransportLevel) { MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive); } return message; } public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) { if (stream == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); } CompressionFormat compressionFormat = this.CheckContentType(contentType); if (TD.BinaryMessageDecodingStartIsEnabled()) { TD.BinaryMessageDecodingStart(); } if (compressionFormat != CompressionFormat.None) { stream = new MaxMessageSizeStream( MessageEncoderCompressionHandler.GetDecompressStream(stream, compressionFormat), this.maxReceivedMessageSize); } XmlDictionaryReader reader = factory.TakeStreamedReader(stream); Message message = Message.CreateMessage(reader, maxSizeOfHeaders, factory.messageVersion); message.Properties.Encoder = this; if (TD.StreamedMessageReadByEncoderIsEnabled()) { TD.StreamedMessageReadByEncoder( EventTraceActivityHelper.TryExtractActivity(message, true)); } if (MessageLogger.LogMessagesAtTransportLevel) { MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive); } return message; } public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { if (message == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); } if (bufferManager == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bufferManager"); } if (maxMessageSize < 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxMessageSize", maxMessageSize, SR.GetString(SR.ValueMustBeNonNegative))); } EventTraceActivity eventTraceActivity = null; if (TD.BinaryMessageEncodingStartIsEnabled()) { eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message); TD.BinaryMessageEncodingStart(eventTraceActivity); } message.Properties.Encoder = this; if (isSession) { if (writerSession == null) { writerSession = new XmlBinaryWriterSessionWithQuota(maxSessionSize); sessionMessageWriter = new BinaryBufferedMessageWriter(factory.binaryVersion.Dictionary, writerSession); } messageOffset += IntEncoder.MaxEncodedSize; } if (messageOffset < 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("messageOffset", messageOffset, SR.GetString(SR.ValueMustBeNonNegative))); } if (messageOffset > maxMessageSize) { string excMsg = SR.GetString(SR.MaxSentMessageSizeExceeded, maxMessageSize); if (TD.MaxSentMessageSizeExceededIsEnabled()) { TD.MaxSentMessageSizeExceeded(excMsg); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QuotaExceededException(excMsg)); } ThrowIfMismatchedMessageVersion(message); BinaryBufferedMessageWriter messageWriter; if (isSession) { messageWriter = sessionMessageWriter; } else { messageWriter = factory.TakeBufferedWriter(); } ArraySegment messageData = messageWriter.WriteMessage(message, bufferManager, messageOffset, maxMessageSize); if (MessageLogger.LogMessagesAtTransportLevel && !this.readerSessionForLoggingIsInvalid) { if (isSession) { if (this.readerSessionForLogging == null) { this.readerSessionForLogging = new XmlBinaryReaderSession(); } if (this.writerSession.HasNewStrings) { foreach (XmlDictionaryString xmlDictionaryString in this.writerSession.GetNewStrings()) { this.readerSessionForLogging.Add(this.writeIdCounter++, xmlDictionaryString.Value); } } } XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateBinaryReader(messageData.Array, messageData.Offset, messageData.Count, XD.Dictionary, XmlDictionaryReaderQuotas.Max, this.readerSessionForLogging, null); MessageLogger.LogMessage(ref message, xmlDictionaryReader, MessageLoggingSource.TransportSend); } else { this.readerSessionForLoggingIsInvalid = true; } if (isSession) { messageData = AddSessionInformationToMessage(messageData, bufferManager, maxMessageSize); } else { factory.ReturnMessageWriter(messageWriter); } if (TD.MessageWrittenByEncoderIsEnabled() && messageData != null) { TD.MessageWrittenByEncoder( eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message), messageData.Count, this); } CompressionFormat compressionFormat = this.CheckCompressedWrite(message); if (compressionFormat != CompressionFormat.None) { MessageEncoderCompressionHandler.CompressBuffer(ref messageData, bufferManager, compressionFormat); } return messageData; } public override void WriteMessage(Message message, Stream stream) { if (message == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message")); } if (stream == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("stream")); } EventTraceActivity eventTraceActivity = null; if (TD.BinaryMessageEncodingStartIsEnabled()) { eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message); TD.BinaryMessageEncodingStart(eventTraceActivity); } CompressionFormat compressionFormat = this.CheckCompressedWrite(message); if (compressionFormat != CompressionFormat.None) { stream = MessageEncoderCompressionHandler.GetCompressStream(stream, compressionFormat); } ThrowIfMismatchedMessageVersion(message); message.Properties.Encoder = this; XmlDictionaryWriter xmlWriter = factory.TakeStreamedWriter(stream); message.WriteMessage(xmlWriter); xmlWriter.Flush(); if (TD.StreamedMessageWrittenByEncoderIsEnabled()) { TD.StreamedMessageWrittenByEncoder(eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message)); } factory.ReturnStreamedWriter(xmlWriter); if (MessageLogger.LogMessagesAtTransportLevel) { MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend); } if (compressionFormat != CompressionFormat.None) { stream.Close(); } } public override bool IsContentTypeSupported(string contentType) { bool supported = true; if (!base.IsContentTypeSupported(contentType)) { if (this.CompressionEnabled) { supported = (this.factory.CompressionFormat == CompressionFormat.GZip && base.IsContentTypeSupported(contentType, this.gzipCompressedContentType, this.gzipCompressedContentType)) || (this.factory.CompressionFormat == CompressionFormat.Deflate && base.IsContentTypeSupported(contentType, this.deflateCompressedContentType, this.deflateCompressedContentType)) || base.IsContentTypeSupported(contentType, this.normalContentType, this.normalContentType); } else { supported = false; } } return supported; } public void SetSessionContentType(string contentType) { if (base.IsContentTypeSupported(contentType, this.gzipCompressedContentType, this.gzipCompressedContentType)) { this.sessionCompressionFormat = CompressionFormat.GZip; } else if (base.IsContentTypeSupported(contentType, this.deflateCompressedContentType, this.deflateCompressedContentType)) { this.sessionCompressionFormat = CompressionFormat.Deflate; } else { this.sessionCompressionFormat = CompressionFormat.None; } } public void AddCompressedMessageProperties(Message message, string supportedCompressionTypes) { message.Properties.Add(SupportedCompressionTypesMessageProperty, supportedCompressionTypes); } static bool ContentTypeEqualsOrStartsWith(string contentType, string supportedContentType) { return contentType == supportedContentType || contentType.StartsWith(supportedContentType, StringComparison.OrdinalIgnoreCase); } CompressionFormat CheckContentType(string contentType) { CompressionFormat compressionFormat = CompressionFormat.None; if (contentType == null) { compressionFormat = this.sessionCompressionFormat; } else { if (!this.CompressionEnabled) { if (!ContentTypeEqualsOrStartsWith(contentType, this.ContentType)) { throw FxTrace.Exception.AsError(new ProtocolException(SR.GetString(SR.EncoderUnrecognizedContentType, contentType, this.ContentType))); } } else { if (this.factory.CompressionFormat == CompressionFormat.GZip && ContentTypeEqualsOrStartsWith(contentType, this.gzipCompressedContentType)) { compressionFormat = CompressionFormat.GZip; } else if (this.factory.CompressionFormat == CompressionFormat.Deflate && ContentTypeEqualsOrStartsWith(contentType, this.deflateCompressedContentType)) { compressionFormat = CompressionFormat.Deflate; } else if (ContentTypeEqualsOrStartsWith(contentType, this.normalContentType)) { compressionFormat = CompressionFormat.None; } else { throw FxTrace.Exception.AsError(new ProtocolException(SR.GetString(SR.EncoderUnrecognizedContentType, contentType, this.ContentType))); } } } return compressionFormat; } CompressionFormat CheckCompressedWrite(Message message) { CompressionFormat compressionFormat = this.sessionCompressionFormat; if (compressionFormat != CompressionFormat.None && !this.isSession) { string acceptEncoding; if (message.Properties.TryGetValue(SupportedCompressionTypesMessageProperty, out acceptEncoding) && acceptEncoding != null) { acceptEncoding = acceptEncoding.ToLowerInvariant(); if ((compressionFormat == CompressionFormat.GZip && !acceptEncoding.Contains(MessageEncoderCompressionHandler.GZipContentEncoding)) || (compressionFormat == CompressionFormat.Deflate && !acceptEncoding.Contains(MessageEncoderCompressionHandler.DeflateContentEncoding))) { compressionFormat = CompressionFormat.None; } } } return compressionFormat; } string ITraceSourceStringProvider.GetSourceString() { return base.GetTraceSourceString(); } } class XmlBinaryWriterSessionWithQuota : XmlBinaryWriterSession { int bytesRemaining; List newStrings; public XmlBinaryWriterSessionWithQuota(int maxSessionSize) { bytesRemaining = maxSessionSize; } public bool HasNewStrings { get { return newStrings != null; } } public override bool TryAdd(XmlDictionaryString s, out int key) { if (bytesRemaining == 0) { key = -1; return false; } int bytesRequired = Encoding.UTF8.GetByteCount(s.Value); bytesRequired += IntEncoder.GetEncodedSize(bytesRequired); if (bytesRequired > bytesRemaining) { key = -1; bytesRemaining = 0; return false; } if (base.TryAdd(s, out key)) { if (newStrings == null) { newStrings = new List(); } newStrings.Add(s); bytesRemaining -= bytesRequired; return true; } else { return false; } } public IList GetNewStrings() { return newStrings; } public void ClearNewStrings() { newStrings = null; } } } class BinaryFormatBuilder { List bytes; public BinaryFormatBuilder() { this.bytes = new List(); } public int Count { get { return bytes.Count; } } public void AppendPrefixDictionaryElement(char prefix, int key) { this.AppendNode(XmlBinaryNodeType.PrefixDictionaryElementA + GetPrefixOffset(prefix)); this.AppendKey(key); } public void AppendDictionaryXmlnsAttribute(char prefix, int key) { this.AppendNode(XmlBinaryNodeType.DictionaryXmlnsAttribute); this.AppendUtf8(prefix); this.AppendKey(key); } public void AppendPrefixDictionaryAttribute(char prefix, int key, char value) { this.AppendNode(XmlBinaryNodeType.PrefixDictionaryAttributeA + GetPrefixOffset(prefix)); this.AppendKey(key); if (value == '1') { this.AppendNode(XmlBinaryNodeType.OneText); } else { this.AppendNode(XmlBinaryNodeType.Chars8Text); this.AppendUtf8(value); } } public void AppendDictionaryAttribute(char prefix, int key, char value) { this.AppendNode(XmlBinaryNodeType.DictionaryAttribute); this.AppendUtf8(prefix); this.AppendKey(key); this.AppendNode(XmlBinaryNodeType.Chars8Text); this.AppendUtf8(value); } public void AppendDictionaryTextWithEndElement(int key) { this.AppendNode(XmlBinaryNodeType.DictionaryTextWithEndElement); this.AppendKey(key); } public void AppendDictionaryTextWithEndElement() { this.AppendNode(XmlBinaryNodeType.DictionaryTextWithEndElement); } public void AppendUniqueIDWithEndElement() { this.AppendNode(XmlBinaryNodeType.UniqueIdTextWithEndElement); } public void AppendEndElement() { this.AppendNode(XmlBinaryNodeType.EndElement); } void AppendKey(int key) { if (key < 0 || key >= 0x4000) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("key", key, SR.GetString(SR.ValueMustBeInRange, 0, 0x4000))); } if (key >= 0x80) { this.AppendByte((key & 0x7f) | 0x80); this.AppendByte(key >> 7); } else { this.AppendByte(key); } } void AppendNode(XmlBinaryNodeType value) { this.AppendByte((int)value); } void AppendByte(int value) { if (value < 0 || value > 0xFF) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.ValueMustBeInRange, 0, 0xFF))); } this.bytes.Add((byte)value); } void AppendUtf8(char value) { AppendByte(1); AppendByte((int)value); } public int GetStaticKey(int value) { return value * 2; } public int GetSessionKey(int value) { return value * 2 + 1; } int GetPrefixOffset(char prefix) { if (prefix < 'a' && prefix > 'z') { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("prefix", prefix, SR.GetString(SR.ValueMustBeInRange, 'a', 'z'))); } return prefix - 'a'; } public byte[] ToByteArray() { byte[] array = this.bytes.ToArray(); this.bytes.Clear(); return array; } } static class BinaryFormatParser { public static bool IsSessionKey(int value) { return (value & 1) != 0; } public static int GetSessionKey(int value) { return value / 2; } public static int GetStaticKey(int value) { return value / 2; } public static int ParseInt32(byte[] buffer, int offset, int size) { switch (size) { case 1: return buffer[offset]; case 2: return (buffer[offset] & 0x7f) + (buffer[offset + 1] << 7); case 3: return (buffer[offset] & 0x7f) + ((buffer[offset + 1] & 0x7f) << 7) + (buffer[offset + 2] << 14); case 4: return (buffer[offset] & 0x7f) + ((buffer[offset + 1] & 0x7f) << 7) + ((buffer[offset + 2] & 0x7f) << 14) + (buffer[offset + 3] << 21); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.GetString(SR.ValueMustBeInRange, 1, 4))); } } public static int ParseKey(byte[] buffer, int offset, int size) { return ParseInt32(buffer, offset, size); } public unsafe static UniqueId ParseUniqueID(byte[] buffer, int offset, int size) { return new UniqueId(buffer, offset); } public static int MatchBytes(byte[] buffer, int offset, int size, byte[] buffer2) { if (size < buffer2.Length) { return 0; } int j = offset; for (int i = 0; i < buffer2.Length; i++, j++) { if (buffer2[i] != buffer[j]) { return 0; } } return buffer2.Length; } public static bool MatchAttributeNode(byte[] buffer, int offset, int size) { const XmlBinaryNodeType minAttribute = XmlBinaryNodeType.ShortAttribute; const XmlBinaryNodeType maxAttribute = XmlBinaryNodeType.DictionaryAttribute; if (size < 1) { return false; } XmlBinaryNodeType nodeType = (XmlBinaryNodeType)buffer[offset]; return nodeType >= minAttribute && nodeType <= maxAttribute; } public static int MatchKey(byte[] buffer, int offset, int size) { return MatchInt32(buffer, offset, size); } public static int MatchInt32(byte[] buffer, int offset, int size) { if (size > 0) { if ((buffer[offset] & 0x80) == 0) { return 1; } } if (size > 1) { if ((buffer[offset + 1] & 0x80) == 0) { return 2; } } if (size > 2) { if ((buffer[offset + 2] & 0x80) == 0) { return 3; } } if (size > 3) { if ((buffer[offset + 3] & 0x80) == 0) { return 4; } } return 0; } public static int MatchUniqueID(byte[] buffer, int offset, int size) { if (size < 16) { return 0; } return 16; } } class MessagePatterns { static readonly byte[] commonFragment; // static readonly byte[] requestFragment1; // static readonly byte[] requestFragment2; // ...session-to-key static readonly byte[] responseFragment1; // static readonly byte[] responseFragment2; // static-anonymous-key static readonly byte[] bodyFragment; // const int ToValueSessionKey = 1; IXmlDictionary dictionary; XmlBinaryReaderSession readerSession; ToHeader toHeader; MessageVersion messageVersion; static MessagePatterns() { BinaryFormatBuilder builder = new BinaryFormatBuilder(); MessageDictionary messageDictionary = XD.MessageDictionary; Message12Dictionary message12Dictionary = XD.Message12Dictionary; AddressingDictionary addressingDictionary = XD.AddressingDictionary; Addressing10Dictionary addressing10Dictionary = XD.Addressing10Dictionary; char messagePrefix = MessageStrings.Prefix[0]; char addressingPrefix = AddressingStrings.Prefix[0]; // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Envelope.Key)); builder.AppendDictionaryXmlnsAttribute(messagePrefix, builder.GetStaticKey(message12Dictionary.Namespace.Key)); builder.AppendDictionaryXmlnsAttribute(addressingPrefix, builder.GetStaticKey(addressing10Dictionary.Namespace.Key)); // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Header.Key)); // ... builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.Action.Key)); builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); builder.AppendDictionaryTextWithEndElement(); commonFragment = builder.ToByteArray(); // ... builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.MessageId.Key)); builder.AppendUniqueIDWithEndElement(); requestFragment1 = builder.ToByteArray(); // static-anonymous-key builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.ReplyTo.Key)); builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.Address.Key)); builder.AppendDictionaryTextWithEndElement(builder.GetStaticKey(addressing10Dictionary.Anonymous.Key)); builder.AppendEndElement(); // session-to-key builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.To.Key)); builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); builder.AppendDictionaryTextWithEndElement(builder.GetSessionKey(ToValueSessionKey)); // builder.AppendEndElement(); // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); requestFragment2 = builder.ToByteArray(); // ... builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.RelatesTo.Key)); builder.AppendUniqueIDWithEndElement(); responseFragment1 = builder.ToByteArray(); // static-anonymous-key builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.To.Key)); builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); builder.AppendDictionaryTextWithEndElement(builder.GetStaticKey(addressing10Dictionary.Anonymous.Key)); // builder.AppendEndElement(); // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); responseFragment2 = builder.ToByteArray(); // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Envelope.Key)); builder.AppendDictionaryXmlnsAttribute(messagePrefix, builder.GetStaticKey(message12Dictionary.Namespace.Key)); builder.AppendDictionaryXmlnsAttribute(addressingPrefix, builder.GetStaticKey(addressing10Dictionary.Namespace.Key)); // builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); bodyFragment = builder.ToByteArray(); } public MessagePatterns(IXmlDictionary dictionary, XmlBinaryReaderSession readerSession, MessageVersion messageVersion) { this.dictionary = dictionary; this.readerSession = readerSession; this.messageVersion = messageVersion; } public Message TryCreateMessage(byte[] buffer, int offset, int size, BufferManager bufferManager, BufferedMessageData messageData) { RelatesToHeader relatesToHeader; MessageIDHeader messageIDHeader; XmlDictionaryString toString; int currentOffset = offset; int remainingSize = size; int bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, commonFragment); if (bytesMatched == 0) { return null; } currentOffset += bytesMatched; remainingSize -= bytesMatched; bytesMatched = BinaryFormatParser.MatchKey(buffer, currentOffset, remainingSize); if (bytesMatched == 0) { return null; } int actionOffset = currentOffset; int actionSize = bytesMatched; currentOffset += bytesMatched; remainingSize -= bytesMatched; int totalBytesMatched; bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, requestFragment1); if (bytesMatched != 0) { currentOffset += bytesMatched; remainingSize -= bytesMatched; bytesMatched = BinaryFormatParser.MatchUniqueID(buffer, currentOffset, remainingSize); if (bytesMatched == 0) { return null; } int messageIDOffset = currentOffset; int messageIDSize = bytesMatched; currentOffset += bytesMatched; remainingSize -= bytesMatched; bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, requestFragment2); if (bytesMatched == 0) { return null; } currentOffset += bytesMatched; remainingSize -= bytesMatched; if (BinaryFormatParser.MatchAttributeNode(buffer, currentOffset, remainingSize)) { return null; } UniqueId messageId = BinaryFormatParser.ParseUniqueID(buffer, messageIDOffset, messageIDSize); messageIDHeader = MessageIDHeader.Create(messageId, messageVersion.Addressing); relatesToHeader = null; if (!readerSession.TryLookup(ToValueSessionKey, out toString)) { return null; } totalBytesMatched = requestFragment1.Length + messageIDSize + requestFragment2.Length; } else { bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, responseFragment1); if (bytesMatched == 0) { return null; } currentOffset += bytesMatched; remainingSize -= bytesMatched; bytesMatched = BinaryFormatParser.MatchUniqueID(buffer, currentOffset, remainingSize); if (bytesMatched == 0) { return null; } int messageIDOffset = currentOffset; int messageIDSize = bytesMatched; currentOffset += bytesMatched; remainingSize -= bytesMatched; bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, responseFragment2); if (bytesMatched == 0) { return null; } currentOffset += bytesMatched; remainingSize -= bytesMatched; if (BinaryFormatParser.MatchAttributeNode(buffer, currentOffset, remainingSize)) { return null; } UniqueId messageId = BinaryFormatParser.ParseUniqueID(buffer, messageIDOffset, messageIDSize); relatesToHeader = RelatesToHeader.Create(messageId, messageVersion.Addressing); messageIDHeader = null; toString = XD.Addressing10Dictionary.Anonymous; totalBytesMatched = responseFragment1.Length + messageIDSize + responseFragment2.Length; } totalBytesMatched += commonFragment.Length + actionSize; int actionKey = BinaryFormatParser.ParseKey(buffer, actionOffset, actionSize); XmlDictionaryString actionString; if (!TryLookupKey(actionKey, out actionString)) { return null; } ActionHeader actionHeader = ActionHeader.Create(actionString, messageVersion.Addressing); if (toHeader == null) { toHeader = ToHeader.Create(new Uri(toString.Value), messageVersion.Addressing); } int abandonedSize = totalBytesMatched - bodyFragment.Length; offset += abandonedSize; size -= abandonedSize; Buffer.BlockCopy(bodyFragment, 0, buffer, offset, bodyFragment.Length); messageData.Open(new ArraySegment(buffer, offset, size), bufferManager); PatternMessage patternMessage = new PatternMessage(messageData, this.messageVersion); MessageHeaders headers = patternMessage.Headers; headers.AddActionHeader(actionHeader); if (messageIDHeader != null) { headers.AddMessageIDHeader(messageIDHeader); headers.AddReplyToHeader(ReplyToHeader.AnonymousReplyTo10); } else { headers.AddRelatesToHeader(relatesToHeader); } headers.AddToHeader(toHeader); return patternMessage; } bool TryLookupKey(int key, out XmlDictionaryString result) { if (BinaryFormatParser.IsSessionKey(key)) { return readerSession.TryLookup(BinaryFormatParser.GetSessionKey(key), out result); } else { return dictionary.TryLookup(BinaryFormatParser.GetStaticKey(key), out result); } } sealed class PatternMessage : ReceivedMessage { IBufferedMessageData messageData; MessageHeaders headers; RecycledMessageState recycledMessageState; MessageProperties properties; XmlDictionaryReader reader; public PatternMessage(IBufferedMessageData messageData, MessageVersion messageVersion) { this.messageData = messageData; recycledMessageState = messageData.TakeMessageState(); if (recycledMessageState == null) { recycledMessageState = new RecycledMessageState(); } properties = recycledMessageState.TakeProperties(); if (properties == null) { this.properties = new MessageProperties(); } headers = recycledMessageState.TakeHeaders(); if (headers == null) { headers = new MessageHeaders(messageVersion); } else { headers.Init(messageVersion); } XmlDictionaryReader reader = messageData.GetMessageReader(); reader.ReadStartElement(); VerifyStartBody(reader, messageVersion.Envelope); ReadStartBody(reader); this.reader = reader; } public PatternMessage(IBufferedMessageData messageData, MessageVersion messageVersion, KeyValuePair[] properties, MessageHeaders headers) { this.messageData = messageData; this.messageData.Open(); this.recycledMessageState = this.messageData.TakeMessageState(); if (this.recycledMessageState == null) { this.recycledMessageState = new RecycledMessageState(); } this.properties = recycledMessageState.TakeProperties(); if (this.properties == null) { this.properties = new MessageProperties(); } if (properties != null) { this.properties.CopyProperties(properties); } this.headers = recycledMessageState.TakeHeaders(); if (this.headers == null) { this.headers = new MessageHeaders(messageVersion); } if (headers != null) { this.headers.CopyHeadersFrom(headers); } XmlDictionaryReader reader = messageData.GetMessageReader(); reader.ReadStartElement(); VerifyStartBody(reader, messageVersion.Envelope); ReadStartBody(reader); this.reader = reader; } public override MessageHeaders Headers { get { if (IsDisposed) #pragma warning suppress 56503 // Microsoft, Invalid State after dispose { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); } return headers; } } public override MessageProperties Properties { get { if (IsDisposed) #pragma warning suppress 56503 // Microsoft, Invalid State after dispose { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); } return properties; } } internal override void SetProperty(string name, object value) { MessageProperties prop = this.properties; if (prop != null) { prop[name] = value; } } public override MessageVersion Version { get { if (IsDisposed) { #pragma warning suppress 56503 // Microsoft, Invalid State after dispose throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); } return headers.MessageVersion; } } internal override RecycledMessageState RecycledMessageState { get { return recycledMessageState; } } XmlDictionaryReader GetBufferedReaderAtBody() { XmlDictionaryReader reader = messageData.GetMessageReader(); reader.ReadStartElement(); reader.ReadStartElement(); return reader; } protected override void OnBodyToString(XmlDictionaryWriter writer) { using (XmlDictionaryReader reader = GetBufferedReaderAtBody()) { while (reader.NodeType != XmlNodeType.EndElement) { writer.WriteNode(reader, false); } } } protected override void OnClose() { Exception ex = null; try { base.OnClose(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } ex = e; } try { properties.Dispose(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ex == null) { ex = e; } } try { if (reader != null) { reader.Close(); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ex == null) { ex = e; } } try { recycledMessageState.ReturnHeaders(headers); recycledMessageState.ReturnProperties(properties); messageData.ReturnMessageState(recycledMessageState); recycledMessageState = null; messageData.Close(); messageData = null; } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ex == null) { ex = e; } } if (ex != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ex); } } protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) { KeyValuePair[] properties = new KeyValuePair[Properties.Count]; ((ICollection>)Properties).CopyTo(properties, 0); messageData.EnableMultipleUsers(); return new PatternMessageBuffer(this.messageData, this.Version, properties, this.headers); } protected override XmlDictionaryReader OnGetReaderAtBodyContents() { XmlDictionaryReader reader = this.reader; this.reader = null; return reader; } protected override string OnGetBodyAttribute(string localName, string ns) { return null; } } class PatternMessageBuffer : MessageBuffer { bool closed; MessageHeaders headers; IBufferedMessageData messageDataAtBody; MessageVersion messageVersion; KeyValuePair[] properties; object thisLock = new object(); RecycledMessageState recycledMessageState; public PatternMessageBuffer(IBufferedMessageData messageDataAtBody, MessageVersion messageVersion, KeyValuePair[] properties, MessageHeaders headers) { this.messageDataAtBody = messageDataAtBody; this.messageDataAtBody.Open(); this.recycledMessageState = this.messageDataAtBody.TakeMessageState(); if (this.recycledMessageState == null) { this.recycledMessageState = new RecycledMessageState(); } this.headers = this.recycledMessageState.TakeHeaders(); if (this.headers == null) { this.headers = new MessageHeaders(messageVersion); } this.headers.CopyHeadersFrom(headers); this.properties = properties; this.messageVersion = messageVersion; } public override int BufferSize { get { lock (this.ThisLock) { if (this.closed) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); } return messageDataAtBody.Buffer.Count; } } } object ThisLock { get { return this.thisLock; } } public override void Close() { lock (this.thisLock) { if (!this.closed) { this.closed = true; this.recycledMessageState.ReturnHeaders(this.headers); this.messageDataAtBody.ReturnMessageState(this.recycledMessageState); this.messageDataAtBody.Close(); this.recycledMessageState = null; this.messageDataAtBody = null; this.properties = null; this.messageVersion = null; this.headers = null; } } } public override Message CreateMessage() { lock (this.ThisLock) { if (this.closed) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); } return new PatternMessage(this.messageDataAtBody, this.messageVersion, this.properties, this.headers); } } } } }