//---------------------------------------------------------------- // 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 : 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(); } 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 redirectionEndpoints = null; ResolveDuplexAsyncResult thisPtr = (ResolveDuplexAsyncResult)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 thisPtr = (ResolveDuplexAsyncResult)result.AsyncState; EndpointDiscoveryMetadata matchingEndpoint = thisPtr.discoveryServiceImpl.EndResolve(result); return thisPtr.SendResolveResponse(matchingEndpoint); } static bool OnSendResolveResponseCompleted(IAsyncResult result) { ResolveDuplexAsyncResult thisPtr = (ResolveDuplexAsyncResult)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 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 resolveDuplexAsyncResult; Collection redirectionEndpoints; public ProxyAnnouncementsSendAsyncResult( ResolveDuplexAsyncResult resolveDuplexAsyncResult, Collection 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(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); } } } }