Ref parameter was not covered by ParameterInfo.IsOut. Fixed bug #696784.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ServiceHostBase.cs
1 //
2 // ServiceHostBase.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006 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.ServiceModel.Channels;
33 using System.ServiceModel.Configuration;
34 using System.ServiceModel.Description;
35 using System.ServiceModel.Dispatcher;
36 using System.ServiceModel.Security;
37 using System.Reflection;
38 using System.Threading;
39
40 namespace System.ServiceModel
41 {
42         public abstract partial class ServiceHostBase
43                 : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
44         {
45                 // It is used for mapping a ServiceHostBase to HttpChannelListener precisely.
46                 internal static ServiceHostBase CurrentServiceHostHack;
47
48                 ServiceCredentials credentials;
49                 ServiceDescription description;
50                 UriSchemeKeyedCollection base_addresses;
51                 TimeSpan open_timeout, close_timeout, instance_idle_timeout;
52                 List<InstanceContext> contexts;
53                 ReadOnlyCollection<InstanceContext> exposed_contexts;
54                 ChannelDispatcherCollection channel_dispatchers;
55                 IDictionary<string,ContractDescription> contracts;
56                 int flow_limit = int.MaxValue;
57                 IExtensionCollection<ServiceHostBase> extensions;
58
59                 protected ServiceHostBase ()
60                 {
61                         open_timeout = DefaultOpenTimeout;
62                         close_timeout = DefaultCloseTimeout;
63
64                         credentials = new ServiceCredentials ();
65                         contexts = new List<InstanceContext> ();
66                         exposed_contexts = new ReadOnlyCollection<InstanceContext> (contexts);
67                         channel_dispatchers = new ChannelDispatcherCollection (this);
68                 }
69
70                 public event EventHandler<UnknownMessageReceivedEventArgs>
71                         UnknownMessageReceived;
72
73                 internal void OnUnknownMessageReceived (Message message)
74                 {
75                         if (UnknownMessageReceived != null)
76                                 UnknownMessageReceived (this, new UnknownMessageReceivedEventArgs (message));
77                         else
78                                 // FIXME: better be logged
79                                 throw new EndpointNotFoundException (String.Format ("The request message has the target '{0}' with action '{1}' which is not reachable in this service contract", message.Headers.To, message.Headers.Action));
80                 }
81
82                 public ReadOnlyCollection<Uri> BaseAddresses {
83                         get {
84                                 if (base_addresses == null)
85                                         base_addresses = new UriSchemeKeyedCollection ();
86                                 return new ReadOnlyCollection<Uri> (base_addresses.InternalItems);
87                         }
88                 }
89
90                 internal Uri CreateUri (string scheme, Uri relativeUri)
91                 {
92                         Uri baseUri = base_addresses.Contains (scheme) ? base_addresses [scheme] : null;
93
94                         if (relativeUri == null)
95                                 return baseUri;
96                         if (relativeUri.IsAbsoluteUri)
97                                 return relativeUri;
98                         if (baseUri == null)
99                                 return null;
100                         var s = relativeUri.ToString ();
101                         if (s.Length == 0)
102                                 return baseUri;
103                         var l = baseUri.LocalPath;
104                         var r = relativeUri.ToString ();
105
106                         if (l.Length > 0 && l [l.Length - 1] != '/' && r [0] != '/')
107                                 return new Uri (String.Concat (baseUri.ToString (), "/", r));
108                         else
109                                 return new Uri (String.Concat (baseUri.ToString (), r));
110                 }
111
112                 public ChannelDispatcherCollection ChannelDispatchers {
113                         get { return channel_dispatchers; }
114                 }
115
116                 public ServiceAuthorizationBehavior Authorization {
117                         get;
118                         private set;
119                 }
120
121                 public ServiceCredentials Credentials {
122                         get { return credentials; }
123                 }
124
125                 public ServiceDescription Description {
126                         get { return description; }
127                 }
128
129                 protected internal IDictionary<string,ContractDescription> ImplementedContracts {
130                         get { return contracts; }
131                 }
132
133                 public IExtensionCollection<ServiceHostBase> Extensions {
134                         get {
135                                 if (extensions == null)
136                                         extensions = new ExtensionCollection<ServiceHostBase> (this);
137                                 return extensions;
138                         }
139                 }
140
141                 protected internal override TimeSpan DefaultCloseTimeout {
142                         get { return DefaultCommunicationTimeouts.Instance.CloseTimeout; }
143                 }
144
145                 protected internal override TimeSpan DefaultOpenTimeout {
146                         get { return DefaultCommunicationTimeouts.Instance.OpenTimeout; }
147                 }
148
149                 public TimeSpan CloseTimeout {
150                         get { return close_timeout; }
151                         set { close_timeout = value; }
152                 }
153
154                 public TimeSpan OpenTimeout {
155                         get { return open_timeout; }
156                         set { open_timeout = value; }
157                 }
158
159                 public int ManualFlowControlLimit {
160                         get { return flow_limit; }
161                         set { flow_limit = value; }
162                 }
163
164                 protected void AddBaseAddress (Uri baseAddress)
165                 {
166                         if (base_addresses == null)
167                                 throw new InvalidOperationException ("Base addresses must be added before the service description is initialized");
168                         base_addresses.Add (baseAddress);
169                 }
170
171                 public ServiceEndpoint AddServiceEndpoint (
172                         string implementedContract, Binding binding, string address)
173                 {
174                         return AddServiceEndpoint (implementedContract,
175                                 binding,
176                                 new Uri (address, UriKind.RelativeOrAbsolute));
177                 }
178
179                 public ServiceEndpoint AddServiceEndpoint (
180                         string implementedContract, Binding binding,
181                         string address, Uri listenUri)
182                 {
183                         Uri uri = new Uri (address, UriKind.RelativeOrAbsolute);
184                         return AddServiceEndpoint (
185                                 implementedContract, binding, uri, listenUri);
186                 }
187
188                 public ServiceEndpoint AddServiceEndpoint (
189                         string implementedContract, Binding binding,
190                         Uri address)
191                 {
192                         return AddServiceEndpoint (implementedContract, binding, address, null);
193                 }
194
195                 public ServiceEndpoint AddServiceEndpoint (
196                         string implementedContract, Binding binding,
197                         Uri address, Uri listenUri)
198                 {
199                         EndpointAddress ea = new EndpointAddress (BuildAbsoluteUri (address, binding));
200                         ContractDescription cd = GetContract (implementedContract, binding.Namespace == "http://schemas.microsoft.com/ws/2005/02/mex/bindings");
201                         if (cd == null)
202                                 throw new InvalidOperationException (String.Format ("Contract '{0}' was not found in the implemented contracts in this service host.", implementedContract));
203                         return AddServiceEndpointCore (cd, binding, ea, listenUri);
204                 }
205
206 #if NET_4_0
207                 public virtual void AddServiceEndpoint (ServiceEndpoint endpoint)
208                 {
209                         if (endpoint == null)
210                                 throw new ArgumentNullException ("endpoint");
211
212                         ThrowIfDisposedOrImmutable ();
213
214                         if (endpoint.Address == null)
215                                 throw new ArgumentException ("Address on the argument endpoint is null");
216                         if (endpoint.Contract == null)
217                                 throw new ArgumentException ("Contract on the argument endpoint is null");
218                         if (endpoint.Binding == null)
219                                 throw new ArgumentException ("Binding on the argument endpoint is null");
220
221                         if (!ImplementedContracts.Values.Any (cd => cd.ContractType == endpoint.Contract.ContractType) &&
222                             endpoint.Binding.Namespace != "http://schemas.microsoft.com/ws/2005/02/mex/bindings") // special case
223                                 throw new InvalidOperationException (String.Format ("Contract '{0}' is not implemented in this service '{1}'", endpoint.Contract.Name, Description.Name));
224
225                         Description.Endpoints.Add (endpoint);
226                 }
227 #endif
228
229                 Type PopulateType (string typeName)
230                 {
231                         Type type = Type.GetType (typeName);
232                         if (type != null)
233                                 return type;
234                         foreach (ContractDescription cd in ImplementedContracts.Values) {
235                                 type = cd.ContractType.Assembly.GetType (typeName);
236                                 if (type != null)
237                                         return type;
238                         }
239                         return null;
240                 }
241
242                 ContractDescription mex_contract, help_page_contract;
243
244                 ContractDescription GetContract (string name, bool mexBinding)
245                 {
246                         // FIXME: not sure if they should really be special cases.
247                         switch (name) {
248                         case "IHttpGetHelpPageAndMetadataContract":
249                                 if (help_page_contract == null)
250                                         help_page_contract = ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract));
251                                 return help_page_contract;
252                         case "IMetadataExchange":
253                                 // this is certainly looking special (or we may 
254                                 // be missing something around ServiceMetadataExtension).
255                                 // It seems .NET WCF has some "infrastructure"
256                                 // endpoints. .NET ServiceHost fails to Open()
257                                 // if it was added only IMetadataExchange 
258                                 // endpoint (and you'll see the word
259                                 // "infrastructure" in the exception message).
260                                 if (mexBinding && Description.Behaviors.Find<ServiceMetadataBehavior> () == null)
261                                         break;
262                                 if (mex_contract == null)
263                                         mex_contract = ContractDescription.GetContract (typeof (IMetadataExchange));
264                                 return mex_contract;
265                         }
266
267                         Type type = PopulateType (name);
268                         if (type == null)
269                                 return null;
270
271                         foreach (ContractDescription cd in ImplementedContracts.Values) {
272                                 // This check is a negative side effect of the above match-by-name design.
273                                 if (cd.ContractType == typeof (IMetadataExchange))
274                                         continue;
275
276                                 if (cd.ContractType == type ||
277                                     cd.ContractType.IsSubclassOf (type) ||
278                                     type.IsInterface && cd.ContractType.GetInterface (type.FullName) == type)
279                                         return cd;
280                         }
281                         return null;
282                 }
283
284                 internal Uri BuildAbsoluteUri (Uri address, Binding binding)
285                 {
286                         if (!address.IsAbsoluteUri) {
287                                 // Find a Base address with matching scheme,
288                                 // and build new absolute address
289                                 if (!base_addresses.Contains (binding.Scheme))
290                                         throw new InvalidOperationException (String.Format ("Could not find base address that matches Scheme {0} for endpoint {1}", binding.Scheme, binding.Name));
291
292                                 Uri baseaddr = base_addresses [binding.Scheme];
293
294                                 if (!baseaddr.AbsoluteUri.EndsWith ("/") && address.OriginalString.Length > 0) // with empty URI it should not add '/' to possible file name of the absolute URI
295                                         baseaddr = new Uri (baseaddr.AbsoluteUri + "/");
296                                 address = new Uri (baseaddr, address);
297                         }
298                         return address;
299                 }
300
301                 internal ServiceEndpoint AddServiceEndpointCore (
302                         ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri)
303                 {
304                         if (listenUri != null)
305                                 listenUri = BuildAbsoluteUri (listenUri, binding);
306
307                         foreach (ServiceEndpoint e in Description.Endpoints)
308                                 if (e.Contract == cd && e.Binding == binding && e.Address == address && e.ListenUri.Equals (listenUri))
309                                         return e;
310                         ServiceEndpoint se = new ServiceEndpoint (cd, binding, address);
311                         // FIXME: should we reject relative ListenUri?
312                         se.ListenUri = listenUri ?? address.Uri;
313                         Description.Endpoints.Add (se);
314                         return se;
315                 }
316
317                 protected virtual void ApplyConfiguration ()
318                 {
319                         if (Description == null)
320                                 throw new InvalidOperationException ("ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation");
321
322                         ServiceElement service = GetServiceElement ();
323                         
324                         if (service != null)
325                                 LoadConfigurationSection (service);
326 #if NET_4_0
327                         // simplified configuration
328                         AddServiceBehaviors (String.Empty, false);
329 #endif
330                         // TODO: consider commonBehaviors here
331
332                         // ensure ServiceAuthorizationBehavior
333                         Authorization = Description.Behaviors.Find<ServiceAuthorizationBehavior> ();
334                         if (Authorization == null) {
335                                 Authorization = new ServiceAuthorizationBehavior ();
336                                 Description.Behaviors.Add (Authorization);
337                         }
338
339                         // ensure ServiceDebugBehavior
340                         ServiceDebugBehavior debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior> ();
341                         if (debugBehavior == null) {
342                                 debugBehavior = new ServiceDebugBehavior ();
343                                 Description.Behaviors.Add (debugBehavior);
344                         }
345                 }
346
347                 void AddServiceBehaviors (string configurationName, bool throwIfNotFound)
348                 {
349 #if NET_4_0
350                         if (configurationName == null)
351                                 return;
352 #else
353                         if (String.IsNullOrEmpty (configurationName))
354                                 return;
355 #endif
356                         ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors [configurationName];
357                         if (behavior == null) {
358                                 if (throwIfNotFound)
359                                         throw new ArgumentException (String.Format ("Service behavior configuration '{0}' was not found", configurationName));
360                                 return;
361                         }
362
363                         KeyedByTypeCollection<IServiceBehavior> behaviors = Description.Behaviors;
364                         foreach (var bxe in behavior) {
365                                 IServiceBehavior b = (IServiceBehavior) bxe.CreateBehavior ();
366                                 if (behaviors.Contains (b.GetType ()))
367                                         continue;
368                                 behaviors.Add (b);
369                         }
370                 }
371                 
372                 void ApplyServiceElement (ServiceElement service)
373                 {
374                         //base addresses
375                         HostElement host = service.Host;
376                         foreach (BaseAddressElement baseAddress in host.BaseAddresses) {
377                                 AddBaseAddress (new Uri (baseAddress.BaseAddress));
378                         }
379
380                         // behaviors
381                         AddServiceBehaviors (service.BehaviorConfiguration, true);
382
383                         // endpoints
384                         foreach (ServiceEndpointElement endpoint in service.Endpoints) {
385                                 ServiceEndpoint se;
386
387 #if NET_4_0
388                                 var binding = String.IsNullOrEmpty (endpoint.Binding) ? null : ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration);
389
390                                 if (!String.IsNullOrEmpty (endpoint.Kind)) {
391                                         var contract = String.IsNullOrEmpty (endpoint.Contract) ? null : GetContract (endpoint.Contract, false);
392                                         se = ConfigUtil.ConfigureStandardEndpoint (contract, endpoint);
393                                         if (se.Binding == null)
394                                                 se.Binding = binding;
395                                         if (se.Address == null && se.Binding != null) // standard endpoint might have empty address
396                                                 se.Address = new EndpointAddress (CreateUri (se.Binding.Scheme, endpoint.Address));
397                                         if (se.Binding == null && se.Address != null) // look for protocol mapping
398                                                 se.Binding = ConfigUtil.GetBindingByProtocolMapping (se.Address.Uri);
399
400                                         AddServiceEndpoint (se);
401                                 }
402                                 else {
403                                         if (binding == null && endpoint.Address != null) // look for protocol mapping
404                                                 binding = ConfigUtil.GetBindingByProtocolMapping (endpoint.Address);
405                                         se = AddServiceEndpoint (endpoint.Contract, binding, endpoint.Address);
406                                 }
407 #else
408                                 var binding = ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration);
409                                 se = AddServiceEndpoint (endpoint.Contract, binding, endpoint.Address);
410 #endif
411
412                                 // endpoint behaviors
413                                 EndpointBehaviorElement epbehavior = ConfigUtil.BehaviorsSection.EndpointBehaviors [endpoint.BehaviorConfiguration];
414                                 if (epbehavior != null)
415                                         foreach (var bxe in epbehavior) {
416                                                 IEndpointBehavior b = (IEndpointBehavior) bxe.CreateBehavior ();
417                                                 se.Behaviors.Add (b);
418                                 }
419                         }
420                 }
421
422                 private ServiceElement GetServiceElement() {
423                         Type serviceType = Description.ServiceType;
424                         if (serviceType == null)
425                                 return null;
426
427                         return ConfigUtil.ServicesSection.Services [serviceType.FullName];                      
428                 }
429
430                 protected abstract ServiceDescription CreateDescription (
431                         out IDictionary<string,ContractDescription> implementedContracts);
432
433                 protected void InitializeDescription (UriSchemeKeyedCollection baseAddresses)
434                 {
435                         this.base_addresses = baseAddresses;
436                         IDictionary<string,ContractDescription> retContracts;
437                         description = CreateDescription (out retContracts);
438                         contracts = retContracts;
439
440                         ApplyConfiguration ();
441                 }
442
443                 protected virtual void InitializeRuntime ()
444                 {
445                         //First validate the description, which should call all behaviors
446                         //'Validate' method.
447                         ValidateDescription ();
448                         
449                         //Build all ChannelDispatchers, one dispatcher per user configured EndPoint.
450                         //We must keep thet ServiceEndpoints as a seperate collection, since the user
451                         //can change the collection in the description during the behaviors events.
452                         ServiceEndpoint[] endPoints = new ServiceEndpoint[Description.Endpoints.Count];
453                         Description.Endpoints.CopyTo (endPoints, 0);
454                         var builder = new DispatcherBuilder (this);
455                         foreach (ServiceEndpoint se in endPoints) {
456                                 var commonParams = new BindingParameterCollection ();
457                                 foreach (IServiceBehavior b in Description.Behaviors)
458                                         b.AddBindingParameters (Description, this, Description.Endpoints, commonParams);
459
460                                 var channel = builder.BuildChannelDispatcher (Description.ServiceType, se, commonParams);
461                                 if (!ChannelDispatchers.Contains (channel))
462                                         ChannelDispatchers.Add (channel);
463                         }
464
465                         //After the ChannelDispatchers are created, and attached to the service host
466                         //Apply dispatching behaviors.
467                         //
468                         // This behavior application order is tricky: first only
469                         // ServiceDebugBehavior and ServiceMetadataBehavior are
470                         // applied, and then other service behaviors are applied.
471                         // It is because those two behaviors adds ChannelDispatchers
472                         // and any other service behaviors must be applied to
473                         // those newly populated dispatchers.
474                         foreach (IServiceBehavior b in Description.Behaviors)
475                                 if (b is ServiceMetadataBehavior || b is ServiceDebugBehavior)
476                                         b.ApplyDispatchBehavior (Description, this);
477                         foreach (IServiceBehavior b in Description.Behaviors)
478                                 if (!(b is ServiceMetadataBehavior || b is ServiceDebugBehavior))
479                                         b.ApplyDispatchBehavior (Description, this);
480
481                         builder.ApplyDispatchBehaviors ();
482                 }
483
484                 private void ValidateDescription ()
485                 {
486                         foreach (IServiceBehavior b in Description.Behaviors)
487                                 b.Validate (Description, this);
488                         foreach (ServiceEndpoint endPoint in Description.Endpoints)
489                                 endPoint.Validate ();
490
491 #if NET_4_0
492                         // In 4.0, it seems that if there is no configured ServiceEndpoint, infer them from the service type.
493                         if (Description.Endpoints.Count == 0) {
494                                 foreach (Type iface in Description.ServiceType.GetInterfaces ())
495                                         if (iface.GetCustomAttributes (typeof (ServiceContractAttribute), true).Length > 0)
496                                                 foreach (var baddr in BaseAddresses) {
497                                                         if (!baddr.IsAbsoluteUri)
498                                                                 continue;
499                                                         var binding = ConfigUtil.GetBindingByProtocolMapping (baddr);
500                                                         if (binding == null)
501                                                                 continue;
502                                                         AddServiceEndpoint (iface.FullName, binding, baddr);
503                                                 }
504                         }
505 #endif
506
507                         if (Description.Endpoints.FirstOrDefault (e => e.Contract != mex_contract && !e.IsSystemEndpoint) == null)
508                                 throw new InvalidOperationException ("The ServiceHost must have at least one application endpoint (that does not include metadata exchange endpoint) defined by either configuration, behaviors or call to AddServiceEndpoint methods.");
509                 }
510
511                 protected void LoadConfigurationSection (ServiceElement element)
512                 {
513                         ApplyServiceElement (element);
514                 }
515
516                 protected override sealed void OnAbort ()
517                 {
518                         OnCloseOrAbort (TimeSpan.Zero);
519                 }
520
521                 Action<TimeSpan> close_delegate;
522                 Action<TimeSpan> open_delegate;
523
524                 protected override sealed IAsyncResult OnBeginClose (
525                         TimeSpan timeout, AsyncCallback callback, object state)
526                 {
527                         if (close_delegate != null)
528                                 close_delegate = new Action<TimeSpan> (OnClose);
529                         return close_delegate.BeginInvoke (timeout, callback, state);
530                 }
531
532                 protected override sealed IAsyncResult OnBeginOpen (
533                         TimeSpan timeout, AsyncCallback callback, object state)
534                 {
535                         if (open_delegate == null)
536                                 open_delegate = new Action<TimeSpan> (OnOpen);
537                         return open_delegate.BeginInvoke (timeout, callback, state);
538                 }
539
540                 protected override void OnClose (TimeSpan timeout)
541                 {
542                         OnCloseOrAbort (timeout);
543                 }
544                 
545                 void OnCloseOrAbort (TimeSpan timeout)
546                 {
547                         DateTime start = DateTime.Now;
548                         ReleasePerformanceCounters ();
549                         List<ChannelDispatcherBase> l = new List<ChannelDispatcherBase> (ChannelDispatchers);
550                         foreach (ChannelDispatcherBase e in l) {
551                                 try {
552                                         TimeSpan ts = timeout - (DateTime.Now - start);
553                                         if (ts < TimeSpan.Zero)
554                                                 e.Abort ();
555                                         else
556                                                 e.Close (ts);
557                                 } catch (Exception ex) {
558                                         Console.WriteLine ("ServiceHostBase failed to close the channel dispatcher:");
559                                         Console.WriteLine (ex);
560                                 }
561                         }
562                 }
563
564                 protected override sealed void OnOpen (TimeSpan timeout)
565                 {
566                         DateTime start = DateTime.Now;
567                         InitializeRuntime ();
568                         for (int i = 0; i < ChannelDispatchers.Count; i++) {
569                                 // Skip ServiceMetadataExtension-based one. special case.
570                                 for (int j = i + 1; j < ChannelDispatchers.Count; j++) {
571                                         var cd1 = ChannelDispatchers [i];
572                                         var cd2 = ChannelDispatchers [j];
573                                         if (cd1.IsMex || cd2.IsMex)
574                                                 continue;
575                                         // surprisingly, some ChannelDispatcherBase implementations have null Listener property.
576                                         if (cd1.Listener != null && cd2.Listener != null && cd1.Listener.Uri.Equals (cd2.Listener.Uri))
577                                                 throw new InvalidOperationException ("Two or more service endpoints with different Binding instance are bound to the same listen URI.");
578                                 }
579                         }
580
581                         var waits = new List<ManualResetEvent> ();
582                         foreach (var cd in ChannelDispatchers) {
583                                 var wait = new ManualResetEvent (false);
584                                 cd.Opened += delegate { wait.Set (); };
585                                 waits.Add (wait);
586                                 cd.Open (timeout - (DateTime.Now - start));
587                         }
588
589                         WaitHandle.WaitAll (waits.ToArray ());
590                 }
591
592                 protected override void OnEndClose (IAsyncResult result)
593                 {
594                         if (close_delegate == null)
595                                 throw new InvalidOperationException ("Async close operation has not started");
596                         close_delegate.EndInvoke (result);
597                 }
598
599                 protected override sealed void OnEndOpen (IAsyncResult result)
600                 {
601                         if (open_delegate == null)
602                                 throw new InvalidOperationException ("Aync open operation has not started");
603                         open_delegate.EndInvoke (result);
604                 }
605
606                 protected override void OnOpened ()
607                 {
608                         base.OnOpened ();
609                 }
610
611                 [MonoTODO]
612                 protected void ReleasePerformanceCounters ()
613                 {
614                 }
615
616                 void IDisposable.Dispose ()
617                 {
618                         Close ();
619                 }
620         }
621
622         /// <summary>
623         ///  Builds ChannelDispatchers as appropriate to service the service endpoints. 
624         /// </summary>
625         partial class DispatcherBuilder
626         {
627                 ServiceHostBase host;
628
629                 public DispatcherBuilder (ServiceHostBase host)
630                 {
631                         this.host = host;
632                 }
633
634                 Dictionary<Binding,ChannelDispatcher> built_dispatchers = new Dictionary<Binding,ChannelDispatcher> ();
635                 Dictionary<ServiceEndpoint, EndpointDispatcher> ep_to_dispatcher_ep = new Dictionary<ServiceEndpoint, EndpointDispatcher> ();
636
637                 internal static Action<ChannelDispatcher> ChannelDispatcherSetter;
638
639                 internal ChannelDispatcher BuildChannelDispatcher (Type serviceType, ServiceEndpoint se, BindingParameterCollection commonParams)
640                 {
641                         //Let all behaviors add their binding parameters
642                         AddBindingParameters (commonParams, se);
643                         
644                         // See if there's an existing channel that matches this endpoint
645                         var version = se.Binding.GetProperty<MessageVersion> (commonParams);
646                         if (version == null)
647                                 throw new InvalidOperationException ("At least one BindingElement in the Binding must override GetProperty method to return a MessageVersion and no prior binding element should return null instead of calling GetInnerProperty method on BindingContext.");
648
649                         ChannelDispatcher cd = FindExistingDispatcher (se);
650                         EndpointDispatcher ep;
651                         if (cd != null) {
652                                 ep = cd.InitializeServiceEndpoint (serviceType, se);
653                         } else {
654                                 // Use the binding parameters to build the channel listener and Dispatcher.
655                                 lock (HttpTransportBindingElement.ListenerBuildLock) {
656                                         ServiceHostBase.CurrentServiceHostHack = host;
657                                         IChannelListener lf = BuildListener (se, commonParams);
658                                         cd = new ChannelDispatcher (lf, se.Binding.Name);
659                                         cd.MessageVersion = version;
660                                         if (ChannelDispatcherSetter != null) {
661                                                 ChannelDispatcherSetter (cd);
662                                                 ChannelDispatcherSetter = null;
663                                         }
664                                         ServiceHostBase.CurrentServiceHostHack = null;
665                                 }
666                                 ep = cd.InitializeServiceEndpoint (serviceType, se);
667                                 built_dispatchers.Add (se.Binding, cd);
668                         }
669                         ep_to_dispatcher_ep[se] = ep;
670                         return cd;
671                 }
672                 
673                 ChannelDispatcher FindExistingDispatcher (ServiceEndpoint se)
674                 {
675                         return built_dispatchers.FirstOrDefault ((KeyValuePair<Binding,ChannelDispatcher> p) => se.Binding == p.Key).Value;
676                 }
677
678                 internal void ApplyDispatchBehaviors ()
679                 {
680                         foreach (KeyValuePair<ServiceEndpoint, EndpointDispatcher> val in ep_to_dispatcher_ep)
681                                 ApplyDispatchBehavior (val.Value, val.Key);
682                 }
683                 
684                 private void ApplyDispatchBehavior (EndpointDispatcher ed, ServiceEndpoint endPoint)
685                 {
686                         foreach (IContractBehavior b in endPoint.Contract.Behaviors)
687                                 b.ApplyDispatchBehavior (endPoint.Contract, endPoint, ed.DispatchRuntime);
688                         foreach (IEndpointBehavior b in endPoint.Behaviors)
689                                 b.ApplyDispatchBehavior (endPoint, ed);
690                         foreach (OperationDescription operation in endPoint.Contract.Operations) {
691                                 if (operation.InCallbackContract)
692                                         continue; // irrelevant
693                                 foreach (IOperationBehavior b in operation.Behaviors)
694                                         b.ApplyDispatchBehavior (operation, ed.DispatchRuntime.Operations [operation.Name]);
695                         }
696
697                 }
698
699                 private void AddBindingParameters (BindingParameterCollection commonParams, ServiceEndpoint endPoint) {
700
701                         commonParams.Add (ChannelProtectionRequirements.CreateFromContract (endPoint.Contract));
702
703                         foreach (IContractBehavior b in endPoint.Contract.Behaviors)
704                                 b.AddBindingParameters (endPoint.Contract, endPoint, commonParams);
705                         foreach (IEndpointBehavior b in endPoint.Behaviors)
706                                 b.AddBindingParameters (endPoint, commonParams);
707                         foreach (OperationDescription operation in endPoint.Contract.Operations) {
708                                 foreach (IOperationBehavior b in operation.Behaviors)
709                                         b.AddBindingParameters (operation, commonParams);
710                         }
711                 }
712
713                 static IChannelListener BuildListener (ServiceEndpoint se,
714                         BindingParameterCollection pl)
715                 {
716                         Binding b = se.Binding;
717                         if (b.CanBuildChannelListener<IReplySessionChannel> (pl))
718                                 return b.BuildChannelListener<IReplySessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
719                         if (b.CanBuildChannelListener<IReplyChannel> (pl))
720                                 return b.BuildChannelListener<IReplyChannel> (se.ListenUri, "", se.ListenUriMode, pl);
721                         if (b.CanBuildChannelListener<IInputSessionChannel> (pl))
722                                 return b.BuildChannelListener<IInputSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
723                         if (b.CanBuildChannelListener<IInputChannel> (pl))
724                                 return b.BuildChannelListener<IInputChannel> (se.ListenUri, "", se.ListenUriMode, pl);
725
726                         if (b.CanBuildChannelListener<IDuplexChannel> (pl))
727                                 return b.BuildChannelListener<IDuplexChannel> (se.ListenUri, "", se.ListenUriMode, pl);
728                         if (b.CanBuildChannelListener<IDuplexSessionChannel> (pl))
729                                 return b.BuildChannelListener<IDuplexSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
730                         throw new InvalidOperationException ("None of the listener channel types is supported");
731                 }
732         }
733 }