public async Task <Uri> ListenToSingleRequestAndRespondAsync( int port, Func <Uri, MessageAndHttpCode> responseProducer, CancellationToken cancellationToken) { TestBeforeTopLevelCall?.Invoke(); cancellationToken.ThrowIfCancellationRequested(); HttpListener httpListener = null; try { string urlToListenTo = "http://localhost:" + port + "/"; httpListener = new HttpListener(); httpListener.Prefixes.Add(urlToListenTo); TestBeforeStart?.Invoke(); httpListener.Start(); _logger.Info("Listening for authorization code on " + urlToListenTo); using (cancellationToken.Register(() => { _logger.Warning("HttpListener stopped because cancellation was requested."); TryStopListening(httpListener); })) { TestBeforeGetContext?.Invoke(); HttpListenerContext context = await httpListener.GetContextAsync() .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); Respond(responseProducer, context); _logger.Verbose("HttpListner received a message on " + urlToListenTo); // the request URL should now contain the auth code and pkce return(context.Request.Url); } } // If cancellation is requested before GetContextAsync is called, then either // an ObjectDisposedException or an HttpListenerException is thrown. // But this is just cancellation... catch (Exception ex) when(ex is HttpListenerException || ex is ObjectDisposedException) { _logger.Info("HttpListenerException - cancellation requested? " + cancellationToken.IsCancellationRequested); cancellationToken.ThrowIfCancellationRequested(); // if cancellation was not requested, propagate original ex throw; } finally { TryStopListening(httpListener); } }
public async Task <Uri> ListenToSingleRequestAndRespondAsync( int port, string path, Func <Uri, MessageAndHttpCode> responseProducer, CancellationToken cancellationToken) { TestBeforeTopLevelCall?.Invoke(); cancellationToken.ThrowIfCancellationRequested(); HttpListener httpListener = null; string urlToListenTo = string.Empty; try { if (string.IsNullOrEmpty(path)) { path = "/"; } else { path = (path.StartsWith("/") ? path : "/" + path); } urlToListenTo = "http://localhost:" + port + path; if (!urlToListenTo.EndsWith("/")) { urlToListenTo += "/"; } httpListener = new HttpListener(); httpListener.Prefixes.Add(urlToListenTo); TestBeforeStart?.Invoke(urlToListenTo); httpListener.Start(); _logger.Info("Listening for authorization code on " + urlToListenTo); using (cancellationToken.Register(() => { _logger.Warning("HttpListener stopped because cancellation was requested."); TryStopListening(httpListener); })) { TestBeforeGetContext?.Invoke(); HttpListenerContext context = await httpListener.GetContextAsync() .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); Respond(responseProducer, context); _logger.Verbose("HttpListner received a message on " + urlToListenTo); // the request URL should now contain the auth code and pkce return(context.Request.Url); } } // If cancellation is requested before GetContextAsync is called, then either // an ObjectDisposedException or an HttpListenerException is thrown. // But this is just cancellation... catch (Exception ex) when(ex is HttpListenerException || ex is ObjectDisposedException) { _logger.Info("HttpListenerException - cancellation requested? " + cancellationToken.IsCancellationRequested); cancellationToken.ThrowIfCancellationRequested(); if (ex is HttpListenerException) { throw new MsalClientException(MsalError.HttpListenerError, $"An HttpListenerException occurred while listening on {urlToListenTo} for the system browser to complete the login. " + "Possible cause and mitigation: the app is unable to listen on the specified URL; " + "run 'netsh http add iplisten 127.0.0.1' from the Admin command prompt.", ex); } // if cancellation was not requested, propagate original ex throw; } finally { TryStopListening(httpListener); } }