Merge pull request #231 from linquize/a853199c497bb0977970974303fac7e42080809d
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Dispatcher / ChannelDispatcher.cs
1 //
2 // ChannelDispatcher.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005,2009 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.Linq;
32 using System.Reflection;
33 using System.ServiceModel.Channels;
34 using System.Threading;
35 using System.Transactions;
36 using System.ServiceModel;
37 using System.ServiceModel.Description;
38
39 namespace System.ServiceModel.Dispatcher
40 {
41         public class ChannelDispatcher : ChannelDispatcherBase
42         {
43                 class EndpointDispatcherCollection : SynchronizedCollection<EndpointDispatcher>
44                 {
45                         public EndpointDispatcherCollection (ChannelDispatcher owner)
46                         {
47                                 this.owner = owner;
48                         }
49
50                         ChannelDispatcher owner;
51
52                         protected override void ClearItems ()
53                         {
54                                 foreach (var ed in this)
55                                         ed.ChannelDispatcher = null;
56                                 base.ClearItems ();
57                         }
58
59                         protected override void InsertItem (int index, EndpointDispatcher item)
60                         {
61                                 item.ChannelDispatcher = owner;
62                                 base.InsertItem (index, item);
63                         }
64
65                         protected override void RemoveItem (int index)
66                         {
67                                 if (index < Count)
68                                         this [index].ChannelDispatcher = null;
69                                 base.RemoveItem (index);
70                         }
71
72                         protected override void SetItem (int index, EndpointDispatcher item)
73                         {
74                                 item.ChannelDispatcher = owner;
75                                 base.SetItem (index, item);
76                         }
77                 }
78
79                 ServiceHostBase host;
80
81                 string binding_name;            
82                 Collection<IErrorHandler> error_handlers
83                         = new Collection<IErrorHandler> ();
84                 IChannelListener listener;
85                 internal IDefaultCommunicationTimeouts timeouts; // FIXME: remove internal
86                 MessageVersion message_version;
87                 bool receive_sync, include_exception_detail_in_faults,
88                         manual_addressing, is_tx_receive;
89                 int max_tx_batch_size;
90                 SynchronizedCollection<IChannelInitializer> initializers
91                         = new SynchronizedCollection<IChannelInitializer> ();
92                 IsolationLevel tx_isolation_level;
93                 TimeSpan tx_timeout;
94                 ServiceThrottle throttle;
95
96                 Guid identifier = Guid.NewGuid ();
97                 ManualResetEvent async_event = new ManualResetEvent (false);
98
99                 ListenerLoopManager loop_manager;
100                 SynchronizedCollection<EndpointDispatcher> endpoints;
101
102                 [MonoTODO ("get binding info from config")]
103                 public ChannelDispatcher (IChannelListener listener)
104                         : this (listener, null)
105                 {
106                 }
107
108                 public ChannelDispatcher (
109                         IChannelListener listener, string bindingName)
110                         : this (listener, bindingName, null)
111                 {
112                 }
113
114                 public ChannelDispatcher (
115                         IChannelListener listener, string bindingName,
116                         IDefaultCommunicationTimeouts timeouts)
117                 {
118                         if (listener == null)
119                                 throw new ArgumentNullException ("listener");
120                         Init (listener, bindingName, timeouts);
121                 }
122
123                 private void Init (IChannelListener listener, string bindingName,
124                         IDefaultCommunicationTimeouts timeouts)
125                 {
126                         this.listener = listener;
127                         this.binding_name = bindingName;
128                         // IChannelListener is often a ChannelListenerBase
129                         // which implements IDefaultCommunicationTimeouts.
130                         this.timeouts = timeouts ?? listener as IDefaultCommunicationTimeouts ?? DefaultCommunicationTimeouts.Instance;
131                         endpoints = new EndpointDispatcherCollection (this);
132                 }
133
134                 internal EndpointDispatcher InitializeServiceEndpoint (Type serviceType, ServiceEndpoint se)
135                 {
136                         //Attach one EndpointDispacher to the ChannelDispatcher
137                         EndpointDispatcher ed = new EndpointDispatcher (se.Address, se.Contract.Name, se.Contract.Namespace);
138                         this.Endpoints.Add (ed);
139                         ed.InitializeServiceEndpoint (false, serviceType, se);
140                         return ed;
141                 }
142
143                 public string BindingName {
144                         get { return binding_name; }
145                 }
146
147                 public SynchronizedCollection<IChannelInitializer> ChannelInitializers {
148                         get { return initializers; }
149                 }
150
151                 protected internal override TimeSpan DefaultCloseTimeout {
152                         get { return timeouts.CloseTimeout; }
153                 }
154
155                 protected internal override TimeSpan DefaultOpenTimeout {
156                         get { return timeouts.OpenTimeout; }
157                 }
158
159                 public Collection<IErrorHandler> ErrorHandlers {
160                         get { return error_handlers; }
161                 }
162
163                 public SynchronizedCollection<EndpointDispatcher> Endpoints {
164                         get { return endpoints; }
165                 }
166
167                 [MonoTODO]
168                 public bool IsTransactedAccept {
169                         get { throw new NotImplementedException (); }
170                 }
171
172                 public bool IsTransactedReceive {
173                         get { return is_tx_receive; }
174                         set { is_tx_receive = value; }
175                 }
176
177                 public bool ManualAddressing {
178                         get { return manual_addressing; }
179                         set { manual_addressing = value; }
180                 }
181
182                 public int MaxTransactedBatchSize {
183                         get { return max_tx_batch_size; }
184                         set { max_tx_batch_size = value; }
185                 }
186
187                 public override ServiceHostBase Host {
188                         get { return host; }
189                 }
190
191                 public override IChannelListener Listener {
192                         get { return listener; }
193                 }
194
195                 public MessageVersion MessageVersion {
196                         get { return message_version; }
197                         set { message_version = value; }
198                 }
199
200                 public bool ReceiveSynchronously {
201                         get { return receive_sync; }
202                         set {
203                                 ThrowIfDisposedOrImmutable ();
204                                 receive_sync = value; 
205                         }
206                 }
207
208                 public bool IncludeExceptionDetailInFaults {
209                         get { return include_exception_detail_in_faults; }
210                         set { include_exception_detail_in_faults = value; }
211                 }
212
213                 public ServiceThrottle ServiceThrottle {
214                         get { return throttle; }
215                         set { throttle = value; }
216                 }
217
218                 public IsolationLevel TransactionIsolationLevel {
219                         get { return tx_isolation_level; }
220                         set { tx_isolation_level = value; }
221                 }
222
223                 public TimeSpan TransactionTimeout {
224                         get { return tx_timeout; }
225                         set { tx_timeout = value; }
226                 }
227
228                 protected internal override void Attach (ServiceHostBase host)
229                 {
230                         this.host = host;
231                         var bl = listener as IChannelDispatcherBoundListener;
232                         if (bl != null)
233                                 bl.ChannelDispatcher = this;
234                 }
235
236                 public override void CloseInput ()
237                 {
238                         if (loop_manager != null)
239                                 loop_manager.CloseInput ();
240                 }
241
242                 protected internal override void Detach (ServiceHostBase host)
243                 {                       
244                         this.host = null;                       
245                 }
246
247                 protected override void OnAbort ()
248                 {
249                         if (loop_manager != null)
250                                 loop_manager.Stop (TimeSpan.FromTicks (1));
251                 }
252
253                 Action<TimeSpan> open_delegate;
254                 Action<TimeSpan> close_delegate;
255
256                 protected override IAsyncResult OnBeginClose (TimeSpan timeout,
257                         AsyncCallback callback, object state)
258                 {
259                         if (close_delegate == null)
260                                 close_delegate = new Action<TimeSpan> (OnClose);
261                         return close_delegate.BeginInvoke (timeout, callback, state);
262                 }
263
264                 protected override IAsyncResult OnBeginOpen (TimeSpan timeout,
265                         AsyncCallback callback, object state)
266                 {
267                         if (open_delegate == null)
268                                 open_delegate = new Action<TimeSpan> (OnOpen);
269                         return open_delegate.BeginInvoke (timeout, callback, state);
270                 }
271
272                 protected override void OnClose (TimeSpan timeout)
273                 {
274                         if (loop_manager != null)
275                                 loop_manager.Stop (timeout);
276                 }
277
278                 protected override void OnClosed ()
279                 {
280                         if (host != null)
281                                 host.ChannelDispatchers.Remove (this);
282                         base.OnClosed ();
283                 }
284
285                 protected override void OnEndClose (IAsyncResult result)
286                 {
287                         close_delegate.EndInvoke (result);
288                 }
289
290                 protected override void OnEndOpen (IAsyncResult result)
291                 {
292                         open_delegate.EndInvoke (result);
293                 }
294
295                 protected override void OnOpen (TimeSpan timeout)
296                 {
297                         if (Host == null || MessageVersion == null)
298                                 throw new InvalidOperationException ("Service host is not attached to this ChannelDispatcher.");
299
300                         loop_manager.Setup (timeout);
301                 }
302
303                 protected override void OnOpening ()
304                 {
305                         base.OnOpening ();
306                         loop_manager = new ListenerLoopManager (this);
307                 }
308
309                 protected override void OnOpened ()
310                 {
311                         base.OnOpened ();
312                         StartLoop ();
313                 }
314
315                 void StartLoop ()
316                 {
317                         // FIXME: not sure if it should be filled here.
318                         if (ServiceThrottle == null)
319                                 ServiceThrottle = new ServiceThrottle (this);
320
321                         loop_manager.Start ();
322                 }
323         }
324
325                 // isolated from ChannelDispatcher
326                 class ListenerLoopManager
327                 {
328                         ChannelDispatcher owner;
329                         AutoResetEvent throttle_wait_handle = new AutoResetEvent (false);
330                         AutoResetEvent creator_handle = new AutoResetEvent (false);
331                         ManualResetEvent stop_handle = new ManualResetEvent (false);
332                         bool loop;
333                         Thread loop_thread;
334                         DateTime close_started;
335                         TimeSpan close_timeout;
336                         Func<IAsyncResult> channel_acceptor;
337                         List<IChannel> channels = new List<IChannel> ();
338                         AddressFilterMode address_filter_mode;
339                         List<ISession> sessions = new List<ISession> ();
340
341                         public ListenerLoopManager (ChannelDispatcher owner)
342                         {
343                                 this.owner = owner;
344                                 var sba = owner.Host != null ? owner.Host.Description.Behaviors.Find<ServiceBehaviorAttribute> () : null;
345                                 if (sba != null)
346                                         address_filter_mode = sba.AddressFilterMode;
347                         }
348
349                         public void Setup (TimeSpan openTimeout)
350                         {
351                                 if (owner.Listener.State != CommunicationState.Created)
352                                         throw new InvalidOperationException ("Tried to open the channel listener which is bound to ChannelDispatcher, but it is not at Created state");
353                                 owner.Listener.Open (openTimeout);
354
355                                 // It is tested at Open(), but strangely it is not instantiated at this point.
356                                 foreach (var ed in owner.Endpoints)
357                                         if (ed.DispatchRuntime.InstanceContextProvider == null && (ed.DispatchRuntime.Type == null || ed.DispatchRuntime.Type.GetConstructor (Type.EmptyTypes) == null))
358                                                 throw new InvalidOperationException ("There is no default constructor for the service Type in the DispatchRuntime");
359                                 SetupChannelAcceptor ();
360                         }
361
362                         public void Start ()
363                         {
364                                 if (loop_thread == null)
365                                         loop_thread = new Thread (new ThreadStart (Loop));
366                                 loop_thread.Start ();
367                         }
368
369                         Func<IAsyncResult> CreateAcceptor<TChannel> (IChannelListener l) where TChannel : class, IChannel
370                         {
371                                 IChannelListener<TChannel> r = l as IChannelListener<TChannel>;
372                                 if (r == null)
373                                         return null;
374                                 AsyncCallback callback = delegate (IAsyncResult result) {
375                                         try {
376                                                 ChannelAccepted (r.EndAcceptChannel (result));
377                                         } catch (Exception ex) {
378                                                 Logger.Error ("Exception during finishing channel acceptance.", ex);
379                                                 creator_handle.Set ();
380                                         }
381                                 };
382                                 return delegate {
383                                         try {
384                                                 return r.BeginAcceptChannel (callback, null);
385                                         } catch (Exception ex) {
386                                                 Logger.Error ("Exception during accepting channel.", ex);
387                                                 throw;
388                                         }
389                                 };
390                         }
391
392                         void SetupChannelAcceptor ()
393                         {
394                                 var l = owner.Listener;
395                                 channel_acceptor =
396                                         CreateAcceptor<IReplyChannel> (l) ??
397                                         CreateAcceptor<IReplySessionChannel> (l) ??
398                                         CreateAcceptor<IInputChannel> (l) ??
399                                         CreateAcceptor<IInputSessionChannel> (l) ??
400                                         CreateAcceptor<IDuplexChannel> (l) ??
401                                         CreateAcceptor<IDuplexSessionChannel> (l);
402                                 if (channel_acceptor == null)
403                                         throw new InvalidOperationException (String.Format ("Unrecognized channel listener type: {0}", l.GetType ()));
404                         }
405
406                         public void Stop (TimeSpan timeout)
407                         {
408                                 if (loop_thread == null)
409                                         return;
410
411                                 close_started = DateTime.Now;
412                                 close_timeout = timeout;
413                                 loop = false;
414                                 creator_handle.Set ();
415                                 throttle_wait_handle.Set (); // break primary loop
416                                 if (stop_handle != null) {
417                                         stop_handle.WaitOne (timeout > TimeSpan.Zero ? timeout : TimeSpan.FromTicks (1));
418                                         stop_handle.Close ();
419                                         stop_handle = null;
420                                 }
421                                 if (owner.Listener.State != CommunicationState.Closed) {
422                                         Logger.Warning (String.Format ("Channel listener '{0}' is not closed. Aborting.", owner.Listener.GetType ()));
423                                         owner.Listener.Abort ();
424                                 }
425                                 if (loop_thread != null && loop_thread.IsAlive)
426                                         loop_thread.Abort ();
427                                 loop_thread = null;
428                         }
429
430                         void AddChannel (IChannel ch)
431                         {
432                                 channels.Add (ch);
433                                 var ich = ch as ISessionChannel<IInputSession>;
434                                 if (ich != null && ich.Session != null) {
435                                         lock (sessions) {
436                                                 var session = sessions.FirstOrDefault (s => s.Id == ich.Session.Id);
437                                                 if (session == null)
438                                                         sessions.Add (session);
439                                         }
440                                 }
441                         }
442
443                         void RemoveChannel (IChannel ch)
444                         {
445                                 channels.Remove (ch); // zonbie, if exists
446                                 var ich = ch as ISessionChannel<IInputSession>;
447                                 
448                                 if (ich != null && ich.Session != null)
449                                         sessions.Remove (ich.Session);
450                         }
451
452                         public void CloseInput ()
453                         {
454                                 foreach (var ch in channels.ToArray ()) {
455                                         if (ch.State == CommunicationState.Closed)
456                                                 RemoveChannel (ch);
457                                         else {
458                                                 try {
459                                                         ch.Close (close_timeout - (DateTime.Now - close_started));
460                                                 } catch (Exception ex) {
461                                                         // FIXME: log it.
462                                                         Logger.Error (String.Format ("Exception on closing channel ({0})", ch.GetType ()), ex);
463                                                         ch.Abort ();
464                                                 }
465                                         }
466                                 }
467                         }
468
469                         void Loop ()
470                         {
471                                 try {
472                                         LoopCore ();
473                                 } catch (Exception ex) {
474                                         Logger.Error (String.Format ("ListenerLoopManager caught an exception inside dispatcher loop, which is likely thrown by the channel listener {0}", owner.Listener), ex);
475                                 } finally {
476                                         if (stop_handle != null)
477                                                 stop_handle.Set ();
478                                 }
479                         }
480
481                         void LoopCore ()
482                         {
483                                 loop = true;
484
485                                 // FIXME: use WaitForChannel() for (*only* for) transacted channel listeners.
486                                 // http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/3faa4a5e-8602-4dbe-a181-73b3f581835e
487
488                                 while (loop) {
489                                         // FIXME: take MaxConcurrentCalls into consideration appropriately.
490                                         while (loop && channels.Count < Math.Min (owner.ServiceThrottle.MaxConcurrentSessions, owner.ServiceThrottle.MaxConcurrentCalls)) {
491                                                 // FIXME: this should not be required, but saves multi-ChannelDispatcher case (Throttling enabled) for HTTP standalone listener...
492                                                 Thread.Sleep (100);
493                                                 channel_acceptor ();
494                                                 creator_handle.WaitOne (); // released by ChannelAccepted()
495                                         }
496                                         if (!loop)
497                                                 break;
498                                         throttle_wait_handle.WaitOne (); // released by IChannel.Close()
499                                 }
500                                 try {
501                                         owner.Listener.Close ();
502                                 } catch (Exception ex) {
503                                         Logger.Error (String.Format ("Exception while closing IChannelListener ({0})", owner.Listener.GetType ()), ex);
504                                 } finally {
505                                         // make sure to close both listener and channels.
506                                         owner.CloseInput ();
507                                 }
508                         }
509
510                         void ChannelAccepted (IChannel ch)
511                         {
512                         try {
513                                 if (ch == null) // could happen when it was aborted
514                                         return;
515                                 if (!loop) {
516                                         var dis = ch as IDisposable;
517                                         if (dis != null)
518                                                 dis.Dispose ();
519                                         return;
520                                 }
521
522                                 lock (channels)
523                                         AddChannel (ch);
524                                 ch.Opened += delegate {
525                                         ch.Faulted += delegate {
526                                                 lock (channels)
527                                                         if (channels.Contains (ch))
528                                                                 RemoveChannel (ch);
529                                                 throttle_wait_handle.Set (); // release loop wait lock.
530                                                 };
531                                         ch.Closed += delegate {
532                                                 lock (channels)
533                                                         if (channels.Contains (ch))
534                                                                 RemoveChannel (ch);
535                                                 throttle_wait_handle.Set (); // release loop wait lock.
536                                                 };
537                                         };
538                                 ch.Open ();
539                         } finally {
540                                 creator_handle.Set ();
541                         }
542
543                                 ProcessRequestOrInput (ch);
544                         }
545
546                         void ProcessRequestOrInput (IChannel ch)
547                         {
548                                 var reply = ch as IReplyChannel;
549                                 var input = ch as IInputChannel;
550
551                                 if (reply != null) {
552                                         if (owner.ReceiveSynchronously) {
553                                                 RequestContext rc;
554                                                 if (reply.TryReceiveRequest (owner.timeouts.ReceiveTimeout, out rc))
555                                                         ProcessRequest (reply, rc);
556                                         } else {
557                                                 reply.BeginTryReceiveRequest (owner.timeouts.ReceiveTimeout, TryReceiveRequestDone, reply);
558                                         }
559                                 } else if (input != null) {
560                                         if (owner.ReceiveSynchronously) {
561                                                 Message msg;
562                                                 if (input.TryReceive (owner.timeouts.ReceiveTimeout, out msg))
563                                                         ProcessInput (input, msg);
564                                         } else {
565                                                 input.BeginTryReceive (owner.timeouts.ReceiveTimeout, TryReceiveDone, input);
566                                         }
567                                 }
568                         }
569
570                         void TryReceiveRequestDone (IAsyncResult result)
571                         {
572                                 RequestContext rc;
573                                 var reply = (IReplyChannel) result.AsyncState;
574                                 if (reply.EndTryReceiveRequest (result, out rc))
575                                         ProcessRequest (reply, rc);
576                                 else
577                                         reply.Close ();
578                         }
579
580                         void TryReceiveDone (IAsyncResult result)
581                         {
582                                 Message msg;
583                                 var input = (IInputChannel) result.AsyncState;
584                                 if (input.EndTryReceive (result, out msg))
585                                         ProcessInput (input, msg);
586                                 else
587                                         input.Close ();
588                         }
589
590                         void ProcessRequest (IReplyChannel reply, RequestContext rc)
591                         {
592                                 try {
593                                         var req = rc.RequestMessage;
594                                         var ed = FindEndpointDispatcher (req);
595                                         new InputOrReplyRequestProcessor (ed.DispatchRuntime, reply).ProcessReply (rc);
596                                 } catch (Exception ex) {
597                                         Message res;
598                                         if (ProcessErrorWithHandlers (reply, ex, out res))
599                                                 return;
600
601                                         rc.Reply (res);
602                                         
603                                         reply.Close (owner.DefaultCloseTimeout); // close the channel
604                                 } finally {
605                                         if (rc != null)
606                                                 rc.Close ();
607                                         // unless it is closed by session/call manager, move it back to the loop to receive the next message.
608                                         if (loop && reply.State != CommunicationState.Closed)
609                                                 ProcessRequestOrInput (reply);
610                                 }
611                         }
612
613                         bool ProcessErrorWithHandlers (IChannel ch, Exception ex, out Message res)
614                         {
615                                 res = null;
616
617                                 foreach (var eh in owner.ErrorHandlers)
618                                         if (eh.HandleError (ex))
619                                                 return true; // error is handled appropriately.
620
621                                 Logger.Error ("An error occured, to be handled", ex);
622
623                                 foreach (var eh in owner.ErrorHandlers)
624                                         eh.ProvideFault (ex, owner.MessageVersion, ref res);
625                                 if (res == null) {
626                                         var conv = ch.GetProperty<FaultConverter> () ?? FaultConverter.GetDefaultFaultConverter (owner.MessageVersion);
627                                         if (!conv.TryCreateFaultMessage (ex, out res))
628                                                 res = Message.CreateMessage (owner.MessageVersion, new FaultCode ("Receiver"), ex.Message, owner.MessageVersion.Addressing.FaultNamespace);
629                                 }
630
631                                 return false;
632                         }
633
634                         void ProcessInput (IInputChannel input, Message message)
635                         {
636                                 try {
637                                         EndpointDispatcher candidate = null;
638                                         candidate = FindEndpointDispatcher (message);
639                                         new InputOrReplyRequestProcessor (candidate.DispatchRuntime, input).ProcessInput (message);
640                                 }
641                                 catch (Exception ex) {
642                                         Message dummy;
643                                         ProcessErrorWithHandlers (input, ex, out dummy);
644                                 } finally {
645                                         // unless it is closed by session/call manager, move it back to the loop to receive the next message.
646                                         if (loop && input.State != CommunicationState.Closed)
647                                                 ProcessRequestOrInput (input);
648                                 }
649                         }
650
651                         EndpointDispatcher FindEndpointDispatcher (Message message) {
652                                 EndpointDispatcher candidate = null;
653                                 bool hasEndpointMatch = false;
654                                 foreach (var endpoint in owner.Endpoints) {
655                                         if (endpoint.AddressFilter.Match (message)) {
656                                                 hasEndpointMatch = true;
657                                                 if (!endpoint.ContractFilter.Match (message))
658                                                         continue;
659                                                 var newdis = endpoint;
660                                                 if (candidate == null || candidate.FilterPriority < newdis.FilterPriority)
661                                                         candidate = newdis;
662                                                 else if (candidate.FilterPriority == newdis.FilterPriority)
663                                                         throw new MultipleFilterMatchesException ();
664                                         }
665                                 }
666                                 if (candidate == null && !hasEndpointMatch) {
667                                         if (owner.Host != null)
668                                                 owner.Host.OnUnknownMessageReceived (message);
669                                         // we have to return a fault to the client anyways...
670                                         throw new EndpointNotFoundException ();
671                                 }
672                                 else if (candidate == null)
673                                         // FIXME: It is not a good place to check, but anyways detach this error from EndpointNotFoundException.
674                                         throw new ActionNotSupportedException (String.Format ("Action '{0}' did not match any operations in the target contract", message.Headers.Action));
675
676                                 return candidate;
677                         }
678                 }
679 }