diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs index 9e7509851..7c72a72e9 100644 --- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs +++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs @@ -7,17 +7,21 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http.Proxy; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common.Http.Dispatchers { public class ManagedHttpDispatcher : IHttpDispatcher { private const string NO_PROXY_KEY = "no-proxy"; - private const int connection_establish_timeout = 2000; + + private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(ManagedHttpDispatcher)); + private static bool useIPv6 = Socket.OSSupportsIPv6; private static bool hasResolvedIPv6Availability; @@ -29,10 +33,10 @@ namespace NzbDrone.Common.Http.Dispatchers private readonly ICached _credentialCache; public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, - ICreateManagedWebProxy createManagedWebProxy, - ICertificateValidationService certificateValidationService, - IUserAgentBuilder userAgentBuilder, - ICacheManager cacheManager) + ICreateManagedWebProxy createManagedWebProxy, + ICertificateValidationService certificateValidationService, + IUserAgentBuilder userAgentBuilder, + ICacheManager cacheManager) { _proxySettingsProvider = proxySettingsProvider; _createManagedWebProxy = createManagedWebProxy; @@ -235,16 +239,21 @@ namespace NzbDrone.Common.Http.Dispatchers private static async ValueTask onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) { + Logger.Trace($"useIPv6: {useIPv6} hasResolvedipv6availability: {hasResolvedIPv6Availability}"); + // Until .NET supports an implementation of Happy Eyeballs (https://tools.ietf.org/html/rfc8305#section-2), let's make IPv4 fallback work in a simple way. // This issue is being tracked at https://github.com/dotnet/runtime/issues/26177 and expected to be fixed in .NET 6. if (useIPv6) { + Logger.Trace("Trying Ipv6"); try { var localToken = cancellationToken; if (!hasResolvedIPv6Availability) { + Logger.Trace($"Using fast timeout {connection_establish_timeout}"); + // to make things move fast, use a very low timeout for the initial ipv6 attempt. var quickFailCts = new CancellationTokenSource(connection_establish_timeout); var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, quickFailCts.Token); @@ -254,8 +263,10 @@ namespace NzbDrone.Common.Http.Dispatchers return await attemptConnection(AddressFamily.InterNetworkV6, context, localToken); } - catch + catch (Exception e) { + Logger.Trace(e, "Error in ipv6 attempt"); + // very naively fallback to ipv4 permanently for this execution based on the response of the first connection attempt. // note that this may cause users to eventually get switched to ipv4 (on a random failure when they are switching networks, for instance) // but in the interest of keeping this implementation simple, this is acceptable. @@ -267,6 +278,8 @@ namespace NzbDrone.Common.Http.Dispatchers } } + Logger.Trace("Falling back to ipv4"); + // fallback to IPv4. return await attemptConnection(AddressFamily.InterNetwork, context, cancellationToken); }