File: System\Web\Services\Protocols\SoapServerProtocol.cs
Project: ndp\cdf\src\NetFx20\System.Web.Services\System.Web.Services.csproj (System.Web.Services)
//------------------------------------------------------------------------------
// <copyright file="SoapServerProtocol.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Web.Services.Protocols {
    using System;
    using System.Diagnostics;
    using System.Collections;
    using System.IO;
    using System.Reflection;
    using System.Xml.Serialization;
    using System.Xml;
    using System.Xml.Schema;
    using System.Web.Services.Description;
    using System.Text;
    using System.Net;
    using System.Web.Services.Configuration;
    using System.Threading;
    using System.Security.Policy;
    using System.Security.Permissions;
    using System.Web.Services.Diagnostics;
 
    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    public sealed class SoapServerType : ServerType {
        Hashtable methods = new Hashtable();
        Hashtable duplicateMethods = new Hashtable();
 
        internal SoapReflectedExtension[] HighPriExtensions;
        internal SoapReflectedExtension[] LowPriExtensions;
        internal object[] HighPriExtensionInitializers;
        internal object[] LowPriExtensionInitializers;
 
        internal string serviceNamespace;
        internal bool serviceDefaultIsEncoded;
        internal bool routingOnSoapAction;
        internal WebServiceProtocols protocolsSupported;
 
        public string ServiceNamespace
        {
            get
            {
                return serviceNamespace;
            }
        }
 
        public bool ServiceDefaultIsEncoded
        {
            get
            {
                return serviceDefaultIsEncoded;
            }
        }
 
        public bool ServiceRoutingOnSoapAction
        {
            get
            {
                return routingOnSoapAction;
            }
        }
 
        public SoapServerType(Type type, WebServiceProtocols protocolsSupported) : base(type) {
            this.protocolsSupported = protocolsSupported;
            bool soap11 = (protocolsSupported & WebServiceProtocols.HttpSoap) != 0;
            bool soap12 = (protocolsSupported & WebServiceProtocols.HttpSoap12) != 0;
            LogicalMethodInfo[] methodInfos = WebMethodReflector.GetMethods(type);
            ArrayList mappings = new ArrayList();
            WebServiceAttribute serviceAttribute = WebServiceReflector.GetAttribute(type);
            object soapServiceAttribute = SoapReflector.GetSoapServiceAttribute(type);
            routingOnSoapAction = SoapReflector.GetSoapServiceRoutingStyle(soapServiceAttribute) == SoapServiceRoutingStyle.SoapAction;
            serviceNamespace = serviceAttribute.Namespace;
            serviceDefaultIsEncoded = SoapReflector.ServiceDefaultIsEncoded(type);
            SoapReflectionImporter soapImporter = SoapReflector.CreateSoapImporter(serviceNamespace, serviceDefaultIsEncoded);
            XmlReflectionImporter xmlImporter = SoapReflector.CreateXmlImporter(serviceNamespace, serviceDefaultIsEncoded);
            SoapReflector.IncludeTypes(methodInfos, soapImporter);
            WebMethodReflector.IncludeTypes(methodInfos, xmlImporter);
            SoapReflectedMethod[] soapMethods = new SoapReflectedMethod[methodInfos.Length];
 
            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];
                if (element == null)
                    continue;
                SoapReflectedExtension extension = new SoapReflectedExtension(element.Type, null, element.Priority);
                if (element.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);
 
            for (int i = 0; i < methodInfos.Length; i++) {
                LogicalMethodInfo methodInfo = methodInfos[i];
                SoapReflectedMethod soapMethod = SoapReflector.ReflectMethod(methodInfo, false, xmlImporter, soapImporter, serviceAttribute.Namespace);
                mappings.Add(soapMethod.requestMappings);
                if (soapMethod.responseMappings != null) mappings.Add(soapMethod.responseMappings);
                mappings.Add(soapMethod.inHeaderMappings);
                if (soapMethod.outHeaderMappings != null) mappings.Add(soapMethod.outHeaderMappings);
                soapMethods[i] = soapMethod;
            }
            
            XmlMapping[] xmlMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping));
            TraceMethod caller = Tracing.On ? new TraceMethod(this, ".ctor", type, protocolsSupported) : null;
            if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceCreateSerializer), caller, new TraceMethod(typeof(XmlSerializer), "FromMappings", xmlMappings, this.Evidence));
            XmlSerializer[] serializers = null;
            if (AppDomain.CurrentDomain.IsHomogenous) {
                serializers = XmlSerializer.FromMappings(xmlMappings);
            }
            else {
#pragma warning disable 618 // If we're in a non-homogenous domain, legacy CAS mode is enabled, so passing through evidence will not fail
                serializers = XmlSerializer.FromMappings((xmlMappings), this.Evidence);
#pragma warning restore 618
            }
            if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceCreateSerializer), caller);
            
            int count = 0;
            for (int i = 0; i < soapMethods.Length; i++) {
                SoapServerMethod serverMethod = new SoapServerMethod();
                SoapReflectedMethod soapMethod = soapMethods[i];
                serverMethod.parameterSerializer = serializers[count++]; 
                if (soapMethod.responseMappings != null) serverMethod.returnSerializer = serializers[count++];
                serverMethod.inHeaderSerializer = serializers[count++];
                if (soapMethod.outHeaderMappings != null) serverMethod.outHeaderSerializer = serializers[count++];
                serverMethod.methodInfo = soapMethod.methodInfo;
                serverMethod.action = soapMethod.action;
                serverMethod.extensions = soapMethod.extensions;
                serverMethod.extensionInitializers = SoapReflectedExtension.GetInitializers(serverMethod.methodInfo, soapMethod.extensions);
                serverMethod.oneWay = soapMethod.oneWay;
                serverMethod.rpc = soapMethod.rpc;
                serverMethod.use = soapMethod.use;
                serverMethod.paramStyle = soapMethod.paramStyle;
                serverMethod.wsiClaims = soapMethod.binding == null ? WsiProfiles.None : soapMethod.binding.ConformsTo;
                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)
                        inHeaders.Add(mapping);
                    else if (mapping.direction == SoapHeaderDirection.Out)
                        outHeaders.Add(mapping);
                    else {
                        inHeaders.Add(mapping);
                        outHeaders.Add(mapping);
                    }
                }
                serverMethod.inHeaderMappings = (SoapHeaderMapping[])inHeaders.ToArray(typeof(SoapHeaderMapping));
                if (serverMethod.outHeaderSerializer != null)
                    serverMethod.outHeaderMappings = (SoapHeaderMapping[])outHeaders.ToArray(typeof(SoapHeaderMapping));
            
                // check feasibility of routing on request element for soap 1.1
                if (soap11 && !routingOnSoapAction && soapMethod.requestElementName.IsEmpty)
                    throw new SoapException(Res.GetString(Res.TheMethodDoesNotHaveARequestElementEither1, serverMethod.methodInfo.Name), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
 
                // we can lookup methods by action or request element
                if (methods[soapMethod.action] == null)
                    methods[soapMethod.action] = serverMethod;
                else {
                    // duplicate soap actions not allowed in soap 1.1 if we're routing on soap action
                    if (soap11 && routingOnSoapAction) {
                        SoapServerMethod duplicateMethod = (SoapServerMethod)methods[soapMethod.action];
                        throw new SoapException(Res.GetString(Res.TheMethodsAndUseTheSameSoapActionWhenTheService3, serverMethod.methodInfo.Name, duplicateMethod.methodInfo.Name, soapMethod.action), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
                    }
                    duplicateMethods[soapMethod.action] = serverMethod;
                }
 
                if (methods[soapMethod.requestElementName] == null)
                    methods[soapMethod.requestElementName] = serverMethod;
                else {
                    // duplicate request elements not allowed in soap 1.1 if we're routing on request element
                    if (soap11 && !routingOnSoapAction) {
                        SoapServerMethod duplicateMethod = (SoapServerMethod)methods[soapMethod.requestElementName];
                        throw new SoapException(Res.GetString(Res.TheMethodsAndUseTheSameRequestElementXmlns4, serverMethod.methodInfo.Name, duplicateMethod.methodInfo.Name, soapMethod.requestElementName.Name, soapMethod.requestElementName.Namespace), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
                    }
                    duplicateMethods[soapMethod.requestElementName] = serverMethod;
                }
            }
        }
 
        public SoapServerMethod GetMethod(object key) {
            return (SoapServerMethod)methods[key];
        }
 
        public SoapServerMethod GetDuplicateMethod(object key) {
            return (SoapServerMethod)duplicateMethods[key];
        }
    }
 
    [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    public class SoapServerProtocolFactory : ServerProtocolFactory {
        // POST requests without pathinfo (the "/Foo" in "foo.asmx/Foo") 
        // are treated as soap. if the server supports both versions we route requests
        // with soapaction to 1.1 and other requests to 1.2
        protected override ServerProtocol CreateIfRequestCompatible(HttpRequest request) {
            if (request.PathInfo.Length > 0)
                return null;
            
            if (request.HttpMethod != "POST")
                // MethodNotAllowed = 405,
                return new UnsupportedRequestProtocol(405);
 
            // at this point we know it's probably soap. we're still not sure of the version
            // but we leave that to the SoapServerProtocol to figure out
            return new SoapServerProtocol();
        }
    }
 
    [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    public class SoapServerProtocol : ServerProtocol {
        SoapServerType serverType;
        SoapServerMethod serverMethod;
        SoapServerMessage message;
        bool isOneWay;
        Exception onewayInitException;
        SoapProtocolVersion version;
        WebServiceProtocols protocolsSupported;
        SoapServerProtocolHelper helper;
 
        //
        // Default constructor is provided since WebServicesConfiguration is inaccessible
        // 
        protected internal SoapServerProtocol()
        {
            this.protocolsSupported = WebServicesSection.Current.EnabledProtocols;
        }
 
        /// <include file='doc\SoapServerProtocol.uex' path='docs/doc[@for="SoapServerProtocol.GetWriterForMessage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Allows to intercept XmlWriter creation.
        ///    </para>
        /// </devdoc>
        [PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
        protected virtual XmlWriter GetWriterForMessage(SoapServerMessage message, int bufferSize) {
            if (bufferSize < 512)
                bufferSize = 512;
            return new XmlTextWriter(new StreamWriter(message.Stream, new UTF8Encoding(false), bufferSize));
            /*
            XmlWriterSettings ws = new XmlWriterSettings();
            ws.Encoding = new UTF8Encoding(false);
            ws.Indent = false;
            ws.NewLineHandling = NewLineHandling.None;
            return XmlWriter.Create(message.Stream, ws);
            */
        }
 
        /// <include file='doc\SoapServerProtocol.uex' path='docs/doc[@for="SoapServerProtocol.GetReaderForMessage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Allows to intercept XmlReader creation.
        ///    </para>
        /// </devdoc>
        [PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
        protected virtual XmlReader GetReaderForMessage(SoapServerMessage message, int bufferSize) {
            Encoding enc = RequestResponseUtils.GetEncoding2(message.ContentType);
            if (bufferSize < 512)
                bufferSize = 512;
            int readTimeout = WebServicesSection.Current.SoapEnvelopeProcessing.ReadTimeout;
            Int64 timeout = readTimeout < 0 ? 0L : (Int64)readTimeout * 10000000;
            Int64 nowTicks = DateTime.UtcNow.Ticks;
            Int64 timeoutTicks = Int64.MaxValue - timeout <= nowTicks ? Int64.MaxValue : nowTicks + timeout;
            XmlTextReader reader;
            if (enc != null) {
                if (timeoutTicks == Int64.MaxValue) {
                    reader = new XmlTextReader(new StreamReader(message.Stream, enc, true, bufferSize));
                }
                else {
                    reader = new SoapEnvelopeReader(new StreamReader(message.Stream, enc, true, bufferSize), timeoutTicks);
                }
            }
            else {
                if (timeoutTicks == Int64.MaxValue) {
                    reader = new XmlTextReader(message.Stream);
                }
                else {
                    reader = new SoapEnvelopeReader(message.Stream, timeoutTicks);
                }
            }
            reader.DtdProcessing = DtdProcessing.Prohibit;
            reader.Normalization = true;
            reader.XmlResolver = null;
            return reader;
        }
 
        internal override bool Initialize() {
            // try to guess the request version so we can handle any exceptions that might come up
            GuessVersion();
 
            message = new SoapServerMessage(this);
            onewayInitException = null;
 
            if (null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type))
                && null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type, true)))
            {
                lock (InternalSyncObject)
                {
                    if (null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type))
                        && null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type, true)))
                    {
                        bool excludeSchemeHostPortFromCachingKey = this.IsCacheUnderPressure(typeof(SoapServerProtocol), Type);
                        serverType = new SoapServerType(Type, protocolsSupported);
                        AddToCache(typeof(SoapServerProtocol), Type, serverType, excludeSchemeHostPortFromCachingKey);
                    }
                }
            }
 
            // We delay throwing any exceptions out of the extension until we determine if the method is one-way or not.
            Exception extensionException = null;
            try {
                message.highPriConfigExtensions = SoapMessage.InitializeExtensions(serverType.HighPriExtensions, serverType.HighPriExtensionInitializers);
                //
                // Allow derived classes to modify the high priority extensions list.
                //
                message.highPriConfigExtensions = ModifyInitializedExtensions(PriorityGroup.High, message.highPriConfigExtensions);
 
                // For one-way methods we rely on Request.InputStream guaranteeing that the entire request body has arrived
                message.SetStream(Request.InputStream);
        
                #if DEBUG
                    //Debug.Assert(message.Stream.CanSeek, "Web services SOAP handler assumes a seekable stream.");
                    // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                    if (!message.Stream.CanSeek) throw new InvalidOperationException("Non-Seekable stream " + message.Stream.GetType().FullName + " Web services SOAP handler assumes a seekable stream.");
 
                #endif
 
                message.InitExtensionStreamChain(message.highPriConfigExtensions);
                message.SetStage(SoapMessageStage.BeforeDeserialize);
                message.ContentType = Request.ContentType;
                message.ContentEncoding = Request.Headers[ContentType.ContentEncoding];
                message.RunExtensions(message.highPriConfigExtensions, false);
                extensionException = message.Exception;
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
                    throw;
                }
                if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "Initialize", e);
                extensionException = e;
            }
 
            // set this here since we might throw before we init the other extensions
            message.allExtensions = message.highPriConfigExtensions;
                                
            // maybe the extensions that just ran changed some of the request data so we can make a better version guess
            GuessVersion();
            try {
                this.serverMethod = RouteRequest(message);
 
                // the RouteRequest impl should throw an exception if it can't route the request but just in case...
                if (this.serverMethod == null)
                    throw new SoapException(Res.GetString(Res.UnableToHandleRequest0), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace));
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
                    throw;
                }
                if (helper.RequestNamespace != null)
                    SetHelper(SoapServerProtocolHelper.GetHelper(this, helper.RequestNamespace));
 
                // version mismatches override other errors
                CheckHelperVersion();
 
                throw;
            }
            this.isOneWay = serverMethod.oneWay;
            if (extensionException == null) {
                try {
                    SoapReflectedExtension[] otherReflectedExtensions = (SoapReflectedExtension[]) CombineExtensionsHelper(serverMethod.extensions, serverType.LowPriExtensions, typeof(SoapReflectedExtension));
                    object[] otherInitializers = (object[]) CombineExtensionsHelper(serverMethod.extensionInitializers, serverType.LowPriExtensionInitializers, typeof(object));
                    message.otherExtensions = SoapMessage.InitializeExtensions(otherReflectedExtensions, otherInitializers);
                    //
                    // Allow derived classes to modify the other extensions list.
                    //
                    message.otherExtensions = ModifyInitializedExtensions(PriorityGroup.Low, message.otherExtensions);
                    message.allExtensions = (SoapExtension[]) CombineExtensionsHelper(message.highPriConfigExtensions, message.otherExtensions, typeof(SoapExtension));
                }
                catch (Exception e) {
                    if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
                        throw;
                    }
                    if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "Initialize", e);
                    extensionException = e;
                }
            }
 
            if (extensionException != null) {
                if (isOneWay)
                    onewayInitException = extensionException;
                else if (extensionException is SoapException)
                    throw extensionException;
                else
                    throw SoapException.Create(Version, Res.GetString(Res.WebConfigExtensionError), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), extensionException);
            }
 
            return true;
        }
 
        /// <devdoc>
        /// Allows derived classes to reorder or to replace intialized soap extensions prior to
        /// the system calling ChainStream or executing them in any stage.        
        /// </devdoc>
        protected virtual SoapExtension[] ModifyInitializedExtensions(PriorityGroup group, SoapExtension[] extensions)
        {
            return extensions;
        }
 
        /// <devdoc>
        /// Determines which SoapMethod should be invoked for a particular
        /// message.
        /// </devdoc>
        protected virtual SoapServerMethod RouteRequest(SoapServerMessage message)
        {
            return helper.RouteRequest();
        }
 
        private void GuessVersion() {
            // make a best guess as to the version. we'll get more info when we ---- the envelope
            if (IsSupported(WebServiceProtocols.AnyHttpSoap)) {
                // both versions supported, we need to pick one
                if (Request.Headers[Soap.Action] == null || ContentType.MatchesBase(Request.ContentType, ContentType.ApplicationSoap))
                    SetHelper(new Soap12ServerProtocolHelper(this));
                else
                    SetHelper(new Soap11ServerProtocolHelper(this));
            }
            else if (IsSupported(WebServiceProtocols.HttpSoap)) {
                SetHelper(new Soap11ServerProtocolHelper(this));
            }
            else if (IsSupported(WebServiceProtocols.HttpSoap12)) {
                SetHelper(new Soap12ServerProtocolHelper(this));
            }
        }
 
        internal bool IsSupported(WebServiceProtocols protocol) {
            return ((protocolsSupported & protocol) == protocol);
        }
 
        internal override ServerType ServerType {
            get { return serverType; }
        }
 
        internal override LogicalMethodInfo MethodInfo {
            get { return serverMethod.methodInfo; }
        }
 
        internal SoapServerMethod ServerMethod {
            get { return serverMethod; }
        }
 
        internal SoapServerMessage Message {
            get { return message; }
        }
 
        internal override bool IsOneWay {
            get { return this.isOneWay; }            
        }            
        
        internal override Exception OnewayInitException {
            get {
                Debug.Assert(isOneWay || (onewayInitException == null), "initException is meant to be used for oneWay methods only.");
                return this.onewayInitException;
            }
        }            
            
        internal SoapProtocolVersion Version {
            get { return version; }
        }
 
        /*
        internal bool IsInitialized {
            get { return serverMethod != null; }
        }
        */
 
        internal override void CreateServerInstance() {
            base.CreateServerInstance();
            message.SetStage(SoapMessageStage.AfterDeserialize);
 
            message.RunExtensions(message.allExtensions, true);
            SoapHeaderHandling.SetHeaderMembers(message.Headers, this.Target, serverMethod.inHeaderMappings, SoapHeaderDirection.In, false);
        }
 
        /*
        #if DEBUG
        private static void CopyStream(Stream source, Stream dest) {
            byte[] bytes = new byte[1024];
            int numRead = 0;
            while ((numRead = source.Read(bytes, 0, 1024)) > 0)
                dest.Write(bytes, 0, numRead);
        }
        #endif
        */
 
        private void SetHelper(SoapServerProtocolHelper helper) {
            this.helper = helper;
            this.version = helper.Version;
            Context.Items[WebService.SoapVersionContextSlot] = helper.Version;
        }
 
        private static Array CombineExtensionsHelper(Array array1, Array array2, Type elementType) {
            if (array1 == null) return array2;
            if (array2 == null) return array1;
            int length = array1.Length + array2.Length;
            if (length == 0)
                return null;
            Array result = null;
            if (elementType == typeof(SoapReflectedExtension))
                result = new SoapReflectedExtension[length];
            else if (elementType == typeof(SoapExtension))
                result = new SoapExtension[length];
            else if (elementType == typeof(object))
                result  = new object[length];
            else 
                throw new ArgumentException(Res.GetString(Res.ElementTypeMustBeObjectOrSoapExtensionOrSoapReflectedException), "elementType");
            
            Array.Copy(array1, 0, result, 0, array1.Length);
            Array.Copy(array2, 0, result, array1.Length, array2.Length);
            return result;
        }
 
        private void CheckHelperVersion() {
            if (helper.RequestNamespace == null) return;
 
            // looks at the helper request namespace and version information to see if we need to return a 
            // version mismatch fault (and if so, what version fault). there are two conditions to check:
            // unknown envelope ns and known but unsupported envelope ns. there are a few rules this code must follow:
            // * a 1.1 node responds with a 1.1 fault. 
            // * a 1.2 node responds to a 1.1 request with a 1.1 fault but responds to an unknown request with a 1.2 fault.
            // * a both node can respond with either but we prefer 1.1.
 
            // GetHelper returns an arbitrary helper when the envelope ns is unknown, so we can check the helper's
            // expected envelope against the actual request ns to see if the request ns is unknown
 
            if (helper.RequestNamespace != helper.EnvelopeNs) { // unknown envelope ns -- version mismatch
                // respond with the version we support or 1.1 if we support both
                string requestNamespace = helper.RequestNamespace;
                if (IsSupported(WebServiceProtocols.HttpSoap))
                    SetHelper(new Soap11ServerProtocolHelper(this));
                else
                    SetHelper(new Soap12ServerProtocolHelper(this));
                throw new SoapException(Res.GetString(Res.WebInvalidEnvelopeNamespace, requestNamespace, helper.EnvelopeNs), SoapException.VersionMismatchFaultCode);
            }
            else if (!IsSupported(helper.Protocol)) { // known envelope ns but we don't support this version -- version mismatch
                // always respond with 1.1
                string requestNamespace = helper.RequestNamespace;
                string expectedNamespace = IsSupported(WebServiceProtocols.HttpSoap) ? Soap.Namespace : Soap12.Namespace;
                SetHelper(new Soap11ServerProtocolHelper(this));
                throw new SoapException(Res.GetString(Res.WebInvalidEnvelopeNamespace, requestNamespace, expectedNamespace), SoapException.VersionMismatchFaultCode);
            }
        }
 
        internal override object[] ReadParameters() {
            message.InitExtensionStreamChain(message.otherExtensions);
            message.RunExtensions(message.otherExtensions, true);
 
            // do a sanity check on the content-type before we check the version since otherwise the error might be really nasty
            if (!ContentType.IsSoap(message.ContentType))
                throw new SoapException(Res.GetString(Res.WebRequestContent, message.ContentType, helper.HttpContentType), 
                    new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), new SoapFaultSubCode(Soap12FaultCodes.UnsupportedMediaTypeFaultCode));
 
            // now that all the extensions have run, establish the real version of the request
            XmlReader reader = null;
            try {
                reader = GetXmlReader();
                reader.MoveToContent();
                SetHelper(SoapServerProtocolHelper.GetHelper(this, reader.NamespaceURI));
            }
            catch (XmlException e) {
                throw new SoapException(Res.GetString(Res.WebRequestUnableToRead), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), e);
            }
            CheckHelperVersion();
 
            // now do a more specific content-type check for soap 1.1 only (soap 1.2 allows various xml content types)
            if (version == SoapProtocolVersion.Soap11 && !ContentType.MatchesBase(message.ContentType, helper.HttpContentType))
                throw new SoapException(Res.GetString(Res.WebRequestContent, message.ContentType, helper.HttpContentType), 
                    new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), new SoapFaultSubCode(Soap12FaultCodes.UnsupportedMediaTypeFaultCode));
 
            if (message.Exception != null) {
                throw message.Exception;
            }
            try {
                if (!reader.IsStartElement(Soap.Element.Envelope, helper.EnvelopeNs))
                    throw new InvalidOperationException(Res.GetString(Res.WebMissingEnvelopeElement));
 
                if (reader.IsEmptyElement)
                    throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
 
                int depth = reader.Depth;
 
                reader.ReadStartElement(Soap.Element.Envelope, helper.EnvelopeNs);
                reader.MoveToContent();
 
                // run time check for R2738 A MESSAGE MUST include all soapbind:headers specified on a wsdl:input or wsdl:output of a wsdl:operationwsdl:binding that describes it. 
                bool checkRequiredHeaders = (this.serverMethod.wsiClaims & WsiProfiles.BasicProfile1_1) != 0 && version != SoapProtocolVersion.Soap12;
                string missingHeader = new SoapHeaderHandling().ReadHeaders(reader, serverMethod.inHeaderSerializer, message.Headers, serverMethod.inHeaderMappings, SoapHeaderDirection.In, helper.EnvelopeNs, serverMethod.use == SoapBindingUse.Encoded ? helper.EncodingNs : null, checkRequiredHeaders);
                        
                if (missingHeader != null) {
                    throw new SoapHeaderException(Res.GetString(Res.WebMissingHeader, missingHeader), 
                        new XmlQualifiedName(Soap.Code.MustUnderstand, Soap.Namespace));
                }
 
                if (!reader.IsStartElement(Soap.Element.Body, helper.EnvelopeNs))
                    throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
 
                reader.ReadStartElement(Soap.Element.Body, helper.EnvelopeNs);
                reader.MoveToContent();
 
                object[] values;
                bool isEncodedSoap = serverMethod.use == SoapBindingUse.Encoded;
                TraceMethod caller = Tracing.On ? new TraceMethod(this, "ReadParameters") : null;
                if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceReadRequest), caller, new TraceMethod(serverMethod.parameterSerializer, "Deserialize", reader, serverMethod.use == SoapBindingUse.Encoded ? helper.EncodingNs : null));
 
                bool useDeserializationEvents = !isEncodedSoap && (WebServicesSection.Current.SoapEnvelopeProcessing.IsStrict || Tracing.On);
                if (useDeserializationEvents) {
                    XmlDeserializationEvents events = Tracing.On ? Tracing.GetDeserializationEvents() : RuntimeUtils.GetDeserializationEvents();
                    values = (object[])serverMethod.parameterSerializer.Deserialize(reader, null, events);
                }
                else {
                    values = (object[])serverMethod.parameterSerializer.Deserialize(reader, isEncodedSoap ? helper.EncodingNs : null);
                }
                if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceReadRequest), 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.SetParameterValues(values);
 
                return values;
            }
            catch (SoapException) {
                throw;
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
                    throw;
                }
                throw new SoapException(Res.GetString(Res.WebRequestUnableToRead), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), e);
            }
        }
 
        internal override void WriteReturns(object[] returnValues, Stream outputStream) {
 
            if (serverMethod.oneWay) return;
            bool isEncoded = serverMethod.use == SoapBindingUse.Encoded;
            SoapHeaderHandling.EnsureHeadersUnderstood(message.Headers);
            message.Headers.Clear();
            SoapHeaderHandling.GetHeaderMembers(message.Headers, this.Target, serverMethod.outHeaderMappings, SoapHeaderDirection.Out, false);
 
            if (message.allExtensions != null)
                message.SetExtensionStream(new SoapExtensionStream());
            
            message.InitExtensionStreamChain(message.allExtensions);
            
            message.SetStage(SoapMessageStage.BeforeSerialize);
            message.ContentType = ContentType.Compose(helper.HttpContentType, Encoding.UTF8);
            message.SetParameterValues(returnValues);
            message.RunExtensions(message.allExtensions, true);
            message.SetStream(outputStream);
            Response.ContentType = message.ContentType;
            if (message.ContentEncoding != null && message.ContentEncoding.Length > 0)
                Response.AppendHeader(ContentType.ContentEncoding, message.ContentEncoding);
 
            XmlWriter writer = GetWriterForMessage(message, 1024);
            if (writer == null)
                throw new InvalidOperationException(Res.GetString(Res.WebNullWriterForMessage));
 
            writer.WriteStartDocument();
            writer.WriteStartElement("soap", Soap.Element.Envelope, helper.EnvelopeNs);
            writer.WriteAttributeString("xmlns", "soap", null, helper.EnvelopeNs);
            if (isEncoded) {
                writer.WriteAttributeString("xmlns", "soapenc", null, helper.EncodingNs);
                writer.WriteAttributeString("xmlns", "tns", null, serverType.serviceNamespace);
                writer.WriteAttributeString("xmlns", "types", null, SoapReflector.GetEncodedNamespace(serverType.serviceNamespace, serverType.serviceDefaultIsEncoded));
            }
            if (serverMethod.rpc && version == SoapProtocolVersion.Soap12) {
                writer.WriteAttributeString("xmlns", "rpc", null, Soap12.RpcNamespace);
            }
            writer.WriteAttributeString("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
            writer.WriteAttributeString("xmlns", "xsd", null, XmlSchema.Namespace);
            SoapHeaderHandling.WriteHeaders(writer, serverMethod.outHeaderSerializer, message.Headers, serverMethod.outHeaderMappings, SoapHeaderDirection.Out, isEncoded, serverType.serviceNamespace, serverType.serviceDefaultIsEncoded, helper.EnvelopeNs);
            writer.WriteStartElement(Soap.Element.Body, helper.EnvelopeNs);
            if (isEncoded && version != SoapProtocolVersion.Soap12) // don't write encodingStyle on soap:Body for soap 1.2
                writer.WriteAttributeString("soap", Soap.Attribute.EncodingStyle, null, helper.EncodingNs);
 
            TraceMethod caller = Tracing.On ? new TraceMethod(this, "WriteReturns") : null;
            if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceWriteResponse), caller, new TraceMethod(serverMethod.returnSerializer, "Serialize", writer, returnValues, null, isEncoded ? helper.EncodingNs : null));
            serverMethod.returnSerializer.Serialize(writer, returnValues, null, isEncoded ? helper.EncodingNs : null);
            if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceWriteResponse), caller);
 
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.Flush();
 
            message.SetStage(SoapMessageStage.AfterSerialize);
            message.RunExtensions(message.allExtensions, true);
        }
 
        internal override bool WriteException(Exception e, Stream outputStream) {
            if (message == null) return false;
 
            message.Headers.Clear();
            if (serverMethod != null && this.Target != null)
                SoapHeaderHandling.GetHeaderMembers(message.Headers, this.Target, serverMethod.outHeaderMappings, SoapHeaderDirection.Fault, false);
 
            SoapException soapException;
            if (e is SoapException)
                soapException = (SoapException)e;
            else if (serverMethod != null && serverMethod.rpc && helper.Version == SoapProtocolVersion.Soap12 && e is ArgumentException)
                // special case to handle soap 1.2 rpc "BadArguments" fault
                soapException = SoapException.Create(Version, Res.GetString(Res.WebRequestUnableToProcess), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), null, null, null, new SoapFaultSubCode(Soap12FaultCodes.RpcBadArgumentsFaultCode), e);
            else 
                soapException = SoapException.Create(Version, Res.GetString(Res.WebRequestUnableToProcess), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), e);
 
            if (SoapException.IsVersionMismatchFaultCode(soapException.Code)) {
                if (IsSupported(WebServiceProtocols.HttpSoap12)) {
                    SoapUnknownHeader unknownHeader = CreateUpgradeHeader();
                    if (unknownHeader != null)
                        Message.Headers.Add(unknownHeader);
                }
            }
            Response.ClearHeaders();
            Response.Clear();
            HttpStatusCode statusCode = helper.SetResponseErrorCode(Response, soapException);
 
            bool disableExtensions = false;
            SoapExtensionStream extensionStream = new SoapExtensionStream();
 
            if (message.allExtensions != null)
                message.SetExtensionStream(extensionStream);
 
            try {
                message.InitExtensionStreamChain(message.allExtensions);
            }
            catch (Exception ex) {
                if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
                    throw;
                }
                if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
                disableExtensions = true;
            }
 
            message.SetStage(SoapMessageStage.BeforeSerialize);
            message.ContentType = ContentType.Compose(helper.HttpContentType, Encoding.UTF8);
            message.Exception = soapException;
 
            if (!disableExtensions) {
                try {
                    message.RunExtensions(message.allExtensions, false);
                }
                catch (Exception ex) {
                    if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
                        throw;
                    }
                    if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
                    disableExtensions = true;
                }
            }
 
            message.SetStream(outputStream);
 
            Response.ContentType = message.ContentType;
            if (message.ContentEncoding != null && message.ContentEncoding.Length > 0) {
                Response.AppendHeader(ContentType.ContentEncoding, message.ContentEncoding);
            }
 
            XmlWriter writer = GetWriterForMessage(message, 512);
            if (writer == null)
                throw new InvalidOperationException(Res.GetString(Res.WebNullWriterForMessage));
 
            helper.WriteFault(writer, message.Exception, statusCode);
 
            if (!disableExtensions) {
                SoapException extensionException = null;
                try {
                    message.SetStage(SoapMessageStage.AfterSerialize);
                    message.RunExtensions(message.allExtensions, false);
                }
                catch (Exception ex) 
                {
                    if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) 
                    {
                        throw;
                    }
                    if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
                    if (!extensionStream.HasWritten) {
                        // if we haven't already written to the stream, we may be able to send an error
                        extensionException = SoapException.Create(Version, Res.GetString(Res.WebExtensionError), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), ex);
                    }
                }
 
                if (extensionException != null) {
                    Response.ContentType = ContentType.Compose(ContentType.TextPlain, Encoding.UTF8);
                    StreamWriter sw = new StreamWriter(outputStream, new UTF8Encoding(false));
                    sw.WriteLine(GenerateFaultString(message.Exception));
 
                    sw.Flush();
                }
            }
 
 
            return true;
        }
 
        bool WriteException_TryWriteFault(SoapServerMessage message, Stream outputStream, HttpStatusCode statusCode, bool disableExtensions)
        {
 
            return true;
        }
        
        internal SoapUnknownHeader CreateUpgradeHeader() {
            XmlDocument doc = new XmlDocument();
            XmlElement upgradeElement = doc.CreateElement(Soap12.Prefix, Soap12.Element.Upgrade, Soap12.Namespace);
            if (IsSupported(WebServiceProtocols.HttpSoap))
                upgradeElement.AppendChild(CreateUpgradeEnvelope(doc, Soap.Prefix, Soap.Namespace));
            if (IsSupported(WebServiceProtocols.HttpSoap12))
                upgradeElement.AppendChild(CreateUpgradeEnvelope(doc, Soap12.Prefix, Soap12.Namespace));
 
            SoapUnknownHeader upgradeHeader = new SoapUnknownHeader();
            upgradeHeader.Element = upgradeElement;
            return upgradeHeader;
        }
 
        private static XmlElement CreateUpgradeEnvelope(XmlDocument doc, string prefix, string envelopeNs) {
            XmlElement envelopeElement = doc.CreateElement(Soap12.Prefix, Soap12.Element.UpgradeEnvelope, Soap12.Namespace);
            XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns", prefix, "http://www.w3.org/2000/xmlns/");
            xmlnsAttr.Value = envelopeNs;
            XmlAttribute qnameAttr = doc.CreateAttribute(Soap12.Attribute.UpgradeEnvelopeQname);
            qnameAttr.Value = prefix + ":" + Soap.Element.Envelope;
            envelopeElement.Attributes.Append(qnameAttr);
            envelopeElement.Attributes.Append(xmlnsAttr);
            return envelopeElement;
        }
 
        internal XmlReader GetXmlReader() {
            Encoding enc = RequestResponseUtils.GetEncoding2(Message.ContentType);
            // in the case of conformant service check that input encodig is utg8 or urf16
            //
            bool checkEncoding = serverMethod != null && (serverMethod.wsiClaims & WsiProfiles.BasicProfile1_1) != 0 && Version != SoapProtocolVersion.Soap12;
            if (checkEncoding && enc != null) {
                // WS-I BP 1.1: R1012: A MESSAGE MUST be serialized as either UTF-8 or UTF-16.
                if (!(enc is UTF8Encoding) && !(enc is UnicodeEncoding)) {
                    throw new InvalidOperationException(Res.GetString(Res.WebWsiContentTypeEncoding));
                }
            }
            XmlReader reader = GetReaderForMessage(Message, RequestResponseUtils.GetBufferSize(Request.ContentLength));
            if (reader == null)
                throw new InvalidOperationException(Res.GetString(Res.WebNullReaderForMessage));
            return reader;
        }
 
        internal class SoapEnvelopeReader : XmlTextReader {
            Int64 readerTimedout;
 
            internal SoapEnvelopeReader(TextReader input, Int64 timeout) : base(input) {
                this.readerTimedout = timeout;
            }
  
            internal SoapEnvelopeReader(Stream input, Int64 timeout) : base(input) {
                this.readerTimedout = timeout;
            }
 
            public override bool Read() {
                CheckTimeout();
                return base.Read(); 
            }
 
            public override bool MoveToNextAttribute() {
                CheckTimeout();
                return base.MoveToNextAttribute(); 
            }
 
            public override XmlNodeType MoveToContent() {
                CheckTimeout();
                return base.MoveToContent(); 
            }
 
            private void CheckTimeout() {
                if (DateTime.UtcNow.Ticks > readerTimedout) {
                    throw new InvalidOperationException(Res.GetString(Res.WebTimeout));
                }
            }
        }
    }
 
    internal abstract class SoapServerProtocolHelper {
        SoapServerProtocol protocol;
        string requestNamespace;
 
        protected SoapServerProtocolHelper(SoapServerProtocol protocol) {
            this.protocol = protocol;
        }
 
        protected SoapServerProtocolHelper(SoapServerProtocol protocol, string requestNamespace) {
            this.protocol = protocol;
            this.requestNamespace = requestNamespace;
        }
 
        internal static SoapServerProtocolHelper GetHelper(SoapServerProtocol protocol, string envelopeNs) {
            SoapServerProtocolHelper helper;
            if (envelopeNs == Soap.Namespace)
                helper = new Soap11ServerProtocolHelper(protocol, envelopeNs);
            else if (envelopeNs == Soap12.Namespace)
                helper = new Soap12ServerProtocolHelper(protocol, envelopeNs);
            else
                // just return a soap 1.1 helper -- the fact that the requestNs doesn't match will signal a version mismatch
                helper = new Soap11ServerProtocolHelper(protocol, envelopeNs);
            return helper;
        }
 
        internal HttpStatusCode SetResponseErrorCode(HttpResponse response, SoapException soapException) {
            if (soapException.SubCode != null && soapException.SubCode.Code == Soap12FaultCodes.UnsupportedMediaTypeFaultCode) {
                response.StatusCode = (int) HttpStatusCode.UnsupportedMediaType;
                soapException.ClearSubCode();
            }
            else if (SoapException.IsClientFaultCode(soapException.Code)) {
                System.Web.Services.Protocols.ServerProtocol.SetHttpResponseStatusCode(response,
                    (int)HttpStatusCode.InternalServerError);
 
                for (Exception inner = soapException; inner != null; inner = inner.InnerException) {
                    if (inner is XmlException) {
                        response.StatusCode = (int) HttpStatusCode.BadRequest;
                    }
                }
            }
            else {
                System.Web.Services.Protocols.ServerProtocol.SetHttpResponseStatusCode(response,
                    (int)HttpStatusCode.InternalServerError);
            }
            response.StatusDescription = HttpWorkerRequest.GetStatusDescription(response.StatusCode);
            return (HttpStatusCode)response.StatusCode;
        }
 
        internal abstract void WriteFault(XmlWriter writer, SoapException soapException, HttpStatusCode statusCode);
        internal abstract SoapServerMethod RouteRequest();
        internal abstract SoapProtocolVersion Version { get; }
        internal abstract WebServiceProtocols Protocol { get; }
        internal abstract string EnvelopeNs { get; }
        internal abstract string EncodingNs { get; }
        internal abstract string HttpContentType { get; }
 
        internal string RequestNamespace {
            get { return requestNamespace; }
        }
 
        protected SoapServerProtocol ServerProtocol {
            get { return protocol; }
        }
 
        protected SoapServerType ServerType {
            get { return (SoapServerType)protocol.ServerType; }
        }
        
        // tries to get to the first child element of body, ignoring details
        // such as the namespace of Envelope and Body (a version mismatch check will come later)
        protected XmlQualifiedName GetRequestElement() {
            SoapServerMessage message = ServerProtocol.Message;
            long savedPosition = message.Stream.Position;
            XmlReader reader = protocol.GetXmlReader();
            reader.MoveToContent();
            
            requestNamespace = reader.NamespaceURI;
            if (!reader.IsStartElement(Soap.Element.Envelope, requestNamespace))
                throw new InvalidOperationException(Res.GetString(Res.WebMissingEnvelopeElement));
 
            if (reader.IsEmptyElement)
                throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
 
            reader.ReadStartElement(Soap.Element.Envelope, requestNamespace);
            reader.MoveToContent();
 
            while (!reader.EOF && !reader.IsStartElement(Soap.Element.Body, requestNamespace))
                reader.Skip();
 
            if (reader.EOF) {
                throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
            }
 
            XmlQualifiedName element;
            if (reader.IsEmptyElement) {
                element = XmlQualifiedName.Empty;
            }
            else {
                reader.ReadStartElement(Soap.Element.Body, requestNamespace);
                reader.MoveToContent();
                element = new XmlQualifiedName(reader.LocalName, reader.NamespaceURI);
            }
            message.Stream.Position = savedPosition;
            return element;
        }
    }
}