File: System\ServiceModel\Channels\StreamBodyWriter.cs
Project: ndp\cdf\src\NetFx35\System.ServiceModel.Web\System.ServiceModel.Web.csproj (System.ServiceModel.Web)
//----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System;
    using System.IO;
    using System.Runtime;
    using System.ServiceModel;
    using System.Xml;
    using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
 
    public abstract class StreamBodyWriter : BodyWriter
    {
        // if isQuirkedTo40Behavior = true, does not try to write out <Binary> tags 
        //   this maintains compatibility for 4.0 implementers of a derived StreamBodyWriter if they 
        //   depended on behaviour where StreamBodyWriter isn't ByteStreamEncoder aware. 
        //   e.g., if they wrote their own <Binary> tags and relied on StreamBodyWriter to only write out the body. 
        //
        // if isQuirkedTo40Behavior = false, XmlWriterBackedStream will write out <Binary> tags (default in 4.5)
        readonly bool isQuirkedTo40Behavior;
 
        // externally accessible constructor quirked: 
        // if version <  4.5, XmlWriterBackedStream does not try to write out <Binary> tags
        // if version >= 4.5, XmlWriterBackedStream will write out <Binary> tags
        protected StreamBodyWriter(bool isBuffered)
            : this(isBuffered, !OSEnvironmentHelper.IsApplicationTargeting45)
        { }
 
        // internally accessible constructor allows derived types to determine whether StreamBodyWriter should be ByteStream aware
        // internal implementations SHOULD use isQuirkedTo40Behavior = false. 
        internal StreamBodyWriter(bool isBuffered, bool isQuirkedTo40Behavior)
            : base(isBuffered)
        {
            this.isQuirkedTo40Behavior = isQuirkedTo40Behavior;  
        }
 
        internal static StreamBodyWriter CreateStreamBodyWriter(Action<Stream> streamAction)
        {
            if (streamAction == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actionOfStream");
            }
            return new ActionOfStreamBodyWriter(streamAction);
        }
 
        protected abstract void OnWriteBodyContents(Stream stream);
 
        protected override BodyWriter OnCreateBufferedCopy(int maxBufferSize)
        {
            using (BufferManagerOutputStream bufferedStream = new BufferManagerOutputStream(SR2.MaxReceivedMessageSizeExceeded, maxBufferSize))
            {
                this.OnWriteBodyContents(bufferedStream);
                int size;
                byte[] bytesArray = bufferedStream.ToArray(out size);
                return new BufferedBytesStreamBodyWriter(bytesArray, size);
            }
        }
 
        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            using (XmlWriterBackedStream stream = new XmlWriterBackedStream(writer, this.isQuirkedTo40Behavior))
            {
                OnWriteBodyContents(stream);
            }
        }
 
        class XmlWriterBackedStream : Stream
        {
            private const string StreamElementName = "Binary";
            private readonly bool isQuirkedTo40Behavior; 
 
            XmlWriter writer;
 
            public XmlWriterBackedStream(XmlWriter writer, bool isQuirkedTo40Behavior)
            {
                if (writer == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
                }
                this.writer = writer;
 
                this.isQuirkedTo40Behavior = isQuirkedTo40Behavior;
            }
 
            public override bool CanRead
            {
                get { return false; }
            }
 
            public override bool CanSeek
            {
                get { return false; }
            }
 
            public override bool CanWrite
            {
                get { return true; }
            }
 
            public override void Flush()
            {
                this.writer.Flush();
            }
 
            public override long Length
            {
                get
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamPropertyGetNotSupported, "Length")));
                }
            }
 
            public override long Position
            {
                get
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamPropertyGetNotSupported, "Position")));
                }
                set
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamPropertySetNotSupported, "Position")));
                }
            }
 
            public override int Read(byte[] buffer, int offset, int count)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "Read")));
            }
 
            public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "BeginRead")));
            }
 
            public override int EndRead(IAsyncResult asyncResult)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "EndRead")));
            }
 
            public override int ReadByte()
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "ReadByte")));
            }
 
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "Seek")));
            }
 
            public override void SetLength(long value)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.XmlWriterBackedStreamMethodNotSupported, "SetLength")));
            }
 
            public override void Write(byte[] buffer, int offset, int count)
            {
                if (writer.WriteState == WriteState.Content || this.isQuirkedTo40Behavior)
                {
                    // if isQuirkedTo40Behavior == true, maintains compatibility for 4.0 implementers of a derived StreamBodyWriter 
                    // if they depended on behaviour without state checks, e.g., if they wrote their own <Binary> tags 
                    this.writer.WriteBase64(buffer, offset, count);
                }
                else if (writer.WriteState == WriteState.Start)
                {
                    writer.WriteStartElement(StreamElementName, string.Empty);
                    this.writer.WriteBase64(buffer, offset, count);
                }
            }
        }
 
        class BufferedBytesStreamBodyWriter : StreamBodyWriter
        {
            byte[] array;
            int size;
 
            public BufferedBytesStreamBodyWriter(byte[] array, int size)
                : base(true, false)
            {
                this.array = array;
                this.size = size;
            }
 
            protected override void OnWriteBodyContents(Stream stream)
            {
                stream.Write(this.array, 0, this.size);
            }
        }
 
        class ActionOfStreamBodyWriter : StreamBodyWriter
        {
            Action<Stream> actionOfStream;
 
            public ActionOfStreamBodyWriter(Action<Stream> actionOfStream)
                : base(false, false)
            {
                this.actionOfStream = actionOfStream;
            }
 
            protected override void OnWriteBodyContents(Stream stream)
            {
                actionOfStream(stream);
            }
        }
    }
}