File: System\Web\Services\Protocols\SoapClientProtocol.cs
Project: ndp\cdf\src\NetFx20\System.Web.Services\System.Web.Services.csproj (System.Web.Services)
//------------------------------------------------------------------------------
// <copyright file="SoapClientProtocol.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Web.Services.Protocols {
    using System;
    using System.Text;
    using System.Collections;
    using System.IO;
    using System.Reflection;
    using System.Xml.Serialization;
    using System.Xml;
    using System.Diagnostics;
    using System.Xml.Schema;
    using System.Web.Services.Description;
    using System.Web.Services.Discovery;
    using System.Web.Services.Configuration;
    using System.Net;
    using System.Security.Permissions;
    using System.Threading;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Web.Services.Diagnostics;
 
    internal class SoapClientType {
        Hashtable methods = new Hashtable();
        WebServiceBindingAttribute binding;
 
        internal SoapReflectedExtension[] HighPriExtensions;
        internal SoapReflectedExtension[] LowPriExtensions;
        internal object[] HighPriExtensionInitializers;
        internal object[] LowPriExtensionInitializers;
 
        internal string serviceNamespace;
        internal bool serviceDefaultIsEncoded;
 
        internal SoapClientType(Type type) {
            this.binding = WebServiceBindingReflector.GetAttribute(type);
            if (this.binding == null) throw new InvalidOperationException(Res.GetString(Res.WebClientBindingAttributeRequired));
            // Note: Service namespace is taken from WebserviceBindingAttribute and not WebserviceAttribute because
            // the generated proxy does not have a WebServiceAttribute; however all have a WebServiceBindingAttribute. 
            serviceNamespace = binding.Namespace;
            serviceDefaultIsEncoded = SoapReflector.ServiceDefaultIsEncoded(type);
            ArrayList soapMethodList = new ArrayList();
            ArrayList mappings = new ArrayList();
            GenerateXmlMappings(type, soapMethodList, serviceNamespace, serviceDefaultIsEncoded, mappings);
            XmlMapping[] xmlMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping));
 
            TraceMethod caller = Tracing.On ? new TraceMethod(this, ".ctor", type) : null;
            if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceCreateSerializer), caller, new TraceMethod(typeof(XmlSerializer), "FromMappings", xmlMappings, type));
            XmlSerializer[] serializers = XmlSerializer.FromMappings(xmlMappings, type);
            if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceCreateSerializer), caller);
 
            SoapExtensionTypeElementCollection extensionTypes = WebServicesSection.Current.SoapExtensionTypes;
            ArrayList highPri = new ArrayList();
            ArrayList lowPri = new ArrayList();
            for (int i = 0; i < extensionTypes.Count; i++) {
                SoapExtensionTypeElement element = extensionTypes[i];
                SoapReflectedExtension extension = new SoapReflectedExtension(extensionTypes[i].Type, null, extensionTypes[i].Priority);
                if (extensionTypes[i].Group == PriorityGroup.High)
                    highPri.Add(extension);
                else
                    lowPri.Add(extension);
            }
 
            HighPriExtensions = (SoapReflectedExtension[]) highPri.ToArray(typeof(SoapReflectedExtension));
            LowPriExtensions = (SoapReflectedExtension[]) lowPri.ToArray(typeof(SoapReflectedExtension));
            Array.Sort(HighPriExtensions);
            Array.Sort(LowPriExtensions);
            HighPriExtensionInitializers = SoapReflectedExtension.GetInitializers(type, HighPriExtensions);
            LowPriExtensionInitializers = SoapReflectedExtension.GetInitializers(type, LowPriExtensions);
 
            int count = 0;
            for (int i = 0; i < soapMethodList.Count; i++) {
                SoapReflectedMethod soapMethod = (SoapReflectedMethod)soapMethodList[i];
                SoapClientMethod clientMethod = new SoapClientMethod();
                clientMethod.parameterSerializer = serializers[count++]; 
                if (soapMethod.responseMappings != null) clientMethod.returnSerializer = serializers[count++];
                clientMethod.inHeaderSerializer = serializers[count++];
                if (soapMethod.outHeaderMappings != null) clientMethod.outHeaderSerializer = serializers[count++];
                clientMethod.action = soapMethod.action;
                clientMethod.oneWay = soapMethod.oneWay;
                clientMethod.rpc = soapMethod.rpc;
                clientMethod.use = soapMethod.use;
                clientMethod.paramStyle = soapMethod.paramStyle;
                clientMethod.methodInfo = soapMethod.methodInfo;
                clientMethod.extensions = soapMethod.extensions;
                clientMethod.extensionInitializers = SoapReflectedExtension.GetInitializers(clientMethod.methodInfo, soapMethod.extensions);
                ArrayList inHeaders = new ArrayList();
                ArrayList outHeaders = new ArrayList();
                for (int j = 0; j < soapMethod.headers.Length; j++) {
                    SoapHeaderMapping mapping = new SoapHeaderMapping();
                    SoapReflectedHeader soapHeader = soapMethod.headers[j];
                    mapping.memberInfo = soapHeader.memberInfo;
                    mapping.repeats = soapHeader.repeats;
                    mapping.custom = soapHeader.custom;
                    mapping.direction = soapHeader.direction;
                    mapping.headerType = soapHeader.headerType;
                    if ((mapping.direction & SoapHeaderDirection.In) != 0)
                        inHeaders.Add(mapping);
                    if ((mapping.direction & (SoapHeaderDirection.Out | SoapHeaderDirection.Fault)) != 0)
                        outHeaders.Add(mapping);
                }
                clientMethod.inHeaderMappings = (SoapHeaderMapping[])inHeaders.ToArray(typeof(SoapHeaderMapping));
                if (clientMethod.outHeaderSerializer != null)
                    clientMethod.outHeaderMappings = (SoapHeaderMapping[])outHeaders.ToArray(typeof(SoapHeaderMapping));
                methods.Add(soapMethod.name, clientMethod);
            }
        }
 
        internal static void GenerateXmlMappings(Type type, ArrayList soapMethodList, string serviceNamespace, bool serviceDefaultIsEncoded, ArrayList mappings) {
            LogicalMethodInfo[] methodInfos = LogicalMethodInfo.Create(type.GetMethods(BindingFlags.Public | BindingFlags.Instance), LogicalMethodTypes.Sync);
           
            SoapReflectionImporter soapImporter = SoapReflector.CreateSoapImporter(serviceNamespace, serviceDefaultIsEncoded);
            XmlReflectionImporter xmlImporter = SoapReflector.CreateXmlImporter(serviceNamespace, serviceDefaultIsEncoded);
            WebMethodReflector.IncludeTypes(methodInfos, xmlImporter);
            SoapReflector.IncludeTypes(methodInfos, soapImporter);
 
 
            for (int i = 0; i < methodInfos.Length; i++) {
                LogicalMethodInfo methodInfo = methodInfos[i];
                SoapReflectedMethod soapMethod = SoapReflector.ReflectMethod(methodInfo, true, xmlImporter, soapImporter, serviceNamespace);
                if (soapMethod == null) continue;
                soapMethodList.Add(soapMethod);
                mappings.Add(soapMethod.requestMappings);
                if (soapMethod.responseMappings != null) mappings.Add(soapMethod.responseMappings);
                mappings.Add(soapMethod.inHeaderMappings);
                if (soapMethod.outHeaderMappings != null) mappings.Add(soapMethod.outHeaderMappings);
            }
        }
 
        internal SoapClientMethod GetMethod(string name) {
            return (SoapClientMethod)methods[name];
        }
 
        internal WebServiceBindingAttribute Binding {
            get { return binding; }
        }
    }
 
    internal class SoapClientMethod {
        internal XmlSerializer returnSerializer;
        internal XmlSerializer parameterSerializer;
        internal XmlSerializer inHeaderSerializer;
        internal XmlSerializer outHeaderSerializer;
        internal string action;
        internal LogicalMethodInfo methodInfo;
        internal SoapHeaderMapping[] inHeaderMappings;
        internal SoapHeaderMapping[] outHeaderMappings;
        internal SoapReflectedExtension[] extensions;
        internal object[] extensionInitializers;
        internal bool oneWay;
        internal bool rpc;
        internal SoapBindingUse use;
        internal SoapParameterStyle paramStyle;
    }
 
    /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Specifies most of the implementation for communicating with a SOAP web service over HTTP.
    ///    </para>
    /// </devdoc>
    [ComVisible(true)]
    public class SoapHttpClientProtocol : HttpWebClientProtocol {
        SoapClientType clientType;
        SoapProtocolVersion version = SoapProtocolVersion.Default;
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.SoapHttpClientProtocol"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Web.Services.Protocols.SoapHttpClientProtocol'/> class.
        ///    </para>
        /// </devdoc>
        public SoapHttpClientProtocol() 
            : base() {
            Type type = this.GetType();
            clientType = (SoapClientType)GetFromCache(type);
            if (clientType == null) {
                lock (InternalSyncObject) {
                    clientType = (SoapClientType)GetFromCache(type);
                    if (clientType == null) {
                        clientType = new SoapClientType(type);
                        AddToCache(type, clientType);
                    }
                }
            }
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.Discover"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void Discover() {
            if (clientType.Binding == null)
                throw new InvalidOperationException(Res.GetString(Res.DiscoveryIsNotPossibleBecauseTypeIsMissing1, this.GetType().FullName));
            DiscoveryClientProtocol disco = new DiscoveryClientProtocol(this);            
            DiscoveryDocument doc = disco.Discover(Url);
            foreach (object item in doc.References) {
                System.Web.Services.Discovery.SoapBinding soapBinding = item as System.Web.Services.Discovery.SoapBinding;
                if (soapBinding != null) {
                    if (clientType.Binding.Name == soapBinding.Binding.Name &&
                        clientType.Binding.Namespace == soapBinding.Binding.Namespace) {
                        Url = soapBinding.Address;
                        return;
                    }
                }
            }
            throw new InvalidOperationException(Res.GetString(Res.TheBindingNamedFromNamespaceWasNotFoundIn3, clientType.Binding.Name, clientType.Binding.Namespace, Url));
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.GetWebRequest"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected override WebRequest GetWebRequest(Uri uri) {            
            WebRequest request = base.GetWebRequest(uri);            
            return request;
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.SoapVersion"]/*' />
        [DefaultValue(SoapProtocolVersion.Default), WebServicesDescription(Res.ClientProtocolSoapVersion), ComVisible(false)]
        public SoapProtocolVersion SoapVersion {
            get { return version; }
            set { version = value; }
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.GetWriterForMessage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Allows to intercept XmlWriter creation.
        ///    </para>
        /// </devdoc>
        [PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
        protected virtual XmlWriter GetWriterForMessage(SoapClientMessage message, int bufferSize) {
            if (bufferSize < 512)
                bufferSize = 512;
            XmlTextWriter writer = new XmlTextWriter(new StreamWriter(message.Stream, RequestEncoding != null ? RequestEncoding : new UTF8Encoding(false), bufferSize));
            /*
            if (RequestEncoding != null && RequestEncoding.GetType() != typeof(UTF8Encoding)) {
                writer = new XmlTextWriter(new StreamWriter(message.Stream, RequestEncoding, bufferSize));
            }
            else {
                XmlWriterSettings ws = new XmlWriterSettings();
                ws.Encoding = new UTF8Encoding(false);
                ws.Indent = false;
                ws.NewLineHandling = NewLineHandling.None;
                writer = XmlWriter.Create(message.Stream, ws);
            }
            */
            return writer;
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.GetReaderForMessage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Allows to intercept XmlReader creation.
        ///    </para>
        /// </devdoc>
        
        [PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
        protected virtual XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize) {
            Encoding enc = message.SoapVersion == SoapProtocolVersion.Soap12 ? RequestResponseUtils.GetEncoding2(message.ContentType) : RequestResponseUtils.GetEncoding(message.ContentType);
            if (bufferSize < 512)
                bufferSize = 512;
            XmlTextReader reader;
            if (enc != null)
                reader = new XmlTextReader(new StreamReader(message.Stream, enc, true, bufferSize));
            else
                // 
                reader = new XmlTextReader(message.Stream);
 
            reader.DtdProcessing = DtdProcessing.Prohibit;
            reader.Normalization = true;
            reader.XmlResolver = null;
 
            return reader;
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.Invoke"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Invokes a method of a SOAP web service.
        ///    </para>
        /// </devdoc>
        protected object[] Invoke(string methodName, object[] parameters) {
            WebResponse response = null;                      
            WebRequest request = null;
            try {
                request = GetWebRequest(Uri);
                NotifyClientCallOut(request);
                // 
                PendingSyncRequest = request;
                SoapClientMessage message = BeforeSerialize(request, methodName, parameters);            
                Stream requestStream = request.GetRequestStream();            
                try {                                
                    message.SetStream(requestStream);
                    Serialize(message);           
                }                        
                finally {
                    requestStream.Close();
                }
 
                response = GetWebResponse(request);
                Stream responseStream = null;
                try {
                    responseStream = response.GetResponseStream();
                    return ReadResponse(message, response, responseStream, false);
                }
                catch (XmlException e) {
                    throw new InvalidOperationException(Res.GetString(Res.WebResponseBadXml), e);
                }
                finally {
                    if (responseStream != null)
                        responseStream.Close();
                }
            }
            finally {
                if (request == PendingSyncRequest)
                    PendingSyncRequest = null;
            }
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.BeginInvoke"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Starts an asynchronous invocation of a method of a SOAP web
        ///       service.
        ///    </para>
        /// </devdoc>
        protected IAsyncResult BeginInvoke(string methodName, object[] parameters, AsyncCallback callback, object asyncState) {
            InvokeAsyncState invokeState = new InvokeAsyncState(methodName, parameters);
            WebClientAsyncResult asyncResult = new WebClientAsyncResult(this, invokeState, null, callback, asyncState);
            return BeginSend(Uri, asyncResult, true);
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.InitializeAsyncRequest"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        internal override void InitializeAsyncRequest(WebRequest request, object internalAsyncState) {
            InvokeAsyncState invokeState = (InvokeAsyncState)internalAsyncState;
            invokeState.Message = BeforeSerialize(request, invokeState.MethodName, invokeState.Parameters);            
        }
 
        internal override void AsyncBufferedSerialize(WebRequest request, Stream requestStream, object internalAsyncState) {
            InvokeAsyncState invokeState = (InvokeAsyncState)internalAsyncState;
            SoapClientMessage message = invokeState.Message;
            message.SetStream(requestStream);
            Serialize(invokeState.Message);
        }
 
        class InvokeAsyncState {
            public string MethodName;
            public object[] Parameters;
            public SoapClientMessage Message;
 
            public InvokeAsyncState(string methodName, object[] parameters) {
                this.MethodName = methodName;
                this.Parameters = parameters;
            }
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapHttpClientProtocol.EndInvoke"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Ends an asynchronous invocation of a method of a remote SOAP web service.
        ///    </para>
        /// </devdoc>
        protected object[] EndInvoke(IAsyncResult asyncResult) {
            object o = null;
            Stream responseStream = null;
            try {
                WebResponse response = EndSend(asyncResult, ref o, ref responseStream);
                InvokeAsyncState invokeState = (InvokeAsyncState)o;
                return ReadResponse(invokeState.Message, response, responseStream, true);
            }
            catch (XmlException e) {
                throw new InvalidOperationException(Res.GetString(Res.WebResponseBadXml), e);
            }
            finally {
                if (responseStream != null)
                    responseStream.Close();
            }
        }
 
        private void InvokeAsyncCallback(IAsyncResult result) {
            object[] parameters = null;
            Exception exception = null;
    
            WebClientAsyncResult asyncResult = (WebClientAsyncResult)result;
            if (asyncResult.Request != null) {
                object o = null;
                Stream responseStream = null;
                try {
                    WebResponse response = EndSend(asyncResult, ref o, ref responseStream);
                    InvokeAsyncState invokeState = (InvokeAsyncState)o;
                    parameters = ReadResponse(invokeState.Message, response, responseStream, true);
                } 
                catch (XmlException e) {
                    if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "InvokeAsyncCallback", e);
                    exception = new InvalidOperationException(Res.GetString(Res.WebResponseBadXml), e);
                }
                catch (Exception e) {
                    if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
                        throw;
                    if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "InvokeAsyncCallback", e);
                    exception = e;
                }
                finally {
                    if (responseStream != null)
                        responseStream.Close();
                }
            }
            AsyncOperation asyncOp = (AsyncOperation)result.AsyncState;
            UserToken token = (UserToken)asyncOp.UserSuppliedState;
            OperationCompleted(token.UserState, parameters, exception, false);
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapClientProtocol.InvokeAsync"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected void InvokeAsync(string methodName, object[] parameters, SendOrPostCallback callback) {
            InvokeAsync(methodName, parameters, callback, null);
        }
 
        /// <include file='doc\SoapClientProtocol.uex' path='docs/doc[@for="SoapClientProtocol.InvokeAsync1"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected void InvokeAsync(string methodName, object[] parameters, SendOrPostCallback callback, object userState) {
            if (userState == null)
                userState = NullToken;
            InvokeAsyncState invokeState = new InvokeAsyncState(methodName, parameters);
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(new UserToken(callback, userState));
            WebClientAsyncResult asyncResult = new WebClientAsyncResult(this, invokeState, null, new AsyncCallback(InvokeAsyncCallback), asyncOp);
            try {
                AsyncInvokes.Add(userState, asyncResult);
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
                    throw;
                if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "InvokeAsync", e);
                Exception exception = new ArgumentException(Res.GetString(Res.AsyncDuplicateUserState), e);
                InvokeCompletedEventArgs eventArgs = new InvokeCompletedEventArgs(new object[] { null }, exception, false, userState);
                asyncOp.PostOperationCompleted(callback, eventArgs);
                return;
            }
            try {
                BeginSend(Uri, asyncResult, true);
            } 
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
                    throw;
                if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "InvokeAsync", e);
                OperationCompleted(userState, new object[] { null }, e, false);
            }
        }
 
        private static Array CombineExtensionsHelper(Array array1, Array array2, Array array3, Type elementType) {
            int length = array1.Length + array2.Length + array3.Length;
            if (length == 0)
                return null;
            Array result = null;
            if (elementType == typeof(SoapReflectedExtension))
                result = new SoapReflectedExtension[length];
            else if (elementType == typeof(object))
                result  = new object[length];
            else
                throw new ArgumentException(Res.GetString(Res.ElementTypeMustBeObjectOrSoapReflectedException), "elementType");
            
            int pos = 0;
            Array.Copy(array1, 0, result, pos, array1.Length);
            pos += array1.Length;
            Array.Copy(array2, 0, result, pos, array2.Length);
            pos += array2.Length;
            Array.Copy(array3, 0, result, pos, array3.Length);
            return result;
        }
 
        private string EnvelopeNs {
            get { 
                return this.version == SoapProtocolVersion.Soap12 ? Soap12.Namespace : Soap.Namespace; 
            }
        }
 
        private string EncodingNs {
            get { 
                return this.version == SoapProtocolVersion.Soap12 ? Soap12.Encoding : Soap.Encoding; 
            }
        }
 
        private string HttpContentType {
            get { 
                return this.version == SoapProtocolVersion.Soap12 ? ContentType.ApplicationSoap : ContentType.TextXml; 
            }
        }
 
        SoapClientMessage BeforeSerialize(WebRequest request, string methodName, object[] parameters) {
            if (parameters == null) throw new ArgumentNullException("parameters");
            SoapClientMethod method = clientType.GetMethod(methodName);
            if (method == null) throw new ArgumentException(Res.GetString(Res.WebInvalidMethodName, methodName));            
 
            // Run BeforeSerialize extension pass. Extensions are not allowed
            // to write into the stream during this pass.
            SoapReflectedExtension[] allExtensions = (SoapReflectedExtension[])CombineExtensionsHelper(clientType.HighPriExtensions, method.extensions, clientType.LowPriExtensions, typeof(SoapReflectedExtension));
            object[] allExtensionInitializers = (object[])CombineExtensionsHelper(clientType.HighPriExtensionInitializers, method.extensionInitializers, clientType.LowPriExtensionInitializers, typeof(object));
            SoapExtension[] initializedExtensions = SoapMessage.InitializeExtensions(allExtensions, allExtensionInitializers);
            SoapClientMessage message = new SoapClientMessage(this, method, Url);
            message.initializedExtensions = initializedExtensions;
            if (initializedExtensions != null)
                message.SetExtensionStream(new SoapExtensionStream());
            message.InitExtensionStreamChain(message.initializedExtensions);            
 
            string soapAction = UrlEncoder.EscapeString(method.action, Encoding.UTF8);
            message.SetStage(SoapMessageStage.BeforeSerialize);
            if (this.version == SoapProtocolVersion.Soap12)
                message.ContentType = ContentType.Compose(ContentType.ApplicationSoap, RequestEncoding != null ? RequestEncoding : Encoding.UTF8, soapAction);
            else
                message.ContentType = ContentType.Compose(ContentType.TextXml, RequestEncoding != null ? RequestEncoding : Encoding.UTF8);
            message.SetParameterValues(parameters);
            SoapHeaderHandling.GetHeaderMembers(message.Headers, this, method.inHeaderMappings, SoapHeaderDirection.In, true);
            message.RunExtensions(message.initializedExtensions, true);
 
            // Last chance to set request headers            
            request.ContentType = message.ContentType;
            if (message.ContentEncoding != null && message.ContentEncoding.Length > 0)
                request.Headers[ContentType.ContentEncoding] = message.ContentEncoding;
 
            request.Method = "POST";
            if (this.version != SoapProtocolVersion.Soap12 && request.Headers[Soap.Action] == null) {
                StringBuilder actionStringBuilder = new StringBuilder(soapAction.Length + 2);            
                actionStringBuilder.Append('"');
                actionStringBuilder.Append(soapAction);
                actionStringBuilder.Append('"');
                request.Headers.Add(Soap.Action, actionStringBuilder.ToString());                
            }
 
            return message;
        }
 
        void Serialize(SoapClientMessage message) {
            Stream stream = message.Stream;            
            SoapClientMethod method = message.Method;
            bool isEncoded = method.use == SoapBindingUse.Encoded;
 
            // Serialize the message.  
            string envelopeNs = EnvelopeNs;
            string encodingNs = EncodingNs;
 
            XmlWriter writer = GetWriterForMessage(message, 1024);
            if (writer == null)
                throw new InvalidOperationException(Res.GetString(Res.WebNullWriterForMessage));
 
            writer.WriteStartDocument();
            writer.WriteStartElement(Soap.Prefix, Soap.Element.Envelope, envelopeNs);
            writer.WriteAttributeString("xmlns", Soap.Prefix, null, envelopeNs);
            if (isEncoded) {
                writer.WriteAttributeString("xmlns", "soapenc", null, encodingNs);
                writer.WriteAttributeString("xmlns", "tns", null, clientType.serviceNamespace);
                writer.WriteAttributeString("xmlns", "types", null, SoapReflector.GetEncodedNamespace(clientType.serviceNamespace, clientType.serviceDefaultIsEncoded));
            }
            writer.WriteAttributeString("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
            writer.WriteAttributeString("xmlns", "xsd", null, XmlSchema.Namespace);
            SoapHeaderHandling.WriteHeaders(writer, method.inHeaderSerializer, message.Headers, method.inHeaderMappings, SoapHeaderDirection.In, isEncoded, clientType.serviceNamespace, clientType.serviceDefaultIsEncoded, envelopeNs);
            writer.WriteStartElement(Soap.Element.Body, envelopeNs);
            if (isEncoded && version != SoapProtocolVersion.Soap12) // don't write encodingStyle on soap:Body for soap 1.2
                writer.WriteAttributeString("soap", Soap.Attribute.EncodingStyle, null, encodingNs);
 
            object[] parameters = message.GetParameterValues();
            TraceMethod caller = Tracing.On ? new TraceMethod(this, "Serialize") : null;
 
            if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceWriteRequest), caller, new TraceMethod(method.parameterSerializer, "Serialize", writer, parameters, null, isEncoded ? encodingNs : null));
            method.parameterSerializer.Serialize(writer, parameters, null, isEncoded ? encodingNs : null);
            if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceWriteRequest), caller);
 
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.Flush();
 
            // run the after serialize extension pass. 
            message.SetStage(SoapMessageStage.AfterSerialize);
            message.RunExtensions(message.initializedExtensions, true);
        }
 
        object[] ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, bool asyncCall) {
            SoapClientMethod method = message.Method;
 
            // 
 
 
            HttpWebResponse httpResponse = response as HttpWebResponse;
            int statusCode = httpResponse != null ? (int)httpResponse.StatusCode : -1;
            if (statusCode >= 300 && statusCode != 500 && statusCode != 400)
                throw new WebException(RequestResponseUtils.CreateResponseExceptionString(httpResponse, responseStream), null, 
                    WebExceptionStatus.ProtocolError, httpResponse);
 
            message.Headers.Clear();
            message.SetStream(responseStream);
            message.InitExtensionStreamChain(message.initializedExtensions);
 
            message.SetStage(SoapMessageStage.BeforeDeserialize);
            message.ContentType = response.ContentType;
            message.ContentEncoding = response.Headers[ContentType.ContentEncoding];
            message.RunExtensions(message.initializedExtensions, false);
 
            if (method.oneWay && (httpResponse == null || (int)httpResponse.StatusCode != 500)) {
                return new object[0];
            }
 
            // this statusCode check is just so we don't repeat the contentType check we did above
            bool isSoap = ContentType.IsSoap(message.ContentType);
            if (!isSoap || (isSoap && (httpResponse != null) && (httpResponse.ContentLength == 0))) {
                // special-case 400 since we exempted it above on the off-chance it might be a soap 1.2 sender fault. 
                // based on the content-type, it looks like it's probably just a regular old 400
                if (statusCode == 400) 
                    throw new WebException(RequestResponseUtils.CreateResponseExceptionString(httpResponse, responseStream), null, 
                        WebExceptionStatus.ProtocolError, httpResponse);
                else
                    throw new InvalidOperationException(Res.GetString(Res.WebResponseContent, message.ContentType, HttpContentType) +
                                    Environment.NewLine +
                                    RequestResponseUtils.CreateResponseExceptionString(response, responseStream));
            }
            if (message.Exception != null) {
                throw message.Exception;
            }
 
            // perf fix: changed buffer size passed to StreamReader
            int bufferSize;
            if (asyncCall || httpResponse == null)
                bufferSize = 512;
            else {
                bufferSize = RequestResponseUtils.GetBufferSize((int)httpResponse.ContentLength);
            }
            XmlReader reader = GetReaderForMessage(message, bufferSize);
            if (reader == null)
                throw new InvalidOperationException(Res.GetString(Res.WebNullReaderForMessage));
 
            reader.MoveToContent();
            int depth = reader.Depth;
 
            // should be able to handle no ns, soap 1.1 ns, or soap 1.2 ns
            string encodingNs = EncodingNs;
            string envelopeNs = reader.NamespaceURI;
 
            if (envelopeNs == null || envelopeNs.Length == 0)
                // ok to omit namespace -- assume correct version
                reader.ReadStartElement(Soap.Element.Envelope);
            else if (reader.NamespaceURI == Soap.Namespace)
                reader.ReadStartElement(Soap.Element.Envelope, Soap.Namespace);
            else if (reader.NamespaceURI == Soap12.Namespace)
                reader.ReadStartElement(Soap.Element.Envelope, Soap12.Namespace);
            else
                throw new SoapException(Res.GetString(Res.WebInvalidEnvelopeNamespace, envelopeNs, EnvelopeNs), SoapException.VersionMismatchFaultCode);
 
            reader.MoveToContent();
            SoapHeaderHandling headerHandler = new SoapHeaderHandling();
            headerHandler.ReadHeaders(reader, method.outHeaderSerializer, message.Headers, method.outHeaderMappings, SoapHeaderDirection.Out | SoapHeaderDirection.Fault, envelopeNs, method.use == SoapBindingUse.Encoded ? encodingNs : null, false);
            reader.MoveToContent();
            reader.ReadStartElement(Soap.Element.Body, envelopeNs);
            reader.MoveToContent();
            if (reader.IsStartElement(Soap.Element.Fault, envelopeNs)) {
                message.Exception = ReadSoapException(reader);
            } 
            else {
                if (method.oneWay) {
                    reader.Skip();
                    message.SetParameterValues(new object[0]);
                }
                else {
                    TraceMethod caller = Tracing.On ? new TraceMethod(this, "ReadResponse") : null;
                    bool isEncodedSoap = method.use == SoapBindingUse.Encoded;
                    if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceReadResponse), caller, new TraceMethod(method.returnSerializer, "Deserialize", reader, isEncodedSoap ? encodingNs : null));
 
                    bool useDeserializationEvents = !isEncodedSoap && (WebServicesSection.Current.SoapEnvelopeProcessing.IsStrict || Tracing.On);
                    if (useDeserializationEvents) {
                        XmlDeserializationEvents events = Tracing.On ? Tracing.GetDeserializationEvents() : RuntimeUtils.GetDeserializationEvents();
                        message.SetParameterValues((object[])method.returnSerializer.Deserialize(reader, null, events));
                    }
                    else {
                        message.SetParameterValues((object[])method.returnSerializer.Deserialize(reader, isEncodedSoap ? encodingNs : null));
                    }
 
                    if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceReadResponse), caller);
                }
            }
 
            // Consume soap:Body and soap:Envelope closing tags
            while (depth < reader.Depth && reader.Read()) {
                // Nothing, just read on
            }
            // consume end tag
            if (reader.NodeType == XmlNodeType.EndElement) {
                reader.Read();
            }
 
            message.SetStage(SoapMessageStage.AfterDeserialize);
            message.RunExtensions(message.initializedExtensions, false);
            SoapHeaderHandling.SetHeaderMembers(message.Headers, this, method.outHeaderMappings, SoapHeaderDirection.Out | SoapHeaderDirection.Fault, true);
 
            if (message.Exception != null) throw message.Exception;
            return message.GetParameterValues();        
        }
 
        SoapException ReadSoapException(XmlReader reader) {
            XmlQualifiedName faultCode = XmlQualifiedName.Empty;
            string faultString = null;
            string faultActor = null;
            string faultRole = null;
            XmlNode detail = null;
            SoapFaultSubCode subcode = null;
            string lang = null;
            bool soap12 = (reader.NamespaceURI == Soap12.Namespace);
            if (reader.IsEmptyElement) {
                reader.Skip();
            }
            else {
                reader.ReadStartElement();
                reader.MoveToContent();
                int depth = reader.Depth;
                while (reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.None) {
                    if (reader.NamespaceURI == Soap.Namespace || reader.NamespaceURI == Soap12.Namespace || reader.NamespaceURI == null || reader.NamespaceURI.Length == 0) {
                        if (reader.LocalName == Soap.Element.FaultCode || reader.LocalName == Soap12.Element.FaultCode) {
                            if (soap12)
                                faultCode = ReadSoap12FaultCode(reader, out subcode);
                            else
                                faultCode = ReadFaultCode(reader);
                        }
                        else if (reader.LocalName == Soap.Element.FaultString) {
                            lang = reader.GetAttribute(Soap.Attribute.Lang, Soap.XmlNamespace);
                            reader.MoveToElement();
                            faultString = reader.ReadElementString();
                        }
                        else if (reader.LocalName == Soap12.Element.FaultReason) {
                            if (reader.IsEmptyElement)
                                reader.Skip();
                            else {
                                reader.ReadStartElement(); // consume Reason element to get to Text child
                                reader.MoveToContent();
                                while (reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.None) {
                                    if (reader.LocalName == Soap12.Element.FaultReasonText && reader.NamespaceURI == Soap12.Namespace) {
                                        faultString = reader.ReadElementString();
                                    }
                                    else {
                                        reader.Skip();
                                    }
                                    reader.MoveToContent();
                                }
                                while (reader.NodeType == XmlNodeType.Whitespace) reader.Skip();
                                if (reader.NodeType == XmlNodeType.None) reader.Skip();
                                else reader.ReadEndElement();
                            }
                        }
                        else if (reader.LocalName == Soap.Element.FaultActor || reader.LocalName == Soap12.Element.FaultNode) {
                            faultActor = reader.ReadElementString();
                        }
                        else if (reader.LocalName == Soap.Element.FaultDetail || reader.LocalName == Soap12.Element.FaultDetail) {
                            detail = new XmlDocument().ReadNode(reader);
                        }
                        else if (reader.LocalName == Soap12.Element.FaultRole) {
                            faultRole = reader.ReadElementString();
                        }
                        else {
                            reader.Skip();
                        }
                    }
                    else {
                        reader.Skip();
                    }
                    reader.MoveToContent();
                }
                // Consume soap:Body and soap:Envelope closing tags
                while (reader.Read() && depth < reader.Depth) {
                    // Nothing, just read on
                }
                // consume end tag
                if (reader.NodeType == XmlNodeType.EndElement) {
                    reader.Read();
                }
            }
            if (detail != null || soap12) // with soap 1.2, can't tell if fault is for header
                return new SoapException(faultString, faultCode, faultActor, faultRole, lang, detail, subcode, null);
            else
                return new SoapHeaderException(faultString, faultCode, faultActor, faultRole, lang, subcode, null);
        }
 
        private XmlQualifiedName ReadSoap12FaultCode(XmlReader reader, out SoapFaultSubCode subcode) {
            SoapFaultSubCode code = ReadSoap12FaultCodesRecursive(reader, 0);
            if (code == null) {
                subcode = null;
                return null;
            }
            else {
                subcode = code.SubCode;
                return code.Code;
            }
        }
         
        private SoapFaultSubCode ReadSoap12FaultCodesRecursive(XmlReader reader, int depth) {
            if (depth > 100) return null;
            if (reader.IsEmptyElement) {
                reader.Skip();
                return null;
            }
            XmlQualifiedName code = null;
            SoapFaultSubCode subcode = null;
            int faultDepth = reader.Depth;
            reader.ReadStartElement();
            reader.MoveToContent();
            while (reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.None) {
                if (reader.NamespaceURI == Soap12.Namespace || reader.NamespaceURI == null || reader.NamespaceURI.Length == 0) {
                    if (reader.LocalName == Soap12.Element.FaultCodeValue) {
                        code = ReadFaultCode(reader);
                    }
                    else if (reader.LocalName == Soap12.Element.FaultSubcode) {
                        subcode = ReadSoap12FaultCodesRecursive(reader, depth + 1);
                    }
                    else {
                        reader.Skip();
                    }
                }
                else {
                    reader.Skip();
                }
                reader.MoveToContent();
            }
            // Consume closing tag
            while (faultDepth < reader.Depth && reader.Read()) {
                // Nothing, just read on
            }
            // consume end tag
            if (reader.NodeType == XmlNodeType.EndElement) {
                reader.Read();
            }
 
            return new SoapFaultSubCode(code, subcode);
        }
 
        private XmlQualifiedName ReadFaultCode(XmlReader reader) {
            if (reader.IsEmptyElement) {
                reader.Skip();
                return null;
            }
            reader.ReadStartElement();
            string qnameValue = reader.ReadString();
            int colon = qnameValue.IndexOf(":", StringComparison.Ordinal);
            string ns = reader.NamespaceURI;
            if (colon >= 0) {
                string prefix = qnameValue.Substring(0, colon);
                ns = reader.LookupNamespace(prefix);
                if (ns == null)
                    throw new InvalidOperationException(Res.GetString(Res.WebQNamePrefixUndefined, prefix));
            }
            reader.ReadEndElement();
            
            return new XmlQualifiedName(qnameValue.Substring(colon + 1), ns);
        }
    }
}