Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@ -0,0 +1,484 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Collections.Generic;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Transactions;
using System.Runtime.Diagnostics;
using System.ServiceModel.Diagnostics;
static class ClientFactory
{
public static IRoutingClient Create(RoutingEndpointTrait endpointTrait, RoutingService service, bool impersonating)
{
Type contractType = endpointTrait.RouterContract;
IRoutingClient client;
if (contractType == typeof(ISimplexDatagramRouter))
{
client = new SimplexDatagramClient(endpointTrait, service.RoutingConfig, impersonating);
}
else if (contractType == typeof(IRequestReplyRouter))
{
client = new RequestReplyClient(endpointTrait, service.RoutingConfig, impersonating);
}
else if (contractType == typeof(ISimplexSessionRouter))
{
client = new SimplexSessionClient(endpointTrait, service.RoutingConfig, impersonating);
}
else //if (contractType == typeof(IDuplexSessionRouter))
{
Fx.Assert(contractType == typeof(IDuplexSessionRouter), "Only one contract type remaining.");
client = new DuplexSessionClient(service, endpointTrait, impersonating);
}
return client;
}
abstract class RoutingClientBase<TChannel> : ClientBase<TChannel>, IRoutingClient
where TChannel : class
{
bool openCompleted;
object thisLock;
Queue<OperationAsyncResult> waiters;
protected RoutingClientBase(RoutingEndpointTrait endpointTrait, RoutingConfiguration routingConfig, bool impersonating)
: base(endpointTrait.Endpoint.Binding, endpointTrait.Endpoint.Address)
{
Initialize(endpointTrait, routingConfig, impersonating);
}
protected RoutingClientBase(RoutingEndpointTrait endpointTrait, RoutingConfiguration routingConfig, object callbackInstance, bool impersonating)
: base(new InstanceContext(callbackInstance), endpointTrait.Endpoint.Binding, endpointTrait.Endpoint.Address)
{
Initialize(endpointTrait, routingConfig, impersonating);
}
public RoutingEndpointTrait Key
{
get;
private set;
}
public event EventHandler Faulted;
static void ConfigureImpersonation(ServiceEndpoint endpoint, bool impersonating)
{
// Used for both impersonation and ASP.NET Compatibilty Mode. Both currently require
// everything to be synchronous.
if (impersonating)
{
CustomBinding binding = endpoint.Binding as CustomBinding;
if (binding == null)
{
binding = new CustomBinding(endpoint.Binding);
}
SynchronousSendBindingElement syncSend = binding.Elements.Find<SynchronousSendBindingElement>();
if (syncSend == null)
{
binding.Elements.Insert(0, new SynchronousSendBindingElement());
endpoint.Binding = binding;
}
}
}
static void ConfigureTransactionFlow(ServiceEndpoint endpoint)
{
CustomBinding binding = endpoint.Binding as CustomBinding;
if (binding == null)
{
binding = new CustomBinding(endpoint.Binding);
}
TransactionFlowBindingElement transactionFlow = binding.Elements.Find<TransactionFlowBindingElement>();
if (transactionFlow != null)
{
transactionFlow.AllowWildcardAction = true;
endpoint.Binding = binding;
}
}
void Initialize(RoutingEndpointTrait endpointTrait, RoutingConfiguration routingConfig, bool impersonating)
{
this.thisLock = new object();
this.Key = endpointTrait;
if (TD.RoutingServiceCreatingClientForEndpointIsEnabled())
{
TD.RoutingServiceCreatingClientForEndpoint(this.Key.ToString());
}
ServiceEndpoint clientEndpoint = endpointTrait.Endpoint;
ServiceEndpoint endpoint = this.Endpoint;
KeyedByTypeCollection<IEndpointBehavior> behaviors = endpoint.Behaviors;
endpoint.ListenUri = clientEndpoint.ListenUri;
endpoint.ListenUriMode = clientEndpoint.ListenUriMode;
endpoint.Name = clientEndpoint.Name;
foreach (IEndpointBehavior behavior in clientEndpoint.Behaviors)
{
// Remove if present, ok to call if not there (will simply return false)
behaviors.Remove(behavior.GetType());
behaviors.Add(behavior);
}
// If the configuration doesn't explicitly add MustUnderstandBehavior (to override us)
// add it here, with mustunderstand = false.
if (behaviors.Find<MustUnderstandBehavior>() == null)
{
behaviors.Add(new MustUnderstandBehavior(false));
}
// If the configuration doesn't explicitly turn off marshaling we add it here.
if (routingConfig.SoapProcessingEnabled && behaviors.Find<SoapProcessingBehavior>() == null)
{
behaviors.Add(new SoapProcessingBehavior());
}
ConfigureTransactionFlow(endpoint);
ConfigureImpersonation(endpoint, impersonating);
}
protected override TChannel CreateChannel()
{
TChannel channel = base.CreateChannel();
((ICommunicationObject)channel).Faulted += this.InnerChannelFaulted;
return channel;
}
public IAsyncResult BeginOperation(Message message, Transaction transaction, AsyncCallback callback, object state)
{
return new OperationAsyncResult(this, message, transaction, callback, state);
}
public Message EndOperation(IAsyncResult result)
{
return OperationAsyncResult.End(result);
}
protected abstract IAsyncResult OnBeginOperation(Message message, AsyncCallback callback, object state);
protected abstract Message OnEndOperation(IAsyncResult asyncResult);
void InnerChannelFaulted(object sender, EventArgs args)
{
EventHandler handlers = this.Faulted;
if (handlers != null)
{
handlers(this, args);
}
}
class OperationAsyncResult : TransactedAsyncResult
{
static AsyncCompletion openComplete = OpenComplete;
static AsyncCompletion operationComplete = OperationComplete;
static Action<object> signalWaiter;
RoutingClientBase<TChannel> parent;
Message replyMessage;
Message requestMessage;
Transaction transaction;
public OperationAsyncResult(RoutingClientBase<TChannel> parent, Message requestMessage, Transaction transaction, AsyncCallback callback, object state)
: base(callback, state)
{
this.parent = parent;
this.requestMessage = requestMessage;
this.transaction = transaction;
bool shouldOpen = false;
if (!this.parent.openCompleted)
{
lock (this.parent.thisLock)
{
if (!this.parent.openCompleted)
{
//The first to open initializes the waiters queue others add themselves to it.
if (this.parent.waiters == null)
{
//it's our job to open the proxy
this.parent.waiters = new Queue<OperationAsyncResult>();
shouldOpen = true;
}
else
{
//Someone beat us to it, just join the list of waiters.
this.parent.waiters.Enqueue(this);
return;
}
}
}
}
if (shouldOpen)
{
//we are the first so we need to open this channel
IAsyncResult asyncResult;
using (this.PrepareTransactionalCall(this.transaction))
{
//This will use the binding's OpenTimeout.
asyncResult = ((ICommunicationObject)this.parent).BeginOpen(this.PrepareAsyncCompletion(openComplete), this);
}
if (this.SyncContinue(asyncResult))
{
this.Complete(true);
}
}
else
{
if (this.CallOperation())
{
this.Complete(true);
}
}
}
public static Message End(IAsyncResult result)
{
OperationAsyncResult thisPtr = AsyncResult.End<OperationAsyncResult>(result);
return thisPtr.replyMessage;
}
static bool OpenComplete(IAsyncResult openResult)
{
OperationAsyncResult thisPtr = (OperationAsyncResult)openResult.AsyncState;
try
{
((ICommunicationObject)thisPtr.parent).EndOpen(openResult);
}
finally
{
Queue<OperationAsyncResult> localWaiters = null;
lock (thisPtr.parent.thisLock)
{
localWaiters = thisPtr.parent.waiters;
thisPtr.parent.waiters = null;
thisPtr.parent.openCompleted = true;
}
if (localWaiters != null && localWaiters.Count > 0)
{
if (signalWaiter == null)
{
signalWaiter = new Action<object>(SignalWaiter);
}
//foreach over Queue<T> goes FIFO
foreach (OperationAsyncResult waiter in localWaiters)
{
ActionItem.Schedule(signalWaiter, waiter);
}
}
}
return thisPtr.CallOperation();
}
bool CallOperation()
{
IAsyncResult asyncResult;
using (this.PrepareTransactionalCall(this.transaction))
{
asyncResult = this.parent.OnBeginOperation(this.requestMessage, this.PrepareAsyncCompletion(operationComplete), this);
}
return this.SyncContinue(asyncResult);
}
static bool OperationComplete(IAsyncResult result)
{
OperationAsyncResult thisPtr = (OperationAsyncResult)result.AsyncState;
thisPtr.replyMessage = thisPtr.parent.OnEndOperation(result);
return true;
}
static void SignalWaiter(object state)
{
OperationAsyncResult waiter = (OperationAsyncResult)state;
try
{
if (waiter.CallOperation())
{
waiter.Complete(false);
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
waiter.Complete(false, exception);
}
}
}
}
class SimplexDatagramClient : RoutingClientBase<ISimplexDatagramRouter>
{
public SimplexDatagramClient(RoutingEndpointTrait endpointTrait, RoutingConfiguration routingConfig, bool impersonating)
: base(endpointTrait, routingConfig, impersonating)
{
}
protected override IAsyncResult OnBeginOperation(Message message, AsyncCallback callback, object state)
{
return this.Channel.BeginProcessMessage(message, callback, state);
}
protected override Message OnEndOperation(IAsyncResult result)
{
this.Channel.EndProcessMessage(result);
return null;
}
}
class SimplexSessionClient : RoutingClientBase<ISimplexSessionRouter>
{
public SimplexSessionClient(RoutingEndpointTrait endointTrait, RoutingConfiguration routingConfig, bool impersonating)
: base(endointTrait, routingConfig, impersonating)
{
}
protected override IAsyncResult OnBeginOperation(Message message, AsyncCallback callback, object state)
{
return this.Channel.BeginProcessMessage(message, callback, state);
}
protected override Message OnEndOperation(IAsyncResult result)
{
this.Channel.EndProcessMessage(result);
return null;
}
}
class DuplexSessionClient : RoutingClientBase<IDuplexSessionRouter>
{
public DuplexSessionClient(RoutingService service, RoutingEndpointTrait endpointTrait, bool impersonating)
: base(endpointTrait, service.RoutingConfig, new DuplexCallbackProxy(service.ChannelExtension.ActivityID, endpointTrait.CallbackInstance), impersonating)
{
}
protected override IAsyncResult OnBeginOperation(Message message, AsyncCallback callback, object state)
{
return this.Channel.BeginProcessMessage(message, callback, state);
}
protected override Message OnEndOperation(IAsyncResult result)
{
this.Channel.EndProcessMessage(result);
return null;
}
class DuplexCallbackProxy : IDuplexRouterCallback
{
Guid activityID;
IDuplexRouterCallback callbackInstance;
EventTraceActivity eventTraceActivity;
public DuplexCallbackProxy(Guid activityID, IDuplexRouterCallback callbackInstance)
{
this.activityID = activityID;
this.callbackInstance = callbackInstance;
if (Fx.Trace.IsEtwProviderEnabled)
{
this.eventTraceActivity = new EventTraceActivity(activityID);
}
}
IAsyncResult IDuplexRouterCallback.BeginProcessMessage(Message message, AsyncCallback callback, object state)
{
FxTrace.Trace.SetAndTraceTransfer(this.activityID, true);
try
{
return new CallbackAsyncResult(this.callbackInstance, message, callback, state);
}
catch (Exception e)
{
if (TD.RoutingServiceDuplexCallbackExceptionIsEnabled())
{
TD.RoutingServiceDuplexCallbackException(this.eventTraceActivity, "DuplexCallbackProxy.BeginProcessMessage", e);
}
throw;
}
}
void IDuplexRouterCallback.EndProcessMessage(IAsyncResult result)
{
FxTrace.Trace.SetAndTraceTransfer(this.activityID, true);
try
{
CallbackAsyncResult.End(result);
}
catch (Exception e)
{
if (TD.RoutingServiceDuplexCallbackExceptionIsEnabled())
{
TD.RoutingServiceDuplexCallbackException(this.eventTraceActivity, "DuplexCallbackProxy.EndProcessMessage", e);
}
throw;
}
}
// We have to have an AsyncResult implementation here in order to handle the
// TransactionScope appropriately (use PrepareTransactionalCall, SyncContinue, etc...)
class CallbackAsyncResult : TransactedAsyncResult
{
static AsyncCompletion processCallback = ProcessCallback;
IDuplexRouterCallback callbackInstance;
public CallbackAsyncResult(IDuplexRouterCallback callbackInstance, Message message, AsyncCallback callback, object state)
: base(callback, state)
{
this.callbackInstance = callbackInstance;
IAsyncResult result;
using (this.PrepareTransactionalCall(TransactionMessageProperty.TryGetTransaction(message)))
{
result = this.callbackInstance.BeginProcessMessage(message,
this.PrepareAsyncCompletion(processCallback), this);
}
if (this.SyncContinue(result))
{
this.Complete(true);
}
}
public static void End(IAsyncResult result)
{
AsyncResult.End<CallbackAsyncResult>(result);
}
static bool ProcessCallback(IAsyncResult result)
{
CallbackAsyncResult thisPtr = (CallbackAsyncResult)result.AsyncState;
thisPtr.callbackInstance.EndProcessMessage(result);
return true;
}
}
}
}
class RequestReplyClient : RoutingClientBase<IRequestReplyRouter>
{
public RequestReplyClient(RoutingEndpointTrait endpointTrait, RoutingConfiguration routingConfig, bool impersonating)
: base(endpointTrait, routingConfig, impersonating)
{
}
protected override IAsyncResult OnBeginOperation(Message message, AsyncCallback callback, object state)
{
return this.Channel.BeginProcessRequest(message, callback, state);
}
protected override Message OnEndOperation(IAsyncResult result)
{
return this.Channel.EndProcessRequest(result);
}
}
}
}

View File

@ -0,0 +1,34 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing.Configuration
{
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
sealed class ClientEndpointLoader : ChannelFactory
{
ClientEndpointLoader(string configurationName)
{
base.InitializeEndpoint(configurationName, null);
base.Endpoint.Name = configurationName;
}
public static ServiceEndpoint LoadEndpoint(string configurationName)
{
using (ClientEndpointLoader loader = new ClientEndpointLoader(configurationName))
{
return loader.Endpoint;
}
}
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint ep = new ServiceEndpoint(new ContractDescription("contract"));
ep.Contract.ConfigurationName = "*";
return ep;
}
}
}

View File

@ -0,0 +1,37 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing.Configuration
{
static class ConfigurationStrings
{
internal const string BackupList = "backupList";
internal const string BackupLists = "backupLists";
internal const string CustomType = "customType";
internal const string EndpointName = "endpointName";
internal const string Endpoints = "endpoints";
internal const string Entries = "entries";
internal const string Filter = "filter";
internal const string Filter1 = "filter1";
internal const string Filter2 = "filter2";
internal const string FilterData = "filterData";
internal const string FilterName = "filterName";
internal const string Filters = "filters";
internal const string FilterTable = "filterTable";
internal const string FilterTableName = "filterTableName";
internal const string FilterTables = "filterTables";
internal const string FilterType = "filterType";
//internal const string Impersonation = "impersonation";
internal const string Name = "name";
internal const string Namespace = "namespace";
internal const string NamespaceTable = "namespaceTable";
internal const string Prefix = "prefix";
internal const string Priority = "priority";
internal const string ProcessMessages = "processMessages";
internal const string RouteOnHeadersOnly = "routeOnHeadersOnly";
internal const string SoapProcessingEnabled = "soapProcessingEnabled";
internal const string EnsureOrderedDispatch = "ensureOrderedDispatch";
}
}

View File

@ -0,0 +1,20 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing.Configuration
{
using System;
public enum FilterType
{
Action,
EndpointAddress,
PrefixEndpointAddress,
And,
Custom,
EndpointName,
MatchAll,
XPath
}
}

View File

@ -0,0 +1,102 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing.Configuration
{
using System;
using System.Linq;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
public sealed class RoutingExtensionElement : BehaviorExtensionElement
{
public RoutingExtensionElement()
{
this.RouteOnHeadersOnly = RoutingConfiguration.DefaultRouteOnHeadersOnly;
}
[SuppressMessage(FxCop.Category.Configuration, FxCop.Rule.ConfigurationPropertyAttributeRule, Justification = "this is not a configuration property")]
public override Type BehaviorType
{
get { return typeof(RoutingBehavior); }
}
[ConfigurationProperty(ConfigurationStrings.RouteOnHeadersOnly, DefaultValue = RoutingConfiguration.DefaultRouteOnHeadersOnly, Options = ConfigurationPropertyOptions.None)]
public bool RouteOnHeadersOnly
{
get
{
return (bool)this[ConfigurationStrings.RouteOnHeadersOnly];
}
set
{
this[ConfigurationStrings.RouteOnHeadersOnly] = value;
}
}
[SuppressMessage(FxCop.Category.Configuration, FxCop.Rule.ConfigurationValidatorAttributeRule, Justification = "fxcop didn't like [StringValidator(MinLength = 0)]")]
[ConfigurationProperty(ConfigurationStrings.FilterTableName, DefaultValue = null)]
public string FilterTableName
{
get
{
return (string)this[ConfigurationStrings.FilterTableName];
}
set
{
this[ConfigurationStrings.FilterTableName] = value;
}
}
[ConfigurationProperty(ConfigurationStrings.SoapProcessingEnabled, DefaultValue = RoutingConfiguration.DefaultSoapProcessingEnabled)]
public bool SoapProcessingEnabled
{
get
{
return (bool)this[ConfigurationStrings.SoapProcessingEnabled];
}
set
{
this[ConfigurationStrings.SoapProcessingEnabled] = value;
}
}
[ConfigurationProperty(ConfigurationStrings.EnsureOrderedDispatch, DefaultValue = RoutingConfiguration.DefaultEnsureOrderedDispatch)]
public bool EnsureOrderedDispatch
{
get
{
return (bool)this[ConfigurationStrings.EnsureOrderedDispatch];
}
set
{
this[ConfigurationStrings.EnsureOrderedDispatch] = value;
}
}
protected internal override object CreateBehavior()
{
RoutingConfiguration config;
if (string.IsNullOrEmpty(this.FilterTableName))
{
config = new RoutingConfiguration();
config.RouteOnHeadersOnly = this.RouteOnHeadersOnly;
}
else
{
config = new RoutingConfiguration(RoutingSection.CreateFilterTable(this.FilterTableName), this.RouteOnHeadersOnly);
}
config.SoapProcessingEnabled = this.SoapProcessingEnabled;
config.EnsureOrderedDispatch = this.EnsureOrderedDispatch;
RoutingBehavior behavior = new RoutingBehavior(config);
//behavior.Impersonation = this.Impersonation;
return behavior;
}
}
}

View File

@ -0,0 +1,41 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing.Configuration
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.ServiceModel.Configuration;
using System.Configuration;
public class SoapProcessingExtensionElement : BehaviorExtensionElement
{
[SuppressMessage(FxCop.Category.Configuration, FxCop.Rule.ConfigurationPropertyAttributeRule, Justification = "this is not a configuration property")]
public override Type BehaviorType
{
get { return typeof(SoapProcessingBehavior); }
}
protected internal override object CreateBehavior()
{
SoapProcessingBehavior behavior = new SoapProcessingBehavior();
behavior.ProcessMessages = this.ProcessMessages;
return behavior;
}
[ConfigurationProperty(ConfigurationStrings.ProcessMessages, DefaultValue = true, Options = ConfigurationPropertyOptions.None)]
public bool ProcessMessages
{
get
{
return (bool)this[ConfigurationStrings.ProcessMessages];
}
set
{
this[ConfigurationStrings.ProcessMessages] = value;
}
}
}
}

View File

@ -0,0 +1,84 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Xml;
using System.Runtime;
class DelegatingHeader : MessageHeader
{
int index;
MessageHeaderInfo info;
MessageHeaders originalHeaders;
public DelegatingHeader(MessageHeaderInfo info, MessageHeaders originalHeaders)
{
Fx.Assert(info != null, "info is required");
Fx.Assert(originalHeaders != null, "originalHeaders is required");
this.info = info;
this.originalHeaders = originalHeaders;
this.index = -1;
}
void EnsureIndex()
{
if (this.index < 0)
{
this.index = this.originalHeaders.FindHeader(this.info.Name, this.info.Namespace);
if (this.index < 0)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.SourceHeaderNotFound(this.info.Name, this.info.Namespace)));
}
}
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
this.EnsureIndex();
this.originalHeaders.WriteHeaderContents(index, writer);
this.index = -1;
}
protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
this.EnsureIndex();
this.originalHeaders.WriteStartHeader(this.index, writer);
}
public override string Name
{
get { return this.info.Name; }
}
public override string Namespace
{
get { return this.info.Namespace; }
}
public override bool MustUnderstand
{
get { return this.info.MustUnderstand; }
}
public override string Actor
{
get { return this.info.Actor; }
}
public override bool IsReferenceParameter
{
get { return this.info.IsReferenceParameter; }
}
public override bool Relay
{
get { return base.Relay; }
}
}
}

View File

@ -0,0 +1,42 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
sealed class GenericTransactionFlowAttribute : Attribute, IOperationBehavior
{
TransactionFlowAttribute transactionFlowAttribute;
public GenericTransactionFlowAttribute(TransactionFlowOption flowOption)
{
this.transactionFlowAttribute = new TransactionFlowAttribute(flowOption);
}
void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
((IOperationBehavior)this.transactionFlowAttribute).AddBindingParameters(operationDescription, bindingParameters);
}
void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
((IOperationBehavior)this.transactionFlowAttribute).ApplyClientBehavior(operationDescription, clientOperation);
}
void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
((IOperationBehavior)this.transactionFlowAttribute).ApplyDispatchBehavior(operationDescription, dispatchOperation);
}
void IOperationBehavior.Validate(OperationDescription operationDescription)
{
((IOperationBehavior)this.transactionFlowAttribute).Validate(operationDescription);
}
}
}

View File

@ -0,0 +1,20 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace = RoutingUtilities.RoutingNamespace, SessionMode = SessionMode.Allowed)]
interface IDuplexRouterCallback
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
[GenericTransactionFlow(TransactionFlowOption.Allowed)]
IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
void EndProcessMessage(IAsyncResult result);
}
}

View File

@ -0,0 +1,20 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace = RoutingUtilities.RoutingNamespace, SessionMode = SessionMode.Required, CallbackContract = typeof(IDuplexRouterCallback))]
public interface IDuplexSessionRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
[GenericTransactionFlow(TransactionFlowOption.Allowed)]
IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
void EndProcessMessage(IAsyncResult result);
}
}

View File

@ -0,0 +1,20 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace = RoutingUtilities.RoutingNamespace, SessionMode = SessionMode.Allowed)]
public interface IRequestReplyRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
[GenericTransactionFlow(TransactionFlowOption.Allowed)]
IAsyncResult BeginProcessRequest(Message message, AsyncCallback callback, object state);
Message EndProcessRequest(IAsyncResult result);
}
}

View File

@ -0,0 +1,20 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel.Channels;
using System.Transactions;
interface IRoutingClient
{
IAsyncResult BeginOperation(Message message, Transaction transaction, AsyncCallback callback, object state);
Message EndOperation(IAsyncResult result);
event EventHandler Faulted;
RoutingEndpointTrait Key { get; }
CommunicationState State { get; }
void Open();
}
}

View File

@ -0,0 +1,19 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace = RoutingUtilities.RoutingNamespace, SessionMode = SessionMode.Allowed)]
public interface ISimplexDatagramRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
void EndProcessMessage(IAsyncResult result);
}
}

View File

@ -0,0 +1,19 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace = RoutingUtilities.RoutingNamespace, SessionMode = SessionMode.Required)]
public interface ISimplexSessionRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
void EndProcessMessage(IAsyncResult result);
}
}

View File

@ -0,0 +1,269 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Transactions;
using SR2 = System.ServiceModel.Routing.SR;
using System.Security.Principal;
using System.Runtime.Diagnostics;
using System.ServiceModel.Diagnostics;
// This class wraps a Message, MessageBuffer (if requested), and the OperationContext
// The message is not buffered if nobody calls MessageRpc.CreateBuffer. If the message
// is buffered, then we hang on to the buffer so it can be reused and Clone the message
// which became invalid when we buffered this message.
class MessageRpc
{
const int ERROR_BAD_IMPERSONATION_LEVEL = 1346;
Message originalMessage;
Message clonedMessage;
MessageBuffer messageBuffer;
string uniqueID;
Transaction transaction;
ReceiveContext receiveContext;
IList<SendOperation> operations;
WindowsIdentity windowsIdentity;
EventTraceActivity eventTraceActivity;
public MessageRpc(Message message, OperationContext operationContext, bool impersonationRequired)
{
Fx.Assert(message != null, "message cannot be null");
Fx.Assert(operationContext != null, "operationContext cannot be null");
this.originalMessage = message;
this.OperationContext = operationContext;
if (Fx.Trace.IsEtwProviderEnabled)
{
this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
}
//passing in true causes this to return null if the thread is not impersonating.
this.windowsIdentity = WindowsIdentity.GetCurrent(true);
if (impersonationRequired && !AspNetEnvironment.Current.AspNetCompatibilityEnabled)
{
if (this.windowsIdentity == null || this.windowsIdentity.ImpersonationLevel != TokenImpersonationLevel.Impersonation)
{
//Temporarily revert impersonation to process token to throw an exception
IDisposable autoRevert = null;
try
{
try { }
finally
{
autoRevert = WindowsIdentity.Impersonate(IntPtr.Zero);
}
Win32Exception errorDetail = new Win32Exception(ERROR_BAD_IMPERSONATION_LEVEL);
throw FxTrace.Exception.AsError(new SecurityNegotiationException(errorDetail.Message));
}
finally
{
if (autoRevert != null)
{
autoRevert.Dispose();
}
}
}
}
ReceiveContext.TryGet(message, out this.receiveContext);
this.transaction = Transaction.Current;
if (this.transaction == null)
{
this.transaction = TransactionMessageProperty.TryGetTransaction(message);
}
}
internal bool Impersonating
{
get { return this.windowsIdentity != null; }
}
internal EventTraceActivity EventTraceActivity
{
get
{
return this.eventTraceActivity;
}
}
public OperationContext OperationContext
{
get;
private set;
}
public string UniqueID
{
get
{
if (this.uniqueID == null)
{
if (this.Message.Version != MessageVersion.None &&
this.Message.Headers.MessageId != null)
{
this.uniqueID = this.originalMessage.Headers.MessageId.ToString();
}
else
{
this.uniqueID = this.GetHashCode().ToString(CultureInfo.InvariantCulture);
}
}
return this.uniqueID;
}
}
public Message Message
{
get
{
// If we've created a MessageBuffer then the originalMessage has already been consumed
if (this.messageBuffer != null)
{
Fx.Assert(this.clonedMessage != null, "Need to set clonedMessage if we buffered the message");
return this.clonedMessage;
}
else
{
// Haven't buffered, can use the original.
return this.originalMessage;
}
}
}
public ReceiveContext ReceiveContext
{
get { return this.receiveContext; }
}
public IList<SendOperation> Operations
{
get { return this.operations; }
}
public Transaction Transaction
{
get { return this.transaction; }
}
public MessageBuffer CreateBuffer()
{
if (this.messageBuffer == null)
{
this.messageBuffer = this.originalMessage.CreateBufferedCopy(int.MaxValue);
this.clonedMessage = this.messageBuffer.CreateMessage();
}
return this.messageBuffer;
}
public IDisposable PrepareCall()
{
return new CallState(this);
}
public void RouteToSingleEndpoint<TContract>(RoutingConfiguration routingConfig)
{
IEnumerable<ServiceEndpoint> result;
if (routingConfig.RouteOnHeadersOnly)
{
if (TD.RoutingServiceFilterTableMatchStartIsEnabled()) { TD.RoutingServiceFilterTableMatchStart(this.eventTraceActivity); }
routingConfig.InternalFilterTable.GetMatchingValue(this.Message, out result);
if (TD.RoutingServiceFilterTableMatchStopIsEnabled()) { TD.RoutingServiceFilterTableMatchStop(this.eventTraceActivity); }
}
else
{
MessageBuffer buffer = this.CreateBuffer();
if (TD.RoutingServiceFilterTableMatchStartIsEnabled()) { TD.RoutingServiceFilterTableMatchStart(this.eventTraceActivity); }
routingConfig.InternalFilterTable.GetMatchingValue(buffer, out result);
if (TD.RoutingServiceFilterTableMatchStopIsEnabled()) { TD.RoutingServiceFilterTableMatchStop(this.eventTraceActivity); }
}
if (result == null)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.NoFilterMatched));
}
if (TD.RoutingServiceMessageRoutedToEndpointsIsEnabled())
{
TD.RoutingServiceMessageRoutedToEndpoints(this.eventTraceActivity, this.UniqueID, "1");
}
this.operations = new List<SendOperation>(1);
this.operations.Add(new SendOperation(result, typeof(TContract), this.OperationContext));
}
public void RouteToEndpoints<TContract>(RoutingConfiguration routingConfig)
{
List<IEnumerable<ServiceEndpoint>> endpointLists = new List<IEnumerable<ServiceEndpoint>>();
if (routingConfig.RouteOnHeadersOnly)
{
if (TD.RoutingServiceFilterTableMatchStartIsEnabled()) { TD.RoutingServiceFilterTableMatchStart(this.eventTraceActivity); }
routingConfig.InternalFilterTable.GetMatchingValues(this.Message, endpointLists);
if (TD.RoutingServiceFilterTableMatchStopIsEnabled()) { TD.RoutingServiceFilterTableMatchStop(this.eventTraceActivity); }
}
else
{
MessageBuffer messageBuffer = this.CreateBuffer();
if (TD.RoutingServiceFilterTableMatchStartIsEnabled()) { TD.RoutingServiceFilterTableMatchStart(this.eventTraceActivity); }
routingConfig.InternalFilterTable.GetMatchingValues(messageBuffer, endpointLists);
if (TD.RoutingServiceFilterTableMatchStopIsEnabled()) { TD.RoutingServiceFilterTableMatchStop(this.eventTraceActivity); }
}
if (TD.RoutingServiceMessageRoutedToEndpointsIsEnabled())
{
TD.RoutingServiceMessageRoutedToEndpoints(this.eventTraceActivity, this.UniqueID, endpointLists.Count.ToString(TD.Culture));
}
if (endpointLists.Count == 0)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.NoFilterMatched));
}
this.operations = new List<SendOperation>(endpointLists.Count);
foreach (IEnumerable<ServiceEndpoint> endpointList in endpointLists)
{
this.operations.Add(new SendOperation(endpointList, typeof(TContract), this.OperationContext));
}
}
class CallState : IDisposable
{
OperationContextScope nullContextScope;
WindowsImpersonationContext impersonation;
public CallState (MessageRpc messageRpc)
{
this.nullContextScope = new OperationContextScope((OperationContext)null);
if (messageRpc.windowsIdentity != null)
{
this.impersonation = messageRpc.windowsIdentity.Impersonate();
}
}
void IDisposable.Dispose()
{
if (this.impersonation != null)
{
this.impersonation.Dispose();
}
this.nullContextScope.Dispose();
}
}
}
}

View File

@ -0,0 +1,281 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Configuration;
using System.Runtime;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Threading;
using System.Transactions;
//using System.Security.Principal;
class ProcessRequestAsyncResult<TContract> : TransactedAsyncResult
{
static AsyncCompletion operationCallback = new AsyncCompletion(OperationCallback);
RoutingService service;
IRoutingClient currentClient;
MessageRpc messageRpc;
Message replyMessage;
bool allCompletedSync;
bool abortedRetry;
public ProcessRequestAsyncResult(RoutingService service, Message message, AsyncCallback callback, object state)
: base(callback, state)
{
this.allCompletedSync = true;
this.service = service;
this.messageRpc = new MessageRpc(message, OperationContext.Current, service.ChannelExtension.ImpersonationRequired);
if (TD.RoutingServiceProcessingMessageIsEnabled())
{
TD.RoutingServiceProcessingMessage(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID,
message.Headers.Action, this.messageRpc.OperationContext.EndpointDispatcher.EndpointAddress.Uri.ToString(), messageRpc.Transaction != null ? "True" : "False");
}
try
{
EndpointNameMessageFilter.Set(this.messageRpc.Message.Properties, service.ChannelExtension.EndpointName);
this.messageRpc.RouteToSingleEndpoint<TContract>(this.service.RoutingConfig);
}
catch (MultipleFilterMatchesException matchesException)
{
// Wrap this exception with one that is more meaningful to users of RoutingService:
throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ReqReplyMulticastNotSupported(this.messageRpc.OperationContext.Channel.LocalAddress), matchesException));
}
while (this.StartProcessing())
{
}
}
bool StartProcessing()
{
bool callAgain = false;
SendOperation sendOperation = this.messageRpc.Operations[0];
this.currentClient = this.service.GetOrCreateClient<TContract>(sendOperation.CurrentEndpoint, this.messageRpc.Impersonating);
if (TD.RoutingServiceTransmittingMessageIsEnabled())
{
TD.RoutingServiceTransmittingMessage(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID, "0", this.currentClient.Key.ToString());
}
try
{
if (messageRpc.Transaction != null && sendOperation.HasAlternate)
{
throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ErrorHandlingNotSupportedReqReplyTxn(this.messageRpc.OperationContext.Channel.LocalAddress)));
}
// We always work on cloned message when there are backup endpoints to handle exception cases
Message message;
if (sendOperation.AlternateEndpointCount > 0)
{
message = messageRpc.CreateBuffer().CreateMessage();
}
else
{
message = messageRpc.Message;
}
sendOperation.PrepareMessage(message);
IAsyncResult result = null;
using (this.PrepareTransactionalCall(messageRpc.Transaction))
{
IDisposable impersonationContext = null;
try
{
//Perform the assignment in a finally block so it won't be interrupted asynchronously
try { }
finally
{
impersonationContext = messageRpc.PrepareCall();
}
result = this.currentClient.BeginOperation(message, messageRpc.Transaction, this.PrepareAsyncCompletion(operationCallback), this);
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Dispose();
}
}
}
if (this.CheckSyncContinue(result))
{
if (this.OperationComplete(result))
{
this.Complete(this.allCompletedSync);
}
else
{
callAgain = true;
}
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
if (!this.HandleClientOperationFailure(exception))
{
throw;
}
callAgain = true;
}
return callAgain;
}
static bool OperationCallback(IAsyncResult result)
{
ProcessRequestAsyncResult<TContract> thisPtr = (ProcessRequestAsyncResult<TContract>)result.AsyncState;
FxTrace.Trace.SetAndTraceTransfer(thisPtr.service.ChannelExtension.ActivityID, true);
thisPtr.allCompletedSync = false;
try
{
if (thisPtr.OperationComplete(result))
{
return true;
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
if (!thisPtr.HandleClientOperationFailure(exception))
{
throw;
}
}
while (thisPtr.StartProcessing())
{
}
return false;
}
// Returns true if we're all done and can complete this AsyncResult now
bool OperationComplete(IAsyncResult result)
{
bool completeSelf = false;
Message responseMsg = this.currentClient.EndOperation(result);
if (TD.RoutingServiceTransmitSucceededIsEnabled())
{
TD.RoutingServiceTransmitSucceeded(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID, "0", this.currentClient.Key.ToString());
}
if (responseMsg == null || !responseMsg.IsFault)
{
if (TD.RoutingServiceSendingResponseIsEnabled())
{
string action = (responseMsg != null) ? responseMsg.Headers.Action : string.Empty;
TD.RoutingServiceSendingResponse(this.messageRpc.EventTraceActivity, action);
}
}
else
{
if (TD.RoutingServiceSendingFaultResponseIsEnabled()) { TD.RoutingServiceSendingFaultResponse(this.messageRpc.EventTraceActivity, responseMsg.Headers.Action); }
}
this.replyMessage = responseMsg;
completeSelf = true;
if (TD.RoutingServiceCompletingTwoWayIsEnabled()) { TD.RoutingServiceCompletingTwoWay(this.messageRpc.EventTraceActivity); }
return completeSelf;
}
internal static Message End(IAsyncResult result)
{
ProcessRequestAsyncResult<TContract> processRequest = AsyncResult.End<ProcessRequestAsyncResult<TContract>>(result);
return processRequest.replyMessage;
}
bool HandleClientOperationFailure(Exception exception)
{
SendOperation sendOperation = this.messageRpc.Operations[0];
if (TD.RoutingServiceTransmitFailedIsEnabled()) { TD.RoutingServiceTransmitFailed(this.messageRpc.EventTraceActivity, sendOperation.CurrentEndpoint.ToString(), exception); }
if (!(exception is CommunicationException || exception is TimeoutException))
{
//We only move to backup for CommunicationExceptions and TimeoutExceptions
return false;
}
if ((exception is CommunicationObjectAbortedException || exception is CommunicationObjectFaultedException) &&
!this.service.ChannelExtension.HasSession)
{
// Messages on a non sessionful channel share outbound connections and can
// fail due to other messages failing on the same channel
if (messageRpc.Transaction == null && !this.abortedRetry)
{
//No session and non transactional, retry the message 1 time (before moving to backup)
this.abortedRetry = true;
return true;
}
}
else if (exception is EndpointNotFoundException)
{
// The channel may not fault for this exception for bindings other than netTcpBinding
// We abort the channel in that case. We proactively clean up so that we don't have to cleanup later
SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating);
if (sessionChannels != null)
{
sessionChannels.AbortChannel(sendOperation.CurrentEndpoint);
}
}
else if (exception is MessageSecurityException)
{
// The service may have been stopped and restarted without the routing service knowledge.
// When we try to use a cached channel to the service, the channel can fault due to this exception
// The faulted channel gets cleaned up and we retry one more time only when service has backup
// If there is no backup, we do not retry since we do not create a buffered message to prevent performance degradation
if (!this.abortedRetry && (sendOperation.AlternateEndpointCount > 0))
{
this.abortedRetry = true;
return true;
}
}
else if (exception is ProtocolException)
{
// This exception may happen when the current cached channel was closed due to end service recycles.
// We abort the channel in this case and clean it up from the session.
// We will then retry the request one more time only. In retried request, it will create a new channel because the cached channel has been cleaned up.
if (!this.abortedRetry)
{
SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating);
if (sessionChannels != null)
{
this.abortedRetry = true;
sessionChannels.AbortChannel(sendOperation.CurrentEndpoint);
return true;
}
}
}
if (sendOperation.TryMoveToAlternate(exception))
{
if (TD.RoutingServiceMovedToBackupIsEnabled())
{
TD.RoutingServiceMovedToBackup(this.messageRpc.EventTraceActivity, messageRpc.UniqueID, "0", sendOperation.CurrentEndpoint.ToString());
}
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,282 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
[Fx.Tag.XamlVisible(false)]
public sealed class RoutingBehavior : IServiceBehavior
{
RoutingConfiguration configuration;
public RoutingBehavior(RoutingConfiguration routingConfiguration)
{
if (routingConfiguration == null)
{
throw FxTrace.Exception.ArgumentNull("routingConfiguration");
}
this.configuration = routingConfiguration;
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
RoutingExtension routingExtension = new RoutingExtension(this.configuration);
serviceHostBase.Extensions.Add(routingExtension);
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
{
ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
if (!endpointDispatcher.IsSystemEndpoint &&
RoutingUtilities.IsRoutingServiceNamespace(endpointDispatcher.ContractNamespace))
{
DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
//Since we use PerSession instancing this concurrency only applies to messages
//in the same session, also needed to maintain order.
dispatchRuntime.ConcurrencyMode = ConcurrencyMode.Single;
dispatchRuntime.EnsureOrderedDispatch = this.configuration.EnsureOrderedDispatch;
}
}
}
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
HashSet<string> endpoints = new HashSet<string>();
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
if (!endpoint.IsSystemEndpoint &&
RoutingUtilities.IsRoutingServiceNamespace(endpoint.Contract.Namespace))
{
endpoint.Behaviors.Add(new RoutingEndpointBehavior(endpoint));
endpoints.Add(endpoint.Name);
}
}
EndpointNameMessageFilter.Validate(this.configuration.InternalFilterTable.Keys, endpoints);
}
public static Type GetContractForDescription(ContractDescription description)
{
if (description == null)
{
throw FxTrace.Exception.ArgumentNull("description");
}
if (description.CallbackContractType != null)
{
return typeof(IDuplexSessionRouter);
}
bool allOneWay = true;
foreach (OperationDescription operation in description.Operations)
{
if (!operation.IsOneWay)
{
allOneWay = false;
break;
}
}
if (allOneWay)
{
if (description.SessionMode == SessionMode.Required)
{
return typeof(ISimplexSessionRouter);
}
else
{
return typeof(ISimplexDatagramRouter);
}
}
else
{
return typeof(IRequestReplyRouter);
}
}
internal class RoutingEndpointBehavior : IEndpointBehavior, IChannelInitializer, IInputSessionShutdown
{
public RoutingEndpointBehavior(ServiceEndpoint endpoint)
{
this.Endpoint = endpoint;
this.EndpointName = endpoint.Name;
}
internal ChannelDispatcher ChannelDispatcher
{
get;
private set;
}
internal ServiceEndpoint Endpoint
{
get;
private set;
}
internal string EndpointName
{
get;
private set;
}
internal bool ImpersonationRequired
{
get;
private set;
}
internal bool ReceiveContextEnabled
{
get;
private set;
}
internal bool TransactedReceiveEnabled
{
get;
private set;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
//Turn on ReceiveContext here if supported
IReceiveContextSettings receiveContextSettings = endpoint.Binding.GetProperty<IReceiveContextSettings>(bindingParameters);
if (receiveContextSettings != null)
{
receiveContextSettings.Enabled = true;
}
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
this.ChannelDispatcher = endpointDispatcher.ChannelDispatcher;
this.ChannelDispatcher.ChannelInitializers.Add(this);
endpointDispatcher.DispatchRuntime.InputSessionShutdownHandlers.Add(this);
endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown = false;
if (endpointDispatcher.DispatchRuntime.ImpersonateCallerForAllOperations)
{
this.ImpersonationRequired = true;
}
else if (AspNetEnvironment.Current.AspNetCompatibilityEnabled)
{
this.ImpersonationRequired = true;
}
BindingParameterCollection bindingParams = new BindingParameterCollection();
if (RoutingUtilities.IsTransactedReceive(endpoint.Binding, bindingParams))
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
if (operation.Behaviors.Find<TransactedReceiveOperationBehavior>() == null)
{
operation.Behaviors.Add(new TransactedReceiveOperationBehavior());
}
}
this.ChannelDispatcher.IsTransactedReceive = true;
endpointDispatcher.DispatchRuntime.TransactionAutoCompleteOnSessionClose = true;
this.TransactedReceiveEnabled = true;
}
IReceiveContextSettings rcSettings = endpoint.Binding.GetProperty<IReceiveContextSettings>(bindingParams);
if (rcSettings != null && rcSettings.Enabled)
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
ReceiveContextEnabledAttribute rcEnabled = new ReceiveContextEnabledAttribute();
rcEnabled.ManualControl = true;
operation.Behaviors.Add(rcEnabled);
}
this.ReceiveContextEnabled = true;
//Switch TransactedReceive off, because we don't want the Dispatcher creating any Transaction
endpointDispatcher.ChannelDispatcher.IsTransactedReceive = false;
endpointDispatcher.DispatchRuntime.TransactionAutoCompleteOnSessionClose = false;
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
void IChannelInitializer.Initialize(IClientChannel channel)
{
RoutingChannelExtension channelState = RoutingChannelExtension.Create(this);
channel.Extensions.Add(channelState);
}
void IInputSessionShutdown.ChannelFaulted(IDuplexContextChannel channel)
{
RoutingChannelExtension channelExtension = channel.Extensions.Find<RoutingChannelExtension>();
if (channelExtension != null)
{
channelExtension.Fault(new CommunicationObjectFaultedException());
}
else
{
RoutingUtilities.Abort(channel, channel.LocalAddress);
}
}
void IInputSessionShutdown.DoneReceiving(IDuplexContextChannel channel)
{
RoutingChannelExtension channelExtension = channel.Extensions.Find<RoutingChannelExtension>();
channelExtension.DoneReceiving(this.Endpoint.Binding.CloseTimeout);
}
}
class TransactedReceiveOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
if (operationDescription.Behaviors.Find<ReceiveContextEnabledAttribute>() == null)
{
dispatchOperation.TransactionRequired = true;
}
ContractDescription contract = operationDescription.DeclaringContract;
if (dispatchOperation.IsOneWay && contract.SessionMode == SessionMode.Required)
{
dispatchOperation.Parent.ConcurrencyMode = ConcurrencyMode.Single;
dispatchOperation.Parent.ReleaseServiceInstanceOnTransactionComplete = false;
dispatchOperation.TransactionAutoComplete = false;
}
}
public void Validate(OperationDescription operationDescription)
{
}
}
}
}

View File

@ -0,0 +1,289 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Routing
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.Collections.Generic;
using System.Threading;
abstract class RoutingChannelExtension : IExtension<IContextChannel>
{
static AsyncCallback closeChannelsCallback = Fx.ThunkCallback(CloseChannelsCallback);
static AsyncCallback shutdownCallback = Fx.ThunkCallback(ShutdownCallback);
IContextChannel channel;
bool hasSession;
RoutingBehavior.RoutingEndpointBehavior endpointBehavior;
RoutingService sessionService;
volatile SessionChannels sessionChannels;
[Fx.Tag.SynchronizationObject]
object thisLock;
public RoutingChannelExtension(RoutingBehavior.RoutingEndpointBehavior endpointBehavior)
{
this.ActivityID = Guid.NewGuid();
this.endpointBehavior = endpointBehavior;
this.thisLock = new object();
}
internal Guid ActivityID
{
get;
private set;
}
public string EndpointName
{
get { return this.endpointBehavior.EndpointName; }
}
public bool HasSession
{
get { return this.hasSession; }
}
public bool ImpersonationRequired
{
get { return this.endpointBehavior.ImpersonationRequired; }
}
public TimeSpan OperationTimeout
{
get;
private set;
}
public bool ReceiveContextEnabled
{
get { return this.endpointBehavior.ReceiveContextEnabled; }
}
[SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "get_SessionChannels is called by RoutingService..ctor")]
public SessionChannels SessionChannels
{
get
{
if (this.sessionChannels == null)
{
lock (this.thisLock)
{
if (this.sessionChannels == null)
{
Fx.AssertAndThrow(!(this.ImpersonationRequired && !this.HasSession), "Shouldn't allocate SessionChannels if session-less and impersonating");
this.sessionChannels = new SessionChannels(this.ActivityID);
}
}
}
return this.sessionChannels;
}
}
public bool TransactedReceiveEnabled
{
get { return this.endpointBehavior.TransactedReceiveEnabled; }
}
[SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "AttachService is called by RoutingService..ctor")]
public void AttachService(RoutingService service)
{
SessionChannels channelsToClose = null;
lock (this.thisLock)
{
if (!this.hasSession)
{
RoutingConfiguration oldConfig = null;
if (this.sessionService != null)
{
oldConfig = this.sessionService.RoutingConfig;
}
if (oldConfig != null && !object.ReferenceEquals(service.RoutingConfig, oldConfig))
{
//The RoutingConfiguration has changed. We need to release any old channels that are cached.
channelsToClose = this.sessionChannels;
this.sessionChannels = null;
}
}
else
{
Fx.Assert(this.sessionService == null, "There must only be one RoutingService created for a sessionful channel");
}
this.sessionService = service;
}
if (channelsToClose != null)
{
channelsToClose.BeginClose(this.channel.OperationTimeout, closeChannelsCallback, channelsToClose);
}
}
public abstract IAsyncResult BeginShutdown(RoutingService service, TimeSpan timeout, AsyncCallback callback, object state);
static void CloseChannelsCallback(IAsyncResult asyncResult)
{
SessionChannels channelsToClose = (SessionChannels)asyncResult.AsyncState;
Exception exception = null;
try
{
channelsToClose.EndClose(asyncResult);
}
catch (CommunicationException communicationException)
{
exception = communicationException;
}
catch (TimeoutException timeoutException)
{
exception = timeoutException;
}
if (exception != null && TD.RoutingServiceHandledExceptionIsEnabled())
{
TD.RoutingServiceHandledException(null, exception);
}
}
static void ShutdownCallback(IAsyncResult result)
{
if (result.CompletedSynchronously)
{
return;
}
RoutingChannelExtension thisPtr = (RoutingChannelExtension)result.AsyncState;
try
{
thisPtr.ShutdownComplete(result);
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
thisPtr.Fault(exception);
}
}
void ShutdownComplete(IAsyncResult result)
{
this.EndShutdown(result);
this.channel.Close();
}
public static RoutingChannelExtension Create(RoutingBehavior.RoutingEndpointBehavior endpointBehavior)
{
Type contractType = endpointBehavior.Endpoint.Contract.ContractType;
if (contractType == typeof(IDuplexSessionRouter))
{
return new RoutingChannelExtension<IDuplexSessionRouter>(endpointBehavior);
}
else if (contractType == typeof(ISimplexDatagramRouter))
{
return new RoutingChannelExtension<ISimplexDatagramRouter>(endpointBehavior);
}
else if (contractType == typeof(IRequestReplyRouter))
{
return new RoutingChannelExtension<IRequestReplyRouter>(endpointBehavior);
}
else
{
Fx.Assert(contractType == typeof(ISimplexSessionRouter), "Was a new contract added?");
return new RoutingChannelExtension<ISimplexSessionRouter>(endpointBehavior);
}
}
public void DoneReceiving(TimeSpan closeTimeout)
{
FxTrace.Trace.SetAndTraceTransfer(this.ActivityID, true);
try
{
if (this.sessionService != null)
{
IAsyncResult result = this.BeginShutdown(this.sessionService, closeTimeout, shutdownCallback, this);
if (result.CompletedSynchronously)
{
this.ShutdownComplete(result);
}
}
else
{
this.channel.Close();
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
this.Fault(exception);
}
}
public abstract void EndShutdown(IAsyncResult result);
public void Fault(Exception exception)
{
FxTrace.Trace.SetAndTraceTransfer(this.ActivityID, true);
//Notify the error handlers that a problem occurred
foreach (IErrorHandler errorHandler in this.endpointBehavior.ChannelDispatcher.ErrorHandlers)
{
if (errorHandler.HandleError(exception))
{
break;
}
}
SessionChannels channelsToAbort;
lock (this.thisLock)
{
channelsToAbort = this.sessionChannels;
this.sessionChannels = null;
}
if (channelsToAbort != null)
{
channelsToAbort.AbortAll();
}
RoutingUtilities.Abort(this.channel, this.channel.LocalAddress);
}
void IExtension<IContextChannel>.Attach(IContextChannel owner)
{
this.channel = owner;
this.hasSession = (owner.InputSession != null);
this.OperationTimeout = owner.OperationTimeout;
}
void IExtension<IContextChannel>.Detach(IContextChannel owner)
{
}
}
sealed class RoutingChannelExtension<T> : RoutingChannelExtension
{
public RoutingChannelExtension(RoutingBehavior.RoutingEndpointBehavior endpointBehavior)
: base(endpointBehavior)
{
}
public override IAsyncResult BeginShutdown(RoutingService service, TimeSpan timeout, AsyncCallback callback, object state)
{
return new ProcessMessagesAsyncResult<T>(null, service, timeout, callback, state);
}
public override void EndShutdown(IAsyncResult result)
{
ProcessMessagesAsyncResult<T>.End(result);
}
}
}

Some files were not shown because too many files have changed in this diff Show More