e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
865 lines
44 KiB
C#
865 lines
44 KiB
C#
//------------------------------------------------------------------------------
|
|
// <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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|