Fixed: Workaround for mono 5.16+ bug preventing the closure of sockets on timeouts (Jackett connections)
ref #2802
This commit is contained in:
parent
1fc2866032
commit
bbf74a8835
|
@ -2,9 +2,13 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http.Proxy;
|
using NzbDrone.Common.Http.Proxy;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
using NzbDrone.Common.Security;
|
using NzbDrone.Common.Security;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Http.Dispatchers
|
namespace NzbDrone.Common.Http.Dispatchers
|
||||||
|
@ -14,12 +18,16 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
||||||
private readonly ICreateManagedWebProxy _createManagedWebProxy;
|
private readonly ICreateManagedWebProxy _createManagedWebProxy;
|
||||||
private readonly IUserAgentBuilder _userAgentBuilder;
|
private readonly IUserAgentBuilder _userAgentBuilder;
|
||||||
|
private readonly IPlatformInfo _platformInfo;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy, IUserAgentBuilder userAgentBuilder)
|
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy, IUserAgentBuilder userAgentBuilder, IPlatformInfo platformInfo, Logger logger)
|
||||||
{
|
{
|
||||||
_proxySettingsProvider = proxySettingsProvider;
|
_proxySettingsProvider = proxySettingsProvider;
|
||||||
_createManagedWebProxy = createManagedWebProxy;
|
_createManagedWebProxy = createManagedWebProxy;
|
||||||
_userAgentBuilder = userAgentBuilder;
|
_userAgentBuilder = userAgentBuilder;
|
||||||
|
_platformInfo = platformInfo;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||||
|
@ -39,7 +47,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||||
}
|
}
|
||||||
|
|
||||||
webRequest.Method = request.Method.ToString();
|
webRequest.Method = request.Method.ToString();
|
||||||
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
||||||
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
||||||
|
@ -84,6 +92,9 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
|
|
||||||
if (httpWebResponse == null)
|
if (httpWebResponse == null)
|
||||||
{
|
{
|
||||||
|
// Workaround for mono not closing connections properly in certain situations.
|
||||||
|
AbortWebRequest(webRequest);
|
||||||
|
|
||||||
// The default messages for WebException on mono are pretty horrible.
|
// The default messages for WebException on mono are pretty horrible.
|
||||||
if (e.Status == WebExceptionStatus.NameResolutionFailure)
|
if (e.Status == WebExceptionStatus.NameResolutionFailure)
|
||||||
{
|
{
|
||||||
|
@ -198,5 +209,36 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for mono not closing connections properly on timeouts
|
||||||
|
private void AbortWebRequest(HttpWebRequest webRequest)
|
||||||
|
{
|
||||||
|
// First affected version was mono 5.16
|
||||||
|
if (OsInfo.IsNotWindows && _platformInfo.Version >= new Version(5, 16))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var currentOperationInfo = webRequest.GetType().GetField("currentOperation", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var currentOperation = currentOperationInfo.GetValue(webRequest);
|
||||||
|
|
||||||
|
if (currentOperation != null)
|
||||||
|
{
|
||||||
|
var responseStreamInfo = currentOperation.GetType().GetField("responseStream", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var responseStream = responseStreamInfo.GetValue(currentOperation) as Stream;
|
||||||
|
// Note that responseStream will likely be null once mono fixes it.
|
||||||
|
responseStream?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// This can fail randomly on future mono versions that have been changed/fixed. Log to sentry and ignore.
|
||||||
|
_logger.Trace()
|
||||||
|
.Exception(ex)
|
||||||
|
.Message("Unable to dispose responseStream on mono {0}", _platformInfo.Version)
|
||||||
|
.WriteSentryWarn("MonoCloseWaitPatchFailed", ex.Message)
|
||||||
|
.Write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
|
|
||||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||||
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>()));
|
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), TestLogger));
|
||||||
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<NLog.Logger>()));
|
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||||
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
||||||
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||||
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
|
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
|
||||||
|
|
Loading…
Reference in New Issue