File: System\ServiceModel\Channels\HttpChannelHelpers.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Net.Mime;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Reflection;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.Runtime.Diagnostics;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Diagnostics.Application;
    using System.ServiceModel.Security;
    using System.ServiceModel.Security.Tokens;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Xml;
 
    // abstract out the common functionality of an "HttpInput"
    abstract class HttpInput
    {
        const string multipartRelatedMediaType = "multipart/related";
        const string startInfoHeaderParam = "start-info";
        const string defaultContentType = "application/octet-stream";
 
        BufferManager bufferManager;
        bool isRequest;
        MessageEncoder messageEncoder;
        IHttpTransportFactorySettings settings;
        bool streamed;
        WebException webException;
        Stream inputStream;
        bool enableChannelBinding;
        bool errorGettingInputStream;
 
        protected HttpInput(IHttpTransportFactorySettings settings, bool isRequest, bool enableChannelBinding)
        {
            this.settings = settings;
            this.bufferManager = settings.BufferManager;
            this.messageEncoder = settings.MessageEncoderFactory.Encoder;
            this.webException = null;
            this.isRequest = isRequest;
            this.inputStream = null;
            this.enableChannelBinding = enableChannelBinding;
 
            if (isRequest)
            {
                this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
            }
            else
            {
                this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
            }
        }
 
        internal static HttpInput CreateHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
        {
            return new WebResponseHttpInput(httpWebResponse, settings, channelBinding);
        }
 
        internal WebException WebException
        {
            get { return webException; }
            set { webException = value; }
        }
 
        // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs.
        // Please exercice caution when passing in throwOnError = false.  This should basically only be done in error
        // code paths, or code paths where there is very good reason that you would not want this method to throw.
        // When passing in throwOnError = false, please handle the case where this method returns null.
        public Stream GetInputStream(bool throwOnError)
        {
            if (inputStream == null && (throwOnError || !this.errorGettingInputStream))
            {
                try
                {
                    inputStream = GetInputStream();
                    this.errorGettingInputStream = false;
                }
                catch (Exception e)
                {
                    this.errorGettingInputStream = true;
                    if (throwOnError || Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
                }
            }
 
            return inputStream;
        }
 
        // -1 if chunked
        public abstract long ContentLength { get; }
        protected abstract string ContentTypeCore { get; }
        protected abstract bool HasContent { get; }
        protected abstract string SoapActionHeader { get; }
        protected abstract Stream GetInputStream();
        protected virtual ChannelBinding ChannelBinding { get { return null; } }
 
        protected string ContentType
        {
            get
            {
                string contentType = ContentTypeCore;
 
                if (string.IsNullOrEmpty(contentType))
                {
                    return defaultContentType;
                }
 
                return contentType;
            }
        }
 
        void ThrowMaxReceivedMessageSizeExceeded()
        {
            if (TD.MaxReceivedMessageSizeExceededIsEnabled())
            {
                TD.MaxReceivedMessageSizeExceeded(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize));
            }
 
            if (isRequest)
            {
                ThrowHttpProtocolException(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize), HttpStatusCode.RequestEntityTooLarge);
            }
            else
            {
                string message = SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize);
                Exception inner = new QuotaExceededException(message);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner));
            }
        }
 
        Message DecodeBufferedMessage(ArraySegment<byte> buffer, Stream inputStream)
        {
            try
            {
                // if we're chunked, make sure we've consumed the whole body
                if (ContentLength == -1 && buffer.Count == settings.MaxReceivedMessageSize)
                {
                    byte[] extraBuffer = new byte[1];
                    int extraReceived = inputStream.Read(extraBuffer, 0, 1);
                    if (extraReceived > 0)
                    {
                        ThrowMaxReceivedMessageSizeExceeded();
                    }
                }
 
                try
                {
                    return messageEncoder.ReadMessage(buffer, bufferManager, ContentType);
                }
                catch (XmlException xmlException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
                }
            }
            finally
            {
                inputStream.Close();
            }
        }
 
        Message ReadBufferedMessage(Stream inputStream)
        {
            ArraySegment<byte> messageBuffer = GetMessageBuffer();
            byte[] buffer = messageBuffer.Array;
            int offset = 0;
            int count = messageBuffer.Count;
 
            while (count > 0)
            {
                int bytesRead = inputStream.Read(buffer, offset, count);
                if (bytesRead == 0) // EOF 
                {
                    if (ContentLength != -1)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new ProtocolException(SR.GetString(SR.HttpContentLengthIncorrect)));
                    }
 
                    break;
                }
                count -= bytesRead;
                offset += bytesRead;
            }
 
            return DecodeBufferedMessage(new ArraySegment<byte>(buffer, 0, offset), inputStream);
        }
 
        Message ReadChunkedBufferedMessage(Stream inputStream)
        {
            try
            {
                return messageEncoder.ReadMessage(inputStream, bufferManager, settings.MaxBufferSize, ContentType);
            }
            catch (XmlException xmlException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
            }
        }
 
        Message ReadStreamedMessage(Stream inputStream)
        {
            MaxMessageSizeStream maxMessageSizeStream = new MaxMessageSizeStream(inputStream, settings.MaxReceivedMessageSize);
 
            try
            {
                return messageEncoder.ReadMessage(maxMessageSizeStream, settings.MaxBufferSize, ContentType);
            }
            catch (XmlException xmlException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
            }
        }
 
        protected abstract void AddProperties(Message message);
 
        void ApplyChannelBinding(Message message)
        {
            if (this.enableChannelBinding)
            {
                ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, message, true);
            }
        }
 
        // makes sure that appropriate HTTP level headers are included in the received Message
        Exception ProcessHttpAddressing(Message message)
        {
            Exception result = null;
            AddProperties(message);
 
            // check if user is receiving WS-1 messages
            if (message.Version.Addressing == AddressingVersion.None)
            {
                bool actionAbsent = false;
                try
                {
                    actionAbsent = (message.Headers.Action == null);
                }
                catch (XmlException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (CommunicationException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
 
                if (!actionAbsent)
                {
                    result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
                        XD.AddressingDictionary.Action.Value));
                }
 
                bool toAbsent = false;
                try
                {
                    toAbsent = (message.Headers.To == null);
                }
                catch (XmlException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (CommunicationException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
 
                if (!toAbsent)
                {
                    result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
                        XD.AddressingDictionary.To.Value));
                }
                message.Headers.To = message.Properties.Via;
            }
 
            if (isRequest)
            {
                string action = null;
 
                if (message.Version.Envelope == EnvelopeVersion.Soap11)
                {
                    action = SoapActionHeader;
                }
                else if (message.Version.Envelope == EnvelopeVersion.Soap12 && !String.IsNullOrEmpty(ContentType))
                {
                    ContentType parsedContentType = new ContentType(ContentType);
 
                    if (parsedContentType.MediaType == multipartRelatedMediaType && parsedContentType.Parameters.ContainsKey(startInfoHeaderParam))
                    {
                        // fix to grab action from start-info as stated in RFC2387
                        action = new ContentType(parsedContentType.Parameters[startInfoHeaderParam]).Parameters["action"];
                    }
                    if (action == null)
                    {
                        // only if we can't find an action inside start-info
                        action = parsedContentType.Parameters["action"];
                    }
                }
 
                if (action != null)
                {
                    action = UrlUtility.UrlDecode(action, Encoding.UTF8);
 
                    if (action.Length >= 2 && action[0] == '"' && action[action.Length - 1] == '"')
                    {
                        action = action.Substring(1, action.Length - 2);
                    }
 
                    if (message.Version.Addressing == AddressingVersion.None)
                    {
                        message.Headers.Action = action;
                    }
 
                    try
                    {
 
                        if (action.Length > 0 && string.Compare(message.Headers.Action, action, StringComparison.Ordinal) != 0)
                        {
                            result = new ActionMismatchAddressingException(SR.GetString(SR.HttpSoapActionMismatchFault,
                                message.Headers.Action, action), message.Headers.Action, action);
                        }
 
                    }
                    catch (XmlException e)
                    {
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                    }
                    catch (CommunicationException e)
                    {
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                    }
                }
            }
 
            ApplyChannelBinding(message);
 
            if (DiagnosticUtility.ShouldUseActivity)
            {
                TraceUtility.TransferFromTransport(message);
            }
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageReceived, SR.GetString(SR.TraceCodeMessageReceived),
                    MessageTransmitTraceRecord.CreateReceiveTraceRecord(message), this, null, message);
            }
 
            // MessageLogger doesn't log AddressingVersion.None in the encoder since we want to make sure we log 
            // as much of the message as possible. Here we log after stamping the addressing information
            if (MessageLogger.LoggingEnabled && message.Version.Addressing == AddressingVersion.None)
            {
                MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive | MessageLoggingSource.LastChance);
            }
 
            return result;
        }
 
        void ValidateContentType()
        {
            if (!HasContent)
                return;
 
            if (string.IsNullOrEmpty(ContentType))
            {
                if (MessageLogger.ShouldLogMalformed)
                {
                    // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
                    Stream stream = this.GetInputStream(false);
                    if (stream != null)
                    {
                        MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
                    }
                }
                ThrowHttpProtocolException(SR.GetString(SR.HttpContentTypeHeaderRequired), HttpStatusCode.UnsupportedMediaType, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing);
            }
            if (!messageEncoder.IsContentTypeSupported(ContentType))
            {
                if (MessageLogger.ShouldLogMalformed)
                {
                    // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
                    Stream stream = this.GetInputStream(false);
                    if (stream != null)
                    {
                        MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
                    }
                }
                string statusDescription = string.Format(CultureInfo.InvariantCulture, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMismatch, ContentType, messageEncoder.ContentType);
                ThrowHttpProtocolException(SR.GetString(SR.ContentTypeMismatch, ContentType, messageEncoder.ContentType), HttpStatusCode.UnsupportedMediaType, statusDescription);
            }
        }
 
        public IAsyncResult BeginParseIncomingMessage(AsyncCallback callback, object state)
        {
            return this.BeginParseIncomingMessage(null, callback, state);
        }
 
        public IAsyncResult BeginParseIncomingMessage(HttpRequestMessage httpRequestMessage, AsyncCallback callback, object state)
        {            
            bool throwing = true;
            try
            {
                IAsyncResult result = new ParseMessageAsyncResult(httpRequestMessage, this, callback, state);
                throwing = false;
                return result;
            }
            finally
            {
                if (throwing)
                {
                    Close();
                }
            }
        }
 
        public Message EndParseIncomingMessage(IAsyncResult result, out Exception requestException)
        {
            bool throwing = true;
            try
            {
                Message message = ParseMessageAsyncResult.End(result, out requestException);
                throwing = false;
                return message;
            }
            finally
            {
                if (throwing)
                {
                    Close();
                }
            }
        }
 
        public HttpRequestMessageHttpInput CreateHttpRequestMessageInput()
        {
            HttpRequestMessage message = new HttpRequestMessage();
 
            if (this.HasContent)
            {
                message.Content = new StreamContent(new MaxMessageSizeStream(this.GetInputStream(true), this.settings.MaxReceivedMessageSize));
            }
 
            HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(message);
 
            this.ConfigureHttpRequestMessage(message);
            ChannelBinding channelBinding = this.enableChannelBinding ? this.ChannelBinding : null;
            return new HttpRequestMessageHttpInput(message, this.settings, this.enableChannelBinding, channelBinding);
        }
 
        public abstract void ConfigureHttpRequestMessage(HttpRequestMessage message);
 
        public Message ParseIncomingMessage(out Exception requestException)
        {
            return this.ParseIncomingMessage(null, out requestException);
        }
 
        public Message ParseIncomingMessage(HttpRequestMessage httpRequestMessage, out Exception requestException)
        {
            Message message = null;
            requestException = null;
            bool throwing = true;
            try
            {
                ValidateContentType();
 
                ServiceModelActivity activity = null;
                if (DiagnosticUtility.ShouldUseActivity &&
                    ((ServiceModelActivity.Current == null) ||
                     (ServiceModelActivity.Current.ActivityType != ActivityType.ProcessAction)))
                {
                    activity = ServiceModelActivity.CreateBoundedActivity(true);
                }
                using (activity)
                {
                    if (DiagnosticUtility.ShouldUseActivity && activity != null)
                    {
                        // Only update the Start identifier if the activity is not null.
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
                    }
 
                    if (!this.HasContent)
                    {
                        if (this.messageEncoder.MessageVersion == MessageVersion.None)
                        {
                            message = new NullMessage();
                        }
                        else
                        {
                            return null;
                        }
                    }
                    else
                    {
                        Stream stream = this.GetInputStream(true);
                        if (streamed)
                        {
                            message = ReadStreamedMessage(stream);
                        }
                        else if (this.ContentLength == -1)
                        {
                            message = ReadChunkedBufferedMessage(stream);
                        }
                        else
                        {
                            if (httpRequestMessage == null)
                            {
                                message = ReadBufferedMessage(stream);
                            }
                            else
                            {
                                message = ReadBufferedMessage(httpRequestMessage);
                            }
                        }
                    }
 
                    requestException = ProcessHttpAddressing(message);
 
                    throwing = false;
                    return message;
                }
            }
            finally
            {
                if (throwing)
                {
                    Close();
                }
            }
        }
 
        Message ReadBufferedMessage(HttpRequestMessage httpRequestMessage)
        {
            Fx.Assert(httpRequestMessage != null, "httpRequestMessage cannot be null.");
 
            Message message;
            using (HttpContent currentContent = httpRequestMessage.Content)
            {
                int length = (int)this.ContentLength;
                byte[] buffer = this.bufferManager.TakeBuffer(length);
                bool success = false;
                try
                {
                    MemoryStream ms = new MemoryStream(buffer);
                    currentContent.CopyToAsync(ms).Wait<CommunicationException>();
                    httpRequestMessage.Content = new ByteArrayContent(buffer, 0, length);
 
                    foreach (var header in currentContent.Headers)
                    {
                        httpRequestMessage.Content.Headers.Add(header.Key, header.Value);
                    }
 
                    // 
 
                    message = this.messageEncoder.ReadMessage(new ArraySegment<byte>(buffer, 0, length), this.bufferManager, this.ContentType);
                    success = true;
                }
                finally
                {
                    if (!success)
                    {
                        // We don't have to return it in success case since the buffer will be returned to bufferManager when the message is disposed.
                        this.bufferManager.ReturnBuffer(buffer);
                    }
                }
            }
            return message;
        }
 
        void ThrowHttpProtocolException(string message, HttpStatusCode statusCode)
        {
            ThrowHttpProtocolException(message, statusCode, null);
        }
 
        void ThrowHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateHttpProtocolException(message, statusCode, statusDescription, webException));
        }
 
        internal static ProtocolException CreateHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription, Exception innerException)
        {
            ProtocolException exception = new ProtocolException(message, innerException);
            exception.Data.Add(HttpChannelUtilities.HttpStatusCodeExceptionKey, statusCode);
            if (statusDescription != null && statusDescription.Length > 0)
            {
                exception.Data.Add(HttpChannelUtilities.HttpStatusDescriptionExceptionKey, statusDescription);
            }
 
            return exception;
        }
 
        protected virtual void Close()
        {
        }
 
        ArraySegment<byte> GetMessageBuffer()
        {
            long count = ContentLength;
            int bufferSize;
 
            if (count > settings.MaxReceivedMessageSize)
            {
                ThrowMaxReceivedMessageSizeExceeded();
            }
 
            bufferSize = (int)count;
 
            return new ArraySegment<byte>(bufferManager.TakeBuffer(bufferSize), 0, bufferSize);
        }
 
        class ParseMessageAsyncResult : TraceAsyncResult
        {
            ArraySegment<byte> buffer;
            int count;
            int offset;
            HttpInput httpInput;
            Stream inputStream;
            Message message;
            Exception requestException = null;
            HttpRequestMessage httpRequestMessage;            
            static AsyncCallback onRead = Fx.ThunkCallback(new AsyncCallback(OnRead));
 
            public ParseMessageAsyncResult(
                HttpRequestMessage httpRequestMessage,
                HttpInput httpInput,
                AsyncCallback callback,
                object state)
                : base(callback, state)
            {
                this.httpInput = httpInput;
                this.httpRequestMessage = httpRequestMessage;
                this.BeginParse();
            }
 
            void BeginParse()
            {
                httpInput.ValidateContentType();
                this.inputStream = httpInput.GetInputStream(true);
 
                if (!httpInput.HasContent)
                {
                    if (httpInput.messageEncoder.MessageVersion == MessageVersion.None)
                    {
                        this.message = new NullMessage();
                    }
                    else
                    {
                        base.Complete(true);
                        return;
                    }
                }
                else if (httpInput.streamed || httpInput.ContentLength == -1)
                {
                    if (httpInput.streamed)
                    {
                        this.message = httpInput.ReadStreamedMessage(inputStream);
                    }
                    else
                    {
                        this.message = httpInput.ReadChunkedBufferedMessage(inputStream);
                    }
                }
 
                if (this.message != null)
                {
                    this.requestException = httpInput.ProcessHttpAddressing(this.message);
                    base.Complete(true);
                    return;
                }
 
                AsyncCompletionResult result;
                if (httpRequestMessage == null)
                {
                    result = this.DecodeBufferedMessageAsync();
                }
                else
                {
                    result = this.DecodeBufferedHttpRequestMessageAsync();
                }
 
                if (result == AsyncCompletionResult.Completed)
                {
                    base.Complete(true);
                }
            }
 
            AsyncCompletionResult DecodeBufferedMessageAsync()
            {
                this.buffer = this.httpInput.GetMessageBuffer();
                this.count = this.buffer.Count;
                this.offset = 0;
 
                IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
                if (result.CompletedSynchronously)
                {
                    if (ContinueReading(inputStream.EndRead(result)))
                    {
                        return AsyncCompletionResult.Completed;
                    }
                }
 
                return AsyncCompletionResult.Queued;
            }
 
            bool ContinueReading(int bytesRead)
            {
                while (true)
                {
                    if (bytesRead == 0) // EOF
                    {
                        break;
                    }
                    else
                    {
                        offset += bytesRead;
                        count -= bytesRead;
                        if (count <= 0)
                        {
                            break;
                        }
                        else
                        {
                            IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
                            if (!result.CompletedSynchronously)
                            {
                                return false;
                            }
 
                            bytesRead = inputStream.EndRead(result);
                        }
                    }
                }
 
                using (DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.BoundOperation(this.CallbackActivity) : null)
                {
                    using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity(true) : null)
                    {
                        if (DiagnosticUtility.ShouldUseActivity)
                        {
                            ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
                        }
 
                        this.message = this.httpInput.DecodeBufferedMessage(new ArraySegment<byte>(buffer.Array, 0, offset), inputStream);
                        this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
                    }
                    return true;
                }
            }
 
            static void OnRead(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                    return;
 
                ParseMessageAsyncResult thisPtr = (ParseMessageAsyncResult)result.AsyncState;
 
                Exception completionException = null;
                bool completeSelf;
                try
                {
                    completeSelf = thisPtr.ContinueReading(thisPtr.inputStream.EndRead(result));
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    completeSelf = true;
                    completionException = e;
                }
 
                if (completeSelf)
                {
                    thisPtr.Complete(false, completionException);
                }
            }
 
            public static Message End(IAsyncResult result, out Exception requestException)
            {
                ParseMessageAsyncResult thisPtr = AsyncResult.End<ParseMessageAsyncResult>(result);
                requestException = thisPtr.requestException;
                return thisPtr.message;
            }
 
            AsyncCompletionResult DecodeBufferedHttpRequestMessageAsync()
            {
                // Need to consider moving this to async implemenation for HttpContent reading.(CSDMAIN: 229108)
                this.message = this.httpInput.ReadBufferedMessage(this.httpRequestMessage);
                this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
                return AsyncCompletionResult.Completed;
            }
        }
 
        class WebResponseHttpInput : HttpInput
        {
            HttpWebResponse httpWebResponse;
            byte[] preReadBuffer;
            ChannelBinding channelBinding;
            bool hasContent;
 
            public WebResponseHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
                : base(settings, false, channelBinding != null)
            {
                this.channelBinding = channelBinding;
                this.httpWebResponse = httpWebResponse;
                if (this.httpWebResponse.ContentLength == -1)
                {
                    this.preReadBuffer = new byte[1];
 
                    if (this.httpWebResponse.GetResponseStream().Read(preReadBuffer, 0, 1) == 0)
                    {
                        this.preReadBuffer = null;
                    }
                }
 
                this.hasContent = (this.preReadBuffer != null || this.httpWebResponse.ContentLength > 0);
                if (!this.hasContent)
                {
                    // Close the response stream to avoid leaking the connection.
                    this.httpWebResponse.GetResponseStream().Close();
                }
            }
 
            protected override ChannelBinding ChannelBinding
            {
                get
                {
                    return this.channelBinding;
                }
            }
 
            public override long ContentLength
            {
                get
                {
                    return httpWebResponse.ContentLength;
                }
            }
 
            protected override string ContentTypeCore
            {
                get
                {
                    return httpWebResponse.ContentType;
                }
            }
 
            protected override bool HasContent
            {
                get { return this.hasContent; }
            }
 
            protected override string SoapActionHeader
            {
                get
                {
                    return httpWebResponse.Headers["SOAPAction"];
                }
            }
 
            protected override void AddProperties(Message message)
            {
                HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty(httpWebResponse.Headers);
                responseProperty.StatusCode = httpWebResponse.StatusCode;
                responseProperty.StatusDescription = httpWebResponse.StatusDescription;
                message.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
                message.Properties.Via = message.Version.Addressing.AnonymousUri;
            }
 
            public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
            {
                // HTTP pipeline for client side is not implemented yet
                // DCR CSDMain 216853 is tracking this
                // This API is never going to be called in current stack
                Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
                throw FxTrace.Exception.AsError(new NotSupportedException());
            }
 
            protected override void Close()
            {
                try
                {
                    httpWebResponse.Close();
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
                }
            }
 
            protected override Stream GetInputStream()
            {
                Fx.Assert(this.HasContent, "this.HasContent must be true.");
                if (this.preReadBuffer != null)
                {
                    return new WebResponseInputStream(httpWebResponse, preReadBuffer);
                }
                else
                {
                    return new WebResponseInputStream(httpWebResponse);
                }
            }
 
            class WebResponseInputStream : DetectEofStream
            {
                // in order to avoid ----ing kernel buffers, we throttle our reads. http.sys
                // deals with this fine, but System.Net doesn't do any such throttling.
                const int maxSocketRead = 64 * 1024;
                HttpWebResponse webResponse;
                bool responseClosed;
 
                public WebResponseInputStream(HttpWebResponse httpWebResponse)
                    : base(httpWebResponse.GetResponseStream())
                {
                    this.webResponse = httpWebResponse;
                }
 
                public WebResponseInputStream(HttpWebResponse httpWebResponse, byte[] prereadBuffer)
                    : base(new PreReadStream(httpWebResponse.GetResponseStream(), prereadBuffer))
                {
                    this.webResponse = httpWebResponse;
                }
 
 
                public override void Close()
                {
                    base.Close();
                    CloseResponse();
                }
 
                protected override void OnReceivedEof()
                {
                    base.OnReceivedEof();
                    CloseResponse();
                }
 
                void CloseResponse()
                {
                    if (responseClosed)
                    {
                        return;
                    }
 
                    responseClosed = true;
                    this.webResponse.Close();
                }
 
                public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
                {
                    try
                    {
                        return BaseStream.BeginRead(buffer, offset, Math.Min(count, maxSocketRead), callback, state);
                    }
                    catch (IOException ioException)
                    {
                        throw this.CreateResponseIOException(ioException);
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
                    }
                }
 
                public override int EndRead(IAsyncResult result)
                {
                    try
                    {
                        return BaseStream.EndRead(result);
                    }
                    catch (IOException ioException)
                    {
                        throw this.CreateResponseIOException(ioException);
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
                    }
                }
 
                public override int Read(byte[] buffer, int offset, int count)
                {
                    try
                    {
                        return BaseStream.Read(buffer, offset, Math.Min(count, maxSocketRead));
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
                    }
                    catch (IOException ioException)
                    {
                        throw this.CreateResponseIOException(ioException);
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
                    }
                }
 
 
                public override int ReadByte()
                {
                    try
                    {
                        return BaseStream.ReadByte();
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
                    }
                    catch (IOException ioException)
                    {
                        throw this.CreateResponseIOException(ioException);
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
                    }
                }
 
                private Exception CreateResponseIOException(IOException ioException)
                {
                    TimeSpan timeSpan = this.CanTimeout ? TimeoutHelper.FromMilliseconds(this.ReadTimeout) : TimeSpan.MaxValue;
 
                    return DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, timeSpan));
                } 
 
            }
        }
    }
 
    // abstract out the common functionality of an "HttpOutput"
    abstract class HttpOutput
    {
        const string DefaultMimeVersion = "1.0";
 
        HttpAbortReason abortReason;
        bool isDisposed;
        bool isRequest;
        Message message;
        IHttpTransportFactorySettings settings;
        byte[] bufferToRecycle;
        BufferManager bufferManager;
        MessageEncoder messageEncoder;
        bool streamed;
        static Action<object> onStreamSendTimeout;
        string mtomBoundary;
        Stream outputStream;
        bool supportsConcurrentIO;
        EventTraceActivity eventTraceActivity;
        bool canSendCompressedResponses;
 
        protected HttpOutput(IHttpTransportFactorySettings settings, Message message, bool isRequest, bool supportsConcurrentIO)
        {
            this.settings = settings;
            this.message = message;
            this.isRequest = isRequest;
            this.bufferManager = settings.BufferManager;
            this.messageEncoder = settings.MessageEncoderFactory.Encoder;
            ICompressedMessageEncoder compressedMessageEncoder = this.messageEncoder as ICompressedMessageEncoder;
            this.canSendCompressedResponses = compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled;
            if (isRequest)
            {
                this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
            }
            else
            {
                this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
            }
            this.supportsConcurrentIO = supportsConcurrentIO;
 
            if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
            {
                this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
            }
        }
 
        protected virtual bool IsChannelBindingSupportEnabled { get { return false; } }
        protected virtual ChannelBinding ChannelBinding { get { return null; } }
 
        protected void Abort()
        {
            Abort(HttpAbortReason.Aborted);
        }
 
        public virtual void Abort(HttpAbortReason reason)
        {
            if (isDisposed)
            {
                return;
            }
 
            this.abortReason = reason;
 
            TraceRequestResponseAborted(reason);
 
            CleanupBuffer();
        }
 
        private void TraceRequestResponseAborted(HttpAbortReason reason)
        {
            if (isRequest)
            {
                if (TD.HttpChannelRequestAbortedIsEnabled())
                {
                    TD.HttpChannelRequestAborted(this.eventTraceActivity);
                }
            }
            else if (TD.HttpChannelResponseAbortedIsEnabled())
            {
                TD.HttpChannelResponseAborted(this.eventTraceActivity);
            }
 
            if (DiagnosticUtility.ShouldTraceWarning)
            {
                TraceUtility.TraceEvent(TraceEventType.Warning,
                                        isRequest ? TraceCode.HttpChannelRequestAborted : TraceCode.HttpChannelResponseAborted,
                                        isRequest ? SR.GetString(SR.TraceCodeHttpChannelRequestAborted) : SR.GetString(SR.TraceCodeHttpChannelResponseAborted),
                                        this.message);
            }
        }
 
        public void Close()
        {
            if (isDisposed)
            {
                return;
            }
 
            try
            {
                if (this.outputStream != null)
                {
                    outputStream.Close();
                }
            }
            finally
            {
                CleanupBuffer();
            }
        }
 
        void CleanupBuffer()
        {
            byte[] bufferToRecycleSnapshot = Interlocked.Exchange<byte[]>(ref this.bufferToRecycle, null);
            if (bufferToRecycleSnapshot != null)
            {
                bufferManager.ReturnBuffer(bufferToRecycleSnapshot);
            }
 
            isDisposed = true;
        }
 
        protected abstract void AddMimeVersion(string version);
        protected abstract void AddHeader(string name, string value);
        protected abstract void SetContentType(string contentType);
        protected abstract void SetContentEncoding(string contentEncoding);
        protected abstract void SetStatusCode(HttpStatusCode statusCode);
        protected abstract void SetStatusDescription(string statusDescription);
        protected virtual bool CleanupChannelBinding { get { return true; } }
        protected virtual void SetContentLength(int contentLength)
        {
        }
 
        protected virtual string HttpMethod { get { return null; } }
 
        public virtual ChannelBinding TakeChannelBinding()
        {
            return null;
        }
 
        private void ApplyChannelBinding()
        {
            if (this.IsChannelBindingSupportEnabled)
            {
                ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, this.message, this.CleanupChannelBinding);
            }
        }
 
        protected abstract Stream GetOutputStream();
 
        protected virtual bool WillGetOutputStreamCompleteSynchronously
        {
            get { return true; }
        }
 
        protected bool CanSendCompressedResponses
        {
            get { return this.canSendCompressedResponses; }
        }
 
        protected virtual IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }
 
        protected virtual Stream EndGetOutputStream(IAsyncResult result)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }
 
        public void ConfigureHttpResponseMessage(Message message, HttpResponseMessage httpResponseMessage, HttpResponseMessageProperty responseProperty)
        {
            HttpChannelUtilities.EnsureHttpResponseMessageContentNotNull(httpResponseMessage);
 
            string action = message.Headers.Action;
 
            if (message.Version.Addressing == AddressingVersion.None)
            {
                if (MessageLogger.LogMessagesAtTransportLevel)
                {
                    message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
                }
 
                message.Headers.Action = null;
                message.Headers.To = null;
            }
 
            bool httpResponseMessagePropertyFound = responseProperty != null;
 
            string contentType = null;
            if (message.Version == MessageVersion.None && httpResponseMessagePropertyFound && !string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
            {
                contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
                responseProperty.Headers.Remove(HttpResponseHeader.ContentType);
                if (!messageEncoder.IsContentTypeSupported(contentType))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
                        contentType)));
                }
            }
 
            if (string.IsNullOrEmpty(contentType))
            {
                MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
                if (mtomMessageEncoder == null)
                {
                    contentType = messageEncoder.ContentType;
                }
                else
                {
                    contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
                    // For MTOM messages, add a MIME version header
                    httpResponseMessage.Headers.Add(HttpChannelUtilities.MIMEVersionHeader, DefaultMimeVersion);
                }
            }
 
            if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
            {
                EnsureEventTraceActivity(message);
            }
 
            if (this.CanSendCompressedResponses)
            {
                string contentEncoding;
                string compressionContentType = contentType;
                if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref compressionContentType, out contentEncoding))
                {
                    contentType = compressionContentType;
                    this.SetContentEncoding(contentEncoding);
                }
            }
 
            if (httpResponseMessage.Content != null && !string.IsNullOrEmpty(contentType))
            {
                MediaTypeHeaderValue mediaTypeHeaderValue;
                if (!MediaTypeHeaderValue.TryParse(contentType, out mediaTypeHeaderValue))
                {
                    throw FxTrace.Exception.Argument("contentType", SR.GetString(SR.InvalidContentTypeError, contentType));
                }
                httpResponseMessage.Content.Headers.ContentType = mediaTypeHeaderValue;
            }
 
            bool httpMethodIsHead = string.Compare(this.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
 
            if (httpMethodIsHead ||
                httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
            {
                httpResponseMessage.Content.Headers.ContentLength = 0;
                httpResponseMessage.Content.Headers.ContentType = null;
            }
 
            if (httpResponseMessagePropertyFound)
            {
                httpResponseMessage.StatusCode = responseProperty.StatusCode;
                if (responseProperty.StatusDescription != null)
                {
                    responseProperty.StatusDescription = responseProperty.StatusDescription;
                }
 
                foreach (string key in responseProperty.Headers.AllKeys)
                {
                    httpResponseMessage.AddHeader(key, responseProperty.Headers[key]);
                }
            }
 
            if (!message.IsEmpty)
            {
                using (HttpContent content = httpResponseMessage.Content)
                {
                    if (this.streamed)
                    {
                        IStreamedMessageEncoder streamedMessageEncoder = this.messageEncoder as IStreamedMessageEncoder;
                        Stream stream = null;
                        if (streamedMessageEncoder != null)
                        {
                            stream = streamedMessageEncoder.GetResponseMessageStream(message);
                        }
 
                        if (stream != null)
                        {
                            httpResponseMessage.Content = new StreamContent(stream);
                        }
                        else
                        {
                            httpResponseMessage.Content = new OpaqueContent(this.messageEncoder, message, this.mtomBoundary);
                        }
                    }
                    else
                    {
                        // HttpOutputByteArrayContent assumes responsibility for returning the buffer to the bufferManager. 
                        ArraySegment<byte> messageBytes = this.SerializeBufferedMessage(message, false);
                        httpResponseMessage.Content = new HttpOutputByteArrayContent(messageBytes.Array, messageBytes.Offset, messageBytes.Count, this.bufferManager);
                    }
 
                    httpResponseMessage.Content.Headers.Clear();
                    foreach (var header in content.Headers)
                    {
                        httpResponseMessage.Content.Headers.Add(header.Key, header.Value);
                    }
                }
            }
        }
 
        protected virtual bool PrepareHttpSend(Message message)
        {
            string action = message.Headers.Action;
 
            if (message.Version.Addressing == AddressingVersion.None)
            {
                if (MessageLogger.LogMessagesAtTransportLevel)
                {
                    message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
                }
 
                message.Headers.Action = null;
                message.Headers.To = null;
            }
 
            string contentType = null;
 
            if (message.Version == MessageVersion.None)
            {
                object property = null;
                if (message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out property))
                {
                    HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)property;
                    if (!string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
                    {
                        contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
                        if (!messageEncoder.IsContentTypeSupported(contentType))
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
                                contentType)));
                        }
                    }
                }
            }
 
            if (string.IsNullOrEmpty(contentType))
            {
                MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
                if (mtomMessageEncoder == null)
                {
                    contentType = messageEncoder.ContentType;
                }
                else
                {
                    contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
                    // For MTOM messages, add a MIME version header
                    AddMimeVersion("1.0");
                }
            }
 
            if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
            {
                EnsureEventTraceActivity(message);
            }
 
            SetContentType(contentType);
            return message is NullMessage;
        }
 
        protected bool PrepareHttpSend(HttpResponseMessage httpResponseMessage)
        {
            this.PrepareHttpSendCore(httpResponseMessage);
            return HttpChannelUtilities.IsEmpty(httpResponseMessage);
        }
 
        protected abstract void PrepareHttpSendCore(HttpResponseMessage message);
 
        private static void EnsureEventTraceActivity(Message message)
        {
            //We need to send this only if there is no message id. 
            if (message.Headers.MessageId == null)
            {
                EventTraceActivity eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
                if (eventTraceActivity == null)
                {
                    //Whoops no activity on the message yet.                         
                    eventTraceActivity = new EventTraceActivity();
                    EventTraceActivityHelper.TryAttachActivity(message, eventTraceActivity);
                }
 
                HttpRequestMessageProperty httpProperties;
                if (!message.Properties.TryGetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name, out httpProperties))
                {
                    httpProperties = new HttpRequestMessageProperty();
                    message.Properties.Add(HttpRequestMessageProperty.Name, httpProperties);
                }
                httpProperties.Headers.Add(EventTraceActivity.Name, Convert.ToBase64String(eventTraceActivity.ActivityId.ToByteArray()));
            }
        }
 
        ArraySegment<byte> SerializeBufferedMessage(Message message)
        {
            // by default, the HttpOutput should own the buffer and clean it up
            return SerializeBufferedMessage(message, true);
        }
 
        ArraySegment<byte> SerializeBufferedMessage(Message message, bool shouldRecycleBuffer)
        {
            ArraySegment<byte> result;
 
            MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
            if (mtomMessageEncoder == null)
            {
                result = messageEncoder.WriteMessage(message, int.MaxValue, bufferManager);
            }
            else
            {
                result = mtomMessageEncoder.WriteMessage(message, int.MaxValue, bufferManager, 0, this.mtomBoundary);
            }
 
            if (shouldRecycleBuffer)
            {
                // Only set this.bufferToRecycle if the HttpOutput owns the buffer, we will clean it up upon httpOutput.Close()
                // Otherwise, caller of SerializeBufferedMessage assumes responsiblity for returning the buffer to the buffer pool
            this.bufferToRecycle = result.Array;
            }
            return result;
        }
 
        Stream GetWrappedOutputStream()
        {
            const int ChunkSize = 32768;    // buffer size used for synchronous writes
            const int BufferSize = 16384;   // buffer size used for asynchronous writes
            const int BufferCount = 4;      // buffer count used for asynchronous writes
 
            // Writing an HTTP request chunk has a high fixed cost, so use BufferedStream to avoid writing 
            // small ones. 
            return this.supportsConcurrentIO ? (Stream)new BufferedOutputAsyncStream(this.outputStream, BufferSize, BufferCount) : new BufferedStream(this.outputStream, ChunkSize);
        }
 
        void WriteStreamedMessage(TimeSpan timeout)
        {
            this.outputStream = GetWrappedOutputStream();
 
            // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here. 
            // Rather, we need to run a timer to bound the overall operation
            if (onStreamSendTimeout == null)
            {
                onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
            }
            IOThreadTimer sendTimer = new IOThreadTimer(onStreamSendTimeout, this, true);
            sendTimer.Set(timeout);
 
            try
            {
                MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
                if (mtomMessageEncoder == null)
                {
                    messageEncoder.WriteMessage(this.message, this.outputStream);
                }
                else
                {
                    mtomMessageEncoder.WriteMessage(this.message, this.outputStream, this.mtomBoundary);
                }
 
                if (this.supportsConcurrentIO)
                {
                    this.outputStream.Close();
                }
            }
            finally
            {
                sendTimer.Cancel();
            }
        }
 
        static void OnStreamSendTimeout(object state)
        {
            HttpOutput thisPtr = (HttpOutput)state;
            thisPtr.Abort(HttpAbortReason.TimedOut);
        }
 
        IAsyncResult BeginWriteStreamedMessage(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new WriteStreamedMessageAsyncResult(timeout, this, httpResponseMessage, callback, state);
        }
 
        void EndWriteStreamedMessage(IAsyncResult result)
        {
            WriteStreamedMessageAsyncResult.End(result);
        }
 
        class HttpOutputByteArrayContent : ByteArrayContent
        {
            BufferManager bufferManager;
            volatile bool cleaned = false;
            ArraySegment<byte> content;
 
            public HttpOutputByteArrayContent(byte[] content, int offset, int count, BufferManager bufferManager)
                : base(content, offset, count)
            {
                Fx.Assert(bufferManager != null, "bufferManager should not be null");
                Fx.Assert(content != null, "content should not be null");
                this.content = new ArraySegment<byte>(content, offset, count);
                this.bufferManager = bufferManager;
            }
 
            public ArraySegment<byte> Content
            {
                get
                {
                    return this.content;
                }
            }
 
            protected override Task<Stream> CreateContentReadStreamAsync()
            {
                return base.CreateContentReadStreamAsync().ContinueWith<Stream>(t => 
                    new HttpOutputByteArrayContentStream(t.Result, this));
            }
 
            protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
            {
                return base.SerializeToStreamAsync(stream, context).ContinueWith(t =>
                    {
                        this.Cleanup();
                        HttpChannelUtilities.HandleContinueWithTask(t);
                    });
            }
 
            void Cleanup()
            {
                if (!cleaned)
                {
                    lock (this)
                    {
                        if (!cleaned)
                        {
                            cleaned = true;
                            this.bufferManager.ReturnBuffer(this.content.Array);
                        }
                    }
                }
            }
 
            class HttpOutputByteArrayContentStream : DelegatingStream
            {
                HttpOutputByteArrayContent content;
 
                public HttpOutputByteArrayContentStream(Stream innerStream, HttpOutputByteArrayContent content)
                    : base(innerStream)
                {
                    this.content = content;
                }
 
                public override void Close()
                {
                    base.Close();
                    this.content.Cleanup();
                }
            }
        }
 
        class WriteStreamedMessageAsyncResult : AsyncResult
        {
            HttpOutput httpOutput;
            IOThreadTimer sendTimer;
            static AsyncCallback onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
            HttpResponseMessage httpResponseMessage;
 
            public WriteStreamedMessageAsyncResult(TimeSpan timeout, HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.httpResponseMessage = httpResponseMessage;
                this.httpOutput = httpOutput;
                httpOutput.outputStream = httpOutput.GetWrappedOutputStream();
 
                // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here. 
                // Rather, we need to run a timer to bound the overall operation
                if (onStreamSendTimeout == null)
                {
                    onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
                }
                this.SetTimer(timeout);
 
                bool completeSelf = false;
                bool throwing = true;
 
                try
                {
                    completeSelf = HandleWriteStreamedMessage(null);
                    throwing = false;
                }
                finally
                {
                    if (completeSelf || throwing)
                    {
                        this.sendTimer.Cancel();
                    }
                }
 
                if (completeSelf)
                {
                    this.Complete(true);
                }
            }
 
            bool HandleWriteStreamedMessage(IAsyncResult result)
            {
                if (this.httpResponseMessage == null)
                {
                    if (result == null)
                    {
                        MtomMessageEncoder mtomMessageEncoder = httpOutput.messageEncoder as MtomMessageEncoder;
                        if (mtomMessageEncoder == null)
                        {
                            result = httpOutput.messageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, onWriteStreamedMessage, this);
                        }
                        else
                        {
                            result = mtomMessageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, httpOutput.mtomBoundary, onWriteStreamedMessage, this);
                        }
 
                        if (!result.CompletedSynchronously)
                        {
                            return false;
                        }
                    }
 
                    httpOutput.messageEncoder.EndWriteMessage(result);
 
                    if (this.httpOutput.supportsConcurrentIO)
                    {
                        httpOutput.outputStream.Close();
                    }
 
                    return true;
                }
                else
                {
                    OpaqueContent content = this.httpResponseMessage.Content as OpaqueContent;
                    if (result == null)
                    {
                        Fx.Assert(this.httpResponseMessage.Content != null, "httpOutput.httpResponseMessage.Content should not be null.");
 
                        if (content != null)
                        {
                            result = content.BeginWriteToStream(httpOutput.outputStream, onWriteStreamedMessage, this);
                        }
                        else
                        {
                            result = this.httpResponseMessage.Content.CopyToAsync(httpOutput.outputStream).AsAsyncResult(onWriteStreamedMessage, this);
                        }
 
                        if (!result.CompletedSynchronously)
                        {
                            return false;
                        }
                    }
 
                    if (content != null)
                    {
                        content.EndWriteToStream(result);
                    }
 
                    if (this.httpOutput.supportsConcurrentIO)
                    {
                        httpOutput.outputStream.Close();
                    }
 
                    return true;
                }
            }
 
            static void OnWriteStreamedMessage(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                {
                    return;
                }
 
                WriteStreamedMessageAsyncResult thisPtr = (WriteStreamedMessageAsyncResult)result.AsyncState;
                Exception completionException = null;
                bool completeSelf = false;
 
                try
                {
                    completeSelf = thisPtr.HandleWriteStreamedMessage(result);
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
                    completeSelf = true;
                    completionException = ex;
                }
 
                if (completeSelf)
                {
                    thisPtr.sendTimer.Cancel();
                    thisPtr.Complete(false, completionException);
                }
            }
 
            void SetTimer(TimeSpan timeout)
            {
                Fx.Assert(this.sendTimer == null, "SetTimer should only be called once");
 
                this.sendTimer = new IOThreadTimer(onStreamSendTimeout, this.httpOutput, true);
                this.sendTimer.Set(timeout);
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<WriteStreamedMessageAsyncResult>(result);
            }
        }
 
        public IAsyncResult BeginSend(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
        {
            Fx.Assert(httpResponseMessage != null, "httpResponseMessage should not be null.");
            return this.BeginSendCore(httpResponseMessage, timeout, callback, state);
        }
 
        public IAsyncResult BeginSend(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.BeginSendCore(null, timeout, callback, state);
        }
 
        IAsyncResult BeginSendCore(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
        {
            bool throwing = true;
            try
            {
                bool suppressEntityBody;
                if (httpResponseMessage != null)
                {
                    suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
                }
                else
                {
                    suppressEntityBody = PrepareHttpSend(message);
                }
 
                this.TraceHttpSendStart();
                IAsyncResult result = new SendAsyncResult(this, httpResponseMessage, suppressEntityBody, timeout, callback, state);
                throwing = false;
                return result;
            }
            finally
            {
                if (throwing)
                {
                    Abort();
                }
            }
        }
 
        private void TraceHttpSendStart()
        {
            if (TD.HttpSendMessageStartIsEnabled())
            {
                if (streamed)
                {
                    TD.HttpSendStreamedMessageStart(this.eventTraceActivity);
                }
                else
                {
                    TD.HttpSendMessageStart(this.eventTraceActivity);
                }
            }
        }
 
        public virtual void EndSend(IAsyncResult result)
        {
            bool throwing = true;
            try
            {
                SendAsyncResult.End(result);
                throwing = false;
            }
            finally
            {
                if (throwing)
                {
                    Abort();
                }
            }
        }
 
        void LogMessage()
        {
            if (MessageLogger.LogMessagesAtTransportLevel)
            {
                MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
            }
        }
 
        public void Send(HttpResponseMessage httpResponseMessage, TimeSpan timeout)
        {
            bool suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
 
            TraceHttpSendStart();
 
            if (suppressEntityBody)
            {
                // requests can't always support an output stream (for GET, etc)
                if (!isRequest)
                {
                    outputStream = GetOutputStream();
                }
                else
                {
                    this.SetContentLength(0);
                    LogMessage();
                }
            }
            else if (streamed)
            {
                outputStream = this.GetOutputStream();
                ApplyChannelBinding();
 
                OpaqueContent content = httpResponseMessage.Content as OpaqueContent;
                if (content != null)
                {
                    content.WriteToStream(this.outputStream);
                }
                else
                {
                    if (!httpResponseMessage.Content.CopyToAsync(this.outputStream).Wait<CommunicationException>(timeout))
                    {
                        throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TimeoutOnSend, timeout)));
                    }
                }
            }
            else
            {
                if (this.IsChannelBindingSupportEnabled)
                {
                    //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message                    
                    //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we 
                    //should be safe to always get the stream.
                    outputStream = this.GetOutputStream();
 
                    ApplyChannelBinding();
 
                    ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
 
                    Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
                    outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
                }
                else
                {
                    ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
                    SetContentLength(buffer.Count);
 
                    // requests can't always support an output stream (for GET, etc)
                    if (!isRequest || buffer.Count > 0)
                    {
                        outputStream = this.GetOutputStream();
                        outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
                    }
                }
            }
 
            TraceSend();
        }
 
        ArraySegment<byte> SerializeBufferedMessage(HttpResponseMessage httpResponseMessage)
        {
            HttpOutputByteArrayContent content = httpResponseMessage.Content as HttpOutputByteArrayContent;
            if (content == null)
            {
                byte[] byteArray = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
                return new ArraySegment<byte>(byteArray, 0, byteArray.Length);
            }
            else
            {
                return content.Content;
            }
        }
 
        public void Send(TimeSpan timeout)
        {
            bool suppressEntityBody = PrepareHttpSend(message);
 
            TraceHttpSendStart();
 
            if (suppressEntityBody)
            {
                // requests can't always support an output stream (for GET, etc)
                if (!isRequest)
                {
                    outputStream = GetOutputStream();
                }
                else
                {
                    this.SetContentLength(0);
                    LogMessage();
                }
            }
            else if (streamed)
            {
                outputStream = GetOutputStream();
                ApplyChannelBinding();
                WriteStreamedMessage(timeout);
            }
            else
            {
                if (this.IsChannelBindingSupportEnabled)
                {
                    //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message                    
                    //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we 
                    //should be safe to always get the stream.
                    outputStream = GetOutputStream();
 
                    ApplyChannelBinding();
 
                    ArraySegment<byte> buffer = SerializeBufferedMessage(message);
 
                    Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
                    outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
                }
                else
                {
                    ArraySegment<byte> buffer = SerializeBufferedMessage(message);
                    SetContentLength(buffer.Count);
 
                    // requests can't always support an output stream (for GET, etc)
                    if (!isRequest || buffer.Count > 0)
                    {
                        outputStream = GetOutputStream();
                        outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
                    }
                }
            }
 
            TraceSend();
        }
 
        void TraceSend()
        {
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageSent, SR.GetString(SR.TraceCodeMessageSent),
                    new MessageTraceRecord(this.message), this, null);
            }
 
            if (TD.HttpSendStopIsEnabled())
            {
                TD.HttpSendStop(this.eventTraceActivity);
            }
        }
 
        class SendAsyncResult : AsyncResult
        {
            HttpOutput httpOutput;
            static AsyncCallback onGetOutputStream;
            static Action<object> onWriteStreamedMessageLater;
            static AsyncCallback onWriteStreamedMessage;
            static AsyncCallback onWriteBody;
            bool suppressEntityBody;
            ArraySegment<byte> buffer;
            TimeoutHelper timeoutHelper;
            HttpResponseMessage httpResponseMessage;
 
            public SendAsyncResult(HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, bool suppressEntityBody, TimeSpan timeout, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.httpOutput = httpOutput;
                this.httpResponseMessage = httpResponseMessage;
                this.suppressEntityBody = suppressEntityBody;
 
                if (suppressEntityBody)
                {
                    if (httpOutput.isRequest)
                    {
                        httpOutput.SetContentLength(0);
                        this.httpOutput.TraceSend();
                        this.httpOutput.LogMessage();
                        base.Complete(true);
                        return;
                    }
                }
 
                this.timeoutHelper = new TimeoutHelper(timeout);
                Send();
            }
 
            void Send()
            {
                if (httpOutput.IsChannelBindingSupportEnabled)
                {
                    SendWithChannelBindingToken();
                }
                else
                {
                    SendWithoutChannelBindingToken();
                }
            }
 
            void SendWithoutChannelBindingToken()
            {
                if (!suppressEntityBody && !httpOutput.streamed)
                {
                    if (this.httpResponseMessage != null)
                    {
                        buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
                    }
                    else
                    {
                        buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
                    }
 
                    httpOutput.SetContentLength(buffer.Count);
                }
 
 
                if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
                {
                    httpOutput.outputStream = httpOutput.GetOutputStream();
                }
                else
                {
                    if (onGetOutputStream == null)
                    {
                        onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
                    }
 
                    IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
 
                    if (!result.CompletedSynchronously)
                        return;
 
                    httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
                }
 
                if (WriteMessage(true))
                {
                    this.httpOutput.TraceSend();
                    base.Complete(true);
                }
            }
 
            void SendWithChannelBindingToken()
            {
                if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
                {
                    httpOutput.outputStream = httpOutput.GetOutputStream();
                    httpOutput.ApplyChannelBinding();
                }
                else
                {
                    if (onGetOutputStream == null)
                    {
                        onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
                    }
 
                    IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
 
                    if (!result.CompletedSynchronously)
                        return;
 
                    httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
                    httpOutput.ApplyChannelBinding();
                }
 
                if (!httpOutput.streamed)
                {
                    if (this.httpResponseMessage != null)
                    {
                        buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
                    }
                    else
                    {
                        buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
                    }
 
                    httpOutput.SetContentLength(buffer.Count);
                }
 
                if (WriteMessage(true))
                {
                    this.httpOutput.TraceSend();
                    base.Complete(true);
                }
            }
 
            bool WriteMessage(bool isStillSynchronous)
            {
                if (suppressEntityBody)
                {
                    return true;
                }
                if (httpOutput.streamed)
                {
                    if (isStillSynchronous)
                    {
                        if (onWriteStreamedMessageLater == null)
                        {
                            onWriteStreamedMessageLater = new Action<object>(OnWriteStreamedMessageLater);
                        }
                        ActionItem.Schedule(onWriteStreamedMessageLater, this);
                        return false;
                    }
                    else
                    {
                        return WriteStreamedMessage();
                    }
                }
                else
                {
                    if (onWriteBody == null)
                    {
                        onWriteBody = Fx.ThunkCallback(new AsyncCallback(OnWriteBody));
                    }
 
                    IAsyncResult writeResult =
                        httpOutput.outputStream.BeginWrite(buffer.Array, buffer.Offset, buffer.Count, onWriteBody, this);
 
                    if (!writeResult.CompletedSynchronously)
                    {
                        return false;
                    }
 
                    CompleteWriteBody(writeResult);
                }
 
                return true;
            }
 
            bool WriteStreamedMessage()
            {
                // return a bool to determine if we are sync. 
 
                if (onWriteStreamedMessage == null)
                {
                    onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
                }
 
                return HandleWriteStreamedMessage(null); // completed synchronously
            }
 
            bool HandleWriteStreamedMessage(IAsyncResult result)
            {
                if (result == null)
                {
                    result = httpOutput.BeginWriteStreamedMessage(this.httpResponseMessage, timeoutHelper.RemainingTime(), onWriteStreamedMessage, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                }
 
                httpOutput.EndWriteStreamedMessage(result);
                return true;
            }
 
            static void OnWriteStreamedMessage(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                {
                    return;
                }
 
                SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
                Exception completionException = null;
                bool completeSelf = false;
 
                try
                {
                    completeSelf = thisPtr.HandleWriteStreamedMessage(result);
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
                    completeSelf = true;
                    completionException = ex;
                }
 
                if (completeSelf)
                {
                    if (completionException != null)
                    {
                        thisPtr.httpOutput.TraceSend();
                    }
                    thisPtr.Complete(false, completionException);
                }
            }
 
            void CompleteWriteBody(IAsyncResult result)
            {
                httpOutput.outputStream.EndWrite(result);
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<SendAsyncResult>(result);
            }
 
            static void OnGetOutputStream(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                    return;
 
                SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
 
                Exception completionException = null;
                bool completeSelf = false;
                try
                {
                    thisPtr.httpOutput.outputStream = thisPtr.httpOutput.EndGetOutputStream(result);
                    thisPtr.httpOutput.ApplyChannelBinding();
 
                    if (!thisPtr.httpOutput.streamed && thisPtr.httpOutput.IsChannelBindingSupportEnabled)
                    {
                        thisPtr.buffer = thisPtr.httpOutput.SerializeBufferedMessage(thisPtr.httpOutput.message);
                        thisPtr.httpOutput.SetContentLength(thisPtr.buffer.Count);
                    }
 
                    if (thisPtr.WriteMessage(false))
                    {
                        thisPtr.httpOutput.TraceSend();
                        completeSelf = true;
                    }
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    completeSelf = true;
                    completionException = e;
                }
                if (completeSelf)
                {
                    thisPtr.Complete(false, completionException);
                }
            }
 
            static void OnWriteStreamedMessageLater(object state)
            {
                SendAsyncResult thisPtr = (SendAsyncResult)state;
 
                bool completeSelf = false;
                Exception completionException = null;
                try
                {
                    completeSelf = thisPtr.WriteStreamedMessage();
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    completeSelf = true;
                    completionException = e;
                }
 
                if (completeSelf)
                {
                    if (completionException != null)
                    {
                        thisPtr.httpOutput.TraceSend();
                    }
                    thisPtr.Complete(false, completionException);
                }
            }
 
            static void OnWriteBody(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                    return;
 
                SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
 
                Exception completionException = null;
                try
                {
                    thisPtr.CompleteWriteBody(result);
                    thisPtr.httpOutput.TraceSend();
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    completionException = e;
                }
                thisPtr.Complete(false, completionException);
            }
        }
 
        internal static HttpOutput CreateHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
        {
            return new WebRequestHttpOutput(httpWebRequest, settings, message, enableChannelBindingSupport);
        }
 
        internal static HttpOutput CreateHttpOutput(HttpListenerResponse httpListenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
        {
            return new ListenerResponseHttpOutput(httpListenerResponse, settings, message, httpMethod);
        }
 
        class WebRequestHttpOutput : HttpOutput
        {
            HttpWebRequest httpWebRequest;
            ChannelBinding channelBindingToken;
            bool enableChannelBindingSupport;
 
            public WebRequestHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
                : base(settings, message, true, false)
            {
                this.httpWebRequest = httpWebRequest;
                this.enableChannelBindingSupport = enableChannelBindingSupport;
            }
 
            public override void Abort(HttpAbortReason abortReason)
            {
                httpWebRequest.Abort();
                base.Abort(abortReason);
            }
 
            protected override void AddMimeVersion(string version)
            {
                httpWebRequest.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
            }
 
            protected override void AddHeader(string name, string value)
            {
                httpWebRequest.Headers.Add(name, value);
            }
 
            protected override void SetContentType(string contentType)
            {
                httpWebRequest.ContentType = contentType;
            }
 
            protected override void SetContentEncoding(string contentEncoding)
            {
                this.httpWebRequest.Headers.Add(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
            }
 
            protected override void SetContentLength(int contentLength)
            {
                if (contentLength == 0 // work around whidbey issue with setting ContentLength - (see MB36881)
                    && !this.enableChannelBindingSupport) //When ChannelBinding is enabled, content length isn't supported
                {
                    httpWebRequest.ContentLength = contentLength;
                }
            }
 
            protected override void SetStatusCode(HttpStatusCode statusCode)
            {
            }
 
            protected override void SetStatusDescription(string statusDescription)
            {
            }
 
            protected override bool WillGetOutputStreamCompleteSynchronously
            {
                get { return false; }
            }
 
            protected override bool IsChannelBindingSupportEnabled
            {
                get
                {
                    return this.enableChannelBindingSupport;
                }
            }
 
            protected override ChannelBinding ChannelBinding
            {
                get
                {
                    return this.channelBindingToken;
                }
            }
 
            protected override bool CleanupChannelBinding
            {
                get
                {
                    //client side channel binding token will be attached to the inbound response message also, so
                    //we need to not clean up the CBT object for this HttpOutput object.
                    return false;
                }
            }
 
            //Used to allow the channel binding object to be transferred to the 
            //WebResponseHttpInput object.
            public override ChannelBinding TakeChannelBinding()
            {
                ChannelBinding result = this.channelBindingToken;
                this.channelBindingToken = null;
                return result;
            }
 
            protected override Stream GetOutputStream()
            {
                try
                {
                    Stream outputStream;
                    if (this.IsChannelBindingSupportEnabled)
                    {
                        TransportContext context;
                        outputStream = httpWebRequest.GetRequestStream(out context);
                        this.channelBindingToken = ChannelBindingUtility.GetToken(context);
                    }
                    else
                    {
                        outputStream = httpWebRequest.GetRequestStream();
                    }
 
                    outputStream = new WebRequestOutputStream(outputStream, httpWebRequest, this);
 
                    return outputStream;
                }
                catch (WebException webException)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, abortReason));
                }
            }
 
            protected override IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
            {
                return new GetOutputStreamAsyncResult(httpWebRequest, this, callback, state);
            }
 
            protected override Stream EndGetOutputStream(IAsyncResult result)
            {
                return GetOutputStreamAsyncResult.End(result, out this.channelBindingToken);
            }
 
            protected override bool PrepareHttpSend(Message message)
            {
                bool wasContentTypeSet = false;
 
                string action = message.Headers.Action;
 
                if (action != null)
                {
                    //This code is calling UrlPathEncode due to MessageBus bug 53362.
                    //After reviewing this decision, we
                    //feel that this was probably the wrong thing to do because UrlPathEncode
                    //doesn't escape some characters like '+', '%', etc.  The real issue behind 
                    //bug 53362 may have been as simple as being encoded multiple times on the client
                    //but being decoded one time on the server.  Calling UrlEncode would correctly
                    //escape these characters, but since we don't want to break any customers and no
                    //customers have complained, we will leave this as is for now...
                    action = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", UrlUtility.UrlPathEncode(action));
                }
 
                bool suppressEntityBody = base.PrepareHttpSend(message);
 
                object property;
                if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
                {
                    HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)property;
                    httpWebRequest.Method = requestProperty.Method;
                    // Query string was applied in HttpChannelFactory.ApplyManualAddressing
                    WebHeaderCollection requestHeaders = requestProperty.Headers;
                    suppressEntityBody = suppressEntityBody || requestProperty.SuppressEntityBody;
                    for (int i = 0; i < requestHeaders.Count; i++)
                    {
                        string name = requestHeaders.Keys[i];
                        string value = requestHeaders[i];
                        if (string.Compare(name, "accept", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            httpWebRequest.Accept = value;
                        }
                        else if (string.Compare(name, "connection", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if (value.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                httpWebRequest.KeepAlive = true;
                            }
                            else
                            {
                                httpWebRequest.Connection = value;
                            }
                        }
                        else if (string.Compare(name, "SOAPAction", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if (action == null)
                            {
                                action = value;
                            }
                            else
                            {
                                if (value.Length > 0 && string.Compare(value, action, StringComparison.Ordinal) != 0)
                                {
                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                        new ProtocolException(SR.GetString(SR.HttpSoapActionMismatch, action, value)));
                                }
                            }
                        }
                        else if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // this will be taken care of by System.Net when we write to the content
                        }
                        else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            httpWebRequest.ContentType = value;
                            wasContentTypeSet = true;
                        }
                        else if (string.Compare(name, "expect", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if (value.ToUpperInvariant().IndexOf("100-CONTINUE", StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                httpWebRequest.ServicePoint.Expect100Continue = true;
                            }
                            else
                            {
                                httpWebRequest.Expect = value;
                            }
                        }
                        else if (string.Compare(name, "host", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // this should be controlled through Via
                        }
                        else if (string.Compare(name, "referer", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // referrer is proper spelling, but referer is the what is in the protocol.
                            httpWebRequest.Referer = value;
                        }
                        else if (string.Compare(name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if (value.ToUpperInvariant().IndexOf("CHUNKED", StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                httpWebRequest.SendChunked = true;
                            }
                            else
                            {
                                httpWebRequest.TransferEncoding = value;
                            }
                        }
                        else if (string.Compare(name, "user-agent", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            httpWebRequest.UserAgent = value;
                        }
                        else if (string.Compare(name, "if-modified-since", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            DateTime modifiedSinceDate;
                            if (DateTime.TryParse(value, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, out modifiedSinceDate))
                            {
                                httpWebRequest.IfModifiedSince = modifiedSinceDate;
                            }
                            else
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                    new ProtocolException(SR.GetString(SR.HttpIfModifiedSinceParseError, value)));
                            }
                        }
                        else if (string.Compare(name, "date", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // this will be taken care of by System.Net when we make the request
                        }
                        else if (string.Compare(name, "proxy-connection", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // set by System.Net if using a proxy.
                        }
                        else if (string.Compare(name, "range", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // we don't support ranges in v1.
                        }
                        else
                        {
                            httpWebRequest.Headers.Add(name, value);
                        }
                    }
                }
 
                if (action != null)
                {
                    if (message.Version.Envelope == EnvelopeVersion.Soap11)
                    {
                        httpWebRequest.Headers["SOAPAction"] = action;
                    }
                    else if (message.Version.Envelope == EnvelopeVersion.Soap12)
                    {
                        if (message.Version.Addressing == AddressingVersion.None)
                        {
                            bool shouldSetContentType = true;
                            if (wasContentTypeSet)
                            {
                                if (httpWebRequest.ContentType.Contains("action")
                                    || httpWebRequest.ContentType.ToUpperInvariant().IndexOf("ACTION", StringComparison.OrdinalIgnoreCase) != -1)
                                {
                                    try
                                    {
                                        ContentType parsedContentType = new ContentType(httpWebRequest.ContentType);
                                        if (parsedContentType.Parameters.ContainsKey("action"))
                                        {
                                            string value = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", parsedContentType.Parameters["action"]);
                                            if (string.Compare(value, action, StringComparison.Ordinal) != 0)
                                            {
                                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                                    new ProtocolException(SR.GetString(SR.HttpSoapActionMismatchContentType, action, value)));
                                            }
                                            shouldSetContentType = false;
                                        }
                                    }
                                    catch (FormatException formatException)
                                    {
                                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                            new ProtocolException(SR.GetString(SR.HttpContentTypeFormatException, formatException.Message, httpWebRequest.ContentType), formatException));
                                    }
                                }
                            }
 
                            if (shouldSetContentType)
                            {
                                httpWebRequest.ContentType = string.Format(CultureInfo.InvariantCulture, "{0}; action={1}", httpWebRequest.ContentType, action);
                            }
                        }
                    }
                    else if (message.Version.Envelope != EnvelopeVersion.None)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new ProtocolException(SR.GetString(SR.EnvelopeVersionUnknown,
                            message.Version.Envelope.ToString())));
                    }
                }
 
                // since we don't get the output stream in send when retVal == true, 
                // we need to disable chunking for some verbs (DELETE/PUT)
                if (suppressEntityBody)
                {
                    httpWebRequest.SendChunked = false;
                }
                else if (this.IsChannelBindingSupportEnabled)
                {
                    //force chunked upload since the length of the message is unknown before encoding.
                    httpWebRequest.SendChunked = true;
                }
 
                return suppressEntityBody;
            }
 
            protected override void PrepareHttpSendCore(HttpResponseMessage message)
            {
                // HTTP pipeline for client side is not implemented yet
                // DCR CSDMain 216853 is tracking this
                Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
            }
 
            class GetOutputStreamAsyncResult : AsyncResult
            {
                static AsyncCallback onGetRequestStream = Fx.ThunkCallback(new AsyncCallback(OnGetRequestStream));
                HttpOutput httpOutput;
                HttpWebRequest httpWebRequest;
                Stream outputStream;
                ChannelBinding channelBindingToken;
 
                public GetOutputStreamAsyncResult(HttpWebRequest httpWebRequest, HttpOutput httpOutput, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.httpWebRequest = httpWebRequest;
                    this.httpOutput = httpOutput;
 
                    IAsyncResult result = null;
                    try
                    {
                        result = httpWebRequest.BeginGetRequestStream(onGetRequestStream, this);
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
 
                    if (result.CompletedSynchronously)
                    {
                        CompleteGetRequestStream(result);
                        base.Complete(true);
                    }
                }
 
                void CompleteGetRequestStream(IAsyncResult result)
                {
                    try
                    {
                        TransportContext context;
                        this.outputStream = new WebRequestOutputStream(httpWebRequest.EndGetRequestStream(result, out context), httpWebRequest, this.httpOutput);
                        this.channelBindingToken = ChannelBindingUtility.GetToken(context);
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
                }
 
                public static Stream End(IAsyncResult result, out ChannelBinding channelBindingToken)
                {
                    GetOutputStreamAsyncResult thisPtr = AsyncResult.End<GetOutputStreamAsyncResult>(result);
                    channelBindingToken = thisPtr.channelBindingToken;
                    return thisPtr.outputStream;
                }
 
                static void OnGetRequestStream(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                        return;
 
                    GetOutputStreamAsyncResult thisPtr = (GetOutputStreamAsyncResult)result.AsyncState;
 
                    Exception completionException = null;
                    try
                    {
                        thisPtr.CompleteGetRequestStream(result);
                    }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completionException = e;
                    }
                    thisPtr.Complete(false, completionException);
                }
            }
 
            class WebRequestOutputStream : BytesReadPositionStream
            {
                HttpWebRequest httpWebRequest;
                HttpOutput httpOutput;
                int bytesSent = 0;
 
                public WebRequestOutputStream(Stream requestStream, HttpWebRequest httpWebRequest, HttpOutput httpOutput)
                    : base(requestStream)
                {
                    this.httpWebRequest = httpWebRequest;
                    this.httpOutput = httpOutput;
                }
 
                public override void Close()
                {
                    try
                    {
                        base.Close();
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
                    }
                    catch (IOException ioException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
                }
 
                public override long Position
                {
                    get
                    {
                        return bytesSent;
                    }
                    set
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
                    }
                }
 
                public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
                {
                    this.bytesSent += count;
                    try
                    {
                        return base.BeginWrite(buffer, offset, count, callback, state);
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
                    }
                    catch (IOException ioException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
                }
 
                public override void EndWrite(IAsyncResult result)
                {
                    try
                    {
                        base.EndWrite(result);
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
                    }
                    catch (IOException ioException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
                }
 
                public override void Write(byte[] buffer, int offset, int count)
                {
                    try
                    {
                        base.Write(buffer, offset, count);
                    }
                    catch (ObjectDisposedException objectDisposedException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
                    }
                    catch (IOException ioException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
                    }
                    catch (WebException webException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
                    }
                    this.bytesSent += count;
                }
            }
        }
 
        class ListenerResponseHttpOutput : HttpOutput
        {
            HttpListenerResponse listenerResponse;
            string httpMethod;
 
            [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.Usage, "CA2214", Justification = "No one else is inhiriting from this class.")]
            public ListenerResponseHttpOutput(HttpListenerResponse listenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
                : base(settings, message, false, true)
            {
                this.listenerResponse = listenerResponse;
                this.httpMethod = httpMethod;
 
                if (message.IsFault)
                {
                    this.SetStatusCode(HttpStatusCode.InternalServerError);
                }
                else
                {
                    this.SetStatusCode(HttpStatusCode.OK);
                }
            }
 
            protected override string HttpMethod
            {
                get { return this.httpMethod; }
            }
 
            public override void Abort(HttpAbortReason abortReason)
            {
                listenerResponse.Abort();
                base.Abort(abortReason);
            }
 
            protected override void AddMimeVersion(string version)
            {
                listenerResponse.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
            }
 
            protected override bool PrepareHttpSend(Message message)
            {
                bool result = base.PrepareHttpSend(message);
 
                if (this.CanSendCompressedResponses)
                {
                    string contentType = this.listenerResponse.ContentType;
                    string contentEncoding;
                    if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref contentType, out contentEncoding))
                    {
                        if (contentType != this.listenerResponse.ContentType)
                        {
                            this.SetContentType(contentType);
                        }
                        this.SetContentEncoding(contentEncoding);
                    }
                }
 
                HttpResponseMessageProperty responseProperty = message.Properties.GetValue<HttpResponseMessageProperty>(HttpResponseMessageProperty.Name, true);
                bool httpResponseMessagePropertyFound = responseProperty != null;
                bool httpMethodIsHead = string.Compare(this.httpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
 
                if (httpMethodIsHead ||
                    httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
                {
                    result = true;
                    this.SetContentLength(0);
                    this.SetContentType(null);
                    listenerResponse.SendChunked = false;
                }
 
                if (httpResponseMessagePropertyFound)
                {
                    this.SetStatusCode(responseProperty.StatusCode);
                    if (responseProperty.StatusDescription != null)
                    {
                        this.SetStatusDescription(responseProperty.StatusDescription);
                    }
 
                    WebHeaderCollection responseHeaders = responseProperty.Headers;
                    for (int i = 0; i < responseHeaders.Count; i++)
                    {
                        string name = responseHeaders.Keys[i];
                        string value = responseHeaders[i];
                        if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            int contentLength = -1;
                            if (httpMethodIsHead &&
                                int.TryParse(value, out contentLength))
                            {
                                this.SetContentLength(contentLength);
                            }
                            // else
                            //this will be taken care of by System.Net when we write to the content
                        }
                        else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if (httpMethodIsHead ||
                                !responseProperty.SuppressEntityBody)
                            {
                                this.SetContentType(value);
                            }
                        }
                        else if (string.Compare(name, "Connection", StringComparison.OrdinalIgnoreCase) == 0 &&
                                 value != null &&
                                 string.Compare(value.Trim(), "close", StringComparison.OrdinalIgnoreCase) == 0 &&
                                 !LocalAppContextSwitches.DisableExplicitConnectionCloseHeader)
                        {
                            // HttpListenerResponse will not serialize the Connection:close header
                            // if its KeepAlive is true.  So in the case where a service has explicitly
                            // added Connection:close (not added by default) set KeepAlive to false.
                            // This will cause HttpListenerResponse to add its own Connection:close header
                            // and to serialize it properly.  We do not add a redundant header here.
                            this.listenerResponse.KeepAlive = false;
                        }
                        else
                        {
                            this.AddHeader(name, value);
                        }
                    }
                }
 
                return result;
            }
 
            protected override void PrepareHttpSendCore(HttpResponseMessage message)
            {
                this.listenerResponse.StatusCode = (int)message.StatusCode;
                if (message.ReasonPhrase != null)
                {
                    this.listenerResponse.StatusDescription = message.ReasonPhrase;
                }
                HttpChannelUtilities.CopyHeaders(message, AddHeader);
            }
 
            protected override void AddHeader(string name, string value)
            {
                if (string.Compare(name, "WWW-Authenticate", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    listenerResponse.AddHeader(name, value);
                }
                else
                {
                    listenerResponse.AppendHeader(name, value);
                }
            }
 
            protected override void SetContentType(string contentType)
            {
                listenerResponse.ContentType = contentType;
            }
 
            protected override void SetContentEncoding(string contentEncoding)
            {
                this.listenerResponse.AddHeader(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
            }
 
            protected override void SetContentLength(int contentLength)
            {
                listenerResponse.ContentLength64 = contentLength;
            }
 
            protected override void SetStatusCode(HttpStatusCode statusCode)
            {
                listenerResponse.StatusCode = (int)statusCode;
            }
 
            protected override void SetStatusDescription(string statusDescription)
            {
                listenerResponse.StatusDescription = statusDescription;
            }
 
            protected override Stream GetOutputStream()
            {
                return new ListenerResponseOutputStream(listenerResponse);
            }
 
            class ListenerResponseOutputStream : BytesReadPositionStream
            {
                public ListenerResponseOutputStream(HttpListenerResponse listenerResponse)
                    : base(listenerResponse.OutputStream)
                {
                }
 
                public override void Close()
                {
                    try
                    {
                        base.Close();
                    }
                    catch (HttpListenerException listenerException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            HttpChannelUtilities.CreateCommunicationException(listenerException));
                    }
                }
 
                public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
                {
                    try
                    {
                        return base.BeginWrite(buffer, offset, count, callback, state);
                    }
                    catch (HttpListenerException listenerException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            HttpChannelUtilities.CreateCommunicationException(listenerException));
                    }
                    catch (ApplicationException applicationException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
                            applicationException));
                    }
                }
 
                public override void EndWrite(IAsyncResult result)
                {
                    try
                    {
                        base.EndWrite(result);
                    }
                    catch (HttpListenerException listenerException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            HttpChannelUtilities.CreateCommunicationException(listenerException));
                    }
                    catch (ApplicationException applicationException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
                            applicationException));
                    }
                }
 
                public override void Write(byte[] buffer, int offset, int count)
                {
                    try
                    {
                        base.Write(buffer, offset, count);
                    }
                    catch (HttpListenerException listenerException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            HttpChannelUtilities.CreateCommunicationException(listenerException));
                    }
                    catch (ApplicationException applicationException)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
                            applicationException));
                    }
                }
            }
        }
    }
 
    enum HttpAbortReason
    {
        None,
        Aborted,
        TimedOut
    }
 
    delegate void AddHeaderDelegate(string headerName, string headerValue);
 
    static class HttpChannelUtilities
    {
        internal static class StatusDescriptionStrings
        {
            internal const string HttpContentTypeMissing = "Missing Content Type";
            internal const string HttpContentTypeMismatch = "Cannot process the message because the content type '{0}' was not the expected type '{1}'.";
            internal const string HttpStatusServiceActivationException = "System.ServiceModel.ServiceActivationException";
        }
 
        internal static class ObsoleteDescriptionStrings
        {
            internal const string PropertyObsoleteUseAllowCookies = "This property is obsolete. To enable Http CookieContainer, use the AllowCookies property instead.";
            internal const string TypeObsoleteUseAllowCookies = "This type is obsolete. To enable the Http CookieContainer, use the AllowCookies property on the http binding or on the HttpTransportBindingElement.";
        }
 
        internal const string HttpStatusCodeKey = "HttpStatusCode";
        internal const string HttpStatusCodeExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusCode";
        internal const string HttpStatusDescriptionExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusDescription";
 
        internal const int ResponseStreamExcerptSize = 1024;
 
        internal const string MIMEVersionHeader = "MIME-Version";
 
        internal const string ContentEncodingHeader = "Content-Encoding";
        internal const string AcceptEncodingHeader = "Accept-Encoding";
 
        private const string ContentLengthHeader = "Content-Length";
        private static readonly HashSet<string> httpContentHeaders = new HashSet<string>()
            {
                "Allow", "Content-Encoding", "Content-Language", "Content-Location", "Content-MD5",
                "Content-Range", "Expires", "Last-Modified", "Content-Type", ContentLengthHeader
            };
 
        static bool allReferencedAssembliesLoaded = false;
 
        public static Exception CreateCommunicationException(HttpListenerException listenerException)
        {
            switch (listenerException.NativeErrorCode)
            {
                case UnsafeNativeMethods.ERROR_NO_TRACKING_SERVICE:
                    return new CommunicationException(SR.GetString(SR.HttpNoTrackingService, listenerException.Message), listenerException);
 
                case UnsafeNativeMethods.ERROR_NETNAME_DELETED:
                    return new CommunicationException(SR.GetString(SR.HttpNetnameDeleted, listenerException.Message), listenerException);
 
                case UnsafeNativeMethods.ERROR_INVALID_HANDLE:
                    return new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted), listenerException);
 
                case UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY:
                case UnsafeNativeMethods.ERROR_OUTOFMEMORY:
                case UnsafeNativeMethods.ERROR_NO_SYSTEM_RESOURCES:
                    return new InsufficientMemoryException(SR.GetString(SR.InsufficentMemory), listenerException);
 
                default:
                    return new CommunicationException(listenerException.Message, listenerException);
            }
        }
 
        public static void EnsureHttpRequestMessageContentNotNull(HttpRequestMessage httpRequestMessage)
        {
            if (httpRequestMessage.Content == null)
            {
                httpRequestMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
            }
        }
 
        public static void EnsureHttpResponseMessageContentNotNull(HttpResponseMessage httpResponseMessage)
        {
            if (httpResponseMessage.Content == null)
            {
                httpResponseMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
            }
        }
 
        public static bool IsEmpty(HttpResponseMessage httpResponseMessage)
        {
            return httpResponseMessage.Content == null
               || (httpResponseMessage.Content.Headers.ContentLength.HasValue && httpResponseMessage.Content.Headers.ContentLength.Value == 0);
        }
 
        internal static void HandleContinueWithTask(Task task)
        {
            HandleContinueWithTask(task, null);
        }
 
        internal static void HandleContinueWithTask(Task task, Action<Exception> exceptionHandler)
        {
            if (task.IsFaulted)
            {
                if (exceptionHandler == null)
                {
                    throw FxTrace.Exception.AsError<FaultException>(task.Exception);
                }
                else
                {
                    exceptionHandler.Invoke(task.Exception);
                }
            }
            else if (task.IsCanceled)
            {
                throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TaskCancelledError)));
            }
        }
 
        public static void AbortRequest(HttpWebRequest request)
        {
            request.Abort();
        }
 
        public static void SetRequestTimeout(HttpWebRequest request, TimeSpan timeout)
        {
            int millisecondsTimeout = TimeoutHelper.ToMilliseconds(timeout);
            if (millisecondsTimeout == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(
                    SR.HttpRequestTimedOut, request.RequestUri, timeout)));
            }
            request.Timeout = millisecondsTimeout;
            request.ReadWriteTimeout = millisecondsTimeout;
        }
 
        public static void AddReplySecurityProperty(HttpChannelFactory<IRequestChannel> factory, HttpWebRequest webRequest,
            HttpWebResponse webResponse, Message replyMessage)
        {
            SecurityMessageProperty securityProperty = factory.CreateReplySecurityProperty(webRequest, webResponse);
            if (securityProperty != null)
            {
                replyMessage.Properties.Security = securityProperty;
            }
        }
 
        public static void CopyHeaders(HttpRequestMessage request, AddHeaderDelegate addHeader)
        {
            HttpChannelUtilities.CopyHeaders(request.Headers, addHeader);
            if (request.Content != null)
            {
                HttpChannelUtilities.CopyHeaders(request.Content.Headers, addHeader);
            }
        }
 
        public static void CopyHeaders(HttpResponseMessage response, AddHeaderDelegate addHeader)
        {
            HttpChannelUtilities.CopyHeaders(response.Headers, addHeader);
            if (response.Content != null)
            {
                HttpChannelUtilities.CopyHeaders(response.Content.Headers, addHeader);
            }
        }
 
        static void CopyHeaders(HttpHeaders headers, AddHeaderDelegate addHeader)
        {
            foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
            {
                foreach (string value in header.Value)
                {
                    TryAddToCollection(addHeader, header.Key, value);                    
                }
            }
        }
 
        public static void CopyHeaders(NameValueCollection headers, AddHeaderDelegate addHeader)
        {
            //this nested loop logic was copied from NameValueCollection.Add(NameValueCollection)
            int count = headers.Count;
            for (int i = 0; i < count; i++)
            {
                string key = headers.GetKey(i);
 
                string[] values = headers.GetValues(i);
                if (values != null)
                {
                    for (int j = 0; j < values.Length; j++)
                    {
                        TryAddToCollection(addHeader, key, values[j]);
                    }
                }
                else
                {
                    addHeader(key, null);
                }
            }
        }
 
        public static void CopyHeadersToNameValueCollection(NameValueCollection headers, NameValueCollection destination)
        {
            CopyHeaders(headers, destination.Add); 
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
                            Justification = "The exceptions are traced already.")]
        static void TryAddToCollection(AddHeaderDelegate addHeader, string headerName, string value)
        {
            try
            {
                addHeader(headerName, value);
            }
            catch (ArgumentException ex)
            {
                string encodedValue = null;
                if (TryEncodeHeaderValueAsUri(headerName, value, out encodedValue))
                {
                    //note: if the hosthame of a referer header contains illegal chars, we will still throw from here
                    //because Uri will not fix this up for us, which is ok. The request will get rejected in the error code path.
                    addHeader(headerName, encodedValue);
                }
                else
                {
                    // In self-hosted scenarios, some of the headers like Content-Length cannot be added directly.
                    // It will throw ArgumentException instead.
                    FxTrace.Exception.AsInformation(ex);
                }
            }
        }
 
        static bool TryEncodeHeaderValueAsUri(string headerName, string value, out string result)
        {
            result = null;
            //Internet Explorer will send the referrer header on the wire in unicode without encoding it
            //this will cause errors when added to a WebHeaderCollection.  This is a workaround for sharepoint, 
            //but will only work for WebHosted Scenarios.
            if (String.Compare(headerName, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
            {
                Uri uri;
                if (Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out uri))
                {
                    if (uri.IsAbsoluteUri)
                    {
                        result = uri.AbsoluteUri;
                    }
                    else
                    {
                        result = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
                    }
                    return true;
                }                
            }
            return false;
        }
 
        //
        internal static Type GetTypeFromAssembliesInCurrentDomain(string typeString)
        {
            Type type = Type.GetType(typeString, false);
            if (null == type)
            {
                if (!allReferencedAssembliesLoaded)
                {
                    allReferencedAssembliesLoaded = true;
                    AspNetEnvironment.Current.EnsureAllReferencedAssemblyLoaded();
                }
 
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                for (int i = 0; i < assemblies.Length; i++)
                {
                    type = assemblies[i].GetType(typeString, false);
                    if (null != type)
                    {
                        break;
                    }
                }
            }
 
            return type;
        }
 
        public static NetworkCredential GetCredential(AuthenticationSchemes authenticationScheme,
            SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
            out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
        {
            impersonationLevel = TokenImpersonationLevel.None;
            authenticationLevel = AuthenticationLevel.None;
 
            NetworkCredential result = null;
 
            if (authenticationScheme != AuthenticationSchemes.Anonymous)
            {
                result = GetCredentialCore(authenticationScheme, credentialProvider, timeout, out impersonationLevel, out authenticationLevel);
            }
 
            return result;
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        static NetworkCredential GetCredentialCore(AuthenticationSchemes authenticationScheme,
            SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
            out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
        {
            impersonationLevel = TokenImpersonationLevel.None;
            authenticationLevel = AuthenticationLevel.None;
 
            NetworkCredential result = null;
 
            switch (authenticationScheme)
            {
                case AuthenticationSchemes.Basic:
                    result = TransportSecurityHelpers.GetUserNameCredential(credentialProvider, timeout);
                    impersonationLevel = TokenImpersonationLevel.Delegation;
                    break;
 
                case AuthenticationSchemes.Digest:
                    result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
                        out impersonationLevel, out authenticationLevel);
 
                    HttpChannelUtilities.ValidateDigestCredential(ref result, impersonationLevel);
                    break;
 
                case AuthenticationSchemes.Negotiate:
                    result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
                        out impersonationLevel, out authenticationLevel);
                    break;
 
                case AuthenticationSchemes.Ntlm:
                    result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
                        out impersonationLevel, out authenticationLevel);
                    if (authenticationLevel == AuthenticationLevel.MutualAuthRequired)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.CredentialDisallowsNtlm)));
                    }
                    break;
 
                default:
                    // The setter for this property should prevent this.
                    throw Fx.AssertAndThrow("GetCredential: Invalid authentication scheme");
            }
 
            return result;
        }
 
 
        public static HttpWebResponse ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
        {
            HttpWebResponse response = null;
 
            if (webException.Status == WebExceptionStatus.Success ||
                webException.Status == WebExceptionStatus.ProtocolError)
            {
                response = (HttpWebResponse)webException.Response;
            }
 
            if (response == null)
            {
                Exception convertedException = ConvertWebException(webException, request, abortReason);
 
                if (convertedException != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(convertedException);
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(webException.Message,
                    webException));
            }
 
            if (response.StatusCode == HttpStatusCode.NotFound)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
            }
 
            if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServerTooBusyException(SR.GetString(SR.HttpServerTooBusy, request.RequestUri.AbsoluteUri), webException));
            }
 
            if (response.StatusCode == HttpStatusCode.UnsupportedMediaType)
            {
                string statusDescription = response.StatusDescription;
                if (!string.IsNullOrEmpty(statusDescription))
                {
                    if (string.Compare(statusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.MissingContentType, request.RequestUri), webException));
                    }
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.FramingContentTypeMismatch, request.ContentType, request.RequestUri), webException));
            }
 
            if (response.StatusCode == HttpStatusCode.GatewayTimeout)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(webException.Message, webException));
            }
 
            // if http.sys has a request queue on the TCP port, then if the path fails to match it will send
            // back "<h1>Bad Request (Invalid Hostname)</h1>" in the body of a 400 response.
            // See code at \\index1\sddnsrv\net\http\sys\httprcv.c for details
            if (response.StatusCode == HttpStatusCode.BadRequest)
            {
                const string httpSysRequestQueueNotFound = "<h1>Bad Request (Invalid Hostname)</h1>";
                const string httpSysRequestQueueNotFoundVista = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Invalid Hostname</h2>\r\n<hr><p>HTTP Error 400. The request hostname is invalid.</p>\r\n</BODY></HTML>\r\n";
                string notFoundTestString = null;
 
                if (response.ContentLength == httpSysRequestQueueNotFound.Length)
                {
                    notFoundTestString = httpSysRequestQueueNotFound;
                }
                else if (response.ContentLength == httpSysRequestQueueNotFoundVista.Length)
                {
                    notFoundTestString = httpSysRequestQueueNotFoundVista;
                }
 
                if (notFoundTestString != null)
                {
                    Stream responseStream = response.GetResponseStream();
                    byte[] responseBytes = new byte[notFoundTestString.Length];
                    int bytesRead = responseStream.Read(responseBytes, 0, responseBytes.Length);
 
                    // since the response is buffered by System.Net (it's an error response), we should have read
                    // the amount we were expecting
                    if (bytesRead == notFoundTestString.Length
                        && notFoundTestString == UTF8Encoding.ASCII.GetString(responseBytes))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
                    }
                }
            }
 
            return response;
        }
 
        public static Exception ConvertWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
        {
            switch (webException.Status)
            {
                case WebExceptionStatus.ConnectFailure:
                case WebExceptionStatus.NameResolutionFailure:
                case WebExceptionStatus.ProxyNameResolutionFailure:
                    return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
                case WebExceptionStatus.SecureChannelFailure:
                    return new SecurityNegotiationException(SR.GetString(SR.SecureChannelFailure, request.RequestUri.Authority), webException);
                case WebExceptionStatus.TrustFailure:
                    return new SecurityNegotiationException(SR.GetString(SR.TrustFailure, request.RequestUri.Authority), webException);
                case WebExceptionStatus.Timeout:
                    return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
                case WebExceptionStatus.ReceiveFailure:
                    return new CommunicationException(SR.GetString(SR.HttpReceiveFailure, request.RequestUri), webException);
                case WebExceptionStatus.SendFailure:
                    return new CommunicationException(SR.GetString(SR.HttpSendFailure, request.RequestUri), webException);
                case WebExceptionStatus.RequestCanceled:
                    return CreateRequestCanceledException(webException, request, abortReason);
                case WebExceptionStatus.ProtocolError:
                    HttpWebResponse response = (HttpWebResponse)webException.Response;
                    Fx.Assert(response != null, "'response' MUST NOT be NULL for WebExceptionStatus=='ProtocolError'.");
                    if (response.StatusCode == HttpStatusCode.InternalServerError &&
                        string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        return new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri));
                    }
                    else
                    {
                        return null;
                    }
                default:
                    return null;
            }
        }
 
        public static Exception CreateResponseIOException(IOException ioException, TimeSpan receiveTimeout)
        {
            if (ioException.InnerException is SocketException)
            {
                return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, receiveTimeout, ioException);
            }
 
            return new CommunicationException(SR.GetString(SR.HttpTransferError, ioException.Message), ioException);
        }
 
        public static Exception CreateResponseWebException(WebException webException, HttpWebResponse response)
        {
            switch (webException.Status)
            {
                case WebExceptionStatus.RequestCanceled:
                    return TraceResponseException(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, response.ResponseUri), webException));
                case WebExceptionStatus.ConnectionClosed:
                    return TraceResponseException(new CommunicationException(webException.Message, webException));
                case WebExceptionStatus.Timeout:
                    return TraceResponseException(new TimeoutException(SR.GetString(SR.HttpResponseTimedOut, response.ResponseUri,
                        TimeSpan.FromMilliseconds(response.GetResponseStream().ReadTimeout)), webException));
                default:
                    return CreateUnexpectedResponseException(webException, response);
            }
        }
 
        public static Exception CreateRequestCanceledException(Exception webException, HttpWebRequest request, HttpAbortReason abortReason)
        {
            switch (abortReason)
            {
                case HttpAbortReason.Aborted:
                    return new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, request.RequestUri), webException);
                case HttpAbortReason.TimedOut:
                    return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
                default:
                    return new CommunicationException(SR.GetString(SR.HttpTransferError, webException.Message), webException);
            }
        }
 
        public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request)
        {
            return CreateRequestIOException(ioException, request, null);
        }
 
        public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request, Exception originalException)
        {
            Exception exception = originalException == null ? ioException : originalException;
 
            if (ioException.InnerException is SocketException)
            {
                return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, TimeSpan.FromMilliseconds(request.Timeout), exception);
            }
 
            return new CommunicationException(SR.GetString(SR.HttpTransferError, exception.Message), exception);
        }
 
        static string CreateRequestTimedOutMessage(HttpWebRequest request)
        {
            return SR.GetString(SR.HttpRequestTimedOut, request.RequestUri, TimeSpan.FromMilliseconds(request.Timeout));
        }
 
        public static Exception CreateRequestWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
        {
            Exception convertedException = ConvertWebException(webException, request, abortReason);
 
            if (webException.Response != null)
            {
                //free the connection for use by another request
                webException.Response.Close();
            }
 
            if (convertedException != null)
            {
                return convertedException;
            }
 
            if (webException.InnerException is IOException)
            {
                return CreateRequestIOException((IOException)webException.InnerException, request, webException);
            }
 
            if (webException.InnerException is SocketException)
            {
                return SocketConnectionInitiator.ConvertConnectException((SocketException)webException.InnerException, request.RequestUri, TimeSpan.MaxValue, webException);
            }
 
            return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
        }
 
        static Exception CreateUnexpectedResponseException(WebException responseException, HttpWebResponse response)
        {
            string statusDescription = response.StatusDescription;
            if (string.IsNullOrEmpty(statusDescription))
                statusDescription = response.StatusCode.ToString();
 
            return TraceResponseException(
                new ProtocolException(SR.GetString(SR.UnexpectedHttpResponseCode,
                (int)response.StatusCode, statusDescription), responseException));
        }
 
        public static Exception CreateNullReferenceResponseException(NullReferenceException nullReferenceException)
        {
            return TraceResponseException(
                new ProtocolException(SR.GetString(SR.NullReferenceOnHttpResponse), nullReferenceException));
        }
 
        static string GetResponseStreamString(HttpWebResponse webResponse, out int bytesRead)
        {
            Stream responseStream = webResponse.GetResponseStream();
 
            long bufferSize = webResponse.ContentLength;
 
            if (bufferSize < 0 || bufferSize > ResponseStreamExcerptSize)
            {
                bufferSize = ResponseStreamExcerptSize;
            }
 
            byte[] responseBuffer = DiagnosticUtility.Utility.AllocateByteArray(checked((int)bufferSize));
            bytesRead = responseStream.Read(responseBuffer, 0, (int)bufferSize);
            responseStream.Close();
 
            return System.Text.Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);
        }
 
        static Exception TraceResponseException(Exception exception)
        {
            if (DiagnosticUtility.ShouldTraceError)
            {
                TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.HttpChannelUnexpectedResponse, SR.GetString(SR.TraceCodeHttpChannelUnexpectedResponse), (object)null, exception);
            }
 
            return exception;
        }
 
        static bool ValidateEmptyContent(HttpWebResponse response)
        {
            bool responseIsEmpty = true;
 
            if (response.ContentLength > 0)
            {
                responseIsEmpty = false;
            }
            else if (response.ContentLength == -1) // chunked 
            {
                Stream responseStream = response.GetResponseStream();
                byte[] testBuffer = new byte[1];
                responseIsEmpty = (responseStream.Read(testBuffer, 0, 1) != 1);
            }
 
            return responseIsEmpty;
        }
 
        static void ValidateAuthentication(HttpWebRequest request, HttpWebResponse response,
            WebException responseException, HttpChannelFactory<IRequestChannel> factory)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                string message = SR.GetString(SR.HttpAuthorizationFailed, factory.AuthenticationScheme,
                    response.Headers[HttpResponseHeader.WwwAuthenticate]);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    TraceResponseException(new MessageSecurityException(message, responseException)));
            }
 
            if (response.StatusCode == HttpStatusCode.Forbidden)
            {
                string message = SR.GetString(SR.HttpAuthorizationForbidden, factory.AuthenticationScheme);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    TraceResponseException(new MessageSecurityException(message, responseException)));
            }
 
            if ((request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired) &&
                !response.IsMutuallyAuthenticated)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    TraceResponseException(new SecurityNegotiationException(SR.GetString(SR.HttpMutualAuthNotSatisfied),
                    responseException)));
            }
        }
 
        public static void ValidateDigestCredential(ref NetworkCredential credential, TokenImpersonationLevel impersonationLevel)
        {
            // this is a work-around to VSWhidbey#470545 (Since the service always uses Impersonation,
            // we mitigate EOP by preemtively not allowing Identification)
            if (!SecurityUtils.IsDefaultNetworkCredential(credential))
            {
                // With a non-default credential, Digest will not honor a client impersonation constraint of 
                // TokenImpersonationLevel.Identification.
                if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel,
                    TokenImpersonationLevel.Impersonation))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
                        SR.DigestExplicitCredsImpersonationLevel, impersonationLevel)));
                }
            }
        }
 
        // only valid response codes are 500 (if it's a fault) or 200 (iff it's a response message)
        public static HttpInput ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response,
            HttpChannelFactory<IRequestChannel> factory, WebException responseException, ChannelBinding channelBinding)
        {
            ValidateAuthentication(request, response, responseException, factory);
 
            HttpInput httpInput = null;
 
            // We will close the HttpWebResponse if we got an error code betwen 200 and 300 and 
            // 1) an exception was thrown out or 
            // 2) it's an empty message and we are using SOAP.
            // For responses with status code above 300, System.Net will close the underlying connection so we don't need to worry about that.
            if ((200 <= (int)response.StatusCode && (int)response.StatusCode < 300) || response.StatusCode == HttpStatusCode.InternalServerError)
            {
                if (response.StatusCode == HttpStatusCode.InternalServerError
                    && string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri)));
                }
                else
                {
                    bool throwing = true;
                    try
                    {
                        if (string.IsNullOrEmpty(response.ContentType))
                        {
                            if (!ValidateEmptyContent(response))
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
                                    new ProtocolException(
                                        SR.GetString(SR.HttpContentTypeHeaderRequired),
                                        responseException)));
                            }
                        }
                        else if (response.ContentLength != 0)
                        {
                            MessageEncoder encoder = factory.MessageEncoderFactory.Encoder;
                            if (!encoder.IsContentTypeSupported(response.ContentType))
                            {
                                int bytesRead;
                                String responseExcerpt = GetResponseStreamString(response, out bytesRead);
 
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
                                    new ProtocolException(
                                        SR.GetString(
                                            SR.ResponseContentTypeMismatch,
                                            response.ContentType,
                                            encoder.ContentType,
                                            bytesRead,
                                            responseExcerpt), responseException)));
 
                            }
 
                            httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
                            httpInput.WebException = responseException;
                        }
 
                        throwing = false;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            response.Close();
                        }
                    }
                }
 
                if (httpInput == null)
                {
                    if (factory.MessageEncoderFactory.MessageVersion == MessageVersion.None)
                    {
                        httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
                        httpInput.WebException = responseException;
                    }
                    else
                    {
                        // In this case, we got a response with
                        // 1) status code between 200 and 300
                        // 2) Non-empty Content Type string
                        // 3) Zero content length
                        // Since we are trying to use SOAP here, the message seems to be malicious and we should
                        // just close the response directly.
                        response.Close();
                    }
                }
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateUnexpectedResponseException(responseException, response));
            }
 
            return httpInput;
        }
 
        public static bool GetHttpResponseTypeAndEncodingForCompression(ref string contentType, out string contentEncoding)
        {
            contentEncoding = null;
            bool isSession = false;
            bool isDeflate = false;
 
            if (string.Equals(BinaryVersion.GZipVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
                (isSession = string.Equals(BinaryVersion.GZipVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)) ||
                (isDeflate = (string.Equals(BinaryVersion.DeflateVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
                (isSession = string.Equals(BinaryVersion.DeflateVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)))))
            {
                contentType = isSession ? BinaryVersion.Version1.SessionContentType : BinaryVersion.Version1.ContentType;
                contentEncoding = isDeflate ? MessageEncoderCompressionHandler.DeflateContentEncoding : MessageEncoderCompressionHandler.GZipContentEncoding;
                return true;
            }
            return false;
        }
    }
 
    abstract class HttpDelayedAcceptStream : DetectEofStream
    {
        HttpOutput httpOutput;
        bool isHttpOutputClosed;
 
        /// <summary>
        /// Indicates whether the HttpOutput should be closed when this stream is closed. In the streamed case, 
        /// we�ll leave the HttpOutput opened (and it will be closed by the HttpRequestContext, so we won't leak it).
        /// </summary>
        bool closeHttpOutput;
 
        // sometimes we can't flush the HTTP output until we're done reading the end of the 
        // incoming stream of the HTTP input
        protected HttpDelayedAcceptStream(Stream stream)
            : base(stream)
        {
        }
 
        public bool EnableDelayedAccept(HttpOutput output, bool closeHttpOutput)
        {
            if (IsAtEof)
            {
                return false;
            }
 
            this.closeHttpOutput = closeHttpOutput;
            this.httpOutput = output;
            return true;
        }
 
        protected override void OnReceivedEof()
        {
            if (this.closeHttpOutput)
            {
                CloseHttpOutput();
            }
        }
 
        public override void Close()
        {
            if (this.closeHttpOutput)
            {
                CloseHttpOutput();
            }
 
            base.Close();
        }
 
        void CloseHttpOutput()
        {
            if (this.httpOutput != null && !this.isHttpOutputClosed)
            {
                this.httpOutput.Close();
                this.isHttpOutputClosed = true;
            }
        }
    }
 
    abstract class BytesReadPositionStream : DelegatingStream
    {
        int bytesSent = 0;
 
        protected BytesReadPositionStream(Stream stream)
            : base(stream)
        {
        }
 
        public override long Position
        {
            get
            {
                return bytesSent;
            }
            set
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
            }
        }
 
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            this.bytesSent += count;
            return BaseStream.BeginWrite(buffer, offset, count, callback, state);
        }
 
        public override void Write(byte[] buffer, int offset, int count)
        {
            BaseStream.Write(buffer, offset, count);
            this.bytesSent += count;
        }
 
        public override void WriteByte(byte value)
        {
            BaseStream.WriteByte(value);
            this.bytesSent++;
        }
    }
 
    class PreReadStream : DelegatingStream
    {
        byte[] preReadBuffer;
 
        public PreReadStream(Stream stream, byte[] preReadBuffer)
            : base(stream)
        {
            this.preReadBuffer = preReadBuffer;
        }
 
        bool ReadFromBuffer(byte[] buffer, int offset, int count, out int bytesRead)
        {
            if (this.preReadBuffer != null)
            {
                if (buffer == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
                }
 
                if (offset >= buffer.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset,
                        SR.GetString(SR.OffsetExceedsBufferBound, buffer.Length - 1)));
                }
 
                if (count < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", count,
                        SR.GetString(SR.ValueMustBeNonNegative)));
                }
 
                if (count == 0)
                {
                    bytesRead = 0;
                }
                else
                {
                    buffer[offset] = this.preReadBuffer[0];
                    this.preReadBuffer = null;
                    bytesRead = 1;
                }
 
                return true;
            }
 
            bytesRead = -1;
            return false;
        }
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesRead;
            if (ReadFromBuffer(buffer, offset, count, out bytesRead))
            {
                return bytesRead;
            }
 
            return base.Read(buffer, offset, count);
        }
 
        public override int ReadByte()
        {
            if (this.preReadBuffer != null)
            {
                byte[] tempBuffer = new byte[1];
                int bytesRead;
                if (ReadFromBuffer(tempBuffer, 0, 1, out bytesRead))
                {
                    return tempBuffer[0];
                }
            }
            return base.ReadByte();
        }
 
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            int bytesRead;
            if (ReadFromBuffer(buffer, offset, count, out bytesRead))
            {
                return new CompletedAsyncResult<int>(bytesRead, callback, state);
            }
 
            return base.BeginRead(buffer, offset, count, callback, state);
        }
 
        public override int EndRead(IAsyncResult result)
        {
            if (result is CompletedAsyncResult<int>)
            {
                return CompletedAsyncResult<int>.End(result);
            }
            else
            {
                return base.EndRead(result);
            }
        }
    }
 
    class HttpRequestMessageHttpInput : HttpInput, HttpRequestMessageProperty.IHttpHeaderProvider
    {
        const string SoapAction = "SOAPAction";
        HttpRequestMessage httpRequestMessage;
        ChannelBinding channelBinding;
 
        public HttpRequestMessageHttpInput(HttpRequestMessage httpRequestMessage, IHttpTransportFactorySettings settings, bool enableChannelBinding, ChannelBinding channelBinding)
            : base(settings, true, enableChannelBinding)
        {
            this.httpRequestMessage = httpRequestMessage;
            this.channelBinding = channelBinding;
        }
 
        public override long ContentLength
        {
            get
            {
                if (this.httpRequestMessage.Content.Headers.ContentLength == null)
                {
                    // Chunked transfer mode
                    return -1;
                }
 
                return this.httpRequestMessage.Content.Headers.ContentLength.Value;
            }
        }
 
        protected override ChannelBinding ChannelBinding
        {
            get
            {
                return this.channelBinding;
            }
        }
 
        public HttpRequestMessage HttpRequestMessage
        {
            get { return this.httpRequestMessage; }
        }
 
        protected override bool HasContent
        {
            get
            {
                // In Chunked transfer mode, the ContentLength header is null
                // Otherwise we just rely on the ContentLength header
                return this.httpRequestMessage.Content.Headers.ContentLength == null || this.httpRequestMessage.Content.Headers.ContentLength.Value > 0;
            }
        }
 
        protected override string ContentTypeCore
        {
            get
            {
                if (!this.HasContent)
                {
                    return null;
                }
 
                return this.httpRequestMessage.Content.Headers.ContentType == null ? null : this.httpRequestMessage.Content.Headers.ContentType.MediaType;
            }
        }
 
        public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
        {
            throw FxTrace.Exception.AsError(new InvalidOperationException());
        }
 
        protected override Stream GetInputStream()
        {
            if (this.httpRequestMessage.Content == null)
            {
                return Stream.Null;
            }
 
            return this.httpRequestMessage.Content.ReadAsStreamAsync().Result;
        }
 
        protected override void AddProperties(Message message)
        {
            HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(this.httpRequestMessage);
            message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty);
            message.Properties.Via = this.httpRequestMessage.RequestUri;
 
            foreach (KeyValuePair<string, object> property in this.httpRequestMessage.Properties)
            {
                message.Properties.Add(property.Key, property.Value);
            }
 
            this.httpRequestMessage.Properties.Clear();
        }
 
        protected override string SoapActionHeader
        {
            get
            {
                IEnumerable<string> values;
                if (this.httpRequestMessage.Headers.TryGetValues(SoapAction, out values))
                {
                    foreach (string headerValue in values)
                    {
                        return headerValue;
                    }
                }
 
                return null;
            }
        }
 
        public void CopyHeaders(WebHeaderCollection headers)
        {
            // No special-casing for the "WWW-Authenticate" header required here,
            // because this method is only called for the incoming request
            // and the WWW-Authenticate header is a header only applied to responses.
            HttpChannelUtilities.CopyHeaders(this.httpRequestMessage, headers.Add);
        }        
 
        internal void SetHttpRequestMessage(HttpRequestMessage httpRequestMessage)
        {
            Fx.Assert(httpRequestMessage != null, "httpRequestMessage should not be null.");
            this.httpRequestMessage = httpRequestMessage;
        }
    }
}