e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
236 lines
10 KiB
C#
236 lines
10 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="ClientConfigurationSystem.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Configuration {
|
|
using System.Configuration.Internal;
|
|
using System.Globalization;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Threading;
|
|
using System.Net;
|
|
using Assembly = System.Reflection.Assembly;
|
|
using StringBuilder = System.Text.StringBuilder;
|
|
|
|
internal sealed class ClientConfigurationSystem : IInternalConfigSystem {
|
|
private const string SystemDiagnosticsConfigKey = "system.diagnostics";
|
|
private const string SystemNetGroupKey = "system.net/";
|
|
|
|
private IConfigSystem _configSystem;
|
|
private IInternalConfigRoot _configRoot;
|
|
private ClientConfigurationHost _configHost;
|
|
private IInternalConfigRecord _machineConfigRecord;
|
|
private IInternalConfigRecord _completeConfigRecord;
|
|
private Exception _initError;
|
|
private bool _isInitInProgress;
|
|
private bool _isMachineConfigInited;
|
|
private bool _isUserConfigInited;
|
|
private bool _isAppConfigHttp;
|
|
|
|
|
|
internal ClientConfigurationSystem() {
|
|
_configSystem = new ConfigSystem();
|
|
_configSystem.Init(typeof(ClientConfigurationHost), null, null);
|
|
|
|
_configHost = (ClientConfigurationHost) _configSystem.Host;
|
|
_configRoot = _configSystem.Root;
|
|
|
|
_configRoot.ConfigRemoved += OnConfigRemoved;
|
|
|
|
_isAppConfigHttp = _configHost.IsAppConfigHttp;
|
|
|
|
// VSWhidbey 606116: Config has a dependency on Uri class which has
|
|
// a new static constructor that calls config. We need a dummy reference
|
|
// to Uri class so the static constructor would be involved first to
|
|
// initialize config.
|
|
string dummy = System.Uri.SchemeDelimiter;
|
|
}
|
|
|
|
// Return true if the section might be used during initialization of the configuration system,
|
|
// and thus lead to deadlock if appropriate measures are not taken.
|
|
bool IsSectionUsedInInit(string configKey) {
|
|
return configKey == SystemDiagnosticsConfigKey || (_isAppConfigHttp && configKey.StartsWith(SystemNetGroupKey, StringComparison.Ordinal));
|
|
}
|
|
|
|
// Return true if the section should only use the machine configuration and not use the
|
|
// application configuration. This is only true for system.net sections when the configuration
|
|
// file for the application is downloaded via http using System.Net.WebClient.
|
|
bool DoesSectionOnlyUseMachineConfig(string configKey) {
|
|
return _isAppConfigHttp && configKey.StartsWith(SystemNetGroupKey, StringComparison.Ordinal);
|
|
}
|
|
|
|
// Ensure that initialization has completed, while handling re-entrancy issues
|
|
// for certain sections that may be used during initialization itself.
|
|
void EnsureInit(string configKey) {
|
|
bool doInit = false;
|
|
|
|
lock (this) {
|
|
// If the user config is not initialized, then we must either:
|
|
// a. Perform the initialization ourselves if no other thread is doing it, or
|
|
// b. Wait for the initialization to complete if we know the section is not used during initialization itself, or
|
|
// c. Ignore initialization if the section can be used during initialization. Note that GetSection()
|
|
// returns null is initialization has not completed.
|
|
if (!_isUserConfigInited) {
|
|
if (!_isInitInProgress) {
|
|
_isInitInProgress = true;
|
|
doInit = true;
|
|
}
|
|
else if (!IsSectionUsedInInit(configKey)) {
|
|
// Wait for initialization to complete.
|
|
Monitor.Wait(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (doInit) {
|
|
try {
|
|
try {
|
|
try {
|
|
// Initialize machine configuration.
|
|
_machineConfigRecord = _configRoot.GetConfigRecord(
|
|
ClientConfigurationHost.MachineConfigPath);
|
|
|
|
_machineConfigRecord.ThrowIfInitErrors();
|
|
|
|
// Make machine configuration available to system.net sections
|
|
// when application configuration is downloaded via http.
|
|
_isMachineConfigInited = true;
|
|
|
|
//
|
|
// Prevent deadlocks in the networking classes by loading
|
|
// networking config before making a networking request.
|
|
// Any requests for sections used in initialization during
|
|
// the call to EnsureConfigLoaded() will be served by
|
|
// _machine.config or will return null.
|
|
//
|
|
if (_isAppConfigHttp) {
|
|
ConfigurationManagerHelperFactory.Instance.EnsureNetConfigLoaded();
|
|
}
|
|
|
|
//
|
|
// Now load the rest of configuration
|
|
//
|
|
_configHost.RefreshConfigPaths();
|
|
string configPath;
|
|
if (_configHost.HasLocalConfig) {
|
|
configPath = ClientConfigurationHost.LocalUserConfigPath;
|
|
}
|
|
else if (_configHost.HasRoamingConfig) {
|
|
configPath = ClientConfigurationHost.RoamingUserConfigPath;
|
|
}
|
|
else {
|
|
configPath = ClientConfigurationHost.ExeConfigPath;
|
|
}
|
|
|
|
_completeConfigRecord = _configRoot.GetConfigRecord(configPath);
|
|
_completeConfigRecord.ThrowIfInitErrors();
|
|
|
|
_isUserConfigInited = true;
|
|
}
|
|
catch (Exception e) {
|
|
_initError = new ConfigurationErrorsException(SR.GetString(SR.Config_client_config_init_error), e);
|
|
throw _initError;
|
|
}
|
|
}
|
|
catch {
|
|
ConfigurationManager.SetInitError(_initError);
|
|
_isMachineConfigInited = true;
|
|
_isUserConfigInited = true;
|
|
throw;
|
|
}
|
|
}
|
|
finally {
|
|
lock (this) {
|
|
try {
|
|
// Notify ConfigurationSettings that initialization has fully completed,
|
|
// even if unsuccessful.
|
|
ConfigurationManager.CompleteConfigInit();
|
|
|
|
_isInitInProgress = false;
|
|
|
|
}
|
|
finally {
|
|
// Wake up all threads waiting for initialization to complete.
|
|
Monitor.PulseAll(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PrepareClientConfigSystem(string sectionName) {
|
|
// Ensure the configuration system is inited for this section.
|
|
if (!_isUserConfigInited) {
|
|
EnsureInit(sectionName);
|
|
}
|
|
|
|
// If an error occurred during initialzation, throw it.
|
|
if (_initError != null) {
|
|
throw _initError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If config has been removed because initialization was not complete,
|
|
// fetch a new configuration record. The record will be created and
|
|
// completely initialized as RequireCompleteInit() will have been called
|
|
// on the ClientConfigurationHost before we receive this event.
|
|
//
|
|
private void OnConfigRemoved(object sender, InternalConfigEventArgs e) {
|
|
try {
|
|
IInternalConfigRecord newConfigRecord = _configRoot.GetConfigRecord(_completeConfigRecord.ConfigPath);
|
|
_completeConfigRecord = newConfigRecord;
|
|
_completeConfigRecord.ThrowIfInitErrors();
|
|
}
|
|
catch (Exception ex) {
|
|
_initError = new ConfigurationErrorsException(SR.GetString(SR.Config_client_config_init_error), ex);
|
|
ConfigurationManager.SetInitError(_initError);
|
|
throw _initError;
|
|
}
|
|
}
|
|
|
|
object IInternalConfigSystem.GetSection(string sectionName) {
|
|
PrepareClientConfigSystem(sectionName);
|
|
|
|
// Get the appropriate config record for the section.
|
|
IInternalConfigRecord configRecord = null;
|
|
if (DoesSectionOnlyUseMachineConfig(sectionName)) {
|
|
if (_isMachineConfigInited) {
|
|
configRecord = _machineConfigRecord;
|
|
}
|
|
}
|
|
else {
|
|
if (_isUserConfigInited) {
|
|
configRecord = _completeConfigRecord;
|
|
}
|
|
}
|
|
|
|
// Call GetSection(), or return null if no configuration is yet available.
|
|
if (configRecord != null) {
|
|
return configRecord.GetSection(sectionName);
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void IInternalConfigSystem.RefreshConfig(string sectionName) {
|
|
PrepareClientConfigSystem(sectionName);
|
|
|
|
if (_isMachineConfigInited) {
|
|
_machineConfigRecord.RefreshSection(sectionName);
|
|
}
|
|
}
|
|
|
|
// Supports user config
|
|
bool IInternalConfigSystem.SupportsUserConfig {
|
|
get {return true;}
|
|
}
|
|
}
|
|
}
|