1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 
5 
6 [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(System.Runtime.FxCop.Category.Performance,
7 System.Runtime.FxCop.Rule.AvoidUncalledPrivateCode,
8 Scope = "member",
9 Target = "System.ServiceModel.Routing.SR.RoutingExtensionNotFound",
10 Justification = "gets called in RoutingService.ctor(). bug in fxcop")]
11 
12 namespace System.ServiceModel.Routing
13 {
14     using System;
15     using System.Collections.Generic;
16     using System.Configuration;
17     using System.Diagnostics.CodeAnalysis;
18     using System.Runtime;
19     using System.ServiceModel;
20     using System.ServiceModel.Activation;
21     using System.ServiceModel.Channels;
22     using System.ServiceModel.Description;
23     using System.ServiceModel.Dispatcher;
24     using System.Transactions;
25     using SR2 = System.ServiceModel.Routing.SR;
26     using System.Runtime.Diagnostics;
27     using System.ServiceModel.Diagnostics;
28 
29     // Some of the [ServiceBehavior] settings are configured in RoutingBehavior class since
30     // we need to pick the options dynamically based on whether we have transactions or not
31     [SuppressMessage(FxCop.Category.Xaml, FxCop.Rule.TypesMustHaveXamlCallableConstructors)]
32     [SuppressMessage(FxCop.Category.Xaml, FxCop.Rule.TypesShouldHavePublicParameterlessConstructors)]
33     [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any, InstanceContextMode = InstanceContextMode.PerSession,
34         UseSynchronizationContext = false, ValidateMustUnderstand = false)]
35     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
36     public sealed class RoutingService :
37         ISimplexDatagramRouter,
38         ISimplexSessionRouter,
39         IRequestReplyRouter,
40         IDuplexSessionRouter,
41         IDisposable
42     {
43         SessionChannels perMessageChannels;
44         OperationContext operationContext;
45         EventTraceActivity eventTraceActivity;
46 
RoutingService()47         RoutingService()
48         {
49             this.SessionMessages = new List<MessageRpc>(1);
50 
51             //We only need to call this here if we trace in this method.  BeginXXX methods call it again.
52             //FxTrace.Trace.SetAndTraceTransfer(this.ActivityID, true);
53 
54             this.operationContext = OperationContext.Current;
55             if (Fx.Trace.IsEtwProviderEnabled)
56             {
57                 this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(this.operationContext.IncomingMessage);
58             }
59 
60             IContextChannel channel = this.operationContext.Channel;
61 
62             ServiceHostBase host = this.operationContext.Host;
63             this.ChannelExtension = channel.Extensions.Find<RoutingChannelExtension>();
64             if (this.ChannelExtension == null)
65             {
66                 throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR2.RoutingExtensionNotFound));
67             }
68 
69             this.RoutingConfig = host.Extensions.Find<RoutingExtension>().RoutingConfiguration;
70             this.RoutingConfig.VerifyConfigured();
71             this.ChannelExtension.AttachService(this);
72         }
73 
74         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "private setter does get called")]
75         internal RoutingChannelExtension ChannelExtension
76         {
77             get;
78             private set;
79         }
80 
81         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "private setter does get called")]
82         internal RoutingConfiguration RoutingConfig
83         {
84             get;
85             private set;
86         }
87 
88         internal CommittableTransaction RetryTransaction
89         {
90             get;
91             private set;
92         }
93 
94         internal Exception SessionException
95         {
96             get;
97             set;
98         }
99 
100         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "private setter does get called")]
101         internal IList<MessageRpc> SessionMessages
102         {
103             get;
104             private set;
105         }
106 
107         Transaction ReceiveTransaction
108         {
109             get;
110             set;
111         }
112 
CreateNewTransactionIfNeeded(MessageRpc messageRpc)113         internal void CreateNewTransactionIfNeeded(MessageRpc messageRpc)
114         {
115             if (messageRpc.Transaction != null && this.ChannelExtension.TransactedReceiveEnabled)
116             {
117                 if (TD.RoutingServiceUsingExistingTransactionIsEnabled())
118                 {
119                     TD.RoutingServiceUsingExistingTransaction(messageRpc.EventTraceActivity, messageRpc.Transaction.TransactionInformation.LocalIdentifier);
120                 }
121                 Fx.Assert(this.ReceiveTransaction == null, "Should only happen at the start of a session.");
122                 this.ReceiveTransaction = messageRpc.Transaction;
123                 return;
124             }
125             else if (!this.ChannelExtension.TransactedReceiveEnabled || !this.ChannelExtension.ReceiveContextEnabled)
126             {
127                 return;
128             }
129 
130             Fx.Assert(this.RetryTransaction == null, "Logic error, we shouldn't be calling CreateNewTransactionIfNeeded if we have a RC Transaction");
131 
132             ChannelDispatcher channelDispatcher = this.operationContext.EndpointDispatcher.ChannelDispatcher;
133             TimeSpan timeout = channelDispatcher.TransactionTimeout;
134             IsolationLevel isolation = channelDispatcher.TransactionIsolationLevel;
135             TransactionOptions options = new TransactionOptions();
136             if (timeout > TimeSpan.Zero)
137             {
138                 options.Timeout = timeout;
139             }
140             if (isolation != IsolationLevel.Unspecified)
141             {
142                 options.IsolationLevel = isolation;
143             }
144 
145             this.RetryTransaction = new CommittableTransaction(options);
146             if (TD.RoutingServiceCreatingTransactionIsEnabled())
147             {
148                 TD.RoutingServiceCreatingTransaction(messageRpc.EventTraceActivity, this.RetryTransaction.TransactionInformation.LocalIdentifier);
149             }
150         }
151 
GetSessionChannels(bool impersonating)152         internal SessionChannels GetSessionChannels(bool impersonating)
153         {
154             if (impersonating && !this.ChannelExtension.HasSession)
155             {
156                 return this.perMessageChannels;
157             }
158             else
159             {
160                 return this.ChannelExtension.SessionChannels;
161             }
162         }
163 
GetOrCreateClient(RoutingEndpointTrait endpointTrait, bool impersonating)164         internal IRoutingClient GetOrCreateClient<TContract>(RoutingEndpointTrait endpointTrait, bool impersonating)
165         {
166             if (impersonating && !this.ChannelExtension.HasSession)
167             {
168                 if (this.perMessageChannels == null)
169                 {
170                     this.perMessageChannels = new SessionChannels(this.ChannelExtension.ActivityID);
171                 }
172                 return this.perMessageChannels.GetOrCreateClient<TContract>(endpointTrait, this, impersonating);
173             }
174             else
175             {
176                 return this.ChannelExtension.SessionChannels.GetOrCreateClient<TContract>(endpointTrait, this, impersonating);
177             }
178         }
179 
GetTransactionForSending(MessageRpc messageRpc)180         internal Transaction GetTransactionForSending(MessageRpc messageRpc)
181         {
182             if (messageRpc != null && messageRpc.Transaction != null)
183             {
184                 return messageRpc.Transaction;
185             }
186             if (this.ReceiveTransaction != null)
187             {
188                 //This is the transaction used for the receive, we cannot perform error handling since we
189                 //didn't create it.
190                 return this.ReceiveTransaction;
191             }
192             else
193             {
194                 //This could be null, indicating non-transactional behavior
195                 return this.RetryTransaction;
196             }
197         }
198 
IDisposable.Dispose()199         void IDisposable.Dispose()
200         {
201             if (this.perMessageChannels != null)
202             {
203                 //This is for impersonation, thus it's supposed to complete sync
204                 IAsyncResult result = this.perMessageChannels.BeginClose(this.ChannelExtension.OperationTimeout, null, null);
205                 this.perMessageChannels.EndClose(result);
206                 this.perMessageChannels = null;
207             }
208         }
209 
ResetSession()210         internal void ResetSession()
211         {
212             //Are we in a transactional error handling case (i.e. ReceiveContext)?
213             if (this.RetryTransaction != null)
214             {
215                 if (this.ChannelExtension.HasSession)
216                 {
217                     this.ChannelExtension.SessionChannels.AbortAll();
218                 }
219 
220                 RoutingUtilities.SafeRollbackTransaction(this.RetryTransaction);
221                 this.RetryTransaction = null;
222             }
223         }
224 
225         [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
ISimplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)226         IAsyncResult ISimplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)
227         {
228             return this.BeginProcessMessage<ISimplexSessionRouter>(message, callback, state);
229         }
230 
ISimplexSessionRouter.EndProcessMessage(IAsyncResult result)231         void ISimplexSessionRouter.EndProcessMessage(IAsyncResult result)
232         {
233             this.EndProcessMessage<ISimplexSessionRouter>(result);
234         }
235 
236         [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
IRequestReplyRouter.BeginProcessRequest(Message message, AsyncCallback callback, object state)237         IAsyncResult IRequestReplyRouter.BeginProcessRequest(Message message, AsyncCallback callback, object state)
238         {
239             return this.BeginProcessRequest<IRequestReplyRouter>(message, callback, state);
240         }
241 
IRequestReplyRouter.EndProcessRequest(IAsyncResult result)242         Message IRequestReplyRouter.EndProcessRequest(IAsyncResult result)
243         {
244             return this.EndProcessRequest<IRequestReplyRouter>(result);
245         }
246 
247         [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
IDuplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)248         IAsyncResult IDuplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)
249         {
250             return this.BeginProcessMessage<IDuplexSessionRouter>(message, callback, state);
251         }
252 
IDuplexSessionRouter.EndProcessMessage(IAsyncResult result)253         void IDuplexSessionRouter.EndProcessMessage(IAsyncResult result)
254         {
255             this.EndProcessMessage<IDuplexSessionRouter>(result);
256         }
257 
258         [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
ISimplexDatagramRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)259         IAsyncResult ISimplexDatagramRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state)
260         {
261             return this.BeginProcessMessage<ISimplexDatagramRouter>(message, callback, state);
262         }
263 
ISimplexDatagramRouter.EndProcessMessage(IAsyncResult result)264         void ISimplexDatagramRouter.EndProcessMessage(IAsyncResult result)
265         {
266             this.EndProcessMessage<ISimplexDatagramRouter>(result);
267         }
268 
BeginProcessMessage(Message message, AsyncCallback callback, object state)269         IAsyncResult BeginProcessMessage<TContract>(Message message, AsyncCallback callback, object state)
270         {
271             try
272             {
273                 FxTrace.Trace.SetAndTraceTransfer(this.ChannelExtension.ActivityID, true);
274                 return new ProcessMessagesAsyncResult<TContract>(message, this, this.ChannelExtension.OperationTimeout, callback, state);
275             }
276             catch (Exception exception)
277             {
278                 if (TD.RoutingServiceProcessingFailureIsEnabled())
279                 {
280                     TD.RoutingServiceProcessingFailure(this.eventTraceActivity, OperationContext.Current.Channel.LocalAddress.ToString(), exception);
281                 }
282                 throw;
283             }
284         }
285 
EndProcessMessage(IAsyncResult result)286         void EndProcessMessage<TContract>(IAsyncResult result)
287         {
288             try
289             {
290                 FxTrace.Trace.SetAndTraceTransfer(this.ChannelExtension.ActivityID, true);
291                 ProcessMessagesAsyncResult<TContract>.End(result);
292             }
293             catch (Exception exception)
294             {
295                 if (TD.RoutingServiceProcessingFailureIsEnabled())
296                 {
297                     TD.RoutingServiceProcessingFailure(this.eventTraceActivity, OperationContext.Current.Channel.LocalAddress.ToString(), exception);
298                 }
299                 throw;
300             }
301         }
302 
BeginProcessRequest(Message message, AsyncCallback callback, object state)303         IAsyncResult BeginProcessRequest<TContract>(Message message, AsyncCallback callback, object state)
304         {
305             try
306             {
307                 FxTrace.Trace.SetAndTraceTransfer(this.ChannelExtension.ActivityID, true);
308                 return new ProcessRequestAsyncResult<TContract>(this, message, callback, state);
309             }
310             catch (Exception exception)
311             {
312                 if (TD.RoutingServiceProcessingFailureIsEnabled())
313                 {
314                     TD.RoutingServiceProcessingFailure(this.eventTraceActivity, OperationContext.Current.Channel.LocalAddress.ToString(), exception);
315                 }
316                 throw;
317             }
318         }
319 
EndProcessRequest(IAsyncResult result)320         Message EndProcessRequest<TContract>(IAsyncResult result)
321         {
322             try
323             {
324                 FxTrace.Trace.SetAndTraceTransfer(this.ChannelExtension.ActivityID, true);
325                 return ProcessRequestAsyncResult<TContract>.End(result);
326             }
327             catch (Exception exception)
328             {
329                 if (TD.RoutingServiceProcessingFailureIsEnabled())
330                 {
331                     TD.RoutingServiceProcessingFailure(this.eventTraceActivity, OperationContext.Current.Channel.LocalAddress.ToString(), exception);
332                 }
333                 throw;
334             }
335         }
336     }
337 }
338