private async Task <Uri> SeleniumAcquireAuthAsync( Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken) { NullLogger nullLogger = new NullLogger(); using (var driver = InitDriverAndGoToUrl(authorizationUri.OriginalString)) { var listener = new TcpInterceptor(nullLogger); Uri authCodeUri = null; var listenForAuthCodeTask = listener.ListenToSingleRequestAndRespondAsync( redirectUri.Port, (uri) => { Trace.WriteLine("Intercepted an auth code url: " + uri.ToString()); authCodeUri = uri; return(GetMessageToShowInBroswerAfterAuth(uri)); }, cancellationToken); // Run the TCP listener and the selenium automation in parallel var seleniumAutomationTask = Task.Factory.StartNew(() => _seleniumAutomationLogic(driver)); await Task.WhenAny(seleniumAutomationTask, listenForAuthCodeTask) .ConfigureAwait(false); if (listenForAuthCodeTask.Status == TaskStatus.RanToCompletion) { return(authCodeUri); } if (seleniumAutomationTask.IsFaulted) { Trace.WriteLine("The selenium automation failed: " + seleniumAutomationTask.Exception.Message); RecordException(driver, seleniumAutomationTask.Exception); throw seleniumAutomationTask.Exception; } if (listenForAuthCodeTask.IsCanceled) { Trace.WriteLine("The TCP listener has timed out (or was canceled)."); if (!cancellationToken.IsCancellationRequested) { Assert.Fail("The TCP listener is in a canceled state, but cancellation has not been requested!"); } throw new OperationCanceledException(cancellationToken); } if (listenForAuthCodeTask.IsFaulted) { Trace.WriteLine("The TCP listener failed."); RecordException(driver, listenForAuthCodeTask.Exception); throw listenForAuthCodeTask.Exception; } throw new InvalidOperationException( $"Unknown exception: selenium status: {seleniumAutomationTask.Status} TCP listener status: {listenForAuthCodeTask.Status}"); } }
private async Task <Uri> SeleniumAcquireAuthAsync( Uri authorizationUri, Uri redirectUri, CancellationToken externalCancellationToken) { using (var driver = InitDriverAndGoToUrl(authorizationUri.OriginalString)) { var listener = new TcpInterceptor(_logger); Uri authCodeUri = null; // Run the TCP listener and the selenium automation in parallel // but make sure to start the TCP listener first CancellationTokenSource innerSource = new CancellationTokenSource(); var tcpCancellationToken = CancellationTokenSource.CreateLinkedTokenSource( innerSource.Token, externalCancellationToken); Task <Uri> listenForAuthCodeTask = listener.ListenToSingleRequestAndRespondAsync( redirectUri.Port, (uri) => { authCodeUri = uri; _logger.Info("Auth code intercepted. Writing message back to browser"); return(GetMessageToShowInBroswerAfterAuth(uri)); }, tcpCancellationToken.Token); var seleniumAutomationTask = Task.Factory.StartNew(() => { _seleniumAutomationLogic(driver); _logger.Info("Selenium automation finished"); }); // There is no guarantee over which task will finish first - TCP listener or Selenium automation // as the TCP listener has some post processing to do (extracting the url etc.) await Task.WhenAny(seleniumAutomationTask, listenForAuthCodeTask) .ConfigureAwait(false); // No need to wait to post a nice message in the browser if (authCodeUri != null) { return(authCodeUri); } if (seleniumAutomationTask.IsFaulted) { Trace.WriteLine("The selenium automation failed: " + seleniumAutomationTask.Exception.Message); RecordException(driver, seleniumAutomationTask.Exception); throw seleniumAutomationTask.Exception; } if (listenForAuthCodeTask.IsCanceled) { Trace.WriteLine("The TCP listener has timed out (or was canceled)."); if (!externalCancellationToken.IsCancellationRequested) { Assert.Fail("The TCP listener is in a canceled state, but cancellation has not been requested!"); } throw new OperationCanceledException(externalCancellationToken); } if (listenForAuthCodeTask.IsFaulted) { Trace.WriteLine("The TCP listener failed."); RecordException(driver, listenForAuthCodeTask.Exception); throw listenForAuthCodeTask.Exception; } _logger.Info($"Selenium finished, but TCP listener is still going. " + $"Selenium status: {seleniumAutomationTask.Status} " + $"TCP listener status: { listenForAuthCodeTask.Status}. "); // At this point we need to give the TcpListener some time to complete // but not too much as it should be fast await Task.WhenAny(Task.Delay(tcpTimeoutAfterSelenium), listenForAuthCodeTask).ConfigureAwait(false); if (authCodeUri != null) { return(authCodeUri); } innerSource.Cancel(); throw new InvalidOperationException( $"Unknown exception: selenium status: {seleniumAutomationTask.Status} TCP listener status: {listenForAuthCodeTask.Status}. " + $"Possible cause - the redirect Uri used is not the one configured." + $" A screen shot will be stored in the test results for you to inspect."); } }