//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ 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; } /// /// /// /// Specifies most of the implementation for communicating with a SOAP web service over HTTP. /// /// [ComVisible(true)] public class SoapHttpClientProtocol : HttpWebClientProtocol { SoapClientType clientType; SoapProtocolVersion version = SoapProtocolVersion.Default; /// /// /// /// Initializes a new instance of the class. /// /// 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); } } } } /// /// /// [To be supplied.] /// 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)); } /// /// /// [To be supplied.] /// protected override WebRequest GetWebRequest(Uri uri) { WebRequest request = base.GetWebRequest(uri); return request; } /// [DefaultValue(SoapProtocolVersion.Default), WebServicesDescription(Res.ClientProtocolSoapVersion), ComVisible(false)] public SoapProtocolVersion SoapVersion { get { return version; } set { version = value; } } /// /// /// /// Allows to intercept XmlWriter creation. /// /// [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; } /// /// /// /// Allows to intercept XmlReader creation. /// /// [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; } /// /// /// /// Invokes a method of a SOAP web service. /// /// 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; } } /// /// /// /// Starts an asynchronous invocation of a method of a SOAP web /// service. /// /// 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); } /// /// /// [To be supplied.] /// 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; } } /// /// /// /// Ends an asynchronous invocation of a method of a remote SOAP web service. /// /// 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); } /// /// /// [To be supplied.] /// protected void InvokeAsync(string methodName, object[] parameters, SendOrPostCallback callback) { InvokeAsync(methodName, parameters, callback, null); } /// /// /// [To be supplied.] /// 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); } } }