File: System\ServiceModel\Channels\MtomMessageEncoder.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Globalization;
using System.IO;
using System.Runtime;
using System.Runtime.Diagnostics;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Extensions.ObjectPool;
 
namespace System.ServiceModel.Channels
{
    internal class MtomMessageEncoderFactory : MessageEncoderFactory
    {
        private MessageVersion _messageVersion;
        private Encoding _writeEncoding;
        private int _maxReadPoolSize;
        private int _maxWritePoolSize;
        private int _maxBufferSize;
        private XmlDictionaryReaderQuotas _readerQuotas;
 
        // Pools used by MtomMessageEncoder
        private const int MaxPooledXmlReadersPerMessage = 2;
        private object _thisLock;
        private OnXmlDictionaryReaderClose _onStreamedReaderClose;
        // Double-checked locking pattern requires volatile for read/write synchronization
        private volatile SynchronizedPool<XmlDictionaryWriter> _streamedWriterPool;
        private volatile SynchronizedPool<XmlDictionaryReader> _streamedReaderPool;
        private volatile SynchronizedPool<MtomMessageEncoder.MtomBufferedMessageData> _bufferedReaderPool;
        private volatile SynchronizedPool<MtomMessageEncoder.MtomBufferedMessageWriter> _bufferedWriterPool;
        private volatile SynchronizedPool<RecycledMessageState> _recycledStatePool;
 
        public MtomMessageEncoderFactory(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, int maxBufferSize, XmlDictionaryReaderQuotas quotas)
        {
            _messageVersion = version;
            _writeEncoding = writeEncoding;
            _maxReadPoolSize = maxReadPoolSize;
            _maxWritePoolSize = maxWritePoolSize;
            _maxBufferSize = maxBufferSize;
            _readerQuotas = quotas;
            _thisLock = new object();
            _onStreamedReaderClose = new OnXmlDictionaryReaderClose(ReturnStreamedReader);
            if (version.Envelope == EnvelopeVersion.Soap12)
            {
                ContentEncodingMap = TextMessageEncoderFactory.Soap12Content;
            }
            else if (version.Envelope == EnvelopeVersion.Soap11)
            {
                ContentEncodingMap = TextMessageEncoderFactory.Soap11Content;
            }
            else
            {
                Fx.Assert("Invalid MessageVersion");
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Invalid MessageVersion")));
            }
        }
 
        public override MessageEncoder Encoder => new MtomMessageEncoder(_messageVersion, _writeEncoding, _maxReadPoolSize, _maxWritePoolSize, _maxBufferSize, _readerQuotas, this);
 
        public override MessageVersion MessageVersion => _messageVersion;
 
        public int MaxWritePoolSize => _maxWritePoolSize;
 
        public int MaxReadPoolSize => _maxReadPoolSize;
 
        public XmlDictionaryReaderQuotas ReaderQuotas => _readerQuotas;
 
        public int MaxBufferSize => _maxBufferSize;
 
        internal TextMessageEncoderFactory.ContentEncoding[] ContentEncodingMap { get; }
 
        internal XmlDictionaryWriter TakeStreamedWriter(Stream stream, string startInfo, string boundary, string startUri, bool writeMessageHeaders)
        {
            if (_streamedWriterPool == null)
            {
                lock (_thisLock)
                {
                    if (_streamedWriterPool == null)
                    {
                        _streamedWriterPool = new SynchronizedPool<XmlDictionaryWriter>(MaxWritePoolSize);
                    }
                }
            }
            XmlDictionaryWriter xmlWriter = _streamedWriterPool.Take();
            if (xmlWriter == null)
            {
                xmlWriter = XmlMtomWriter.Create(stream, _writeEncoding, int.MaxValue, startInfo, boundary, startUri, writeMessageHeaders, false);
                if (WcfEventSource.Instance.WritePoolMissIsEnabled())
                {
                    WcfEventSource.Instance.WritePoolMiss(xmlWriter.GetType().Name);
                }
            }
            else
            {
                ((IXmlMtomWriterInitializer)xmlWriter).SetOutput(stream, _writeEncoding, int.MaxValue, startInfo, boundary, startUri, writeMessageHeaders, false);
            }
            return xmlWriter;
        }
 
        internal void ReturnStreamedWriter(XmlDictionaryWriter xmlWriter)
        {
            xmlWriter.Close();
            _streamedWriterPool.Return(xmlWriter);
        }
 
        internal MtomMessageEncoder.MtomBufferedMessageWriter TakeBufferedWriter(MtomMessageEncoder messageEncoder)
        {
            if (_bufferedWriterPool == null)
            {
                lock (_thisLock)
                {
                    if (_bufferedWriterPool == null)
                    {
                        _bufferedWriterPool = new SynchronizedPool<MtomMessageEncoder.MtomBufferedMessageWriter>(MaxWritePoolSize);
                    }
                }
            }
 
            MtomMessageEncoder.MtomBufferedMessageWriter messageWriter = _bufferedWriterPool.Take();
            if (messageWriter == null)
            {
                messageWriter = new MtomMessageEncoder.MtomBufferedMessageWriter(messageEncoder);
                if (WcfEventSource.Instance.WritePoolMissIsEnabled())
                {
                    WcfEventSource.Instance.WritePoolMiss(messageWriter.GetType().Name);
                }
            }
            return messageWriter;
        }
 
        internal void ReturnMessageWriter(MtomMessageEncoder.MtomBufferedMessageWriter messageWriter)
        {
            _bufferedWriterPool.Return(messageWriter);
        }
 
        internal MtomMessageEncoder.MtomBufferedMessageData TakeBufferedReader(MtomMessageEncoder messageEncoder)
        {
            if (_bufferedReaderPool == null)
            {
                lock (_thisLock)
                {
                    if (_bufferedReaderPool == null)
                    {
                        _bufferedReaderPool = new SynchronizedPool<MtomMessageEncoder.MtomBufferedMessageData>(MaxReadPoolSize);
                    }
                }
            }
            MtomMessageEncoder.MtomBufferedMessageData messageData = _bufferedReaderPool.Take();
            if (messageData == null)
            {
                messageData = new MtomMessageEncoder.MtomBufferedMessageData(messageEncoder, MaxPooledXmlReadersPerMessage);
                if (WcfEventSource.Instance.ReadPoolMissIsEnabled())
                {
                    WcfEventSource.Instance.ReadPoolMiss(messageData.GetType().Name);
                }
            }
            return messageData;
        }
 
        internal void ReturnBufferedData(MtomMessageEncoder.MtomBufferedMessageData messageData)
        {
            _bufferedReaderPool.Return(messageData);
        }
 
        internal XmlReader TakeStreamedReader(Stream stream, string contentType, bool isMtomContentType)
        {
            if (_streamedReaderPool == null)
            {
                lock (_thisLock)
                {
                    if (_streamedReaderPool == null)
                    {
                        _streamedReaderPool = new SynchronizedPool<XmlDictionaryReader>(MaxReadPoolSize);
                    }
                }
            }
            XmlDictionaryReader xmlReader = _streamedReaderPool.Take();
            try
            {
                if (contentType == null || isMtomContentType)
                {
                    if (xmlReader != null && xmlReader is IXmlMtomReaderInitializer)
                    {
                        ((IXmlMtomReaderInitializer)xmlReader).SetInput(stream, MtomMessageEncoderFactory.GetSupportedEncodings(), contentType, ReaderQuotas, MaxBufferSize, _onStreamedReaderClose);
                    }
                    else
                    {
                        xmlReader = XmlMtomReader.Create(stream, MtomMessageEncoderFactory.GetSupportedEncodings(), contentType, ReaderQuotas, MaxBufferSize, _onStreamedReaderClose);
                        if (WcfEventSource.Instance.ReadPoolMissIsEnabled())
                        {
                            WcfEventSource.Instance.ReadPoolMiss(xmlReader.GetType().Name);
                        }
                    }
                }
                else
                {
                    if (xmlReader != null && xmlReader is IXmlTextReaderInitializer)
                    {
                        ((IXmlTextReaderInitializer)xmlReader).SetInput(stream, TextMessageEncoderFactory.GetEncodingFromContentType(contentType, ContentEncodingMap), ReaderQuotas, _onStreamedReaderClose);
                    }
                    else
                    {
                        xmlReader = XmlDictionaryReader.CreateTextReader(stream, TextMessageEncoderFactory.GetEncodingFromContentType(contentType, ContentEncodingMap), ReaderQuotas, _onStreamedReaderClose);
                        if (WcfEventSource.Instance.ReadPoolMissIsEnabled())
                        {
                            WcfEventSource.Instance.ReadPoolMiss(xmlReader.GetType().Name);
                        }
                    }
                }
            }
            catch (FormatException fe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SRP.SFxErrorCreatingMtomReader, fe));
            }
            catch (XmlException xe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SRP.SFxErrorCreatingMtomReader, xe));
            }
 
            return xmlReader;
        }
 
        internal void ReturnStreamedReader(XmlDictionaryReader xmlReader)
        {
            _streamedReaderPool.Return(xmlReader);
        }
 
        internal SynchronizedPool<RecycledMessageState> RecycledStatePool
        {
            get
            {
                if (_recycledStatePool == null)
                {
                    lock (_thisLock)
                    {
                        if (_recycledStatePool == null)
                        {
                            _recycledStatePool = new SynchronizedPool<RecycledMessageState>(MaxReadPoolSize);
                        }
                    }
                }
                return _recycledStatePool;
            }
        }
 
        public static Encoding[] GetSupportedEncodings()
        {
            Encoding[] supported = TextEncoderDefaults.SupportedEncodings;
            Encoding[] enc = new Encoding[supported.Length];
            Array.Copy(supported, enc, supported.Length);
            return enc;
        }
 
    }
 
    // Some notes:
    // The Encoding passed in is used for the SOAP envelope
    internal class MtomMessageEncoder : MessageEncoder
    {
        private Encoding _writeEncoding;
        private string _contentType;
        private string _boundary;
        private MessageVersion _version;
        private static UriGenerator s_mimeBoundaryGenerator;
        private XmlDictionaryReaderQuotas _bufferedReadReaderQuotas;
 
        private MtomMessageEncoderFactory _factory;
        private const string MtomMediaType = "multipart/related";
        private const string MtomContentType = MtomMediaType + "; type=\"application/xop+xml\"";
        private const string MtomStartUri = NamingHelper.DefaultNamespace + "0";
 
        public MtomMessageEncoder(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, int maxBufferSize, XmlDictionaryReaderQuotas quotas, MtomMessageEncoderFactory factory)
        {
            if (version == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(version));
            if (writeEncoding == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writeEncoding));
 
            _factory = factory;
            TextEncoderDefaults.ValidateEncoding(writeEncoding);
            _writeEncoding = writeEncoding;
 
            MaxReadPoolSize = maxReadPoolSize;
            MaxWritePoolSize = maxWritePoolSize;
 
            ReaderQuotas = new XmlDictionaryReaderQuotas();
            quotas.CopyTo(ReaderQuotas);
 
            _bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(ReaderQuotas);
            MaxBufferSize = maxBufferSize;
            _version = version;
            _contentType = GetContentType(out _boundary);
        }
 
        private static UriGenerator MimeBoundaryGenerator
        {
            get
            {
                if (s_mimeBoundaryGenerator == null)
                    s_mimeBoundaryGenerator = new UriGenerator("uuid", "+");
                return s_mimeBoundaryGenerator;
            }
        }
 
        public override string ContentType => _contentType;
 
        public int MaxWritePoolSize { get; }
 
        public int MaxReadPoolSize { get; }
 
        public XmlDictionaryReaderQuotas ReaderQuotas { get; }
 
        public int MaxBufferSize { get; }
 
        public override string MediaType => MtomMediaType;
 
        public override MessageVersion MessageVersion => _version;
 
        internal bool IsMTOMContentType(string contentType)
        {
            // check for MTOM contentType: multipart/related; type=\"application/xop+xml\"
            return IsContentTypeSupported(contentType, ContentType, MediaType);
        }
 
        internal bool IsTextContentType(string contentType)
        {
            // check for Text contentType: text/xml or application/soap+xml
            string textMediaType = TextMessageEncoderFactory.GetMediaType(_version);
            string textContentType = TextMessageEncoderFactory.GetContentType(textMediaType, _writeEncoding);
            return IsContentTypeSupported(contentType, textContentType, textMediaType);
        }
 
        public override bool IsContentTypeSupported(string contentType)
        {
            if (contentType == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(contentType)));
            return (IsMTOMContentType(contentType) || IsTextContentType(contentType));
        }
 
        internal override bool IsCharSetSupported(string charSet)
        {
            if (charSet == null || charSet.Length == 0)
                return true;
 
            Encoding tmp;
            return TextEncoderDefaults.TryGetEncoding(charSet, out tmp);
        }
 
        private string GenerateStartInfoString()
        {
            return (_version.Envelope == EnvelopeVersion.Soap12) ? TextMessageEncoderFactory.Soap12MediaType : TextMessageEncoderFactory.Soap11MediaType;
        }
 
        public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
        {
            if (bufferManager == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bufferManager));
 
            if (contentType == ContentType)
                contentType = null;
 
            if (WcfEventSource.Instance.MtomMessageDecodingStartIsEnabled())
            {
                WcfEventSource.Instance.MtomMessageDecodingStart();
            }
 
            MtomBufferedMessageData messageData = _factory.TakeBufferedReader(this);
            messageData._contentType = contentType;
            messageData.Open(buffer, bufferManager);
            RecycledMessageState messageState = messageData.TakeMessageState();
            if (messageState == null)
                messageState = new RecycledMessageState();
            Message message = new BufferedMessage(messageData, messageState);
            message.Properties.Encoder = this;
            if (MessageLogger.LogMessagesAtTransportLevel)
                MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
 
            if (WcfEventSource.Instance.MessageReadByEncoderIsEnabled() && buffer != null)
            {
                WcfEventSource.Instance.MessageReadByEncoder(
                    EventTraceActivityHelper.TryExtractActivity(message, true),
                    buffer.Count,
                    this);
            }
 
            return message;
        }
 
        public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(stream)));
 
            if (contentType == ContentType)
                contentType = null;
 
            if (WcfEventSource.Instance.MtomMessageDecodingStartIsEnabled())
            {
                WcfEventSource.Instance.MtomMessageDecodingStart();
            }
 
            XmlReader reader = _factory.TakeStreamedReader(stream, contentType, contentType == null || IsMTOMContentType(contentType));
            Message message = Message.CreateMessage(reader, maxSizeOfHeaders, _version);
            message.Properties.Encoder = this;
 
            if (WcfEventSource.Instance.StreamedMessageReadByEncoderIsEnabled())
            {
                WcfEventSource.Instance.StreamedMessageReadByEncoder(EventTraceActivityHelper.TryExtractActivity(message, true));
            }
 
            if (MessageLogger.LogMessagesAtTransportLevel)
                MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
            return message;
        }
 
        public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        {
            return WriteMessageInternal(message, maxMessageSize, bufferManager, messageOffset, GenerateStartInfoString(), _boundary, MtomStartUri);
        }
 
        public override ValueTask<ArraySegment<byte>> WriteMessageAsync(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        {
            return new ValueTask<ArraySegment<byte>>(WriteMessageInternal(message, maxMessageSize, bufferManager, messageOffset, GenerateStartInfoString(), _boundary, MtomStartUri));
        }
 
        private string GetContentType(out string boundary)
        {
            string startInfo = GenerateStartInfoString();
            boundary = MimeBoundaryGenerator.Next();
 
            return FormatContentType(boundary, startInfo);
        }
 
        internal string FormatContentType(string boundary, string startInfo)
        {
            return string.Format(CultureInfo.InvariantCulture,
                "{0};start=\"<{1}>\";boundary=\"{2}\";start-info=\"{3}\"",
                MtomContentType, MtomStartUri, boundary, startInfo);
        }
 
        private ArraySegment<byte> WriteMessageInternal(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset, string startInfo, string boundary, string startUri)
        {
            bool writeMessageHeaders = true;
            if (message.Properties.TryGetValue("System.ServiceModel.Channel.MtomMessageEncoder.WriteMessageHeaders", out object boolAsObject) && boolAsObject is bool)
            {
                writeMessageHeaders = (bool)boolAsObject;
            }
 
            if (message == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message));
            if (bufferManager == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bufferManager));
            if (maxMessageSize < 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxMessageSize), maxMessageSize,
                                                    SRP.ValueMustBeNonNegative));
            if (messageOffset < 0 || messageOffset > maxMessageSize)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(messageOffset), messageOffset,
                                                    SRP.Format(SRP.ValueMustBeInRange, 0, maxMessageSize)));
            ThrowIfMismatchedMessageVersion(message);
 
            EventTraceActivity eventTraceActivity = null;
            if (WcfEventSource.Instance.MtomMessageEncodingStartIsEnabled())
            {
                eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
                WcfEventSource.Instance.MtomMessageEncodingStart(eventTraceActivity);
            }
 
            message.Properties.Encoder = this;
 
            MtomBufferedMessageWriter messageWriter = _factory.TakeBufferedWriter(this);
            messageWriter._startInfo = startInfo;
            messageWriter._boundary = boundary;
            messageWriter._startUri = startUri;
            messageWriter._writeMessageHeaders = writeMessageHeaders;
            messageWriter._maxSizeInBytes = maxMessageSize;
            ArraySegment<byte> messageData = messageWriter.WriteMessage(message, bufferManager, messageOffset, maxMessageSize);
            _factory.ReturnMessageWriter(messageWriter);
 
            if (WcfEventSource.Instance.MessageWrittenByEncoderIsEnabled() && messageData != null)
            {
                WcfEventSource.Instance.MessageWrittenByEncoder(
                    eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message),
                    messageData.Count,
                    this);
            }
 
            if (MessageLogger.LogMessagesAtTransportLevel)
            {
                string contentType = null;
                if (boundary != null)
                    contentType = FormatContentType(boundary, startInfo ?? GenerateStartInfoString());
 
                XmlDictionaryReader xmlDictionaryReader = XmlMtomReader.Create(messageData.Array, messageData.Offset, messageData.Count, MtomMessageEncoderFactory.GetSupportedEncodings(), contentType, XmlDictionaryReaderQuotas.Max, int.MaxValue, null);
                MessageLogger.LogMessage(ref message, xmlDictionaryReader, MessageLoggingSource.TransportSend);
            }
 
            return messageData;
        }
 
        public override void WriteMessage(Message message, Stream stream)
        {
            using (TaskHelpers.RunTaskContinuationsOnOurThreads())
            {
                var valueTask = WriteMessageAsync(message, stream);
                if (valueTask.IsCompleted)
                {
                    valueTask.GetAwaiter().GetResult();
                }
                else
                {
                    valueTask.AsTask().WaitForCompletionNoSpin();
                }
            }
        }
 
        public override ValueTask WriteMessageAsync(Message message, Stream stream)
        {
            return WriteMessageInternalAsync(message, stream, GenerateStartInfoString(), _boundary, MtomStartUri);
        }
 
        public override IAsyncResult BeginWriteMessage(Message message, Stream stream, AsyncCallback callback, object state)
        {
            return WriteMessageAsync(message, stream).AsTask().ToApm(callback, state);
        }
 
        public override void EndWriteMessage(IAsyncResult result)
        {
            result.ToApmEnd();
        }
 
        private async ValueTask WriteMessageInternalAsync(Message message, Stream stream, string startInfo, string boundary, string startUri)
        {
            bool writeMessageHeaders = true;
            if (message.Properties.TryGetValue("System.ServiceModel.Channel.MtomMessageEncoder.WriteMessageHeaders", out object boolAsObject) && boolAsObject is bool)
            {
                writeMessageHeaders = (bool)boolAsObject;
            }
 
            if (message == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(message)));
            if (stream == null)
                throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(stream)), message);
            ThrowIfMismatchedMessageVersion(message);
 
            EventTraceActivity eventTraceActivity = null;
            if (WcfEventSource.Instance.MtomMessageEncodingStartIsEnabled())
            {
                eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
                WcfEventSource.Instance.MtomMessageEncodingStart(eventTraceActivity);
            }
 
            message.Properties.Encoder = this;
            if (MessageLogger.LogMessagesAtTransportLevel)
                MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
            XmlDictionaryWriter xmlWriter = _factory.TakeStreamedWriter(stream, startInfo, boundary, startUri, writeMessageHeaders);
            if (_writeEncoding.WebName == "utf-8")
            {
                await message.WriteMessageAsync(xmlWriter);
            }
            else
            {
                await xmlWriter.WriteStartDocumentAsync();
                await message.WriteMessageAsync(xmlWriter);
                await xmlWriter.WriteEndDocumentAsync();
            }
 
            await xmlWriter.FlushAsync();
            _factory.ReturnStreamedWriter(xmlWriter);
 
            if (WcfEventSource.Instance.StreamedMessageWrittenByEncoderIsEnabled())
            {
                WcfEventSource.Instance.StreamedMessageWrittenByEncoder(eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message));
            }
        }
 
        internal class MtomBufferedMessageData : BufferedMessageData
        {
            private MtomMessageEncoder _messageEncoder;
            private ObjectPool<XmlDictionaryReader> _readerPool;
            internal string _contentType;
            private OnXmlDictionaryReaderClose _onClose;
 
            public MtomBufferedMessageData(MtomMessageEncoder messageEncoder, int maxReaderPoolSize)
                : base(messageEncoder._factory.RecycledStatePool)
            {
                _messageEncoder = messageEncoder;
                _readerPool = NullCreatingPooledObjectPolicy<XmlDictionaryReader>.CreatePool(maxReaderPoolSize);
                _onClose = new OnXmlDictionaryReaderClose(OnXmlReaderClosed);
            }
 
            public override MessageEncoder MessageEncoder => _messageEncoder;
 
            public override XmlDictionaryReaderQuotas Quotas => _messageEncoder._bufferedReadReaderQuotas;
 
            protected override void OnClosed()
            {
                _messageEncoder._factory.ReturnBufferedData(this);
            }
 
            protected override XmlDictionaryReader TakeXmlReader()
            {
                try
                {
                    ArraySegment<byte> buffer = Buffer;
 
                    XmlDictionaryReader xmlReader = _readerPool.Get();
                    if (_contentType == null || _messageEncoder.IsMTOMContentType(_contentType))
                    {
                        if (xmlReader != null && xmlReader is IXmlMtomReaderInitializer)
                        {
                            ((IXmlMtomReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, MtomMessageEncoderFactory.GetSupportedEncodings(), _contentType, Quotas, _messageEncoder.MaxBufferSize, _onClose);
                        }
                        else
                        {
                            xmlReader = XmlMtomReader.Create(buffer.Array, buffer.Offset, buffer.Count, MtomMessageEncoderFactory.GetSupportedEncodings(), _contentType, Quotas, _messageEncoder.MaxBufferSize, _onClose);
                            if (WcfEventSource.Instance.ReadPoolMissIsEnabled())
                            {
                                WcfEventSource.Instance.ReadPoolMiss(xmlReader.GetType().Name);
                            }
                        }
                    }
                    else
                    {
                        if (xmlReader != null && xmlReader is IXmlTextReaderInitializer)
                        {
                            ((IXmlTextReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, TextMessageEncoderFactory.GetEncodingFromContentType(_contentType, _messageEncoder._factory.ContentEncodingMap), Quotas, _onClose);
                        }
                        else
                        {
                            xmlReader = XmlDictionaryReader.CreateTextReader(buffer.Array, buffer.Offset, buffer.Count, TextMessageEncoderFactory.GetEncodingFromContentType(_contentType, _messageEncoder._factory.ContentEncodingMap), Quotas, _onClose);
                            if (WcfEventSource.Instance.ReadPoolMissIsEnabled())
                            {
                                WcfEventSource.Instance.ReadPoolMiss(xmlReader.GetType().Name);
                            }
                        }
                    }
                    return xmlReader;
                }
                catch (FormatException fe)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                        SRP.SFxErrorCreatingMtomReader, fe));
                }
                catch (XmlException xe)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                        SRP.SFxErrorCreatingMtomReader, xe));
                }
            }
 
            protected override void ReturnXmlReader(XmlDictionaryReader xmlReader)
            {
                if (xmlReader != null)
                    _readerPool.Return(xmlReader);
            }
        }
 
        internal class MtomBufferedMessageWriter : BufferedMessageWriter
        {
            private MtomMessageEncoder _messageEncoder;
            internal bool _writeMessageHeaders;
            internal string _startInfo;
            internal string _startUri;
            internal string _boundary;
            internal int _maxSizeInBytes = int.MaxValue;
            private XmlDictionaryWriter _writer;
 
            public MtomBufferedMessageWriter(MtomMessageEncoder messageEncoder)
            {
                _messageEncoder = messageEncoder;
            }
 
            protected override XmlDictionaryWriter TakeXmlWriter(Stream stream)
            {
                XmlDictionaryWriter returnedWriter = _writer;
                if (returnedWriter == null)
                {
                    returnedWriter = XmlMtomWriter.Create(stream, _messageEncoder._writeEncoding, _maxSizeInBytes, _startInfo, _boundary, _startUri, _writeMessageHeaders, false);
                }
                else
                {
                    _writer = null;
                    ((IXmlMtomWriterInitializer)returnedWriter).SetOutput(stream, _messageEncoder._writeEncoding, _maxSizeInBytes, _startInfo, _boundary, _startUri, _writeMessageHeaders, false);
                }
                if (_messageEncoder._writeEncoding.WebName != "utf-8")
                    returnedWriter.WriteStartDocument();
                return returnedWriter;
            }
 
            protected override void ReturnXmlWriter(XmlDictionaryWriter writer)
            {
                writer.Close();
 
                if (_writer == null)
                    _writer = writer;
            }
        }
    }
}