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