e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2125 lines
82 KiB
C#
2125 lines
82 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Runtime;
|
|
using System.Security;
|
|
using System.ServiceModel.Administration;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Configuration;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel.Diagnostics;
|
|
using System.ServiceModel.Dispatcher;
|
|
using System.Text;
|
|
using System.Runtime.Diagnostics;
|
|
using System.Threading;
|
|
using System.ServiceModel.Activation;
|
|
using System.ServiceModel.Diagnostics.Application;
|
|
using System.Reflection;
|
|
using System.Linq.Expressions;
|
|
|
|
public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
|
|
{
|
|
internal static readonly Uri EmptyUri = new Uri(string.Empty, UriKind.RelativeOrAbsolute);
|
|
|
|
bool initializeDescriptionHasFinished;
|
|
UriSchemeKeyedCollection baseAddresses;
|
|
ChannelDispatcherCollection channelDispatchers;
|
|
TimeSpan closeTimeout = ServiceDefaults.ServiceHostCloseTimeout;
|
|
ServiceDescription description;
|
|
ExtensionCollection<ServiceHostBase> extensions;
|
|
ReadOnlyCollection<Uri> externalBaseAddresses;
|
|
IDictionary<string, ContractDescription> implementedContracts;
|
|
IInstanceContextManager instances;
|
|
TimeSpan openTimeout = ServiceDefaults.OpenTimeout;
|
|
ServicePerformanceCountersBase servicePerformanceCounters;
|
|
DefaultPerformanceCounters defaultPerformanceCounters;
|
|
ServiceThrottle serviceThrottle;
|
|
ServiceCredentials readOnlyCredentials;
|
|
ServiceAuthorizationBehavior readOnlyAuthorization;
|
|
ServiceAuthenticationBehavior readOnlyAuthentication;
|
|
Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> endpointsByListenUriInfo;
|
|
int busyCount;
|
|
EventTraceActivity eventTraceActivity;
|
|
|
|
internal event EventHandler BusyCountIncremented;
|
|
|
|
public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
|
|
|
|
protected ServiceHostBase()
|
|
{
|
|
TraceUtility.SetEtwProviderId();
|
|
this.baseAddresses = new UriSchemeKeyedCollection(this.ThisLock);
|
|
this.channelDispatchers = new ChannelDispatcherCollection(this, this.ThisLock);
|
|
this.extensions = new ExtensionCollection<ServiceHostBase>(this, this.ThisLock);
|
|
this.instances = new InstanceContextManager(this.ThisLock);
|
|
this.serviceThrottle = new ServiceThrottle(this);
|
|
this.TraceOpenAndClose = true;
|
|
this.Faulted += new EventHandler(OnServiceHostFaulted);
|
|
}
|
|
|
|
|
|
internal EventTraceActivity EventTraceActivity
|
|
{
|
|
get
|
|
{
|
|
if (this.eventTraceActivity == null)
|
|
{
|
|
this.eventTraceActivity = new EventTraceActivity();
|
|
}
|
|
|
|
return eventTraceActivity;
|
|
}
|
|
}
|
|
|
|
public ServiceAuthorizationBehavior Authorization
|
|
{
|
|
get
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
return null;
|
|
}
|
|
else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening)
|
|
{
|
|
return EnsureAuthorization(this.Description);
|
|
}
|
|
else
|
|
{
|
|
return this.readOnlyAuthorization;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ServiceAuthenticationBehavior Authentication
|
|
{
|
|
get
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
return null;
|
|
}
|
|
else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening)
|
|
{
|
|
return EnsureAuthentication(this.Description);
|
|
}
|
|
else
|
|
{
|
|
return this.readOnlyAuthentication;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ReadOnlyCollection<Uri> BaseAddresses
|
|
{
|
|
get
|
|
{
|
|
externalBaseAddresses = new ReadOnlyCollection<Uri>(new List<Uri>(this.baseAddresses));
|
|
return externalBaseAddresses;
|
|
}
|
|
}
|
|
|
|
public ChannelDispatcherCollection ChannelDispatchers
|
|
{
|
|
get { return this.channelDispatchers; }
|
|
}
|
|
|
|
public TimeSpan CloseTimeout
|
|
{
|
|
get { return this.closeTimeout; }
|
|
set
|
|
{
|
|
if (value < TimeSpan.Zero)
|
|
{
|
|
string message = SR.GetString(SR.SFxTimeoutOutOfRange0);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message));
|
|
}
|
|
if (TimeoutHelper.IsTooLarge(value))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
|
|
}
|
|
|
|
lock (this.ThisLock)
|
|
{
|
|
this.ThrowIfClosedOrOpened();
|
|
this.closeTimeout = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal ServicePerformanceCountersBase Counters
|
|
{
|
|
get
|
|
{
|
|
return this.servicePerformanceCounters;
|
|
}
|
|
set
|
|
{
|
|
this.servicePerformanceCounters = value;
|
|
this.serviceThrottle.SetServicePerformanceCounters(this.servicePerformanceCounters);
|
|
}
|
|
}
|
|
|
|
internal DefaultPerformanceCounters DefaultCounters
|
|
{
|
|
get
|
|
{
|
|
return this.defaultPerformanceCounters;
|
|
}
|
|
set
|
|
{
|
|
this.defaultPerformanceCounters = value;
|
|
}
|
|
}
|
|
|
|
public ServiceCredentials Credentials
|
|
{
|
|
get
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
return null;
|
|
}
|
|
else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening)
|
|
{
|
|
return EnsureCredentials(this.Description);
|
|
}
|
|
else
|
|
{
|
|
return this.readOnlyCredentials;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override TimeSpan DefaultCloseTimeout
|
|
{
|
|
get { return this.CloseTimeout; }
|
|
}
|
|
|
|
protected override TimeSpan DefaultOpenTimeout
|
|
{
|
|
get { return this.OpenTimeout; }
|
|
}
|
|
|
|
public ServiceDescription Description
|
|
{
|
|
get { return this.description; }
|
|
}
|
|
|
|
public IExtensionCollection<ServiceHostBase> Extensions
|
|
{
|
|
get { return this.extensions; }
|
|
}
|
|
|
|
protected internal IDictionary<string, ContractDescription> ImplementedContracts
|
|
{
|
|
get { return this.implementedContracts; }
|
|
}
|
|
|
|
internal UriSchemeKeyedCollection InternalBaseAddresses
|
|
{
|
|
get { return this.baseAddresses; }
|
|
}
|
|
|
|
public int ManualFlowControlLimit
|
|
{
|
|
get { return this.ServiceThrottle.ManualFlowControlLimit; }
|
|
set { this.ServiceThrottle.ManualFlowControlLimit = value; }
|
|
}
|
|
|
|
public TimeSpan OpenTimeout
|
|
{
|
|
get { return this.openTimeout; }
|
|
set
|
|
{
|
|
if (value < TimeSpan.Zero)
|
|
{
|
|
string message = SR.GetString(SR.SFxTimeoutOutOfRange0);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message));
|
|
}
|
|
if (TimeoutHelper.IsTooLarge(value))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
|
|
}
|
|
|
|
lock (this.ThisLock)
|
|
{
|
|
this.ThrowIfClosedOrOpened();
|
|
this.openTimeout = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal ServiceThrottle ServiceThrottle
|
|
{
|
|
get
|
|
{
|
|
return this.serviceThrottle;
|
|
}
|
|
}
|
|
|
|
internal virtual object DisposableInstance
|
|
{
|
|
get
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> EndpointsByListenUriInfo
|
|
{
|
|
get
|
|
{
|
|
if (this.endpointsByListenUriInfo == null)
|
|
{
|
|
this.endpointsByListenUriInfo = this.GetEndpointsByListenUriInfo();
|
|
}
|
|
return this.endpointsByListenUriInfo;
|
|
}
|
|
}
|
|
|
|
Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> GetEndpointsByListenUriInfo()
|
|
{
|
|
Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> endpointDictionary = new Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>>();
|
|
foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
|
|
{
|
|
DispatcherBuilder.ListenUriInfo listenUriInfo = DispatcherBuilder.GetListenUriInfoForEndpoint(this, endpoint);
|
|
if (!endpointDictionary.ContainsKey(listenUriInfo))
|
|
{
|
|
endpointDictionary.Add(listenUriInfo, new Collection<ServiceEndpoint>());
|
|
}
|
|
endpointDictionary[listenUriInfo].Add(endpoint);
|
|
}
|
|
return endpointDictionary;
|
|
}
|
|
|
|
protected void AddBaseAddress(Uri baseAddress)
|
|
{
|
|
if (this.initializeDescriptionHasFinished)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
|
SR.GetString(SR.SFxCannotCallAddBaseAddress)));
|
|
}
|
|
this.baseAddresses.Add(baseAddress);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address)
|
|
{
|
|
return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri)
|
|
{
|
|
if (address == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address"));
|
|
}
|
|
|
|
ServiceEndpoint endpoint = this.AddServiceEndpoint(implementedContract, binding, new Uri(address, UriKind.RelativeOrAbsolute));
|
|
if (listenUri != null)
|
|
{
|
|
endpoint.UnresolvedListenUri = listenUri;
|
|
listenUri = MakeAbsoluteUri(listenUri, binding);
|
|
endpoint.ListenUri = listenUri;
|
|
}
|
|
return endpoint;
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address)
|
|
{
|
|
return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri)
|
|
{
|
|
if (address == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address"));
|
|
}
|
|
|
|
if (binding == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("binding"));
|
|
}
|
|
|
|
if (implementedContract == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("implementedContract"));
|
|
}
|
|
|
|
if (this.State != CommunicationState.Created && this.State != CommunicationState.Opening)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointAfterOpen)));
|
|
}
|
|
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointWithoutDescription)));
|
|
}
|
|
|
|
Uri via = this.MakeAbsoluteUri(address, binding);
|
|
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.implementedContracts));
|
|
ContractDescription contract = configLoader.LookupContract(implementedContract, this.Description.Name);
|
|
|
|
ServiceEndpoint serviceEndpoint = new ServiceEndpoint(contract, binding, new EndpointAddress(via));
|
|
this.Description.Endpoints.Add(serviceEndpoint);
|
|
serviceEndpoint.UnresolvedAddress = address;
|
|
|
|
if (listenUri != null)
|
|
{
|
|
serviceEndpoint.UnresolvedListenUri = listenUri;
|
|
listenUri = MakeAbsoluteUri(listenUri, binding);
|
|
serviceEndpoint.ListenUri = listenUri;
|
|
}
|
|
return serviceEndpoint;
|
|
}
|
|
|
|
public virtual void AddServiceEndpoint(ServiceEndpoint endpoint)
|
|
{
|
|
if (endpoint == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
|
|
}
|
|
if (this.State != CommunicationState.Created && this.State != CommunicationState.Opening)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointAfterOpen)));
|
|
}
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointWithoutDescription)));
|
|
}
|
|
if (endpoint.Address == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointAddressNotSpecified));
|
|
}
|
|
if (endpoint.Contract == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointContractNotSpecified));
|
|
}
|
|
if (endpoint.Binding == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointBindingNotSpecified));
|
|
}
|
|
if (!endpoint.IsSystemEndpoint || endpoint.Contract.ContractType == typeof(IMetadataExchange))
|
|
{
|
|
ConfigLoader loader = new ConfigLoader(GetContractResolver(this.implementedContracts));
|
|
loader.LookupContract(endpoint.Contract.ConfigurationName, this.Description.Name);
|
|
}
|
|
this.Description.Endpoints.Add(endpoint);
|
|
}
|
|
|
|
public void SetEndpointAddress(ServiceEndpoint endpoint, string relativeAddress)
|
|
{
|
|
if (endpoint == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
|
|
}
|
|
if (relativeAddress == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("relativeAddress");
|
|
}
|
|
if (endpoint.Binding == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointBindingNotSpecified));
|
|
}
|
|
Uri absoluteUri = MakeAbsoluteUri(new Uri(relativeAddress, UriKind.Relative), endpoint.Binding);
|
|
endpoint.Address = new EndpointAddress(absoluteUri);
|
|
}
|
|
|
|
internal Uri MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding)
|
|
{
|
|
return MakeAbsoluteUri(relativeOrAbsoluteUri, binding, this.InternalBaseAddresses);
|
|
}
|
|
|
|
internal static Uri MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding, UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
Uri result = relativeOrAbsoluteUri;
|
|
if (!result.IsAbsoluteUri)
|
|
{
|
|
if (binding.Scheme == string.Empty)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCustomBindingWithoutTransport)));
|
|
}
|
|
result = GetVia(binding.Scheme, result, baseAddresses);
|
|
if (result == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxEndpointNoMatchingScheme, binding.Scheme, binding.Name, GetBaseAddressSchemes(baseAddresses))));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected virtual void ApplyConfiguration()
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription)));
|
|
}
|
|
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(implementedContracts));
|
|
|
|
// Call the overload of LoadConfigurationSectionInternal which looks up the serviceElement from ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
|
|
LoadConfigurationSectionInternal(configLoader, this.Description, this.Description.ConfigurationName);
|
|
|
|
EnsureAuthenticationAuthorizationDebug(this.Description);
|
|
}
|
|
|
|
internal void EnsureAuthenticationAuthorizationDebug(ServiceDescription description)
|
|
{
|
|
EnsureAuthentication(description);
|
|
EnsureAuthorization(description);
|
|
EnsureDebug(description);
|
|
}
|
|
|
|
public virtual ReadOnlyCollection<ServiceEndpoint> AddDefaultEndpoints()
|
|
{
|
|
List<ServiceEndpoint> defaultEndpoints = new List<ServiceEndpoint>();
|
|
foreach (Uri baseAddress in this.InternalBaseAddresses)
|
|
{
|
|
ProtocolMappingItem protocolMappingItem = ConfigLoader.LookupProtocolMapping(baseAddress.Scheme);
|
|
if (protocolMappingItem != null)
|
|
{
|
|
Binding defaultBinding = ConfigLoader.LookupBinding(protocolMappingItem.Binding, protocolMappingItem.BindingConfiguration);
|
|
if (defaultBinding != null)
|
|
{
|
|
AddDefaultEndpoints(defaultBinding, defaultEndpoints);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Exception(SR.GetString(SR.BindingProtocolMappingNotDefined, baseAddress.Scheme)));
|
|
}
|
|
}
|
|
}
|
|
if (DiagnosticUtility.ShouldTraceInformation && defaultEndpoints.Count > 0)
|
|
{
|
|
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
|
dictionary["ServiceConfigurationName"] = this.description.ConfigurationName;
|
|
TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.DefaultEndpointsAdded, SR.GetString(SR.TraceCodeDefaultEndpointsAdded), new DictionaryTraceRecord(dictionary));
|
|
}
|
|
return new ReadOnlyCollection<ServiceEndpoint>(defaultEndpoints);
|
|
}
|
|
|
|
internal virtual void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)
|
|
{
|
|
}
|
|
|
|
internal virtual void BindInstance(InstanceContext instance)
|
|
{
|
|
this.instances.Add(instance);
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
this.servicePerformanceCounters.ServiceInstanceCreated();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IDisposable.Dispose()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
protected abstract ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts);
|
|
|
|
protected virtual void InitializeRuntime()
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotInitializeRuntimeWithoutDescription)));
|
|
}
|
|
|
|
if (this.Description.Endpoints.Count == 0)
|
|
{
|
|
this.AddDefaultEndpoints();
|
|
}
|
|
|
|
this.EnsureAuthenticationSchemes();
|
|
|
|
DispatcherBuilder dispatcherBuilder = new DispatcherBuilder();
|
|
dispatcherBuilder.InitializeServiceHost(description, this);
|
|
|
|
SecurityValidationBehavior.Instance.AfterBuildTimeValidation(description);
|
|
}
|
|
|
|
internal virtual void AfterInitializeRuntime(TimeSpan timeout)
|
|
{
|
|
}
|
|
|
|
internal virtual IAsyncResult BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new CompletedAsyncResult(callback, state);
|
|
}
|
|
|
|
internal virtual void EndAfterInitializeRuntime(IAsyncResult result)
|
|
{
|
|
CompletedAsyncResult.End(result);
|
|
}
|
|
|
|
ServiceAuthorizationBehavior EnsureAuthorization(ServiceDescription description)
|
|
{
|
|
Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, "");
|
|
ServiceAuthorizationBehavior a = description.Behaviors.Find<ServiceAuthorizationBehavior>();
|
|
|
|
if (a == null)
|
|
{
|
|
a = new ServiceAuthorizationBehavior();
|
|
description.Behaviors.Add(a);
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
ServiceAuthenticationBehavior EnsureAuthentication(ServiceDescription description)
|
|
{
|
|
Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, "");
|
|
ServiceAuthenticationBehavior a = description.Behaviors.Find<ServiceAuthenticationBehavior>();
|
|
|
|
if (a == null)
|
|
{
|
|
a = new ServiceAuthenticationBehavior();
|
|
description.Behaviors.Add(a);
|
|
}
|
|
return a;
|
|
}
|
|
|
|
ServiceDebugBehavior EnsureDebug(ServiceDescription description)
|
|
{
|
|
Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, "");
|
|
ServiceDebugBehavior m = description.Behaviors.Find<ServiceDebugBehavior>();
|
|
|
|
if (m == null)
|
|
{
|
|
m = new ServiceDebugBehavior();
|
|
description.Behaviors.Add(m);
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
ServiceCredentials EnsureCredentials(ServiceDescription description)
|
|
{
|
|
Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, "");
|
|
ServiceCredentials c = description.Behaviors.Find<ServiceCredentials>();
|
|
|
|
if (c == null)
|
|
{
|
|
c = new ServiceCredentials();
|
|
description.Behaviors.Add(c);
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
internal void FaultInternal()
|
|
{
|
|
this.Fault();
|
|
}
|
|
|
|
internal string GetBaseAddressSchemes()
|
|
{
|
|
return GetBaseAddressSchemes(baseAddresses);
|
|
}
|
|
|
|
internal static String GetBaseAddressSchemes(UriSchemeKeyedCollection uriSchemeKeyedCollection)
|
|
{
|
|
StringBuilder buffer = new StringBuilder();
|
|
bool firstScheme = true;
|
|
foreach (Uri address in uriSchemeKeyedCollection)
|
|
{
|
|
if (firstScheme)
|
|
{
|
|
buffer.Append(address.Scheme);
|
|
firstScheme = false;
|
|
}
|
|
else
|
|
{
|
|
buffer.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator).Append(address.Scheme);
|
|
}
|
|
}
|
|
return buffer.ToString();
|
|
}
|
|
|
|
internal BindingParameterCollection GetBindingParameters()
|
|
{
|
|
return DispatcherBuilder.GetBindingParameters(this, new Collection<ServiceEndpoint>());
|
|
}
|
|
|
|
internal BindingParameterCollection GetBindingParameters(ServiceEndpoint inputEndpoint)
|
|
{
|
|
Collection<ServiceEndpoint> endpoints;
|
|
if (inputEndpoint == null)
|
|
{
|
|
endpoints = new Collection<ServiceEndpoint>();
|
|
}
|
|
else if (!this.EndpointsByListenUriInfo.TryGetValue(DispatcherBuilder.GetListenUriInfoForEndpoint(this, inputEndpoint), out endpoints) || !endpoints.Contains(inputEndpoint))
|
|
{
|
|
endpoints = new Collection<ServiceEndpoint>();
|
|
endpoints.Add(inputEndpoint);
|
|
}
|
|
|
|
return DispatcherBuilder.GetBindingParameters(this, endpoints);
|
|
}
|
|
|
|
internal BindingParameterCollection GetBindingParameters(Collection<ServiceEndpoint> endpoints)
|
|
{
|
|
return DispatcherBuilder.GetBindingParameters(this, endpoints);
|
|
}
|
|
|
|
internal ReadOnlyCollection<InstanceContext> GetInstanceContexts()
|
|
{
|
|
return Array.AsReadOnly<InstanceContext>(this.instances.ToArray());
|
|
}
|
|
|
|
internal virtual IContractResolver GetContractResolver(IDictionary<string, ContractDescription> implementedContracts)
|
|
{
|
|
ServiceAndBehaviorsContractResolver resolver = new ServiceAndBehaviorsContractResolver(new ImplementedContractsContractResolver(implementedContracts));
|
|
resolver.AddBehaviorContractsToResolver(this.description == null ? null : this.description.Behaviors);
|
|
return resolver;
|
|
}
|
|
|
|
internal static Uri GetUri(Uri baseUri, Uri relativeUri)
|
|
{
|
|
return GetUri(baseUri, relativeUri.OriginalString);
|
|
}
|
|
|
|
internal static Uri GetUri(Uri baseUri, string path)
|
|
{
|
|
if (path.StartsWith("/", StringComparison.Ordinal) || path.StartsWith("\\", StringComparison.Ordinal))
|
|
{
|
|
int i = 1;
|
|
for (; i < path.Length; ++i)
|
|
{
|
|
if (path[i] != '/' && path[i] != '\\')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
path = path.Substring(i);
|
|
}
|
|
|
|
// VSWhidbey#541152: new Uri(Uri, string.Empty) is broken
|
|
if (path.Length == 0)
|
|
return baseUri;
|
|
|
|
if (!baseUri.AbsoluteUri.EndsWith("/", StringComparison.Ordinal))
|
|
{
|
|
baseUri = new Uri(baseUri.AbsoluteUri + "/");
|
|
}
|
|
return new Uri(baseUri, path);
|
|
}
|
|
|
|
internal Uri GetVia(string scheme, Uri address)
|
|
{
|
|
return ServiceHost.GetVia(scheme, address, InternalBaseAddresses);
|
|
}
|
|
|
|
internal static Uri GetVia(string scheme, Uri address, UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
Uri via = address;
|
|
if (!via.IsAbsoluteUri)
|
|
{
|
|
if (!baseAddresses.Contains(scheme))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
via = GetUri(baseAddresses[scheme], address);
|
|
}
|
|
return via;
|
|
}
|
|
|
|
public int IncrementManualFlowControlLimit(int incrementBy)
|
|
{
|
|
return this.ServiceThrottle.IncrementManualFlowControlLimit(incrementBy);
|
|
}
|
|
|
|
protected void InitializeDescription(UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
foreach (Uri baseAddress in baseAddresses)
|
|
{
|
|
this.baseAddresses.Add(baseAddress);
|
|
}
|
|
IDictionary<string, ContractDescription> implementedContracts = null;
|
|
ServiceDescription description = CreateDescription(out implementedContracts);
|
|
this.description = description;
|
|
this.implementedContracts = implementedContracts;
|
|
|
|
ApplyConfiguration();
|
|
this.initializeDescriptionHasFinished = true;
|
|
}
|
|
|
|
protected void LoadConfigurationSection(ServiceElement serviceSection)
|
|
{
|
|
if (serviceSection == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceSection");
|
|
}
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotLoadConfigurationSectionWithoutDescription)));
|
|
}
|
|
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts));
|
|
LoadConfigurationSectionInternal(configLoader, this.Description, serviceSection);
|
|
}
|
|
|
|
internal void LoadConfigurationSectionHelper(Uri baseAddress)
|
|
{
|
|
this.AddBaseAddress(baseAddress);
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.",
|
|
Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")]
|
|
[SecuritySafeCritical]
|
|
void LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName)
|
|
{
|
|
ServiceElement serviceSection = configLoader.LookupService(configurationName);
|
|
LoadConfigurationSectionInternal(configLoader, description, serviceSection);
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Handles a ServiceElement, which should not be leaked out of SecurityCritical code.",
|
|
Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")]
|
|
[SecuritySafeCritical]
|
|
void LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, ServiceElement serviceSection)
|
|
{
|
|
// caller must validate arguments before calling
|
|
configLoader.LoadServiceDescription(this, description, serviceSection, this.LoadConfigurationSectionHelper);
|
|
}
|
|
|
|
protected override void OnAbort()
|
|
{
|
|
this.instances.Abort();
|
|
|
|
foreach (ChannelDispatcherBase dispatcher in this.ChannelDispatchers)
|
|
{
|
|
if (dispatcher.Listener != null)
|
|
{
|
|
dispatcher.Listener.Abort();
|
|
}
|
|
dispatcher.Abort();
|
|
}
|
|
ThreadTrace.StopTracing();
|
|
}
|
|
|
|
internal void OnAddChannelDispatcher(ChannelDispatcherBase channelDispatcher)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.ThrowIfClosedOrOpened();
|
|
channelDispatcher.AttachInternal(this);
|
|
channelDispatcher.Faulted += new EventHandler(OnChannelDispatcherFaulted);
|
|
}
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new CloseAsyncResult(timeout, callback, state, this);
|
|
}
|
|
|
|
void OnBeginOpen()
|
|
{
|
|
this.TraceServiceHostOpenStart();
|
|
this.TraceBaseAddresses();
|
|
MessageLogger.EnsureInitialized(); //force config validation instead of waiting for the first message exchange
|
|
InitializeRuntime();
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
this.OnBeginOpen();
|
|
return new OpenAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
IAsyncResult BeginOpenChannelDispatchers(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new OpenCollectionAsyncResult(timeout, callback, state, this.SnapshotChannelDispatchers());
|
|
}
|
|
|
|
protected override void OnClose(TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
if (ManagementExtension.IsEnabled && null != this.Description)
|
|
{
|
|
ManagementExtension.OnServiceClosing(this);
|
|
}
|
|
|
|
for (int i = 0; i < this.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i];
|
|
if (dispatcher.Listener != null)
|
|
{
|
|
dispatcher.Listener.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < this.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i];
|
|
dispatcher.CloseInput(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
// Wait for existing work to complete
|
|
this.instances.CloseInput(timeoutHelper.RemainingTime());
|
|
|
|
// Close instances (closes contexts/channels)
|
|
this.instances.Close(timeoutHelper.RemainingTime());
|
|
|
|
// Close dispatchers
|
|
for (int i = 0; i < this.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i];
|
|
dispatcher.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
this.ReleasePerformanceCounters();
|
|
|
|
this.TraceBaseAddresses();
|
|
ThreadTrace.StopTracing();
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose));
|
|
}
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostTimeoutOnClose, SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose), this, e);
|
|
}
|
|
this.Abort();
|
|
}
|
|
|
|
}
|
|
|
|
protected override void OnClosed()
|
|
{
|
|
try
|
|
{
|
|
for (int i = 0; i < this.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcher dispatcher = this.ChannelDispatchers[i] as ChannelDispatcher;
|
|
if (dispatcher != null)
|
|
{
|
|
dispatcher.ReleasePerformanceCounters();
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
base.OnClosed();
|
|
}
|
|
}
|
|
|
|
void TraceBaseAddresses()
|
|
{
|
|
if (DiagnosticUtility.ShouldTraceInformation && this.baseAddresses != null
|
|
&& this.baseAddresses.Count > 0)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Information,
|
|
TraceCode.ServiceHostBaseAddresses,
|
|
SR.GetString(SR.TraceCodeServiceHostBaseAddresses),
|
|
new CollectionTraceRecord("BaseAddresses", "Address", this.baseAddresses),
|
|
this, null);
|
|
}
|
|
}
|
|
|
|
void TraceServiceHostOpenStart()
|
|
{
|
|
if (TD.ServiceHostOpenStartIsEnabled())
|
|
{
|
|
TD.ServiceHostOpenStart(this.EventTraceActivity);
|
|
}
|
|
}
|
|
|
|
protected override void OnEndClose(IAsyncResult result)
|
|
{
|
|
try
|
|
{
|
|
CloseAsyncResult.End(result);
|
|
this.TraceBaseAddresses();
|
|
ThreadTrace.StopTracing();
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose));
|
|
}
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostTimeoutOnClose,
|
|
SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose), this, e);
|
|
}
|
|
this.Abort();
|
|
}
|
|
}
|
|
|
|
protected override void OnEndOpen(IAsyncResult result)
|
|
{
|
|
OpenAsyncResult.End(result);
|
|
}
|
|
|
|
void EndOpenChannelDispatchers(IAsyncResult result)
|
|
{
|
|
OpenCollectionAsyncResult.End(result);
|
|
}
|
|
|
|
void EnsureAuthenticationSchemes()
|
|
{
|
|
if (this.Authentication == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Exit immediately when not hosted in IIS or if VirtualPathExtension is not set. VirtualPathExtension is used as a flag to indicate whether a ServiceHost
|
|
// is webhosted (WsDualHttpBinding-ChannelFactory is using HttpListener instead of IIS even when running in IIS)
|
|
if (!AspNetEnvironment.Enabled ||
|
|
this.Extensions.Find<VirtualPathExtension>() == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints)
|
|
{
|
|
if (serviceEndpoint.Binding != null &&
|
|
serviceEndpoint.ListenUri != null &&
|
|
("http".Equals(serviceEndpoint.ListenUri.Scheme, StringComparison.OrdinalIgnoreCase) || "https".Equals(serviceEndpoint.ListenUri.Scheme, StringComparison.OrdinalIgnoreCase)) &&
|
|
this.baseAddresses.Contains(serviceEndpoint.ListenUri.Scheme))
|
|
{
|
|
HttpTransportBindingElement httpTransportBindingElement = serviceEndpoint.Binding.CreateBindingElements().Find<HttpTransportBindingElement>();
|
|
|
|
if (httpTransportBindingElement != null)
|
|
{
|
|
AuthenticationSchemes hostSupportedAuthenticationSchemes = AspNetEnvironment.Current.GetAuthenticationSchemes(this.baseAddresses[serviceEndpoint.ListenUri.Scheme]);
|
|
|
|
if (hostSupportedAuthenticationSchemes != AuthenticationSchemes.None)
|
|
{
|
|
//If no authentication schemes are explicitly defined for the ServiceHost...
|
|
if (this.Authentication.AuthenticationSchemes == AuthenticationSchemes.None)
|
|
{
|
|
//Inherit authentication schemes from IIS
|
|
this.Authentication.AuthenticationSchemes = hostSupportedAuthenticationSchemes;
|
|
}
|
|
else
|
|
{
|
|
// Build intersection between authenticationSchemes on the ServiceHost and in IIS
|
|
this.Authentication.AuthenticationSchemes &= hostSupportedAuthenticationSchemes;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnOpen(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.OnBeginOpen();
|
|
|
|
AfterInitializeRuntime(timeoutHelper.RemainingTime());
|
|
|
|
for (int i = 0; i < this.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i];
|
|
dispatcher.Open(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
protected override void OnOpened()
|
|
{
|
|
if (this.Description != null)
|
|
{
|
|
ServiceCredentials c = description.Behaviors.Find<ServiceCredentials>();
|
|
if (c != null)
|
|
{
|
|
ServiceCredentials credentialsCopy = c.Clone();
|
|
credentialsCopy.MakeReadOnly();
|
|
this.readOnlyCredentials = credentialsCopy;
|
|
}
|
|
|
|
ServiceAuthorizationBehavior authorization = description.Behaviors.Find<ServiceAuthorizationBehavior>();
|
|
if (authorization != null)
|
|
{
|
|
ServiceAuthorizationBehavior authorizationCopy = authorization.Clone();
|
|
authorizationCopy.MakeReadOnly();
|
|
this.readOnlyAuthorization = authorizationCopy;
|
|
}
|
|
|
|
ServiceAuthenticationBehavior authentication = description.Behaviors.Find<ServiceAuthenticationBehavior>();
|
|
if (authentication != null)
|
|
{
|
|
ServiceAuthenticationBehavior authenticationCopy = authentication.Clone();
|
|
authentication.MakeReadOnly();
|
|
this.readOnlyAuthentication = authenticationCopy;
|
|
}
|
|
|
|
if (ManagementExtension.IsEnabled)
|
|
{
|
|
ManagementExtension.OnServiceOpened(this);
|
|
}
|
|
}
|
|
base.OnOpened();
|
|
|
|
if (TD.ServiceHostOpenStopIsEnabled())
|
|
{
|
|
TD.ServiceHostOpenStop(this.EventTraceActivity);
|
|
}
|
|
}
|
|
|
|
internal void OnRemoveChannelDispatcher(ChannelDispatcherBase channelDispatcher)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
this.ThrowIfClosedOrOpened();
|
|
channelDispatcher.DetachInternal(this);
|
|
}
|
|
}
|
|
|
|
void OnChannelDispatcherFaulted(object sender, EventArgs e)
|
|
{
|
|
this.Fault();
|
|
}
|
|
|
|
void OnServiceHostFaulted(object sender, EventArgs args)
|
|
{
|
|
if (TD.ServiceHostFaultedIsEnabled())
|
|
{
|
|
TD.ServiceHostFaulted(this.EventTraceActivity, this);
|
|
}
|
|
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostFaulted,
|
|
SR.GetString(SR.TraceCodeServiceHostFaulted), this);
|
|
}
|
|
|
|
foreach (ICommunicationObject channelDispatcher in this.SnapshotChannelDispatchers())
|
|
{
|
|
if (channelDispatcher.State == CommunicationState.Opened)
|
|
{
|
|
channelDispatcher.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void RaiseUnknownMessageReceived(Message message)
|
|
{
|
|
try
|
|
{
|
|
EventHandler<UnknownMessageReceivedEventArgs> handler = UnknownMessageReceived;
|
|
if (handler != null)
|
|
{
|
|
handler(this, new UnknownMessageReceivedEventArgs(message));
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
throw;
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
|
|
}
|
|
}
|
|
|
|
protected void ReleasePerformanceCounters()
|
|
{
|
|
if (this.servicePerformanceCounters != null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.servicePerformanceCounters != null)
|
|
{
|
|
this.servicePerformanceCounters.Dispose();
|
|
this.servicePerformanceCounters = null;
|
|
}
|
|
}
|
|
}
|
|
if (this.defaultPerformanceCounters != null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.defaultPerformanceCounters != null)
|
|
{
|
|
this.defaultPerformanceCounters.Dispose();
|
|
this.defaultPerformanceCounters = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ICommunicationObject[] SnapshotChannelDispatchers()
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
ICommunicationObject[] array = new ICommunicationObject[this.ChannelDispatchers.Count];
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
array[i] = this.ChannelDispatchers[i];
|
|
}
|
|
return array;
|
|
}
|
|
}
|
|
|
|
internal virtual void UnbindInstance(InstanceContext instance)
|
|
{
|
|
this.instances.Remove(instance);
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
this.servicePerformanceCounters.ServiceInstanceRemoved();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void IncrementBusyCount()
|
|
{
|
|
if (AspNetEnvironment.Enabled)
|
|
{
|
|
AspNetEnvironment.Current.IncrementBusyCount();
|
|
Interlocked.Increment(ref this.busyCount);
|
|
}
|
|
|
|
EventHandler handler = this.BusyCountIncremented;
|
|
if (handler != null)
|
|
{
|
|
try
|
|
{
|
|
handler(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
if (Fx.IsFatal(exception))
|
|
throw;
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DecrementBusyCount()
|
|
{
|
|
if (AspNetEnvironment.Enabled)
|
|
{
|
|
Interlocked.Decrement(ref this.busyCount);
|
|
AspNetEnvironment.Current.DecrementBusyCount();
|
|
}
|
|
}
|
|
|
|
internal int BusyCount
|
|
{
|
|
get
|
|
{
|
|
return this.busyCount;
|
|
}
|
|
}
|
|
|
|
class OpenAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCompletion handleEndAfterInitializeRuntime = new AsyncCompletion(HandleEndAfterInitializeRuntime);
|
|
static AsyncCompletion handleEndOpenChannelDispatchers = new AsyncCompletion(HandleEndOpenChannelDispatchers);
|
|
|
|
TimeoutHelper timeoutHelper;
|
|
ServiceHostBase host;
|
|
|
|
public OpenAsyncResult(ServiceHostBase host, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.host = host;
|
|
|
|
if (ProcessAfterInitializeRuntime())
|
|
{
|
|
Complete(true);
|
|
}
|
|
}
|
|
|
|
bool ProcessAfterInitializeRuntime()
|
|
{
|
|
IAsyncResult result = this.host.BeginAfterInitializeRuntime(
|
|
this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndAfterInitializeRuntime), this);
|
|
|
|
return SyncContinue(result);
|
|
}
|
|
|
|
static bool HandleEndAfterInitializeRuntime(IAsyncResult result)
|
|
{
|
|
OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState;
|
|
thisPtr.host.EndAfterInitializeRuntime(result);
|
|
|
|
return thisPtr.ProcessOpenChannelDispatchers();
|
|
}
|
|
|
|
bool ProcessOpenChannelDispatchers()
|
|
{
|
|
IAsyncResult result = this.host.BeginOpenChannelDispatchers(
|
|
this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndOpenChannelDispatchers), this);
|
|
|
|
return SyncContinue(result);
|
|
}
|
|
|
|
static bool HandleEndOpenChannelDispatchers(IAsyncResult result)
|
|
{
|
|
OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState;
|
|
thisPtr.host.EndOpenChannelDispatchers(result);
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<OpenAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
class CloseAsyncResult : AsyncResult
|
|
{
|
|
ServiceHostBase serviceHost;
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
public CloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, ServiceHostBase serviceHost)
|
|
: base(callback, state)
|
|
{
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.serviceHost = serviceHost;
|
|
|
|
if (ManagementExtension.IsEnabled && null != serviceHost.Description)
|
|
{
|
|
ManagementExtension.OnServiceClosing(serviceHost);
|
|
}
|
|
|
|
this.CloseListeners(true);
|
|
}
|
|
|
|
void CloseListeners(bool completedSynchronously)
|
|
{
|
|
List<ICommunicationObject> listeners = new List<ICommunicationObject>();
|
|
for (int i = 0; i < this.serviceHost.ChannelDispatchers.Count; i++)
|
|
{
|
|
if (this.serviceHost.ChannelDispatchers[i].Listener != null)
|
|
{
|
|
listeners.Add(this.serviceHost.ChannelDispatchers[i].Listener);
|
|
}
|
|
}
|
|
|
|
AsyncCallback callback = Fx.ThunkCallback(this.CloseListenersCallback);
|
|
TimeSpan timeout = this.timeoutHelper.RemainingTime();
|
|
Exception exception = null;
|
|
IAsyncResult result = null;
|
|
try
|
|
{
|
|
result = new CloseCollectionAsyncResult(timeout, callback, this, listeners);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else if (result.CompletedSynchronously)
|
|
{
|
|
this.FinishCloseListeners(result, completedSynchronously);
|
|
}
|
|
}
|
|
|
|
void CloseListenersCallback(IAsyncResult result)
|
|
{
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
((CloseAsyncResult)result.AsyncState).FinishCloseListeners(result, false);
|
|
}
|
|
}
|
|
|
|
void FinishCloseListeners(IAsyncResult result, bool completedSynchronously)
|
|
{
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
CloseCollectionAsyncResult.End(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else
|
|
{
|
|
this.CloseInput(completedSynchronously);
|
|
}
|
|
}
|
|
|
|
// Wait for existing work to complete
|
|
void CloseInput(bool completedSynchronously)
|
|
{
|
|
AsyncCallback callback = Fx.ThunkCallback(this.CloseInputCallback);
|
|
Exception exception = null;
|
|
IAsyncResult result = null;
|
|
|
|
try
|
|
{
|
|
for (int i = 0; i < this.serviceHost.ChannelDispatchers.Count; i++)
|
|
{
|
|
ChannelDispatcherBase dispatcher = this.serviceHost.ChannelDispatchers[i];
|
|
dispatcher.CloseInput(this.timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
result = this.serviceHost.instances.BeginCloseInput(this.timeoutHelper.RemainingTime(), callback, this);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
// Any exception during async processing causes this
|
|
// async callback to report the error and then relies on
|
|
// Abort to cleanup any unclosed channels or instance contexts.
|
|
FxTrace.Exception.AsWarning(exception);
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else if (result.CompletedSynchronously)
|
|
{
|
|
this.FinishCloseInput(result, completedSynchronously);
|
|
}
|
|
}
|
|
|
|
void CloseInputCallback(IAsyncResult result)
|
|
{
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
((CloseAsyncResult)result.AsyncState).FinishCloseInput(result, false);
|
|
}
|
|
}
|
|
|
|
void FinishCloseInput(IAsyncResult result, bool completedSynchronously)
|
|
{
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
serviceHost.instances.EndCloseInput(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else
|
|
{
|
|
this.CloseInstances(completedSynchronously);
|
|
}
|
|
}
|
|
|
|
// Close instances (closes contexts/channels)
|
|
void CloseInstances(bool completedSynchronously)
|
|
{
|
|
AsyncCallback callback = Fx.ThunkCallback(this.CloseInstancesCallback);
|
|
TimeSpan timeout = this.timeoutHelper.RemainingTime();
|
|
Exception exception = null;
|
|
IAsyncResult result = null;
|
|
|
|
try
|
|
{
|
|
result = this.serviceHost.instances.BeginClose(timeout, callback, this);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else if (result.CompletedSynchronously)
|
|
{
|
|
this.FinishCloseInstances(result, completedSynchronously);
|
|
}
|
|
}
|
|
|
|
void CloseInstancesCallback(IAsyncResult result)
|
|
{
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
((CloseAsyncResult)result.AsyncState).FinishCloseInstances(result, false);
|
|
}
|
|
}
|
|
|
|
void FinishCloseInstances(IAsyncResult result, bool completedSynchronously)
|
|
{
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
this.serviceHost.instances.EndClose(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else
|
|
{
|
|
this.CloseChannelDispatchers(completedSynchronously);
|
|
}
|
|
}
|
|
|
|
void CloseChannelDispatchers(bool completedSynchronously)
|
|
{
|
|
IList<ICommunicationObject> channelDispatchers = this.serviceHost.SnapshotChannelDispatchers();
|
|
AsyncCallback callback = Fx.ThunkCallback(this.CloseChannelDispatchersCallback);
|
|
TimeSpan timeout = this.timeoutHelper.RemainingTime();
|
|
Exception exception = null;
|
|
IAsyncResult result = null;
|
|
try
|
|
{
|
|
result = new CloseCollectionAsyncResult(timeout, callback, this, channelDispatchers);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null)
|
|
{
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
else if (result.CompletedSynchronously)
|
|
{
|
|
this.FinishCloseChannelDispatchers(result, completedSynchronously);
|
|
}
|
|
}
|
|
|
|
void CloseChannelDispatchersCallback(IAsyncResult result)
|
|
{
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
((CloseAsyncResult)result.AsyncState).FinishCloseChannelDispatchers(result, false);
|
|
}
|
|
}
|
|
|
|
void FinishCloseChannelDispatchers(IAsyncResult result, bool completedSynchronously)
|
|
{
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
CloseCollectionAsyncResult.End(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e) || completedSynchronously)
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
this.CallComplete(completedSynchronously, exception);
|
|
}
|
|
|
|
void CallComplete(bool completedSynchronously, Exception exception)
|
|
{
|
|
this.Complete(completedSynchronously, exception);
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<CloseAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
class ImplementedContractsContractResolver : IContractResolver
|
|
{
|
|
IDictionary<string, ContractDescription> implementedContracts;
|
|
|
|
public ImplementedContractsContractResolver(IDictionary<string, ContractDescription> implementedContracts)
|
|
{
|
|
this.implementedContracts = implementedContracts;
|
|
}
|
|
|
|
public ContractDescription ResolveContract(string contractName)
|
|
{
|
|
return this.implementedContracts != null && this.implementedContracts.ContainsKey(contractName) ? this.implementedContracts[contractName] : null;
|
|
}
|
|
}
|
|
|
|
internal class ServiceAndBehaviorsContractResolver : IContractResolver
|
|
{
|
|
IContractResolver serviceResolver;
|
|
Dictionary<string, ContractDescription> behaviorContracts;
|
|
|
|
public Dictionary<string, ContractDescription> BehaviorContracts
|
|
{
|
|
get { return behaviorContracts; }
|
|
}
|
|
|
|
public ServiceAndBehaviorsContractResolver(IContractResolver serviceResolver)
|
|
{
|
|
this.serviceResolver = serviceResolver;
|
|
behaviorContracts = new Dictionary<string, ContractDescription>();
|
|
}
|
|
|
|
public ContractDescription ResolveContract(string contractName)
|
|
{
|
|
ContractDescription contract = serviceResolver.ResolveContract(contractName);
|
|
|
|
if (contract == null)
|
|
{
|
|
contract = this.behaviorContracts.ContainsKey(contractName) ? this.behaviorContracts[contractName] : null;
|
|
}
|
|
|
|
return contract;
|
|
}
|
|
|
|
public void AddBehaviorContractsToResolver(KeyedByTypeCollection<IServiceBehavior> behaviors)
|
|
{
|
|
// It would be nice to make this loop over all Behaviors... someday.
|
|
if (behaviors != null && behaviors.Contains(typeof(ServiceMetadataBehavior)))
|
|
{
|
|
behaviors.Find<ServiceMetadataBehavior>().AddImplementedContracts(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ServiceHost : ServiceHostBase
|
|
{
|
|
object singletonInstance;
|
|
Type serviceType;
|
|
ReflectedContractCollection reflectedContracts;
|
|
IDisposable disposableInstance;
|
|
|
|
protected ServiceHost()
|
|
{
|
|
}
|
|
|
|
public ServiceHost(Type serviceType, params Uri[] baseAddresses)
|
|
{
|
|
if (serviceType == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serviceType"));
|
|
}
|
|
|
|
this.serviceType = serviceType;
|
|
using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
|
|
{
|
|
if (DiagnosticUtility.ShouldUseActivity)
|
|
{
|
|
ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityConstructServiceHost, serviceType.FullName), ActivityType.Construct);
|
|
}
|
|
|
|
InitializeDescription(serviceType, new UriSchemeKeyedCollection(baseAddresses));
|
|
}
|
|
}
|
|
|
|
public ServiceHost(object singletonInstance, params Uri[] baseAddresses)
|
|
{
|
|
if (singletonInstance == null)
|
|
{
|
|
throw new ArgumentNullException("singletonInstance");
|
|
}
|
|
|
|
this.singletonInstance = singletonInstance;
|
|
this.serviceType = singletonInstance.GetType();
|
|
using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
|
|
{
|
|
if (DiagnosticUtility.ShouldUseActivity)
|
|
{
|
|
ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityConstructServiceHost, serviceType.FullName), ActivityType.Construct);
|
|
}
|
|
|
|
InitializeDescription(singletonInstance, new UriSchemeKeyedCollection(baseAddresses));
|
|
}
|
|
}
|
|
|
|
public object SingletonInstance
|
|
{
|
|
get
|
|
{
|
|
return this.singletonInstance;
|
|
}
|
|
}
|
|
|
|
internal override object DisposableInstance
|
|
{
|
|
get
|
|
{
|
|
return this.disposableInstance;
|
|
}
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address)
|
|
{
|
|
return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null);
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri)
|
|
{
|
|
if (address == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address"));
|
|
}
|
|
|
|
ServiceEndpoint endpoint = this.AddServiceEndpoint(implementedContract, binding, new Uri(address, UriKind.RelativeOrAbsolute));
|
|
if (listenUri != null)
|
|
{
|
|
listenUri = MakeAbsoluteUri(listenUri, binding);
|
|
endpoint.ListenUri = listenUri;
|
|
}
|
|
return endpoint;
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address)
|
|
{
|
|
return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null);
|
|
}
|
|
|
|
void ValidateContractType(Type implementedContract, ReflectedAndBehaviorContractCollection reflectedAndBehaviorContracts)
|
|
{
|
|
if (!implementedContract.IsDefined(typeof(ServiceContractAttribute), false))
|
|
{
|
|
#pragma warning suppress 56506 // implementedContract is never null at this point
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxServiceContractAttributeNotFound, implementedContract.FullName)));
|
|
}
|
|
if (!reflectedAndBehaviorContracts.Contains(implementedContract))
|
|
{
|
|
if (implementedContract == typeof(IMetadataExchange))
|
|
#pragma warning suppress 56506 // ServiceType is never null at this point
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFoundIMetadataExchange, this.serviceType.FullName)));
|
|
else
|
|
#pragma warning suppress 56506 // implementedContract and ServiceType are never null at this point
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFound2, implementedContract.FullName, this.serviceType.FullName)));
|
|
}
|
|
}
|
|
|
|
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri)
|
|
{
|
|
if (implementedContract == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("implementedContract"));
|
|
}
|
|
if (this.reflectedContracts == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractsNotInitialized1, implementedContract.FullName)));
|
|
}
|
|
ReflectedAndBehaviorContractCollection reflectedAndBehaviorContracts = new ReflectedAndBehaviorContractCollection(this.reflectedContracts, this.Description.Behaviors);
|
|
ValidateContractType(implementedContract, reflectedAndBehaviorContracts);
|
|
ServiceEndpoint endpoint = AddServiceEndpoint(reflectedAndBehaviorContracts.GetConfigKey(implementedContract), binding, address);
|
|
if (listenUri != null)
|
|
{
|
|
listenUri = MakeAbsoluteUri(listenUri, binding);
|
|
endpoint.ListenUri = listenUri;
|
|
}
|
|
return endpoint;
|
|
}
|
|
|
|
internal override void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)
|
|
{
|
|
// don't generate endpoints for contracts that serve as the base type for other reflected contracts
|
|
List<ContractDescription> mostSpecificContracts = new List<ContractDescription>();
|
|
for (int i = 0; i < this.reflectedContracts.Count; i++)
|
|
{
|
|
bool addContractEndpoint = true;
|
|
ContractDescription contract = this.reflectedContracts[i];
|
|
Type contractType = contract.ContractType;
|
|
if (contractType != null)
|
|
{
|
|
for (int j = 0; j < this.reflectedContracts.Count; j++)
|
|
{
|
|
ContractDescription otherContract = this.reflectedContracts[j];
|
|
Type otherContractType = otherContract.ContractType;
|
|
if (i == j || otherContractType == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (contractType.IsAssignableFrom(otherContractType))
|
|
{
|
|
addContractEndpoint = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (addContractEndpoint)
|
|
{
|
|
mostSpecificContracts.Add(contract);
|
|
}
|
|
}
|
|
|
|
foreach (ContractDescription contract in mostSpecificContracts)
|
|
{
|
|
ServiceEndpoint endpoint = AddServiceEndpoint(contract.ConfigurationName, defaultBinding, string.Empty);
|
|
ConfigLoader.LoadDefaultEndpointBehaviors(endpoint);
|
|
defaultEndpoints.Add(endpoint);
|
|
}
|
|
}
|
|
|
|
// Run static Configure method on service type if it exists, else load configuration from Web.config/App.config
|
|
protected override void ApplyConfiguration()
|
|
{
|
|
// Load from static Configure method if it exists with the right signature
|
|
Type serviceType = this.Description.ServiceType;
|
|
if (serviceType != null)
|
|
{
|
|
MethodInfo configure = GetConfigureMethod(serviceType);
|
|
if (configure != null)
|
|
{
|
|
// load <host> config
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts));
|
|
LoadHostConfigurationInternal(configLoader, this.Description, this.Description.ConfigurationName);
|
|
|
|
// Invoke configure method for service
|
|
ServiceConfiguration configuration = new ServiceConfiguration(this);
|
|
InvokeConfigure(configure, configuration);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// else just load from Web.config/App.config
|
|
base.ApplyConfiguration();
|
|
}
|
|
|
|
// Find the Configure method with the required signature, closest to serviceType in the type hierarchy
|
|
static MethodInfo GetConfigureMethod(Type serviceType)
|
|
{
|
|
// Use recursion instead of BindingFlags.FlattenHierarchy because we require return type to be void
|
|
|
|
// base case: all Types are rooted in object eventually
|
|
if (serviceType == typeof(object))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// signature: "public static void Configure(ServiceConfiguration)"
|
|
MethodInfo configure = serviceType.GetMethod("Configure", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(ServiceConfiguration) }, null);
|
|
|
|
if (configure != null && configure.ReturnType == typeof(void))
|
|
{
|
|
return configure;
|
|
}
|
|
else
|
|
{
|
|
return GetConfigureMethod(serviceType.BaseType);
|
|
}
|
|
}
|
|
|
|
static void InvokeConfigure(MethodInfo configureMethod, ServiceConfiguration configuration)
|
|
{
|
|
Action<ServiceConfiguration> call = Delegate.CreateDelegate(typeof(Action<ServiceConfiguration>), configureMethod) as Action<ServiceConfiguration>;
|
|
call(configuration);
|
|
}
|
|
|
|
// called from ServiceConfiguration.LoadFromConfiguration()
|
|
internal void LoadFromConfiguration()
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription)));
|
|
}
|
|
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts));
|
|
|
|
// Call the overload of LoadConfigurationSectionInternal which looks up the serviceElement from ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
|
|
LoadConfigurationSectionExceptHostInternal(configLoader, this.Description, this.Description.ConfigurationName);
|
|
EnsureAuthenticationAuthorizationDebug(this.Description);
|
|
}
|
|
|
|
// called from ServiceConfiguration.LoadFromConfiguration(configuration)
|
|
internal void LoadFromConfiguration(System.Configuration.Configuration configuration)
|
|
{
|
|
if (this.Description == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription)));
|
|
}
|
|
|
|
ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts));
|
|
|
|
// Look up the serviceElement explicitly on configuration, then call the overload of LoadConfigurationSectionInternal that loads the rest of the config from the same configuration as serviceElement
|
|
ServicesSection servicesSection = (ServicesSection)configuration.GetSection(ConfigurationStrings.ServicesSectionPath);
|
|
ServiceElement serviceElement = configLoader.LookupService(this.Description.ConfigurationName, servicesSection);
|
|
configLoader.LoadServiceDescription(this, this.Description, serviceElement, this.LoadConfigurationSectionHelper, skipHost: true);
|
|
|
|
EnsureAuthenticationAuthorizationDebug(this.Description);
|
|
}
|
|
|
|
// Load only "host" section within "service" tag
|
|
[Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.",
|
|
Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")]
|
|
[SecuritySafeCritical]
|
|
void LoadHostConfigurationInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName)
|
|
{
|
|
ServiceElement serviceSection = configLoader.LookupService(configurationName);
|
|
if (serviceSection != null)
|
|
{
|
|
configLoader.LoadHostConfig(serviceSection, this, (addr => this.InternalBaseAddresses.Add(addr)));
|
|
}
|
|
}
|
|
|
|
// Load service description for service from config, but skip "host" section within "service" tag
|
|
[Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.",
|
|
Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")]
|
|
[SecuritySafeCritical]
|
|
void LoadConfigurationSectionExceptHostInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName)
|
|
{
|
|
ServiceElement serviceSection = configLoader.LookupService(configurationName);
|
|
configLoader.LoadServiceDescription(this, description, serviceSection, this.LoadConfigurationSectionHelper, skipHost: true);
|
|
}
|
|
|
|
internal override string CloseActivityName
|
|
{
|
|
get { return SR.GetString(SR.ActivityCloseServiceHost, this.serviceType.FullName); }
|
|
}
|
|
|
|
internal override string OpenActivityName
|
|
{
|
|
get { return SR.GetString(SR.ActivityOpenServiceHost, this.serviceType.FullName); }
|
|
}
|
|
|
|
protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)
|
|
{
|
|
if (this.serviceType == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostCannotCreateDescriptionWithoutServiceType)));
|
|
}
|
|
|
|
ServiceDescription description;
|
|
if (this.SingletonInstance != null)
|
|
{
|
|
description = ServiceDescription.GetService(this.SingletonInstance);
|
|
}
|
|
else
|
|
{
|
|
description = ServiceDescription.GetService(this.serviceType);
|
|
}
|
|
ServiceBehaviorAttribute serviceBehavior = description.Behaviors.Find<ServiceBehaviorAttribute>();
|
|
object serviceInstanceUsedAsABehavior = serviceBehavior.GetWellKnownSingleton();
|
|
if (serviceInstanceUsedAsABehavior == null)
|
|
{
|
|
serviceInstanceUsedAsABehavior = serviceBehavior.GetHiddenSingleton();
|
|
this.disposableInstance = serviceInstanceUsedAsABehavior as IDisposable;
|
|
}
|
|
|
|
if ((typeof(IServiceBehavior).IsAssignableFrom(this.serviceType) || typeof(IContractBehavior).IsAssignableFrom(this.serviceType))
|
|
&& serviceInstanceUsedAsABehavior == null)
|
|
{
|
|
serviceInstanceUsedAsABehavior = ServiceDescription.CreateImplementation(this.serviceType);
|
|
this.disposableInstance = serviceInstanceUsedAsABehavior as IDisposable;
|
|
}
|
|
|
|
if (this.SingletonInstance == null)
|
|
{
|
|
if (serviceInstanceUsedAsABehavior is IServiceBehavior)
|
|
{
|
|
description.Behaviors.Add((IServiceBehavior)serviceInstanceUsedAsABehavior);
|
|
}
|
|
}
|
|
|
|
ReflectedContractCollection reflectedContracts = new ReflectedContractCollection();
|
|
List<Type> interfaces = ServiceReflector.GetInterfaces(this.serviceType);
|
|
for (int i = 0; i < interfaces.Count; i++)
|
|
{
|
|
Type contractType = interfaces[i];
|
|
if (!reflectedContracts.Contains(contractType))
|
|
{
|
|
ContractDescription contract = null;
|
|
if (serviceInstanceUsedAsABehavior != null)
|
|
{
|
|
contract = ContractDescription.GetContract(contractType, serviceInstanceUsedAsABehavior);
|
|
}
|
|
else
|
|
{
|
|
contract = ContractDescription.GetContract(contractType, this.serviceType);
|
|
}
|
|
|
|
reflectedContracts.Add(contract);
|
|
Collection<ContractDescription> inheritedContracts = contract.GetInheritedContracts();
|
|
for (int j = 0; j < inheritedContracts.Count; j++)
|
|
{
|
|
ContractDescription inheritedContract = inheritedContracts[j];
|
|
if (!reflectedContracts.Contains(inheritedContract.ContractType))
|
|
{
|
|
reflectedContracts.Add(inheritedContract);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.reflectedContracts = reflectedContracts;
|
|
|
|
implementedContracts = reflectedContracts.ToImplementedContracts();
|
|
return description;
|
|
}
|
|
|
|
protected void InitializeDescription(object singletonInstance, UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
if (singletonInstance == null)
|
|
{
|
|
throw new ArgumentNullException("singletonInstance");
|
|
}
|
|
|
|
this.singletonInstance = singletonInstance;
|
|
InitializeDescription(singletonInstance.GetType(), baseAddresses);
|
|
}
|
|
|
|
protected void InitializeDescription(Type serviceType, UriSchemeKeyedCollection baseAddresses)
|
|
{
|
|
if (serviceType == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serviceType"));
|
|
}
|
|
|
|
this.serviceType = serviceType;
|
|
|
|
base.InitializeDescription(baseAddresses);
|
|
}
|
|
|
|
protected override void OnClosed()
|
|
{
|
|
base.OnClosed();
|
|
if (this.disposableInstance != null)
|
|
{
|
|
this.disposableInstance.Dispose();
|
|
}
|
|
}
|
|
|
|
class ReflectedContractCollection : KeyedCollection<Type, ContractDescription>
|
|
{
|
|
public ReflectedContractCollection()
|
|
: base(null, 4)
|
|
{
|
|
}
|
|
|
|
protected override Type GetKeyForItem(ContractDescription item)
|
|
{
|
|
if (item == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
|
|
|
return item.ContractType;
|
|
}
|
|
|
|
public IDictionary<string, ContractDescription> ToImplementedContracts()
|
|
{
|
|
Dictionary<string, ContractDescription> implementedContracts = new Dictionary<string, ContractDescription>();
|
|
foreach (ContractDescription contract in this.Items)
|
|
{
|
|
implementedContracts.Add(GetConfigKey(contract), contract);
|
|
}
|
|
return implementedContracts;
|
|
}
|
|
|
|
internal static string GetConfigKey(ContractDescription contract)
|
|
{
|
|
return contract.ConfigurationName;
|
|
}
|
|
}
|
|
|
|
class ReflectedAndBehaviorContractCollection
|
|
{
|
|
ReflectedContractCollection reflectedContracts;
|
|
KeyedByTypeCollection<IServiceBehavior> behaviors;
|
|
public ReflectedAndBehaviorContractCollection(ReflectedContractCollection reflectedContracts, KeyedByTypeCollection<IServiceBehavior> behaviors)
|
|
{
|
|
this.reflectedContracts = reflectedContracts;
|
|
this.behaviors = behaviors;
|
|
}
|
|
|
|
internal bool Contains(Type implementedContract)
|
|
{
|
|
if (this.reflectedContracts.Contains(implementedContract))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal string GetConfigKey(Type implementedContract)
|
|
{
|
|
if (this.reflectedContracts.Contains(implementedContract))
|
|
{
|
|
return ReflectedContractCollection.GetConfigKey(reflectedContracts[implementedContract]);
|
|
}
|
|
|
|
if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract))
|
|
{
|
|
return ServiceMetadataBehavior.MexContractName;
|
|
}
|
|
|
|
Fx.Assert("Calls to GetConfigKey are preceeded by calls to Contains.");
|
|
#pragma warning suppress 56506 // implementedContract is never null at this point
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFound2, implementedContract.FullName, string.Empty)));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|