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