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