e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
603 lines
19 KiB
C#
603 lines
19 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Dispatcher
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Net;
|
|
using System.Runtime;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Security;
|
|
using System.Xml;
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Compat", Justification = "Compat is an accepted abbreviation")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public class ClientRuntimeCompatBase
|
|
{
|
|
internal ClientRuntimeCompatBase() { }
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
[Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)]
|
|
public IList<IClientMessageInspector> MessageInspectors
|
|
{
|
|
get
|
|
{
|
|
return this.messageInspectors;
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
[Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)]
|
|
public KeyedCollection<string, ClientOperation> Operations
|
|
{
|
|
get
|
|
{
|
|
return this.compatOperations;
|
|
}
|
|
}
|
|
internal SynchronizedCollection<IClientMessageInspector> messageInspectors;
|
|
internal SynchronizedKeyedCollection<string, ClientOperation> operations;
|
|
internal KeyedCollection<string, ClientOperation> compatOperations;
|
|
}
|
|
|
|
public sealed class ClientRuntime : ClientRuntimeCompatBase
|
|
{
|
|
bool addTransactionFlowProperties = true;
|
|
Type callbackProxyType;
|
|
ProxyBehaviorCollection<IChannelInitializer> channelInitializers;
|
|
string contractName;
|
|
string contractNamespace;
|
|
Type contractProxyType;
|
|
DispatchRuntime dispatchRuntime;
|
|
IdentityVerifier identityVerifier;
|
|
ProxyBehaviorCollection<IInteractiveChannelInitializer> interactiveChannelInitializers;
|
|
|
|
IClientOperationSelector operationSelector;
|
|
ImmutableClientRuntime runtime;
|
|
ClientOperation unhandled;
|
|
bool useSynchronizationContext = true;
|
|
Uri via;
|
|
SharedRuntimeState shared;
|
|
int maxFaultSize;
|
|
bool messageVersionNoneFaultsEnabled;
|
|
|
|
internal ClientRuntime(DispatchRuntime dispatchRuntime, SharedRuntimeState shared)
|
|
: this(dispatchRuntime.EndpointDispatcher.ContractName,
|
|
dispatchRuntime.EndpointDispatcher.ContractNamespace,
|
|
shared)
|
|
{
|
|
if (dispatchRuntime == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dispatchRuntime");
|
|
|
|
this.dispatchRuntime = dispatchRuntime;
|
|
this.shared = shared;
|
|
|
|
Fx.Assert(shared.IsOnServer, "Server constructor called on client?");
|
|
}
|
|
|
|
internal ClientRuntime(string contractName, string contractNamespace)
|
|
: this(contractName, contractNamespace, new SharedRuntimeState(false))
|
|
{
|
|
Fx.Assert(!shared.IsOnServer, "Client constructor called on server?");
|
|
}
|
|
|
|
ClientRuntime(string contractName, string contractNamespace, SharedRuntimeState shared)
|
|
{
|
|
this.contractName = contractName;
|
|
this.contractNamespace = contractNamespace;
|
|
this.shared = shared;
|
|
|
|
OperationCollection operations = new OperationCollection(this);
|
|
this.operations = operations;
|
|
this.compatOperations = new OperationCollectionWrapper(operations);
|
|
this.channelInitializers = new ProxyBehaviorCollection<IChannelInitializer>(this);
|
|
this.messageInspectors = new ProxyBehaviorCollection<IClientMessageInspector>(this);
|
|
this.interactiveChannelInitializers = new ProxyBehaviorCollection<IInteractiveChannelInitializer>(this);
|
|
|
|
this.unhandled = new ClientOperation(this, "*", MessageHeaders.WildcardAction, MessageHeaders.WildcardAction);
|
|
this.unhandled.InternalFormatter = new MessageOperationFormatter();
|
|
this.maxFaultSize = TransportDefaults.MaxFaultSize;
|
|
}
|
|
|
|
internal bool AddTransactionFlowProperties
|
|
{
|
|
get { return this.addTransactionFlowProperties; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.addTransactionFlowProperties = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Type CallbackClientType
|
|
{
|
|
get { return this.callbackProxyType; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.callbackProxyType = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public SynchronizedCollection<IChannelInitializer> ChannelInitializers
|
|
{
|
|
get { return this.channelInitializers; }
|
|
}
|
|
|
|
public string ContractName
|
|
{
|
|
get { return this.contractName; }
|
|
}
|
|
|
|
public string ContractNamespace
|
|
{
|
|
get { return this.contractNamespace; }
|
|
}
|
|
|
|
public Type ContractClientType
|
|
{
|
|
get { return this.contractProxyType; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.contractProxyType = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal IdentityVerifier IdentityVerifier
|
|
{
|
|
get
|
|
{
|
|
if (this.identityVerifier == null)
|
|
{
|
|
this.identityVerifier = IdentityVerifier.CreateDefault();
|
|
}
|
|
|
|
return this.identityVerifier;
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
|
}
|
|
this.InvalidateRuntime();
|
|
|
|
this.identityVerifier = value;
|
|
}
|
|
}
|
|
|
|
public Uri Via
|
|
{
|
|
get { return this.via; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.via = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ValidateMustUnderstand
|
|
{
|
|
get { return this.shared.ValidateMustUnderstand; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.shared.ValidateMustUnderstand = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool MessageVersionNoneFaultsEnabled
|
|
{
|
|
get
|
|
{
|
|
return this.messageVersionNoneFaultsEnabled;
|
|
}
|
|
set
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.messageVersionNoneFaultsEnabled = value;
|
|
}
|
|
}
|
|
|
|
internal DispatchRuntime DispatchRuntime
|
|
{
|
|
get { return this.dispatchRuntime; }
|
|
}
|
|
|
|
public DispatchRuntime CallbackDispatchRuntime
|
|
{
|
|
get
|
|
{
|
|
if (this.dispatchRuntime == null)
|
|
this.dispatchRuntime = new DispatchRuntime(this, this.shared);
|
|
|
|
return this.dispatchRuntime;
|
|
}
|
|
}
|
|
|
|
internal bool EnableFaults
|
|
{
|
|
get
|
|
{
|
|
if (this.IsOnServer)
|
|
{
|
|
return this.dispatchRuntime.EnableFaults;
|
|
}
|
|
else
|
|
{
|
|
return this.shared.EnableFaults;
|
|
}
|
|
}
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.IsOnServer)
|
|
{
|
|
string text = SR.GetString(SR.SFxSetEnableFaultsOnChannelDispatcher0);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(text));
|
|
}
|
|
else
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.shared.EnableFaults = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public SynchronizedCollection<IInteractiveChannelInitializer> InteractiveChannelInitializers
|
|
{
|
|
get { return this.interactiveChannelInitializers; }
|
|
}
|
|
|
|
public int MaxFaultSize
|
|
{
|
|
get
|
|
{
|
|
return this.maxFaultSize;
|
|
}
|
|
set
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.maxFaultSize = value;
|
|
}
|
|
}
|
|
|
|
internal bool IsOnServer
|
|
{
|
|
get { return this.shared.IsOnServer; }
|
|
}
|
|
|
|
public bool ManualAddressing
|
|
{
|
|
get
|
|
{
|
|
if (this.IsOnServer)
|
|
{
|
|
return this.dispatchRuntime.ManualAddressing;
|
|
}
|
|
else
|
|
{
|
|
return this.shared.ManualAddressing;
|
|
}
|
|
}
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.IsOnServer)
|
|
{
|
|
string text = SR.GetString(SR.SFxSetManualAddresssingOnChannelDispatcher0);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(text));
|
|
}
|
|
else
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.shared.ManualAddressing = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int MaxParameterInspectors
|
|
{
|
|
get
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
int max = 0;
|
|
|
|
for (int i = 0; i < this.operations.Count; i++)
|
|
max = System.Math.Max(max, this.operations[i].ParameterInspectors.Count);
|
|
|
|
return max;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ICollection<IClientMessageInspector> ClientMessageInspectors
|
|
{
|
|
get { return this.MessageInspectors; }
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new SynchronizedCollection<IClientMessageInspector> MessageInspectors
|
|
{
|
|
get { return this.messageInspectors; }
|
|
}
|
|
|
|
public ICollection<ClientOperation> ClientOperations
|
|
{
|
|
get { return this.Operations; }
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new SynchronizedKeyedCollection<string, ClientOperation> Operations
|
|
{
|
|
get { return this.operations; }
|
|
}
|
|
|
|
public IClientOperationSelector OperationSelector
|
|
{
|
|
get { return this.operationSelector; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.operationSelector = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal object ThisLock
|
|
{
|
|
get { return this.shared; }
|
|
}
|
|
|
|
public ClientOperation UnhandledClientOperation
|
|
{
|
|
get { return this.unhandled; }
|
|
}
|
|
|
|
internal bool UseSynchronizationContext
|
|
{
|
|
get { return this.useSynchronizationContext; }
|
|
set
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.InvalidateRuntime();
|
|
this.useSynchronizationContext = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal T[] GetArray<T>(SynchronizedCollection<T> collection)
|
|
{
|
|
lock (collection.SyncRoot)
|
|
{
|
|
if (collection.Count == 0)
|
|
{
|
|
return EmptyArray<T>.Instance;
|
|
}
|
|
else
|
|
{
|
|
T[] array = new T[collection.Count];
|
|
collection.CopyTo(array, 0);
|
|
return array;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal ImmutableClientRuntime GetRuntime()
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.runtime == null)
|
|
this.runtime = new ImmutableClientRuntime(this);
|
|
|
|
return this.runtime;
|
|
}
|
|
}
|
|
|
|
internal void InvalidateRuntime()
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.shared.ThrowIfImmutable();
|
|
this.runtime = null;
|
|
}
|
|
}
|
|
|
|
internal void LockDownProperties()
|
|
{
|
|
this.shared.LockDownProperties();
|
|
}
|
|
|
|
internal SynchronizedCollection<T> NewBehaviorCollection<T>()
|
|
{
|
|
return new ProxyBehaviorCollection<T>(this);
|
|
}
|
|
|
|
internal bool IsFault(ref Message reply)
|
|
{
|
|
if (reply == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (reply.IsFault)
|
|
{
|
|
return true;
|
|
}
|
|
if (this.MessageVersionNoneFaultsEnabled && IsMessageVersionNoneFault(ref reply, this.MaxFaultSize))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsMessageVersionNoneFault(ref Message message, int maxFaultSize)
|
|
{
|
|
if (message.Version != MessageVersion.None || message.IsEmpty)
|
|
{
|
|
return false;
|
|
}
|
|
HttpResponseMessageProperty prop = message.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
|
|
if (prop == null || prop.StatusCode != HttpStatusCode.InternalServerError)
|
|
{
|
|
return false;
|
|
}
|
|
using (MessageBuffer buffer = message.CreateBufferedCopy(maxFaultSize))
|
|
{
|
|
message.Close();
|
|
message = buffer.CreateMessage();
|
|
using (Message copy = buffer.CreateMessage())
|
|
{
|
|
using (XmlDictionaryReader reader = copy.GetReaderAtBodyContents())
|
|
{
|
|
return reader.IsStartElement(XD.MessageDictionary.Fault, MessageVersion.None.Envelope.DictionaryNamespace);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ProxyBehaviorCollection<T> : SynchronizedCollection<T>
|
|
{
|
|
ClientRuntime outer;
|
|
|
|
internal ProxyBehaviorCollection(ClientRuntime outer)
|
|
: base(outer.ThisLock)
|
|
{
|
|
this.outer = outer;
|
|
}
|
|
|
|
protected override void ClearItems()
|
|
{
|
|
this.outer.InvalidateRuntime();
|
|
base.ClearItems();
|
|
}
|
|
|
|
protected override void InsertItem(int index, T item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
|
}
|
|
|
|
this.outer.InvalidateRuntime();
|
|
base.InsertItem(index, item);
|
|
}
|
|
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
this.outer.InvalidateRuntime();
|
|
base.RemoveItem(index);
|
|
}
|
|
|
|
protected override void SetItem(int index, T item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
|
}
|
|
|
|
this.outer.InvalidateRuntime();
|
|
base.SetItem(index, item);
|
|
}
|
|
}
|
|
|
|
class OperationCollection : SynchronizedKeyedCollection<string, ClientOperation>
|
|
{
|
|
ClientRuntime outer;
|
|
|
|
internal OperationCollection(ClientRuntime outer)
|
|
: base(outer.ThisLock)
|
|
{
|
|
this.outer = outer;
|
|
}
|
|
|
|
protected override void ClearItems()
|
|
{
|
|
this.outer.InvalidateRuntime();
|
|
base.ClearItems();
|
|
}
|
|
|
|
protected override string GetKeyForItem(ClientOperation item)
|
|
{
|
|
return item.Name;
|
|
}
|
|
|
|
protected override void InsertItem(int index, ClientOperation item)
|
|
{
|
|
if (item == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
|
if (item.Parent != this.outer)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxMismatchedOperationParent));
|
|
|
|
this.outer.InvalidateRuntime();
|
|
base.InsertItem(index, item);
|
|
}
|
|
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
this.outer.InvalidateRuntime();
|
|
base.RemoveItem(index);
|
|
}
|
|
|
|
protected override void SetItem(int index, ClientOperation item)
|
|
{
|
|
if (item == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
|
if (item.Parent != this.outer)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxMismatchedOperationParent));
|
|
|
|
this.outer.InvalidateRuntime();
|
|
base.SetItem(index, item);
|
|
}
|
|
|
|
internal void InternalClearItems() { this.ClearItems(); }
|
|
internal string InternalGetKeyForItem(ClientOperation item) { return this.GetKeyForItem(item); }
|
|
internal void InternalInsertItem(int index, ClientOperation item) { this.InsertItem(index, item); }
|
|
internal void InternalRemoveItem(int index) { this.RemoveItem(index); }
|
|
internal void InternalSetItem(int index, ClientOperation item) { this.SetItem(index, item); }
|
|
}
|
|
|
|
|
|
class OperationCollectionWrapper : KeyedCollection<string, ClientOperation>
|
|
{
|
|
OperationCollection inner;
|
|
internal OperationCollectionWrapper(OperationCollection inner) { this.inner = inner; }
|
|
protected override void ClearItems() { this.inner.InternalClearItems(); }
|
|
protected override string GetKeyForItem(ClientOperation item) { return this.inner.InternalGetKeyForItem(item); }
|
|
protected override void InsertItem(int index, ClientOperation item) { this.inner.InternalInsertItem(index, item); }
|
|
protected override void RemoveItem(int index) { this.inner.InternalRemoveItem(index); }
|
|
protected override void SetItem(int index, ClientOperation item) { this.inner.InternalSetItem(index, item); }
|
|
}
|
|
|
|
}
|
|
}
|