File: System\ServiceModel\Discovery\ResolveDuplexAsyncResult.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Discovery\System.ServiceModel.Discovery.csproj (System.ServiceModel.Discovery)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.ServiceModel.Discovery
{
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
    
    abstract class ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> : AsyncResult
    {
        readonly IDiscoveryServiceImplementation discoveryServiceImpl;
        readonly IMulticastSuppressionImplementation multicastSuppressionImpl;
        readonly ResolveCriteria resolveCriteria;
        readonly DiscoveryOperationContext context;
        readonly TimeoutHelper timeoutHelper;
 
        static AsyncCompletion onShouldRedirectResolveCompletedCallback = new AsyncCompletion(OnShouldRedirectResolveCompleted);
        static AsyncCompletion onSendProxyAnnouncementsCompletedCallback = new AsyncCompletion(OnSendProxyAnnouncementsCompleted);
 
        static AsyncCompletion onOnResolveCompletedCallback = new AsyncCompletion(OnOnResolveCompleted);
        static AsyncCompletion onSendResolveResponseCompletedCallback = new AsyncCompletion(OnSendResolveResponseCompleted);
        TResponseChannel responseChannel;
 
        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        protected ResolveDuplexAsyncResult(TResolveMessage resolveMessage,
            IDiscoveryServiceImplementation discoveryServiceImpl,
            IMulticastSuppressionImplementation multicastSuppressionImpl,
            AsyncCallback callback,
            object state)
            : base(callback, state)
        {
            Fx.Assert(resolveMessage != null, "The resolveMessage must be non null.");
            Fx.Assert(discoveryServiceImpl != null, "The discoveryServiceImpl must be non null.");
 
            this.discoveryServiceImpl = discoveryServiceImpl;
            this.multicastSuppressionImpl = multicastSuppressionImpl;
 
            if (!this.Validate(resolveMessage))
            {
                this.Complete(true);
                return;
            }
            else
            {
                this.context = new DiscoveryOperationContext(OperationContext.Current);
                this.resolveCriteria = this.GetResolveCriteria(resolveMessage);
                this.timeoutHelper = new TimeoutHelper(this.resolveCriteria.Duration);
                this.timeoutHelper.RemainingTime();
                this.Process();
            }
        }
 
        TResponseChannel ResponseChannel
        {
            get
            {
                if (this.responseChannel == null)
                {
                    this.responseChannel = this.context.GetCallbackChannel<TResponseChannel>();
                }
 
                return this.responseChannel;
            }
        }
 
        protected DiscoveryOperationContext Context
        {
            get
            {
                return this.context;
            }
        }
 
        protected virtual bool Validate(TResolveMessage resolveMessage)
        {
            return (DiscoveryService.EnsureMessageId() &&
                DiscoveryService.EnsureReplyTo() &&
                this.ValidateContent(resolveMessage) &&
                this.EnsureNotDuplicate());
        }
 
        protected abstract bool ValidateContent(TResolveMessage resolveMessage);
 
        protected abstract ResolveCriteria GetResolveCriteria(TResolveMessage resolveMessage);
 
        protected abstract IAsyncResult BeginSendResolveResponse(
            TResponseChannel responseChannel,
            DiscoveryMessageSequence discoveryMessageSequence, 
            EndpointDiscoveryMetadata matchingEndpoint, 
            AsyncCallback callback, 
            object state);
        protected abstract void EndSendResolveResponse(TResponseChannel responseChannel, IAsyncResult result);
 
        protected abstract IAsyncResult BeginSendProxyAnnouncement(
            TResponseChannel responseChannel,
            DiscoveryMessageSequence discoveryMessageSequence,
            EndpointDiscoveryMetadata proxyEndpointDiscoveryMetadata,
            AsyncCallback callback,
            object state);
        protected abstract void EndSendProxyAnnouncement(TResponseChannel responseChannel, IAsyncResult result);
 
        static bool OnShouldRedirectResolveCompleted(IAsyncResult result)
        {
            Collection<EndpointDiscoveryMetadata> redirectionEndpoints = null;
 
            ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> thisPtr =
                (ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel>)result.AsyncState;
 
            if (thisPtr.multicastSuppressionImpl.EndShouldRedirectResolve(result, out redirectionEndpoints))
            {
                return thisPtr.SendProxyAnnouncements(redirectionEndpoints);
            }
            else
            {
                return thisPtr.ProcessResolveRequest();
            }
        }
 
        static bool OnSendProxyAnnouncementsCompleted(IAsyncResult result)
        {
            ProxyAnnouncementsSendAsyncResult.End(result);
            return true;
        }
 
        static bool OnOnResolveCompleted(IAsyncResult result)
        {
            ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> thisPtr =
                (ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel>)result.AsyncState;
 
            EndpointDiscoveryMetadata matchingEndpoint = thisPtr.discoveryServiceImpl.EndResolve(result);
 
            return thisPtr.SendResolveResponse(matchingEndpoint);
        }
 
        static bool OnSendResolveResponseCompleted(IAsyncResult result)
        {
            ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> thisPtr =
                (ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel>)result.AsyncState;
 
            thisPtr.EndSendResolveResponse(thisPtr.ResponseChannel, result);
            return true;
        }
 
        void Process()
        {
            if ((this.multicastSuppressionImpl != null) && (this.context.DiscoveryMode == ServiceDiscoveryMode.Adhoc))
            {
                if (this.SuppressResolveRequest())
                {
                    Complete(true);
                    return;
                }
            }
            else
            {
                if (this.ProcessResolveRequest())
                {
                    Complete(true);
                    return;
                }
            }
        }
 
        bool SuppressResolveRequest()
        {
            IAsyncResult result = this.multicastSuppressionImpl.BeginShouldRedirectResolve(
                this.resolveCriteria,
                this.PrepareAsyncCompletion(onShouldRedirectResolveCompletedCallback),
                this);
 
            return (result.CompletedSynchronously && OnShouldRedirectResolveCompleted(result));
        }
 
        bool SendProxyAnnouncements(Collection<EndpointDiscoveryMetadata> redirectionEndpoints)
        {
            if ((redirectionEndpoints == null) || (redirectionEndpoints.Count == 0))
            {
                return true;
            }
 
            IAsyncResult result = new ProxyAnnouncementsSendAsyncResult(
                this,
                redirectionEndpoints,
                this.PrepareAsyncCompletion(onSendProxyAnnouncementsCompletedCallback),
                this);
 
            return (result.CompletedSynchronously && OnSendProxyAnnouncementsCompleted(result));
        }
 
        bool ProcessResolveRequest()
        {
            IAsyncResult result = this.discoveryServiceImpl.BeginResolve(
                resolveCriteria,
                PrepareAsyncCompletion(onOnResolveCompletedCallback),
                this);
 
            return (result.CompletedSynchronously && OnOnResolveCompleted(result));
        }
 
        bool SendResolveResponse(EndpointDiscoveryMetadata matchingEndpoint)
        {
            if (matchingEndpoint == null)
            {
                return true;
            }
 
            IContextChannel contextChannel = (IContextChannel)this.ResponseChannel;
            IAsyncResult result = null;
            using (new OperationContextScope(contextChannel))
            {
                this.context.AddressDuplexResponseMessage(OperationContext.Current);
 
                contextChannel.OperationTimeout = this.timeoutHelper.RemainingTime();
 
                result = this.BeginSendResolveResponse(
                    this.ResponseChannel,
                    this.discoveryServiceImpl.GetNextMessageSequence(),
                    matchingEndpoint,
                    this.PrepareAsyncCompletion(onSendResolveResponseCompletedCallback),
                    this);
            }
 
            return (result.CompletedSynchronously && OnSendResolveResponseCompleted(result));
        }
 
        bool EnsureNotDuplicate()
        {
            bool isDuplicate = this.discoveryServiceImpl.IsDuplicate(OperationContext.Current.IncomingMessageHeaders.MessageId);
 
            if (isDuplicate && TD.DuplicateDiscoveryMessageIsEnabled())
            {
                TD.DuplicateDiscoveryMessage(
                    this.context.EventTraceActivity,
                    ProtocolStrings.TracingStrings.Resolve,
                    OperationContext.Current.IncomingMessageHeaders.MessageId.ToString());
            }
 
            return !isDuplicate;
        }
 
        IAsyncResult BeginSendProxyAnnouncement(
            EndpointDiscoveryMetadata proxyEndpoint,
            TimeSpan timeout,
            AsyncCallback callback,
            object state)
        {
            IAsyncResult result;
            IContextChannel contextChannel = (IContextChannel)this.ResponseChannel;
            using (new OperationContextScope(contextChannel))
            {
                this.context.AddressDuplexResponseMessage(OperationContext.Current);
 
                contextChannel.OperationTimeout = timeout;
 
                result = this.BeginSendProxyAnnouncement(
                    this.ResponseChannel,
                    this.discoveryServiceImpl.GetNextMessageSequence(),
                    proxyEndpoint,
                    callback,
                    state);
            }
 
            return result;
        }
 
        void EndSendProxyAnnouncement(IAsyncResult result)
        {
            this.EndSendProxyAnnouncement(this.ResponseChannel, result);
        }
 
        class ProxyAnnouncementsSendAsyncResult : RandomDelaySendsAsyncResult
        {
            ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> resolveDuplexAsyncResult;
            Collection<EndpointDiscoveryMetadata> redirectionEndpoints;
 
            public ProxyAnnouncementsSendAsyncResult(
                ResolveDuplexAsyncResult<TResolveMessage, TResponseChannel> resolveDuplexAsyncResult,
                Collection<EndpointDiscoveryMetadata> redirectionEndpoints,
                AsyncCallback callback,
                object state)
                : base(
                redirectionEndpoints.Count,
                resolveDuplexAsyncResult.context.MaxResponseDelay,
                callback,
                state)
            {
                this.resolveDuplexAsyncResult = resolveDuplexAsyncResult;
                this.redirectionEndpoints = redirectionEndpoints;
                this.Start(this.resolveDuplexAsyncResult.timeoutHelper.RemainingTime());
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<ProxyAnnouncementsSendAsyncResult>(result);
            }
 
            protected override IAsyncResult OnBeginSend(int index, TimeSpan timeout, AsyncCallback callback, object state)
            {
                return this.resolveDuplexAsyncResult.BeginSendProxyAnnouncement(
                    this.redirectionEndpoints[index],
                    timeout,
                    callback,
                    state);
            }
 
            protected override void OnEndSend(IAsyncResult result)
            {
                this.resolveDuplexAsyncResult.EndSendProxyAnnouncement(result);
            }
        }
    }
}