/// <summary> /// Create an instance of <see cref="LocalServerCodeReceiver"/>. /// </summary> /// <param name="closePageResponse">Custom close page response for this instance</param> /// <param name="strategy">The strategy to use to determine the callback URI</param> public LocalServerCodeReceiver(string closePageResponse, CallbackUriChooserStrategy strategy) { _closePageResponse = closePageResponse; // Set the instance field of which callback URI to use. // An instance field is used to ensure any one instance of this class // uses a consistent callback URI. _callbackUriTemplate = CallbackUriChooser.Default.GetUriTemplate(strategy); }
internal string GetUriTemplate(CallbackUriChooserStrategy strategy) { lock (_lock) { if (strategy == CallbackUriChooserStrategy.ForceLoopbackIp) { // We still want to know what happens, we just won't do the initial check. InitUriStatisticsIfNeeded(ref _loopbackIp, CallbackUriTemplate127001, false); return(_loopbackIp.Uri); } if (strategy == CallbackUriChooserStrategy.ForceLocalhost) { // We still want to know what happens, we just won't do the initial check. InitUriStatisticsIfNeeded(ref _localhost, CallbackUriTemplateLocalhost, false); return(_localhost.Uri); } // Listening on 127.0.0.1 is recommended, but can't be done in non-admin Windows 7 & 8 at least. // So use some tests/heuristics to maybe listen on localhost instead. // If this is the first time that we are called, try with the recommended IP. InitUriStatisticsIfNeeded(ref _loopbackIp, CallbackUriTemplate127001, true); // We now know something about the loopback IP for sure. Let's see if we can use it. If so, // let's return it. if (_loopbackIp.CanBeUsed) { return(_loopbackIp.Uri); } // If we are here, we know we can't use the loopback IP, either because it failed or because it // timed out. // Let's try with localhost. InitUriStatisticsIfNeeded(ref _localhost, CallbackUriTemplateLocalhost, true); // We now know something about localhost for sure. Let's see if we can use it. If so, // let's return it. if (_localhost.CanBeUsed) { return(_localhost.Uri); } // If we are here then we haven't been able to use loopback IP or localhost, either // because of failure, or timeout. // This is probably bad, but we can still recover if // a) Timeouts were because of user inaction. // b) Failures were transient. // Let's try our best. UriStatistics retriable = _loopbackIp.TotalResets switch { // We always prefer the one with less resets. var loopbackResets when loopbackResets <_localhost.TotalResets => _loopbackIp, var loopbackResets when loopbackResets> _localhost.TotalResets => _localhost, // If they have the same amount of resets, then we prefer the one that has timed out // and we prefer loopback if both have timed out. _ when _loopbackIp.IsTimedOut => _loopbackIp, _ when _localhost.IsTimedOut => _localhost, // If they have the same amount of resets and none has timed out (they have failed), then we prefer loopback. _ => _loopbackIp }; retriable.Reset(); return(retriable.Uri); } void InitUriStatisticsIfNeeded(ref UriStatistics statistics, string uri, bool checkListener) { if (statistics == null) { statistics = new UriStatistics(uri, _timeout, _clock); // If possible, preemptively check that the uri works on this environment. // For instance, the loopback IP fails at least on Windows 7 and 8, for non-admin users. if (checkListener && _listenerFailsFor(statistics.Uri)) { statistics.Failed(); } } } }