//----------------------------------------------------------------------- // // Copyright (c) Andrew Arnott. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOAuth.Test.Scenarios { using System; using System.Threading; using DotNetOAuth.OAuth; using DotNetOAuth.Test.Mocks; using DotNetOAuth.Test.OAuth; using Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Runs a Consumer and Service Provider simultaneously so they can interact in a full simulation. /// internal class Coordinator { private ConsumerDescription consumerDescription; private ServiceProviderDescription serviceDescription; private Action consumerAction; private Action serviceProviderAction; /// Initializes a new instance of the class. /// The description of the consumer. /// The service description that will be used to construct the Consumer and ServiceProvider objects. /// The code path of the Consumer. /// The code path of the Service Provider. internal Coordinator(ConsumerDescription consumerDescription, ServiceProviderDescription serviceDescription, Action consumerAction, Action serviceProviderAction) { if (consumerDescription == null) { throw new ArgumentNullException("consumerDescription"); } if (serviceDescription == null) { throw new ArgumentNullException("serviceDescription"); } if (consumerAction == null) { throw new ArgumentNullException("consumerAction"); } if (serviceProviderAction == null) { throw new ArgumentNullException("serviceProviderAction"); } this.consumerDescription = consumerDescription; this.serviceDescription = serviceDescription; this.consumerAction = consumerAction; this.serviceProviderAction = serviceProviderAction; } /// /// Starts the simulation. /// internal void Run() { // Clone the template signing binding element. var signingElement = this.serviceDescription.CreateTamperProtectionElement(); var consumerSigningElement = signingElement.Clone(); var spSigningElement = signingElement.Clone(); // Prepare token managers InMemoryTokenManager consumerTokenManager = new InMemoryTokenManager(); InMemoryTokenManager serviceTokenManager = new InMemoryTokenManager(); consumerTokenManager.AddConsumer(this.consumerDescription); serviceTokenManager.AddConsumer(this.consumerDescription); // Prepare channels that will pass messages directly back and forth. CoordinatingOAuthChannel consumerChannel = new CoordinatingOAuthChannel(consumerSigningElement, true, consumerTokenManager); CoordinatingOAuthChannel serviceProviderChannel = new CoordinatingOAuthChannel(spSigningElement, false, serviceTokenManager); consumerChannel.RemoteChannel = serviceProviderChannel; serviceProviderChannel.RemoteChannel = consumerChannel; // Prepare the Consumer and Service Provider objects WebConsumer consumer = new WebConsumer(this.serviceDescription, consumerTokenManager) { OAuthChannel = consumerChannel, ConsumerKey = this.consumerDescription.ConsumerKey, }; ServiceProvider serviceProvider = new ServiceProvider(this.serviceDescription, serviceTokenManager) { OAuthChannel = serviceProviderChannel, }; Thread consumerThread = null, serviceProviderThread = null; Exception failingException = null; // Each thread we create needs a surrounding exception catcher so that we can // terminate the other thread and inform the test host that the test failed. Action safeWrapper = (action) => { try { action(); } catch (Exception ex) { // We may be the second thread in an ThreadAbortException, so check the "flag" if (failingException == null) { failingException = ex; if (Thread.CurrentThread == consumerThread) { serviceProviderThread.Abort(); } else { consumerThread.Abort(); } } } }; // Run the threads, and wait for them to complete. // If this main thread is aborted (test run aborted), go ahead and abort the other two threads. consumerThread = new Thread(() => { safeWrapper(() => { consumerAction(consumer); }); }); serviceProviderThread = new Thread(() => { safeWrapper(() => { serviceProviderAction(serviceProvider); }); }); try { consumerThread.Start(); serviceProviderThread.Start(); consumerThread.Join(); serviceProviderThread.Join(); } catch (ThreadAbortException) { consumerThread.Abort(); serviceProviderThread.Abort(); throw; } // Use the failing reason of a failing sub-thread as our reason, if anything failed. if (failingException != null) { throw new AssertFailedException("Coordinator thread threw unhandled exception: " + failingException, failingException); } } } }