You've already forked linux-packaging-mono
718 lines
26 KiB
C#
718 lines
26 KiB
C#
//
|
|
// ServiceHostBase.cs
|
|
//
|
|
// Author:
|
|
// Atsushi Enomoto <atsushi@ximian.com>
|
|
//
|
|
// Copyright (C) 2005-2006 Novell, Inc. http://www.novell.com
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Configuration;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel.Dispatcher;
|
|
using System.ServiceModel.Security;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
|
|
namespace System.ServiceModel
|
|
{
|
|
public abstract partial class ServiceHostBase
|
|
: CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
|
|
{
|
|
// It is used for mapping a ServiceHostBase to HttpChannelListener precisely.
|
|
internal static ServiceHostBase CurrentServiceHostHack;
|
|
|
|
ServiceCredentials credentials;
|
|
ServiceDescription description;
|
|
UriSchemeKeyedCollection base_addresses;
|
|
TimeSpan open_timeout, close_timeout, instance_idle_timeout;
|
|
List<InstanceContext> contexts;
|
|
ReadOnlyCollection<InstanceContext> exposed_contexts;
|
|
ChannelDispatcherCollection channel_dispatchers;
|
|
IDictionary<string,ContractDescription> contracts;
|
|
int flow_limit = int.MaxValue;
|
|
IExtensionCollection<ServiceHostBase> extensions;
|
|
|
|
protected ServiceHostBase ()
|
|
{
|
|
open_timeout = DefaultOpenTimeout;
|
|
close_timeout = DefaultCloseTimeout;
|
|
|
|
credentials = new ServiceCredentials ();
|
|
contexts = new List<InstanceContext> ();
|
|
exposed_contexts = new ReadOnlyCollection<InstanceContext> (contexts);
|
|
channel_dispatchers = new ChannelDispatcherCollection (this);
|
|
}
|
|
|
|
public event EventHandler<UnknownMessageReceivedEventArgs>
|
|
UnknownMessageReceived;
|
|
|
|
internal void OnUnknownMessageReceived (Message message)
|
|
{
|
|
if (UnknownMessageReceived != null)
|
|
UnknownMessageReceived (this, new UnknownMessageReceivedEventArgs (message));
|
|
else
|
|
// FIXME: better be logged
|
|
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));
|
|
}
|
|
|
|
public ReadOnlyCollection<Uri> BaseAddresses {
|
|
get {
|
|
if (base_addresses == null)
|
|
base_addresses = new UriSchemeKeyedCollection ();
|
|
return new ReadOnlyCollection<Uri> (base_addresses.InternalItems);
|
|
}
|
|
}
|
|
|
|
internal Uri CreateUri (string scheme, Uri relativeUri)
|
|
{
|
|
Uri baseUri = base_addresses.Contains (scheme) ? base_addresses [scheme] : null;
|
|
|
|
if (relativeUri == null)
|
|
return baseUri;
|
|
if (relativeUri.IsAbsoluteUri)
|
|
return relativeUri;
|
|
if (baseUri == null)
|
|
return null;
|
|
var s = relativeUri.ToString ();
|
|
if (s.Length == 0)
|
|
return baseUri;
|
|
var l = baseUri.LocalPath;
|
|
var r = relativeUri.ToString ();
|
|
|
|
if (l.Length > 0 && l [l.Length - 1] != '/' && r [0] != '/')
|
|
return new Uri (String.Concat (baseUri.ToString (), "/", r));
|
|
else
|
|
return new Uri (String.Concat (baseUri.ToString (), r));
|
|
}
|
|
|
|
public ChannelDispatcherCollection ChannelDispatchers {
|
|
get { return channel_dispatchers; }
|
|
}
|
|
|
|
public ServiceAuthorizationBehavior Authorization {
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public ServiceCredentials Credentials {
|
|
get { return credentials; }
|
|
}
|
|
|
|
public ServiceDescription Description {
|
|
get { return description; }
|
|
}
|
|
|
|
protected internal IDictionary<string,ContractDescription> ImplementedContracts {
|
|
get { return contracts; }
|
|
}
|
|
|
|
public IExtensionCollection<ServiceHostBase> Extensions {
|
|
get {
|
|
if (extensions == null)
|
|
extensions = new ExtensionCollection<ServiceHostBase> (this);
|
|
return extensions;
|
|
}
|
|
}
|
|
|
|
protected internal override TimeSpan DefaultCloseTimeout {
|
|
get { return DefaultCommunicationTimeouts.Instance.CloseTimeout; }
|
|
}
|
|
|
|
protected internal override TimeSpan DefaultOpenTimeout {
|
|
get { return DefaultCommunicationTimeouts.Instance.OpenTimeout; }
|
|
}
|
|
|
|
public TimeSpan CloseTimeout {
|
|
get { return close_timeout; }
|
|
set { close_timeout = value; }
|
|
}
|
|
|
|
public TimeSpan OpenTimeout {
|
|
get { return open_timeout; }
|
|
set { open_timeout = value; }
|
|
}
|
|
|
|
public int ManualFlowControlLimit {
|
|
get { return flow_limit; }
|
|
set { flow_limit = value; }
|
|
}
|
|
|
|
protected void AddBaseAddress (Uri baseAddress)
|
|
{
|
|
if (base_addresses == null)
|
|
throw new InvalidOperationException ("Base addresses must be added before the service description is initialized");
|
|
base_addresses.Add (baseAddress);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint (
|
|
string implementedContract, Binding binding, string address)
|
|
{
|
|
return AddServiceEndpoint (implementedContract,
|
|
binding,
|
|
new Uri (address, UriKind.RelativeOrAbsolute));
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint (
|
|
string implementedContract, Binding binding,
|
|
string address, Uri listenUri)
|
|
{
|
|
Uri uri = new Uri (address, UriKind.RelativeOrAbsolute);
|
|
return AddServiceEndpoint (
|
|
implementedContract, binding, uri, listenUri);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint (
|
|
string implementedContract, Binding binding,
|
|
Uri address)
|
|
{
|
|
return AddServiceEndpoint (implementedContract, binding, address, null);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint (
|
|
string implementedContract, Binding binding,
|
|
Uri address, Uri listenUri)
|
|
{
|
|
EndpointAddress ea = new EndpointAddress (BuildAbsoluteUri (address, binding));
|
|
ContractDescription cd = GetContract (implementedContract, binding.Namespace == "http://schemas.microsoft.com/ws/2005/02/mex/bindings");
|
|
if (cd == null)
|
|
throw new InvalidOperationException (String.Format ("Contract '{0}' was not found in the implemented contracts in this service host.", implementedContract));
|
|
return AddServiceEndpointCore (cd, binding, ea, listenUri);
|
|
}
|
|
|
|
public virtual void AddServiceEndpoint (ServiceEndpoint endpoint)
|
|
{
|
|
if (endpoint == null)
|
|
throw new ArgumentNullException ("endpoint");
|
|
|
|
ThrowIfDisposedOrImmutable ();
|
|
|
|
if (endpoint.Address == null)
|
|
throw new ArgumentException ("Address on the argument endpoint is null");
|
|
if (endpoint.Contract == null)
|
|
throw new ArgumentException ("Contract on the argument endpoint is null");
|
|
if (endpoint.Binding == null)
|
|
throw new ArgumentException ("Binding on the argument endpoint is null");
|
|
|
|
if (!ImplementedContracts.Values.Any (cd => cd.ContractType == endpoint.Contract.ContractType) &&
|
|
endpoint.Binding.Namespace != "http://schemas.microsoft.com/ws/2005/02/mex/bindings") // special case
|
|
throw new InvalidOperationException (String.Format ("Contract '{0}' is not implemented in this service '{1}'", endpoint.Contract.Name, Description.Name));
|
|
|
|
Description.Endpoints.Add (endpoint);
|
|
}
|
|
|
|
Type PopulateType (string typeName)
|
|
{
|
|
Type type = Type.GetType (typeName);
|
|
if (type != null)
|
|
return type;
|
|
foreach (ContractDescription cd in ImplementedContracts.Values) {
|
|
type = cd.ContractType.Assembly.GetType (typeName);
|
|
if (type != null)
|
|
return type;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
ContractDescription mex_contract, help_page_contract;
|
|
|
|
ContractDescription GetContract (string name, bool mexBinding)
|
|
{
|
|
// FIXME: not sure if they should really be special cases.
|
|
switch (name) {
|
|
case "IHttpGetHelpPageAndMetadataContract":
|
|
if (help_page_contract == null)
|
|
help_page_contract = ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract));
|
|
return help_page_contract;
|
|
case "IMetadataExchange":
|
|
// this is certainly looking special (or we may
|
|
// be missing something around ServiceMetadataExtension).
|
|
// It seems .NET WCF has some "infrastructure"
|
|
// endpoints. .NET ServiceHost fails to Open()
|
|
// if it was added only IMetadataExchange
|
|
// endpoint (and you'll see the word
|
|
// "infrastructure" in the exception message).
|
|
if (mexBinding && Description.Behaviors.Find<ServiceMetadataBehavior> () == null)
|
|
break;
|
|
if (mex_contract == null)
|
|
mex_contract = ContractDescription.GetContract (typeof (IMetadataExchange));
|
|
return mex_contract;
|
|
}
|
|
|
|
Type type = PopulateType (name);
|
|
if (type == null)
|
|
return null;
|
|
|
|
foreach (ContractDescription cd in ImplementedContracts.Values) {
|
|
// This check is a negative side effect of the above match-by-name design.
|
|
if (cd.ContractType == typeof (IMetadataExchange))
|
|
continue;
|
|
|
|
if (cd.ContractType == type ||
|
|
cd.ContractType.IsSubclassOf (type) ||
|
|
type.IsInterface && cd.ContractType.GetInterface (type.FullName) == type)
|
|
return cd;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal Uri BuildAbsoluteUri (Uri address, Binding binding)
|
|
{
|
|
if (!address.IsAbsoluteUri) {
|
|
// Find a Base address with matching scheme,
|
|
// and build new absolute address
|
|
if (!base_addresses.Contains (binding.Scheme))
|
|
throw new InvalidOperationException (String.Format ("Could not find base address that matches Scheme {0} for endpoint {1}", binding.Scheme, binding.Name));
|
|
|
|
Uri baseaddr = base_addresses [binding.Scheme];
|
|
|
|
if (!baseaddr.AbsoluteUri.EndsWith ("/") && address.OriginalString.Length > 0) // with empty URI it should not add '/' to possible file name of the absolute URI
|
|
baseaddr = new Uri (baseaddr.AbsoluteUri + "/");
|
|
address = new Uri (baseaddr, address);
|
|
}
|
|
return address;
|
|
}
|
|
|
|
internal ServiceEndpoint AddServiceEndpointCore (
|
|
ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri)
|
|
{
|
|
if (listenUri != null)
|
|
listenUri = BuildAbsoluteUri (listenUri, binding);
|
|
|
|
foreach (ServiceEndpoint e in Description.Endpoints)
|
|
if (e.Contract == cd && e.Binding == binding && e.Address == address && e.ListenUri.Equals (listenUri))
|
|
return e;
|
|
ServiceEndpoint se = new ServiceEndpoint (cd, binding, address);
|
|
// FIXME: should we reject relative ListenUri?
|
|
se.ListenUri = listenUri ?? address.Uri;
|
|
Description.Endpoints.Add (se);
|
|
return se;
|
|
}
|
|
|
|
protected virtual void ApplyConfiguration ()
|
|
{
|
|
if (Description == null)
|
|
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");
|
|
|
|
ServiceElement service = GetServiceElement ();
|
|
|
|
if (service != null)
|
|
LoadConfigurationSection (service);
|
|
// simplified configuration
|
|
AddServiceBehaviors (String.Empty, false);
|
|
// TODO: consider commonBehaviors here
|
|
|
|
// ensure ServiceAuthorizationBehavior
|
|
Authorization = Description.Behaviors.Find<ServiceAuthorizationBehavior> ();
|
|
if (Authorization == null) {
|
|
Authorization = new ServiceAuthorizationBehavior ();
|
|
Description.Behaviors.Add (Authorization);
|
|
}
|
|
|
|
// ensure ServiceDebugBehavior
|
|
ServiceDebugBehavior debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior> ();
|
|
if (debugBehavior == null) {
|
|
debugBehavior = new ServiceDebugBehavior ();
|
|
Description.Behaviors.Add (debugBehavior);
|
|
}
|
|
}
|
|
|
|
void AddServiceBehaviors (string configurationName, bool throwIfNotFound)
|
|
{
|
|
if (configurationName == null)
|
|
return;
|
|
ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors [configurationName];
|
|
if (behavior == null) {
|
|
if (throwIfNotFound)
|
|
throw new ArgumentException (String.Format ("Service behavior configuration '{0}' was not found", configurationName));
|
|
return;
|
|
}
|
|
|
|
KeyedByTypeCollection<IServiceBehavior> behaviors = Description.Behaviors;
|
|
foreach (var bxe in behavior) {
|
|
IServiceBehavior b = (IServiceBehavior) bxe.CreateBehavior ();
|
|
if (behaviors.Contains (b.GetType ()))
|
|
continue;
|
|
behaviors.Add (b);
|
|
}
|
|
}
|
|
|
|
void ApplyServiceElement (ServiceElement service)
|
|
{
|
|
//base addresses
|
|
HostElement host = service.Host;
|
|
foreach (BaseAddressElement baseAddress in host.BaseAddresses) {
|
|
AddBaseAddress (new Uri (baseAddress.BaseAddress));
|
|
}
|
|
|
|
// behaviors
|
|
AddServiceBehaviors (service.BehaviorConfiguration, true);
|
|
|
|
// endpoints
|
|
foreach (ServiceEndpointElement endpoint in service.Endpoints) {
|
|
ServiceEndpoint se;
|
|
|
|
var binding = String.IsNullOrEmpty (endpoint.Binding) ? null : ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration);
|
|
|
|
if (!String.IsNullOrEmpty (endpoint.Kind)) {
|
|
var contract = String.IsNullOrEmpty (endpoint.Contract) ? null : GetContract (endpoint.Contract, false);
|
|
se = ConfigUtil.ConfigureStandardEndpoint (contract, endpoint);
|
|
if (se.Binding == null)
|
|
se.Binding = binding;
|
|
if (se.Address == null && se.Binding != null) // standard endpoint might have empty address
|
|
se.Address = new EndpointAddress (CreateUri (se.Binding.Scheme, endpoint.Address));
|
|
if (se.Binding == null && se.Address != null) // look for protocol mapping
|
|
se.Binding = ConfigUtil.GetBindingByProtocolMapping (se.Address.Uri);
|
|
|
|
AddServiceEndpoint (se);
|
|
}
|
|
else {
|
|
if (binding == null && endpoint.Address != null) // look for protocol mapping
|
|
binding = ConfigUtil.GetBindingByProtocolMapping (endpoint.Address);
|
|
se = AddServiceEndpoint (endpoint.Contract, binding, endpoint.Address);
|
|
}
|
|
|
|
// endpoint behaviors
|
|
EndpointBehaviorElement epbehavior = ConfigUtil.BehaviorsSection.EndpointBehaviors [endpoint.BehaviorConfiguration];
|
|
if (epbehavior != null)
|
|
foreach (var bxe in epbehavior) {
|
|
IEndpointBehavior b = (IEndpointBehavior) bxe.CreateBehavior ();
|
|
se.Behaviors.Add (b);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ServiceElement GetServiceElement() {
|
|
Type serviceType = Description.ServiceType;
|
|
if (serviceType == null)
|
|
return null;
|
|
|
|
return ConfigUtil.ServicesSection.Services [serviceType.FullName];
|
|
}
|
|
|
|
protected abstract ServiceDescription CreateDescription (
|
|
out IDictionary<string,ContractDescription> implementedContracts);
|
|
|
|
protected void InitializeDescription (UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
this.base_addresses = baseAddresses;
|
|
IDictionary<string,ContractDescription> retContracts;
|
|
description = CreateDescription (out retContracts);
|
|
contracts = retContracts;
|
|
|
|
ApplyConfiguration ();
|
|
}
|
|
|
|
protected virtual void InitializeRuntime ()
|
|
{
|
|
//First validate the description, which should call all behaviors
|
|
//'Validate' method.
|
|
ValidateDescription ();
|
|
|
|
//Build all ChannelDispatchers, one dispatcher per user configured EndPoint.
|
|
//We must keep thet ServiceEndpoints as a seperate collection, since the user
|
|
//can change the collection in the description during the behaviors events.
|
|
ServiceEndpoint[] endPoints = new ServiceEndpoint[Description.Endpoints.Count];
|
|
Description.Endpoints.CopyTo (endPoints, 0);
|
|
var builder = new DispatcherBuilder (this);
|
|
foreach (ServiceEndpoint se in endPoints) {
|
|
var commonParams = new BindingParameterCollection ();
|
|
foreach (IServiceBehavior b in Description.Behaviors)
|
|
b.AddBindingParameters (Description, this, Description.Endpoints, commonParams);
|
|
|
|
var channel = builder.BuildChannelDispatcher (Description.ServiceType, se, commonParams);
|
|
if (!ChannelDispatchers.Contains (channel))
|
|
ChannelDispatchers.Add (channel);
|
|
}
|
|
|
|
//After the ChannelDispatchers are created, and attached to the service host
|
|
//Apply dispatching behaviors.
|
|
//
|
|
// This behavior application order is tricky: first only
|
|
// ServiceDebugBehavior and ServiceMetadataBehavior are
|
|
// applied, and then other service behaviors are applied.
|
|
// It is because those two behaviors adds ChannelDispatchers
|
|
// and any other service behaviors must be applied to
|
|
// those newly populated dispatchers.
|
|
foreach (IServiceBehavior b in Description.Behaviors)
|
|
if (b is ServiceMetadataBehavior || b is ServiceDebugBehavior)
|
|
b.ApplyDispatchBehavior (Description, this);
|
|
foreach (IServiceBehavior b in Description.Behaviors)
|
|
if (!(b is ServiceMetadataBehavior || b is ServiceDebugBehavior))
|
|
b.ApplyDispatchBehavior (Description, this);
|
|
|
|
builder.ApplyDispatchBehaviors ();
|
|
}
|
|
|
|
private void ValidateDescription ()
|
|
{
|
|
foreach (IServiceBehavior b in Description.Behaviors)
|
|
b.Validate (Description, this);
|
|
foreach (ServiceEndpoint endPoint in Description.Endpoints)
|
|
endPoint.Validate ();
|
|
|
|
// In 4.0, it seems that if there is no configured ServiceEndpoint, infer them from the service type.
|
|
if (Description.Endpoints.Count == 0) {
|
|
foreach (Type iface in Description.ServiceType.GetInterfaces ())
|
|
if (iface.GetCustomAttributes (typeof (ServiceContractAttribute), true).Length > 0)
|
|
foreach (var baddr in BaseAddresses) {
|
|
if (!baddr.IsAbsoluteUri)
|
|
continue;
|
|
var binding = ConfigUtil.GetBindingByProtocolMapping (baddr);
|
|
if (binding == null)
|
|
continue;
|
|
AddServiceEndpoint (iface.FullName, binding, baddr);
|
|
}
|
|
}
|
|
|
|
if (Description.Endpoints.FirstOrDefault (e => e.Contract != mex_contract && !e.IsSystemEndpoint) == null)
|
|
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.");
|
|
}
|
|
|
|
protected void LoadConfigurationSection (ServiceElement serviceSection)
|
|
{
|
|
ApplyServiceElement (serviceSection);
|
|
}
|
|
|
|
protected override sealed void OnAbort ()
|
|
{
|
|
OnCloseOrAbort (TimeSpan.Zero);
|
|
}
|
|
|
|
Action<TimeSpan> close_delegate;
|
|
Action<TimeSpan> open_delegate;
|
|
|
|
protected override sealed IAsyncResult OnBeginClose (
|
|
TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
if (close_delegate != null)
|
|
close_delegate = new Action<TimeSpan> (OnClose);
|
|
return close_delegate.BeginInvoke (timeout, callback, state);
|
|
}
|
|
|
|
protected override sealed IAsyncResult OnBeginOpen (
|
|
TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
if (open_delegate == null)
|
|
open_delegate = new Action<TimeSpan> (OnOpen);
|
|
return open_delegate.BeginInvoke (timeout, callback, state);
|
|
}
|
|
|
|
protected override void OnClose (TimeSpan timeout)
|
|
{
|
|
OnCloseOrAbort (timeout);
|
|
}
|
|
|
|
void OnCloseOrAbort (TimeSpan timeout)
|
|
{
|
|
DateTime start = DateTime.UtcNow;
|
|
ReleasePerformanceCounters ();
|
|
List<ChannelDispatcherBase> l = new List<ChannelDispatcherBase> (ChannelDispatchers);
|
|
foreach (ChannelDispatcherBase e in l) {
|
|
try {
|
|
TimeSpan ts = timeout - (DateTime.UtcNow - start);
|
|
if (ts < TimeSpan.Zero)
|
|
e.Abort ();
|
|
else
|
|
e.Close (ts);
|
|
} catch (Exception ex) {
|
|
Console.WriteLine ("ServiceHostBase failed to close the channel dispatcher:");
|
|
Console.WriteLine (ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override sealed void OnOpen (TimeSpan timeout)
|
|
{
|
|
DateTime start = DateTime.UtcNow;
|
|
InitializeRuntime ();
|
|
for (int i = 0; i < ChannelDispatchers.Count; i++) {
|
|
// Skip ServiceMetadataExtension-based one. special case.
|
|
for (int j = i + 1; j < ChannelDispatchers.Count; j++) {
|
|
var cd1 = ChannelDispatchers [i];
|
|
var cd2 = ChannelDispatchers [j];
|
|
if (cd1.IsMex || cd2.IsMex)
|
|
continue;
|
|
// surprisingly, some ChannelDispatcherBase implementations have null Listener property.
|
|
if (cd1.Listener != null && cd2.Listener != null && cd1.Listener.Uri.Equals (cd2.Listener.Uri))
|
|
throw new InvalidOperationException ("Two or more service endpoints with different Binding instance are bound to the same listen URI.");
|
|
}
|
|
}
|
|
|
|
var waits = new List<ManualResetEvent> ();
|
|
foreach (var cd in ChannelDispatchers) {
|
|
var wait = new ManualResetEvent (false);
|
|
cd.Opened += delegate { wait.Set (); };
|
|
waits.Add (wait);
|
|
cd.Open (timeout - (DateTime.UtcNow - start));
|
|
}
|
|
|
|
WaitHandle.WaitAll (waits.ToArray ());
|
|
}
|
|
|
|
protected override void OnEndClose (IAsyncResult result)
|
|
{
|
|
if (close_delegate == null)
|
|
throw new InvalidOperationException ("Async close operation has not started");
|
|
close_delegate.EndInvoke (result);
|
|
}
|
|
|
|
protected override sealed void OnEndOpen (IAsyncResult result)
|
|
{
|
|
if (open_delegate == null)
|
|
throw new InvalidOperationException ("Aync open operation has not started");
|
|
open_delegate.EndInvoke (result);
|
|
}
|
|
|
|
protected override void OnOpened ()
|
|
{
|
|
base.OnOpened ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
protected void ReleasePerformanceCounters ()
|
|
{
|
|
}
|
|
|
|
void IDisposable.Dispose ()
|
|
{
|
|
Close ();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds ChannelDispatchers as appropriate to service the service endpoints.
|
|
/// </summary>
|
|
partial class DispatcherBuilder
|
|
{
|
|
ServiceHostBase host;
|
|
|
|
public DispatcherBuilder (ServiceHostBase host)
|
|
{
|
|
this.host = host;
|
|
}
|
|
|
|
Dictionary<Binding,ChannelDispatcher> built_dispatchers = new Dictionary<Binding,ChannelDispatcher> ();
|
|
Dictionary<ServiceEndpoint, EndpointDispatcher> ep_to_dispatcher_ep = new Dictionary<ServiceEndpoint, EndpointDispatcher> ();
|
|
|
|
internal static Action<ChannelDispatcher> ChannelDispatcherSetter;
|
|
|
|
internal ChannelDispatcher BuildChannelDispatcher (Type serviceType, ServiceEndpoint se, BindingParameterCollection commonParams)
|
|
{
|
|
//Let all behaviors add their binding parameters
|
|
AddBindingParameters (commonParams, se);
|
|
|
|
// See if there's an existing channel that matches this endpoint
|
|
var version = se.Binding.GetProperty<MessageVersion> (commonParams);
|
|
if (version == null)
|
|
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.");
|
|
|
|
ChannelDispatcher cd = FindExistingDispatcher (se);
|
|
EndpointDispatcher ep;
|
|
if (cd != null) {
|
|
ep = cd.InitializeServiceEndpoint (serviceType, se);
|
|
} else {
|
|
// Use the binding parameters to build the channel listener and Dispatcher.
|
|
lock (HttpTransportBindingElement.ListenerBuildLock) {
|
|
ServiceHostBase.CurrentServiceHostHack = host;
|
|
IChannelListener lf = BuildListener (se, commonParams);
|
|
cd = new ChannelDispatcher (lf, se.Binding.Name);
|
|
cd.MessageVersion = version;
|
|
if (ChannelDispatcherSetter != null) {
|
|
ChannelDispatcherSetter (cd);
|
|
ChannelDispatcherSetter = null;
|
|
}
|
|
ServiceHostBase.CurrentServiceHostHack = null;
|
|
}
|
|
ep = cd.InitializeServiceEndpoint (serviceType, se);
|
|
built_dispatchers.Add (se.Binding, cd);
|
|
}
|
|
ep_to_dispatcher_ep[se] = ep;
|
|
return cd;
|
|
}
|
|
|
|
ChannelDispatcher FindExistingDispatcher (ServiceEndpoint se)
|
|
{
|
|
return built_dispatchers.FirstOrDefault ((KeyValuePair<Binding,ChannelDispatcher> p) => se.Binding == p.Key).Value;
|
|
}
|
|
|
|
internal void ApplyDispatchBehaviors ()
|
|
{
|
|
foreach (KeyValuePair<ServiceEndpoint, EndpointDispatcher> val in ep_to_dispatcher_ep)
|
|
ApplyDispatchBehavior (val.Value, val.Key);
|
|
}
|
|
|
|
private void ApplyDispatchBehavior (EndpointDispatcher ed, ServiceEndpoint endPoint)
|
|
{
|
|
foreach (IContractBehavior b in endPoint.Contract.Behaviors)
|
|
b.ApplyDispatchBehavior (endPoint.Contract, endPoint, ed.DispatchRuntime);
|
|
foreach (IEndpointBehavior b in endPoint.Behaviors)
|
|
b.ApplyDispatchBehavior (endPoint, ed);
|
|
foreach (OperationDescription operation in endPoint.Contract.Operations) {
|
|
if (operation.InCallbackContract)
|
|
continue; // irrelevant
|
|
foreach (IOperationBehavior b in operation.Behaviors)
|
|
b.ApplyDispatchBehavior (operation, ed.DispatchRuntime.Operations [operation.Name]);
|
|
}
|
|
|
|
}
|
|
|
|
private void AddBindingParameters (BindingParameterCollection commonParams, ServiceEndpoint endPoint) {
|
|
|
|
commonParams.Add (ChannelProtectionRequirements.CreateFromContract (endPoint.Contract));
|
|
|
|
foreach (IContractBehavior b in endPoint.Contract.Behaviors)
|
|
b.AddBindingParameters (endPoint.Contract, endPoint, commonParams);
|
|
foreach (IEndpointBehavior b in endPoint.Behaviors)
|
|
b.AddBindingParameters (endPoint, commonParams);
|
|
foreach (OperationDescription operation in endPoint.Contract.Operations) {
|
|
foreach (IOperationBehavior b in operation.Behaviors)
|
|
b.AddBindingParameters (operation, commonParams);
|
|
}
|
|
}
|
|
|
|
static IChannelListener BuildListener (ServiceEndpoint se,
|
|
BindingParameterCollection pl)
|
|
{
|
|
Binding b = se.Binding;
|
|
if (b.CanBuildChannelListener<IReplySessionChannel> (pl))
|
|
return b.BuildChannelListener<IReplySessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
if (b.CanBuildChannelListener<IReplyChannel> (pl))
|
|
return b.BuildChannelListener<IReplyChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
if (b.CanBuildChannelListener<IInputSessionChannel> (pl))
|
|
return b.BuildChannelListener<IInputSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
if (b.CanBuildChannelListener<IInputChannel> (pl))
|
|
return b.BuildChannelListener<IInputChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
|
|
if (b.CanBuildChannelListener<IDuplexChannel> (pl))
|
|
return b.BuildChannelListener<IDuplexChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
if (b.CanBuildChannelListener<IDuplexSessionChannel> (pl))
|
|
return b.BuildChannelListener<IDuplexSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
|
|
throw new InvalidOperationException ("None of the listener channel types is supported");
|
|
}
|
|
}
|
|
}
|