File: FrameworkFork\System.ServiceModel\System\ServiceModel\Description\MetadataExchangeClient.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace System.ServiceModel.Description
{
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.Security;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Xml;
    using WsdlNS = System.Web.Services.Description;
    using XsdNS = Microsoft.Xml.Schema;
 
    public partial class MetadataExchangeClient
    {
        private ChannelFactory<IMetadataExchange> _factory;
        private ICredentials _webRequestCredentials;
 
        private TimeSpan _resolveTimeout = TimeSpan.FromMinutes(1);
        private int _maximumResolvedReferences = 10;
        private bool _resolveMetadataReferences = true;
        private long _maxMessageSize;
        private XmlDictionaryReaderQuotas _readerQuotas;
 
        private EndpointAddress _ctorEndpointAddress = null;
        private Uri _ctorUri = null;
 
        private object _thisLock = new object();
 
        internal const string MetadataExchangeClientKey = "MetadataExchangeClientKey";
 
        public MetadataExchangeClient()
        {
            _factory = new ChannelFactory<IMetadataExchange>("*");
            _maxMessageSize = GetMaxMessageSize(_factory.Endpoint.Binding);
        }
        public MetadataExchangeClient(Uri address, MetadataExchangeClientMode mode)
        {
            Validate(address, mode);
 
            if (mode == MetadataExchangeClientMode.HttpGet)
            {
                _ctorUri = address;
            }
            else
            {
                _ctorEndpointAddress = new EndpointAddress(address);
            }
 
            CreateChannelFactory(address.Scheme);
        }
        public MetadataExchangeClient(EndpointAddress address)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            _ctorEndpointAddress = address;
 
            CreateChannelFactory(address.Uri.Scheme);
        }
        public MetadataExchangeClient(string endpointConfigurationName)
        {
            if (endpointConfigurationName == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
            }
            _factory = new ChannelFactory<IMetadataExchange>(endpointConfigurationName);
            _maxMessageSize = GetMaxMessageSize(_factory.Endpoint.Binding);
        }
        public MetadataExchangeClient(Binding mexBinding)
        {
            if (mexBinding == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("mexBinding");
            }
            _factory = new ChannelFactory<IMetadataExchange>(mexBinding);
            _maxMessageSize = GetMaxMessageSize(_factory.Endpoint.Binding);
        }
 
        //Configuration for credentials
        public ClientCredentials SoapCredentials
        {
            get { return _factory.Credentials; }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
                _factory.Endpoint.Behaviors.RemoveAll<ClientCredentials>();
                _factory.Endpoint.Behaviors.Add(value);
            }
        }
        public ICredentials HttpCredentials
        {
            get { return _webRequestCredentials; }
            set { _webRequestCredentials = value; }
        }
 
        // Configuration options for the entire MetadataResolver
        public TimeSpan OperationTimeout
        {
            get { return _resolveTimeout; }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRange0));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRangeTooBig));
                }
 
                _resolveTimeout = value;
            }
        }
        public int MaximumResolvedReferences
        {
            get { return _maximumResolvedReferences; }
            set
            {
                if (value < 1)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", string.Format(SRServiceModel.SFxMaximumResolvedReferencesOutOfRange, value)));
                }
                _maximumResolvedReferences = value;
            }
        }
        public bool ResolveMetadataReferences
        {
            get { return _resolveMetadataReferences; }
            set { _resolveMetadataReferences = value; }
        }
 
        internal object ThisLock
        {
            get { return _thisLock; }
        }
 
        internal long MaxMessageSize
        {
            get { return _maxMessageSize; }
            set { _maxMessageSize = value; }
        }
 
        internal XmlDictionaryReaderQuotas ReaderQuotas
        {
            get
            {
                if (_readerQuotas == null)
                {
                    if (_factory != null)
                    {
                        BindingElementCollection bindingElementCollection = _factory.Endpoint.Binding.CreateBindingElements();
                        if (bindingElementCollection != null)
                        {
                            MessageEncodingBindingElement bindingElement = bindingElementCollection.Find<MessageEncodingBindingElement>();
                            if (bindingElement != null)
                            {
                                _readerQuotas = bindingElement.GetIndividualProperty<XmlDictionaryReaderQuotas>();
                            }
                        }
                    }
                    _readerQuotas = _readerQuotas ?? EncoderDefaults.ReaderQuotas;
                }
                return _readerQuotas;
            }
        }
 
        private bool IsHttpOrHttps(Uri address)
        {
            // TODO
            // return address.Scheme == Uri.UriSchemeHttp || address.Scheme == Uri.UriSchemeHttps;
            return address.Scheme == "http" || address.Scheme == "https";
        }
 
        private void CreateChannelFactory(string scheme)
        {
            Binding mexBinding = null;
            if (MetadataExchangeBindings.TryGetBindingForScheme(scheme, out mexBinding))
            {
                _factory = new ChannelFactory<IMetadataExchange>(mexBinding);
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("scheme", string.Format(SRServiceModel.SFxMetadataExchangeClientCouldNotCreateChannelFactoryBadScheme, scheme));
            }
            _maxMessageSize = GetMaxMessageSize(_factory.Endpoint.Binding);
        }
 
        private void Validate(Uri address, MetadataExchangeClientMode mode)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            if (!address.IsAbsoluteUri)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("address", string.Format(SRServiceModel.SFxCannotGetMetadataFromRelativeAddress, address));
            }
 
            if (mode == MetadataExchangeClientMode.HttpGet && !IsHttpOrHttps(address))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("address", string.Format(SRServiceModel.SFxCannotHttpGetMetadataFromAddress, address));
            }
 
            MetadataExchangeClientModeHelper.Validate(mode);
        }
 
        public IAsyncResult BeginGetMetadata(AsyncCallback callback, object asyncState)
        {
            if (_ctorUri != null)
                return BeginGetMetadata(_ctorUri, MetadataExchangeClientMode.HttpGet, callback, asyncState);
            if (_ctorEndpointAddress != null)
                return BeginGetMetadata(_ctorEndpointAddress, callback, asyncState);
            else
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxMetadataExchangeClientNoMetadataAddress));
        }
 
        public IAsyncResult BeginGetMetadata(Uri address, MetadataExchangeClientMode mode, AsyncCallback callback, object asyncState)
        {
            Validate(address, mode);
 
            if (mode == MetadataExchangeClientMode.HttpGet)
            {
                return this.BeginGetMetadata(new MetadataLocationRetriever(address, this), callback, asyncState);
            }
            else
            {
                return this.BeginGetMetadata(new MetadataReferenceRetriever(new EndpointAddress(address), this), callback, asyncState);
            }
        }
 
        public IAsyncResult BeginGetMetadata(EndpointAddress address, AsyncCallback callback, object asyncState)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
            return this.BeginGetMetadata(new MetadataReferenceRetriever(address, this), callback, asyncState);
        }
 
        private IAsyncResult BeginGetMetadata(MetadataRetriever retriever, AsyncCallback callback, object asyncState)
        {
            ResolveCallState state = new ResolveCallState(_maximumResolvedReferences, _resolveMetadataReferences, new TimeoutHelper(this.OperationTimeout), this);
            state.StackedRetrievers.Push(retriever);
            return new AsyncMetadataResolver(state, callback, asyncState);
        }
 
        public MetadataSet EndGetMetadata(IAsyncResult result)
        {
            return AsyncMetadataResolver.End(result);
        }
 
        public Task<MetadataSet> GetMetadataAsync()
        {
            if (_ctorUri != null)
                return GetMetadataAsync(_ctorUri, MetadataExchangeClientMode.HttpGet);
            if (_ctorEndpointAddress != null)
                return GetMetadataAsync(_ctorEndpointAddress);
            else
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxMetadataExchangeClientNoMetadataAddress));
        }
 
        public Task<MetadataSet> GetMetadataAsync(Uri address, MetadataExchangeClientMode mode)
        {
            Validate(address, mode);
 
            MetadataRetriever retriever = (mode == MetadataExchangeClientMode.HttpGet)
                                                ? (MetadataRetriever)new MetadataLocationRetriever(address, this)
                                                : (MetadataRetriever)new MetadataReferenceRetriever(new EndpointAddress(address), this);
 
            return Task.Factory.FromAsync<MetadataRetriever, MetadataSet>(this.BeginGetMetadata, this.EndGetMetadata, retriever, /* state */ null);
        }
 
        public Task<MetadataSet> GetMetadataAsync(EndpointAddress address)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            return Task.Factory.FromAsync<MetadataRetriever, MetadataSet>(this.BeginGetMetadata, this.EndGetMetadata, new MetadataReferenceRetriever(address, this), /* state */ null);
        }
 
        public Task<MetadataSet> GetMetadataAsync(EndpointAddress address, Uri via)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            if (via == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("via");
            }
 
            return Task.Factory.FromAsync<MetadataRetriever, MetadataSet>(this.BeginGetMetadata, this.EndGetMetadata, new MetadataReferenceRetriever(address, via, this), /* state */ null);
        }
 
        public MetadataSet GetMetadata()
        {
            if (_ctorUri != null)
                return GetMetadata(_ctorUri, MetadataExchangeClientMode.HttpGet);
            if (_ctorEndpointAddress != null)
                return GetMetadata(_ctorEndpointAddress);
            else
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxMetadataExchangeClientNoMetadataAddress));
        }
 
        public MetadataSet GetMetadata(Uri address, MetadataExchangeClientMode mode)
        {
            Validate(address, mode);
 
            MetadataRetriever retriever;
            if (mode == MetadataExchangeClientMode.HttpGet)
            {
                retriever = new MetadataLocationRetriever(address, this);
            }
            else
            {
                retriever = new MetadataReferenceRetriever(new EndpointAddress(address), this);
            }
            return GetMetadata(retriever);
        }
 
        public MetadataSet GetMetadata(EndpointAddress address)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            MetadataReferenceRetriever retriever = new MetadataReferenceRetriever(address, this);
            return GetMetadata(retriever);
        }
 
        public MetadataSet GetMetadata(EndpointAddress address, Uri via)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
 
            if (via == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("via");
            }
 
            MetadataReferenceRetriever retriever = new MetadataReferenceRetriever(address, via, this);
            return GetMetadata(retriever);
        }
 
        private MetadataSet GetMetadata(MetadataRetriever retriever)
        {
            ResolveCallState resolveCallState = new ResolveCallState(_maximumResolvedReferences, _resolveMetadataReferences, new TimeoutHelper(this.OperationTimeout), this);
            resolveCallState.StackedRetrievers.Push(retriever);
            this.ResolveNext(resolveCallState);
 
            return resolveCallState.MetadataSet;
        }
 
        private void ResolveNext(ResolveCallState resolveCallState)
        {
            if (resolveCallState.StackedRetrievers.Count > 0)
            {
                MetadataRetriever retriever = resolveCallState.StackedRetrievers.Pop();
 
                if (resolveCallState.HasBeenUsed(retriever))
                {
                    this.ResolveNext(resolveCallState);
                }
                else
                {
                    if (resolveCallState.ResolvedMaxResolvedReferences)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxResolvedMaxResolvedReferences));
                    }
 
                    resolveCallState.LogUse(retriever);
                    resolveCallState.HandleSection(retriever.Retrieve(resolveCallState.TimeoutHelper));
                    this.ResolveNext(resolveCallState);
                }
            }
        }
 
        protected internal virtual ChannelFactory<IMetadataExchange> GetChannelFactory(EndpointAddress metadataAddress, string dialect, string identifier)
        {
            return _factory;
        }
 
        private static long GetMaxMessageSize(Binding mexBinding)
        {
            BindingElementCollection bindingElementCollection = mexBinding.CreateBindingElements();
            TransportBindingElement bindingElement = bindingElementCollection.Find<TransportBindingElement>();
            if (bindingElement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxBindingDoesNotHaveATransportBindingElement));
            }
            return bindingElement.MaxReceivedMessageSize;
        }
 
        protected internal virtual HttpWebRequest GetWebRequest(Uri location, string dialect, string identifier)
        {
            ServicePointManager.CheckCertificateRevocationList = true;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(location);
            request.Method = "GET";
            request.Credentials = this.HttpCredentials;
 
            return request;
        }
 
        internal static void TraceSendRequest(Uri address)
        {
            TraceSendRequest(TraceCode.MetadataExchangeClientSendRequest, SRServiceModel.TraceCodeMetadataExchangeClientSendRequest,
                address.ToString(), MetadataExchangeClientMode.HttpGet.ToString());
        }
 
        internal static void TraceSendRequest(EndpointAddress address)
        {
            TraceSendRequest(TraceCode.MetadataExchangeClientSendRequest, SRServiceModel.TraceCodeMetadataExchangeClientSendRequest,
                address.ToString(), MetadataExchangeClientMode.MetadataExchange.ToString());
        }
 
        private static void TraceSendRequest(int traceCode, string traceDescription, string address, string mode)
        {
        }
 
        internal static void TraceReceiveReply(string sourceUrl, Type metadataType)
        {
        }
 
        private class ResolveCallState
        {
            private Dictionary<MetadataRetriever, MetadataRetriever> _usedRetrievers;   // to prevent looping when chasing MetadataReferences
            private MetadataSet _metadataSet;
            private int _maxResolvedReferences;
            private bool _resolveMetadataReferences;
            private Stack<MetadataRetriever> _stackedRetrievers;
            private MetadataExchangeClient _resolver;
            private TimeoutHelper _timeoutHelper;
 
            internal ResolveCallState(int maxResolvedReferences, bool resolveMetadataReferences,
                TimeoutHelper timeoutHelper, MetadataExchangeClient resolver)
            {
                _maxResolvedReferences = maxResolvedReferences;
                _resolveMetadataReferences = resolveMetadataReferences;
                _resolver = resolver;
                _timeoutHelper = timeoutHelper;
                _metadataSet = new MetadataSet();
                _usedRetrievers = new Dictionary<MetadataRetriever, MetadataRetriever>();
                _stackedRetrievers = new Stack<MetadataRetriever>();
            }
 
            internal MetadataSet MetadataSet
            {
                get { return _metadataSet; }
            }
 
            internal Stack<MetadataRetriever> StackedRetrievers
            {
                get { return _stackedRetrievers; }
            }
 
            internal bool ResolvedMaxResolvedReferences
            {
                get { return _usedRetrievers.Count == _maxResolvedReferences; }
            }
 
            internal TimeoutHelper TimeoutHelper
            {
                get { return _timeoutHelper; }
            }
 
            internal void HandleSection(MetadataSection section)
            {
                if (section.Metadata is MetadataSet)
                {
                    foreach (MetadataSection innerSection in ((MetadataSet)section.Metadata).MetadataSections)
                    {
                        innerSection.SourceUrl = section.SourceUrl;
                        this.HandleSection(innerSection);
                    }
                }
                else if (section.Metadata is MetadataReference)
                {
                    if (_resolveMetadataReferences)
                    {
                        EndpointAddress address = ((MetadataReference)section.Metadata).Address;
                        MetadataRetriever retriever = new MetadataReferenceRetriever(address, _resolver, section.Dialect, section.Identifier);
                        _stackedRetrievers.Push(retriever);
                    }
                    else
                    {
                        _metadataSet.MetadataSections.Add(section);
                    }
                }
                else if (section.Metadata is MetadataLocation)
                {
                    if (_resolveMetadataReferences)
                    {
                        string location = ((MetadataLocation)section.Metadata).Location;
                        MetadataRetriever retriever = new MetadataLocationRetriever(this.CreateUri(section.SourceUrl, location), _resolver, section.Dialect, section.Identifier);
                        _stackedRetrievers.Push(retriever);
                    }
                    else
                    {
                        _metadataSet.MetadataSections.Add(section);
                    }
                }
                else if (section.Metadata is WsdlNS.ServiceDescription)
                {
                    if (_resolveMetadataReferences)
                    {
                        this.HandleWsdlImports(section);
                    }
                    _metadataSet.MetadataSections.Add(section);
                }
                else if (section.Metadata is XsdNS.XmlSchema)
                {
                    if (_resolveMetadataReferences)
                    {
                        this.HandleSchemaImports(section);
                    }
                    _metadataSet.MetadataSections.Add(section);
                }
                else
                {
                    _metadataSet.MetadataSections.Add(section);
                }
            }
 
            private void HandleSchemaImports(MetadataSection section)
            {
                XsdNS.XmlSchema schema = (XsdNS.XmlSchema)section.Metadata;
                foreach (XsdNS.XmlSchemaExternal external in schema.Includes)
                {
                    if (!String.IsNullOrEmpty(external.SchemaLocation))
                    {
                        EnqueueRetrieverIfShouldResolve(
                            new MetadataLocationRetriever(
                                this.CreateUri(section.SourceUrl, external.SchemaLocation),
                                _resolver));
                    }
                }
            }
 
            private void HandleWsdlImports(MetadataSection section)
            {
                WsdlNS.ServiceDescription wsdl = (WsdlNS.ServiceDescription)section.Metadata;
                foreach (WsdlNS.Import import in wsdl.Imports)
                {
                    if (!String.IsNullOrEmpty(import.Location))
                    {
                        EnqueueRetrieverIfShouldResolve(new MetadataLocationRetriever(this.CreateUri(section.SourceUrl, import.Location), _resolver));
                    }
                }
 
                foreach (XsdNS.XmlSchema schema in wsdl.Types.Schemas)
                {
                    MetadataSection schemaSection = new MetadataSection(null, null, schema);
                    schemaSection.SourceUrl = section.SourceUrl;
                    this.HandleSchemaImports(schemaSection);
                }
            }
 
            private Uri CreateUri(string baseUri, string relativeUri)
            {
                return new Uri(new Uri(baseUri), relativeUri);
            }
 
            private void EnqueueRetrieverIfShouldResolve(MetadataRetriever retriever)
            {
                if (_resolveMetadataReferences)
                {
                    _stackedRetrievers.Push(retriever);
                }
            }
 
            internal bool HasBeenUsed(MetadataRetriever retriever)
            {
                return _usedRetrievers.ContainsKey(retriever);
            }
 
            internal void LogUse(MetadataRetriever retriever)
            {
                _usedRetrievers.Add(retriever, retriever);
            }
        }
 
        internal abstract class MetadataRetriever
        {
            protected MetadataExchangeClient resolver;
            protected string dialect;
            protected string identifier;
 
            public MetadataRetriever(MetadataExchangeClient resolver, string dialect, string identifier)
            {
                this.resolver = resolver;
                this.dialect = dialect;
                this.identifier = identifier;
            }
 
            internal MetadataSection Retrieve(TimeoutHelper timeoutHelper)
            {
                try
                {
                    XmlReader reader = this.DownloadMetadata(timeoutHelper);
                    {
                        return MetadataRetriever.CreateMetadataSection(reader, this.SourceUrl);
                    }
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxBadMetadataReference, this.SourceUrl), e));
                }
            }
 
            internal abstract IAsyncResult BeginRetrieve(TimeoutHelper timeoutHelper, AsyncCallback callback, object state);
            internal abstract MetadataSection EndRetrieve(IAsyncResult result);
 
            static internal MetadataSection CreateMetadataSection(XmlReader reader, string sourceUrl)
            {
                MetadataSection section = null;
                Type metadataType = null;
 
                if (CanReadMetadataSet(reader))
                {
                    MetadataSet newSet = MetadataSet.ReadFrom(reader);
                    section = new MetadataSection(MetadataSection.MetadataExchangeDialect, null, newSet);
                    metadataType = typeof(MetadataSet);
                }
                else if (WsdlNS.ServiceDescription.CanRead(reader))
                {
                    WsdlNS.ServiceDescription wsdl = WsdlNS.ServiceDescription.Read(reader);
                    section = MetadataSection.CreateFromServiceDescription(wsdl);
                    metadataType = typeof(WsdlNS.ServiceDescription);
                }
                else if (CanReadSchema(reader))
                {
                    XsdNS.XmlSchema schema = XsdNS.XmlSchema.Read(reader, null);
                    section = MetadataSection.CreateFromSchema(schema);
                    metadataType = typeof(XsdNS.XmlSchema);
                }
                else
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(reader);
                    section = new MetadataSection(null, null, doc.DocumentElement);
                    metadataType = typeof(XmlElement);
                }
 
                section.SourceUrl = sourceUrl;
 
                TraceReceiveReply(sourceUrl, metadataType);
 
                return section;
            }
 
            protected abstract XmlReader DownloadMetadata(TimeoutHelper timeoutHelper);
 
            protected abstract string SourceUrl { get; }
 
            private static bool CanReadSchema(XmlReader reader)
            {
                return reader.LocalName == MetadataStrings.XmlSchema.Schema
                    && reader.NamespaceURI == XsdNS.XmlSchema.Namespace;
            }
 
            private static bool CanReadMetadataSet(XmlReader reader)
            {
                return reader.LocalName == MetadataStrings.MetadataExchangeStrings.Metadata
                    && reader.NamespaceURI == MetadataStrings.MetadataExchangeStrings.Namespace;
            }
        }
 
        private class MetadataLocationRetriever : MetadataRetriever
        {
            private Uri _location;
            private Uri _responseLocation;
 
            internal MetadataLocationRetriever(Uri location, MetadataExchangeClient resolver)
                : this(location, resolver, null, null)
            {
            }
 
            internal MetadataLocationRetriever(Uri location, MetadataExchangeClient resolver, string dialect, string identifier)
                : base(resolver, dialect, identifier)
            {
                ValidateLocation(location);
                _location = location;
                _responseLocation = location;
            }
 
            internal static void ValidateLocation(Uri location)
            {
                if (location == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("location");
                }
 
                // TODO
                //if (location.Scheme != Uri.UriSchemeHttp && location.Scheme != Uri.UriSchemeHttps)
                if (location.Scheme != "http" && location.Scheme != "https")
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("location", string.Format(SRServiceModel.SFxCannotGetMetadataFromLocation, location.ToString()));
                }
            }
 
            public override bool Equals(object obj)
            {
                return obj is MetadataLocationRetriever && ((MetadataLocationRetriever)obj)._location == _location;
            }
 
            public override int GetHashCode()
            {
                return _location.GetHashCode();
            }
 
            protected override XmlReader DownloadMetadata(TimeoutHelper timeoutHelper)
            {
                HttpWebResponse response;
                HttpWebRequest request;
 
                try
                {
                    request = this.resolver.GetWebRequest(_location, this.dialect, this.identifier);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxMetadataExchangeClientCouldNotCreateWebRequest, _location, this.dialect, this.identifier), e));
                }
 
                TraceSendRequest(_location);
 
                Task<WebResponse> task = request.GetResponseAsync();
                task.Wait();
                response = task.Result as HttpWebResponse;
                return MetadataLocationRetriever.GetXmlReader(response, this.resolver.MaxMessageSize, this.resolver.ReaderQuotas);
            }
 
            internal static XmlReader GetXmlReader(HttpWebResponse response, long maxMessageSize, XmlDictionaryReaderQuotas readerQuotas)
            {
                // the response stream is not owned by us, the XmlReader will own the stream so we need to pass a copy to it.
                MemoryStream stream = new MemoryStream();
                response.GetResponseStream().CopyTo(stream);
                stream.Position = 0;
 
                readerQuotas = readerQuotas ?? EncoderDefaults.ReaderQuotas;
                XmlReader reader = XmlDictionaryReader.CreateTextReader(
                    new MaxMessageSizeStream(stream, maxMessageSize),
                    EncodingHelper.GetDictionaryReaderEncoding(response.ContentType),
                    readerQuotas,
                    null);
 
                reader.Read();
                reader.MoveToContent();
 
                return reader;
            }
 
            internal override IAsyncResult BeginRetrieve(TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
            {
                AsyncMetadataLocationRetriever result;
                try
                {
                    HttpWebRequest request;
                    try
                    {
                        request = this.resolver.GetWebRequest(_location, this.dialect, this.identifier);
                    }
#pragma warning disable 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                            throw;
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            string.Format(SRServiceModel.SFxMetadataExchangeClientCouldNotCreateWebRequest, _location, this.dialect, this.identifier), e));
                    }
 
                    TraceSendRequest(_location);
                    result = new AsyncMetadataLocationRetriever(request, this.resolver.MaxMessageSize, this.resolver.ReaderQuotas, timeoutHelper, callback, state);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxBadMetadataReference, this.SourceUrl), e));
                }
                return result;
            }
 
            internal override MetadataSection EndRetrieve(IAsyncResult result)
            {
                try
                {
                    return AsyncMetadataLocationRetriever.End(result);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxBadMetadataReference, this.SourceUrl), e));
                }
            }
 
            protected override string SourceUrl
            {
                get { return _responseLocation.ToString(); }
            }
 
            private class AsyncMetadataLocationRetriever : AsyncResult
            {
                private MetadataSection _section;
                private long _maxMessageSize;
                private XmlDictionaryReaderQuotas _readerQuotas;
 
                internal AsyncMetadataLocationRetriever(WebRequest request, long maxMessageSize, XmlDictionaryReaderQuotas readerQuotas, TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    _maxMessageSize = maxMessageSize;
                    _readerQuotas = readerQuotas;
                    IAsyncResult result = request.BeginGetResponse(Fx.ThunkCallback(new AsyncCallback(this.GetResponseCallback)), request);
                }
 
                private static void RetrieveTimeout(object state, bool timedOut)
                {
                    if (timedOut)
                    {
                        HttpWebRequest request = state as HttpWebRequest;
                        if (request != null)
                        {
                            request.Abort();
                        }
                    }
                }
 
                internal static MetadataSection End(IAsyncResult result)
                {
                    AsyncMetadataLocationRetriever retrieverResult = AsyncResult.End<AsyncMetadataLocationRetriever>(result);
                    return retrieverResult._section;
                }
 
                internal void GetResponseCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                        return;
 
                    Exception exception = null;
                    try
                    {
                        HandleResult(result);
                    }
#pragma warning disable 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                            throw;
                        exception = e;
                    }
                    this.Complete(false, exception);
                }
 
                private void HandleResult(IAsyncResult result)
                {
                    HttpWebRequest request = (HttpWebRequest)result.AsyncState;
 
                    using (XmlReader reader =
                        MetadataLocationRetriever.GetXmlReader((HttpWebResponse)request.EndGetResponse(result), _maxMessageSize, _readerQuotas))
                    {
                        // TODO (test): section = MetadataRetriever.CreateMetadataSection(reader, request.Address.ToString());
                        _section = MetadataRetriever.CreateMetadataSection(reader, request.RequestUri.ToString());
                    }
                }
            }
        }
 
        private class MetadataReferenceRetriever : MetadataRetriever
        {
            private EndpointAddress _address;
            private Uri _via;
 
            public MetadataReferenceRetriever(EndpointAddress address, MetadataExchangeClient resolver)
                : this(address, null, resolver, null, null)
            {
            }
 
            public MetadataReferenceRetriever(EndpointAddress address, Uri via, MetadataExchangeClient resolver)
                : this(address, via, resolver, null, null)
            {
            }
 
            public MetadataReferenceRetriever(EndpointAddress address, MetadataExchangeClient resolver, string dialect, string identifier)
                : this(address, null, resolver, dialect, identifier)
            {
            }
 
            private MetadataReferenceRetriever(EndpointAddress address, Uri via, MetadataExchangeClient resolver, string dialect, string identifier)
                : base(resolver, dialect, identifier)
            {
                if (address == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
                }
 
                _address = address;
                _via = via;
            }
 
            protected override string SourceUrl
            {
                get { return _address.Uri.ToString(); }
            }
 
            internal override IAsyncResult BeginRetrieve(TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
            {
                try
                {
                    IMetadataExchange metadataClient;
                    MessageVersion messageVersion;
                    lock (this.resolver.ThisLock)
                    {
                        ChannelFactory<IMetadataExchange> channelFactory;
                        try
                        {
                            channelFactory = this.resolver.GetChannelFactory(_address, this.dialect, this.identifier);
                        }
#pragma warning disable 56500 // covered by FxCOP
                        catch (Exception e)
                        {
                            if (Fx.IsFatal(e))
                                throw;
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                                string.Format(SRServiceModel.SFxMetadataExchangeClientCouldNotCreateChannelFactory, _address, this.dialect, this.identifier), e));
                        }
                        metadataClient = CreateChannel(channelFactory);
                        messageVersion = channelFactory.Endpoint.Binding.MessageVersion;
                    }
                    TraceSendRequest(_address);
                    return new AsyncMetadataReferenceRetriever(metadataClient, messageVersion, timeoutHelper, callback, state);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxBadMetadataReference, this.SourceUrl), e));
                }
            }
 
            private IMetadataExchange CreateChannel(ChannelFactory<IMetadataExchange> channelFactory)
            {
                if (_via != null)
                {
                    return channelFactory.CreateChannel(_address, _via);
                }
                else
                {
                    return channelFactory.CreateChannel(_address);
                }
            }
 
            private static Message CreateGetMessage(MessageVersion messageVersion)
            {
                return Message.CreateMessage(messageVersion, MetadataStrings.WSTransfer.GetAction);
            }
 
            protected override XmlReader DownloadMetadata(TimeoutHelper timeoutHelper)
            {
                IMetadataExchange metadataClient;
                MessageVersion messageVersion;
 
                lock (this.resolver.ThisLock)
                {
                    ChannelFactory<IMetadataExchange> channelFactory;
                    try
                    {
                        channelFactory = this.resolver.GetChannelFactory(_address, this.dialect, this.identifier);
                    }
#pragma warning disable 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                            throw;
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            string.Format(SRServiceModel.SFxMetadataExchangeClientCouldNotCreateChannelFactory, _address, this.dialect, this.identifier), e));
                    }
 
                    metadataClient = CreateChannel(channelFactory);
                    messageVersion = channelFactory.Endpoint.Binding.MessageVersion;
                }
                Message response;
 
                TraceSendRequest(_address);
 
                try
                {
                    using (Message getMessage = CreateGetMessage(messageVersion))
                    {
                        ((IClientChannel)metadataClient).OperationTimeout = timeoutHelper.RemainingTime();
                        response = metadataClient.Get(getMessage);
                    }
 
                    ((IClientChannel)metadataClient).Close();
                }
                finally
                {
                    ((IClientChannel)metadataClient).Abort();
                }
 
                if (response.IsFault)
                {
                    MessageFault fault = MessageFault.CreateFault(response, 64 * 1024);
                    StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
                    XmlWriter xmlWriter = XmlWriter.Create(stringWriter);
                    fault.WriteTo(xmlWriter, response.Version.Envelope);
                    xmlWriter.Flush();
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(stringWriter.ToString()));
                }
 
                return response.GetReaderAtBodyContents();
            }
 
            internal override MetadataSection EndRetrieve(IAsyncResult result)
            {
                try
                {
                    return AsyncMetadataReferenceRetriever.End(result);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        string.Format(SRServiceModel.SFxBadMetadataReference, this.SourceUrl), e));
                }
            }
 
            public override bool Equals(object obj)
            {
                return obj is MetadataReferenceRetriever && ((MetadataReferenceRetriever)obj)._address == _address;
            }
 
            public override int GetHashCode()
            {
                return _address.GetHashCode();
            }
 
            private class AsyncMetadataReferenceRetriever : AsyncResult
            {
                private MetadataSection _section;
                private Message _message;
                internal AsyncMetadataReferenceRetriever(IMetadataExchange metadataClient, MessageVersion messageVersion, TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    _message = MetadataReferenceRetriever.CreateGetMessage(messageVersion);
                    ((IClientChannel)metadataClient).OperationTimeout = timeoutHelper.RemainingTime();
                    IAsyncResult result = metadataClient.BeginGet(_message, Fx.ThunkCallback(new AsyncCallback(this.RequestCallback)), metadataClient);
 
                    if (result.CompletedSynchronously)
                    {
                        HandleResult(result);
 
                        this.Complete(true);
                    }
                }
 
                internal static MetadataSection End(IAsyncResult result)
                {
                    AsyncMetadataReferenceRetriever retrieverResult = AsyncResult.End<AsyncMetadataReferenceRetriever>(result);
                    return retrieverResult._section;
                }
 
                internal void RequestCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                        return;
 
                    Exception exception = null;
                    try
                    {
                        HandleResult(result);
                    }
#pragma warning disable 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                            throw;
                        exception = e;
                    }
                    this.Complete(false, exception);
                }
 
                private void HandleResult(IAsyncResult result)
                {
                    IMetadataExchange metadataClient = (IMetadataExchange)result.AsyncState;
                    Message response = metadataClient.EndGet(result);
 
                    using (_message)
                    {
                        if (response.IsFault)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(SRServiceModel.SFxBadMetadataReference,
                                ((IClientChannel)metadataClient).RemoteAddress.Uri.ToString())));
                        }
                        else
                        {
                            using (XmlReader reader = response.GetReaderAtBodyContents())
                            {
                                _section = MetadataRetriever.CreateMetadataSection(reader, ((IClientChannel)metadataClient).RemoteAddress.Uri.ToString());
                            }
                        }
                    }
                }
            }
        }
 
        private class AsyncMetadataResolver : AsyncResult
        {
            private ResolveCallState _resolveCallState;
 
            internal AsyncMetadataResolver(ResolveCallState resolveCallState, AsyncCallback callerCallback, object callerAsyncState)
                : base(callerCallback, callerAsyncState)
            {
                if (resolveCallState == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("resolveCallState");
                }
 
                _resolveCallState = resolveCallState;
 
 
                Exception exception = null;
                bool doneResolving = false;
                try
                {
                    doneResolving = this.ResolveNext();
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    exception = e;
                    doneResolving = true;
                }
 
                if (doneResolving)
                {
                    this.Complete(true, exception);
                }
            }
 
            private bool ResolveNext()
            {
                bool doneResolving = false;
                if (_resolveCallState.StackedRetrievers.Count > 0)
                {
                    MetadataRetriever retriever = _resolveCallState.StackedRetrievers.Pop();
 
                    if (_resolveCallState.HasBeenUsed(retriever))
                    {
                        doneResolving = this.ResolveNext();
                    }
                    else
                    {
                        if (_resolveCallState.ResolvedMaxResolvedReferences)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.SFxResolvedMaxResolvedReferences));
                        }
                        else
                        {
                            _resolveCallState.LogUse(retriever);
                            IAsyncResult result = retriever.BeginRetrieve(_resolveCallState.TimeoutHelper, Fx.ThunkCallback(new AsyncCallback(this.RetrieveCallback)), retriever);
 
                            if (result.CompletedSynchronously)
                            {
                                doneResolving = HandleResult(result);
                            }
                        }
                    }
                }
                else
                {
                    doneResolving = true;
                }
 
                return doneResolving;
            }
 
            internal static MetadataSet End(IAsyncResult result)
            {
                AsyncMetadataResolver resolverResult = AsyncResult.End<AsyncMetadataResolver>(result);
                return resolverResult._resolveCallState.MetadataSet;
            }
 
            internal void RetrieveCallback(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                    return;
 
                Exception exception = null;
                bool doneResolving = false;
                try
                {
                    doneResolving = HandleResult(result);
                }
#pragma warning disable 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
                    exception = e;
                    doneResolving = true;
                }
 
                if (doneResolving)
                {
                    this.Complete(false, exception);
                }
            }
 
            private bool HandleResult(IAsyncResult result)
            {
                MetadataRetriever retriever = (MetadataRetriever)result.AsyncState;
                MetadataSection section = retriever.EndRetrieve(result);
                _resolveCallState.HandleSection(section);
                return this.ResolveNext();
            }
        }
    }
 
    internal class EncodingHelper
    {
        internal const string ApplicationBase = "application";
 
        internal static Encoding GetRfcEncoding(string contentTypeStr)
        {
            return new ASCIIEncoding();
        }
 
        internal static Encoding GetDictionaryReaderEncoding(string contentTypeStr)
        {
            if (String.IsNullOrEmpty(contentTypeStr))
                return TextEncoderDefaults.Encoding;
 
            Encoding encoding = GetRfcEncoding(contentTypeStr);
 
            if (encoding == null)
                return TextEncoderDefaults.Encoding;
 
            string charSet = encoding.WebName;
            Encoding[] supportedEncodings = TextEncoderDefaults.SupportedEncodings;
            for (int i = 0; i < supportedEncodings.Length; i++)
            {
                if (charSet == supportedEncodings[i].WebName)
                    return encoding;
            }
 
            return TextEncoderDefaults.Encoding;
        }
    }
 
    public enum MetadataExchangeClientMode
    {
        MetadataExchange,
        HttpGet,
    }
 
    internal static class MetadataExchangeClientModeHelper
    {
        static public bool IsDefined(MetadataExchangeClientMode x)
        {
            return
                x == MetadataExchangeClientMode.MetadataExchange ||
                x == MetadataExchangeClientMode.HttpGet ||
                false;
        }
 
        public static void Validate(MetadataExchangeClientMode value)
        {
            if (!IsDefined(value))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value,
                    typeof(MetadataExchangeClientMode)));
            }
        }
    }
}