mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-27 02:43:57 +02:00
New: Removed libcurl http fallback since mono 5.16+ doesn't need it. Also bumped minimum mono version check to 5.16 (5.20 is the best choice atm)
This commit is contained in:
parent
72902c8984
commit
b3e84f407a
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,4 +0,0 @@
|
||||
[submodule "src/ExternalModules/CurlSharp"]
|
||||
path = src/ExternalModules/CurlSharp
|
||||
url = https://github.com/Sonarr/CurlSharp.git
|
||||
branch = master
|
9
build.sh
9
build.sh
@ -194,9 +194,6 @@ PackageMono()
|
||||
echo "Adding Sonarr.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/Sonarr.Core.dll.config $outputFolderLinux
|
||||
|
||||
echo "Adding CurlSharp.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderLinux
|
||||
|
||||
# Below we deal with some mono incompatibilities with windows-only dotnet core/standard libs
|
||||
# See: https://github.com/mono/mono/blob/master/tools/nuget-hash-extractor/download.sh
|
||||
# That list defines assemblies that are prohibited from being loaded from the appdir, instead loading from mono GAC.
|
||||
@ -308,12 +305,6 @@ PackageTests()
|
||||
echo "Adding Sonarr.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/Sonarr.Core.dll.config $testPackageFolder
|
||||
|
||||
echo "Adding CurlSharp.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $testPackageFolder
|
||||
|
||||
echo "Copying CurlSharp libraries"
|
||||
cp $sourceFolder/ExternalModules/CurlSharp/libs/i386/* $testPackageFolder
|
||||
|
||||
ProgressEnd 'Creating Test Package'
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ Architecture: all
|
||||
Provides: nzbdrone
|
||||
Conflicts: nzbdrone
|
||||
Replaces: nzbdrone
|
||||
Depends: adduser, libsqlite3-0 (>= 3.7), libmediainfo0v5 (>= 0.7.52) | libmediainfo0 (>= 0.7.52), mono-runtime (>= 5.4), libmono-system-runtime-interopservices-runtimeinformation4.0-cil, libmono-system-net-http4.0-cil, ${cli:Depends}, ${misc:Depends}
|
||||
Recommends: libmediainfo0v5 (>= 18.03) | libmediainfo0 (>= 18.03), libcurl4 | libcurl3
|
||||
Depends: adduser, libsqlite3-0 (>= 3.7), libmediainfo0v5 (>= 0.7.52) | libmediainfo0 (>= 0.7.52), mono-runtime (>= 5.4), libmono-system-runtime-interopservices-runtimeinformation4.0-cil (>= 4.0.0~alpha1), libmono-system-net-http4.0-cil (>= 4.0.0~alpha1), ${cli:Depends}, ${misc:Depends}
|
||||
Recommends: libmediainfo0v5 (>= 18.03) | libmediainfo0 (>= 18.03)
|
||||
Suggests: sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
||||
Description: Internet PVR
|
||||
|
@ -3,9 +3,7 @@
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
# Note: System.Native is a dependency of System.Runtime.InteropServices.RuntimeInformation used by SharpRaven,
|
||||
# but SharpRaven doesn't use any functions that need System.Native
|
||||
EXCLUDE_MODULEREFS = crypt32 httpapi System.Native
|
||||
EXCLUDE_MODULEREFS = crypt32 httpapi
|
||||
|
||||
%:
|
||||
dh $@ --with=systemd --with=cli
|
||||
|
@ -1,4 +1,2 @@
|
||||
ignores msbuild
|
||||
ignores libmediainfo0v5
|
||||
ignores libcurl3
|
||||
ignores libcurl4
|
||||
|
@ -154,12 +154,26 @@ class Health extends Component {
|
||||
const internalLink = getInternalLink(item.source);
|
||||
const testLink = getTestLink(item.source, this.props);
|
||||
|
||||
let kind = kinds.WARNING;
|
||||
switch (item.type.toLowerCase()) {
|
||||
case 'error':
|
||||
kind = kinds.DANGER;
|
||||
break;
|
||||
default:
|
||||
case 'warning':
|
||||
kind = kinds.WARNING;
|
||||
break;
|
||||
case 'notice':
|
||||
kind = kinds.INFO;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow key={`health${item.message}`}>
|
||||
<TableRowCell>
|
||||
<Icon
|
||||
name={icons.DANGER}
|
||||
kind={item.type.toLowerCase() === 'error' ? kinds.DANGER : kinds.WARNING}
|
||||
kind={kind}
|
||||
title={titleCase(item.type)}
|
||||
/>
|
||||
</TableRowCell>
|
||||
|
@ -20,8 +20,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Configuration Condition="'$(Configuration)'==''">Release</Configuration>
|
||||
<!-- Centralize intermediate and default outputs -->
|
||||
<IntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
|
||||
<BaseIntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(SonarrRootDir)_temp\obj\$(MSBuildProjectName)\$(Configuration)\</IntermediateOutputPath>
|
||||
<OutputPath>$(SonarrRootDir)_temp\bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
|
||||
|
||||
<!-- Output to _output and _tests respectively -->
|
||||
@ -30,6 +32,7 @@
|
||||
<OutputPath Condition="'$(SonarrOutputType)'=='Update'">$(SonarrRootDir)_output\Sonarr.Update\</OutputPath>
|
||||
|
||||
<!-- Paths relative to project file for better readability -->
|
||||
<BaseIntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)'))</IntermediateOutputPath>
|
||||
<OutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(OutputPath)'))</OutputPath>
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit cfdbbbd9c6b9612c2756245049a8234ce87dc576
|
@ -22,7 +22,6 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
[IntegrationTest]
|
||||
[TestFixture(typeof(ManagedHttpDispatcher))]
|
||||
[TestFixture(typeof(CurlHttpDispatcher))]
|
||||
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
||||
{
|
||||
private string[] _httpBinHosts;
|
||||
|
@ -11,9 +11,4 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\ExternalModules\CurlSharp\libs\i386\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<dllmap dll="libcurl.dll" target="libcurl.so.4" />
|
||||
<dllmap os="osx" dll="libcurl.dll" target="libcurl.4.dylib"/>
|
||||
<!--<dllmap os="freebsd" dll="libcurl.dll" target="libcurl.so.4" />-->
|
||||
<!--<dllmap os="solaris" dll="libcurl.dll" target="libcurl.so.4" />-->
|
||||
</configuration>
|
@ -1,335 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CurlSharp;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public class CurlHttpDispatcher : IHttpDispatcher
|
||||
{
|
||||
private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
||||
private readonly IUserAgentBuilder _userAgentBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private const string _caBundleFileName = "curl-ca-bundle.crt";
|
||||
private static readonly string _caBundleFilePath;
|
||||
|
||||
static CurlHttpDispatcher()
|
||||
{
|
||||
if (Assembly.GetExecutingAssembly().Location.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_caBundleFilePath = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", _caBundleFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_caBundleFilePath = _caBundleFileName;
|
||||
}
|
||||
}
|
||||
|
||||
public CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, IUserAgentBuilder userAgentBuilder, Logger logger)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_userAgentBuilder = userAgentBuilder;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool CheckAvailability()
|
||||
{
|
||||
try
|
||||
{
|
||||
return CurlGlobalHandle.Instance.Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex, "Initializing curl failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
if (!CheckAvailability())
|
||||
{
|
||||
throw new ApplicationException("Curl failed to initialize.");
|
||||
}
|
||||
|
||||
lock (CurlGlobalHandle.Instance)
|
||||
{
|
||||
Stream responseStream = new MemoryStream();
|
||||
Stream headerStream = new MemoryStream();
|
||||
|
||||
using (var curlEasy = new CurlEasy())
|
||||
{
|
||||
curlEasy.AutoReferer = false;
|
||||
curlEasy.WriteFunction = (b, s, n, o) =>
|
||||
{
|
||||
responseStream.Write(b, 0, s * n);
|
||||
return s * n;
|
||||
};
|
||||
curlEasy.HeaderFunction = (b, s, n, o) =>
|
||||
{
|
||||
headerStream.Write(b, 0, s * n);
|
||||
return s * n;
|
||||
};
|
||||
|
||||
AddProxy(curlEasy, request);
|
||||
|
||||
curlEasy.Url = request.Url.FullUri;
|
||||
|
||||
switch (request.Method)
|
||||
{
|
||||
case HttpMethod.GET:
|
||||
curlEasy.HttpGet = true;
|
||||
break;
|
||||
|
||||
case HttpMethod.POST:
|
||||
curlEasy.Post = true;
|
||||
break;
|
||||
|
||||
case HttpMethod.PUT:
|
||||
curlEasy.Put = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"HttpCurl method {request.Method} not supported");
|
||||
}
|
||||
curlEasy.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
||||
curlEasy.FollowLocation = false;
|
||||
|
||||
if (request.RequestTimeout != TimeSpan.Zero)
|
||||
{
|
||||
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
|
||||
}
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
curlEasy.CaInfo = _caBundleFilePath;
|
||||
}
|
||||
|
||||
if (cookies != null)
|
||||
{
|
||||
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
|
||||
}
|
||||
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
curlEasy.PostFieldSize = request.ContentData.Length;
|
||||
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
|
||||
}
|
||||
|
||||
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
|
||||
using (var httpRequestHeaders = SerializeHeaders(request))
|
||||
{
|
||||
curlEasy.HttpHeader = httpRequestHeaders;
|
||||
|
||||
var result = curlEasy.Perform();
|
||||
|
||||
if (result != CurlCode.Ok)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case CurlCode.SslCaCert:
|
||||
case (CurlCode)77:
|
||||
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
|
||||
default:
|
||||
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
|
||||
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
|
||||
|
||||
var httpHeader = new HttpHeader(webHeaderCollection);
|
||||
|
||||
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddProxy(CurlEasy curlEasy, HttpRequest request)
|
||||
{
|
||||
var proxySettings = _proxySettingsProvider.GetProxySettings(request);
|
||||
if (proxySettings != null)
|
||||
|
||||
{
|
||||
switch (proxySettings.Type)
|
||||
{
|
||||
case ProxyType.Http:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Http);
|
||||
curlEasy.SetOpt(CurlOption.ProxyAuth, CurlHttpAuth.Basic);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUserPwd, proxySettings.Username + ":" + proxySettings.Password.ToString());
|
||||
break;
|
||||
case ProxyType.Socks4:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks4);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
||||
break;
|
||||
case ProxyType.Socks5:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks5);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
||||
break;
|
||||
}
|
||||
curlEasy.SetOpt(CurlOption.Proxy, proxySettings.Host + ":" + proxySettings.Port.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private CurlSlist SerializeHeaders(HttpRequest request)
|
||||
{
|
||||
if (!request.Headers.ContainsKey("Accept-Encoding"))
|
||||
{
|
||||
request.Headers.Add("Accept-Encoding", "gzip");
|
||||
}
|
||||
|
||||
if (request.Headers.ContentType == null)
|
||||
{
|
||||
request.Headers.ContentType = string.Empty;
|
||||
}
|
||||
|
||||
var curlHeaders = new CurlSlist();
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
curlHeaders.Append(header.Key + ": " + header.Value.ToString());
|
||||
}
|
||||
|
||||
return curlHeaders;
|
||||
}
|
||||
|
||||
private WebHeaderCollection ProcessHeaderStream(HttpRequest request, CookieContainer cookies, Stream headerStream)
|
||||
{
|
||||
headerStream.Position = 0;
|
||||
var headerData = headerStream.ToBytes();
|
||||
var headerString = Encoding.ASCII.GetString(headerData);
|
||||
|
||||
var webHeaderCollection = new WebHeaderCollection();
|
||||
|
||||
// following a redirect we could have two sets of headers, so only process the last one
|
||||
foreach (var header in headerString.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
{
|
||||
if (!header.Contains(":")) break;
|
||||
webHeaderCollection.Add(header);
|
||||
}
|
||||
|
||||
var setCookie = webHeaderCollection.Get("Set-Cookie");
|
||||
if (setCookie != null && setCookie.Length > 0 && cookies != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
cookies.SetCookies((Uri)request.Url, FixSetCookieHeader(setCookie));
|
||||
}
|
||||
catch (CookieException ex)
|
||||
{
|
||||
_logger.Debug("Rejected cookie {0}: {1}", ex.InnerException.Message, setCookie);
|
||||
}
|
||||
}
|
||||
|
||||
return webHeaderCollection;
|
||||
}
|
||||
|
||||
private string FixSetCookieHeader(string setCookie)
|
||||
{
|
||||
// fix up the date if it was malformed
|
||||
var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match)
|
||||
{
|
||||
string shortFormat = "ddd, dd-MMM-yy HH:mm:ss";
|
||||
string longFormat = "ddd, dd-MMM-yyyy HH:mm:ss";
|
||||
DateTime dt;
|
||||
if (DateTime.TryParseExact(match.Groups[2].Value, longFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
||||
DateTime.TryParseExact(match.Groups[2].Value, shortFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
||||
DateTime.TryParse(match.Groups[2].Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt))
|
||||
return match.Groups[1].Value + dt.ToUniversalTime().ToString(longFormat, CultureInfo.InvariantCulture) + " GMT";
|
||||
else
|
||||
return match.Value;
|
||||
});
|
||||
return setCookieClean;
|
||||
}
|
||||
|
||||
private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection)
|
||||
{
|
||||
responseStream.Position = 0;
|
||||
|
||||
if (responseStream.Length != 0)
|
||||
{
|
||||
var encoding = webHeaderCollection["Content-Encoding"];
|
||||
if (encoding != null)
|
||||
{
|
||||
if (encoding.IndexOf("gzip") != -1)
|
||||
{
|
||||
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
|
||||
|
||||
webHeaderCollection.Remove("Content-Encoding");
|
||||
}
|
||||
else if (encoding.IndexOf("deflate") != -1)
|
||||
{
|
||||
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
|
||||
|
||||
webHeaderCollection.Remove("Content-Encoding");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return responseStream.ToBytes();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CurlGlobalHandle : SafeHandle
|
||||
{
|
||||
public static readonly CurlGlobalHandle Instance = new CurlGlobalHandle();
|
||||
|
||||
private bool _initialized;
|
||||
private bool _available;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private CurlGlobalHandle()
|
||||
: base(IntPtr.Zero, true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
lock (CurlGlobalHandle.Instance)
|
||||
{
|
||||
if (_initialized)
|
||||
return _available;
|
||||
|
||||
_initialized = true;
|
||||
_available = Curl.GlobalInit(CurlInitFlag.All) == CurlCode.Ok;
|
||||
|
||||
return _available;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (_initialized && _available)
|
||||
{
|
||||
Curl.GlobalCleanup();
|
||||
_available = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsInvalid => !_initialized || !_available;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public class FallbackHttpDispatcher : IHttpDispatcher
|
||||
{
|
||||
private readonly ManagedHttpDispatcher _managedDispatcher;
|
||||
private readonly CurlHttpDispatcher _curlDispatcher;
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly ICached<bool> _curlTLSFallbackCache;
|
||||
|
||||
public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, IPlatformInfo platformInfo, Logger logger)
|
||||
{
|
||||
_managedDispatcher = managedDispatcher;
|
||||
_curlDispatcher = curlDispatcher;
|
||||
_platformInfo = platformInfo;
|
||||
_curlTLSFallbackCache = cacheManager.GetCache<bool>(GetType(), "curlTLSFallback");
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
if (PlatformInfo.IsMono && request.Url.Scheme == "https")
|
||||
{
|
||||
if (!_curlTLSFallbackCache.Find(request.Url.Host))
|
||||
{
|
||||
try
|
||||
{
|
||||
return _managedDispatcher.GetResponse(request, cookies);
|
||||
}
|
||||
catch (TlsFailureException)
|
||||
{
|
||||
_logger.Debug("https request failed in tls error for {0}, trying curl fallback.", request.Url.Host);
|
||||
|
||||
_curlTLSFallbackCache.Set(request.Url.Host, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (_curlDispatcher.CheckAvailability())
|
||||
{
|
||||
return _curlDispatcher.GetResponse(request, cookies);
|
||||
}
|
||||
|
||||
_logger.Trace("Curl not available, using default WebClient.");
|
||||
}
|
||||
|
||||
return _managedDispatcher.GetResponse(request, cookies);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
|
||||
public class TlsFailureException : WebException
|
||||
{
|
||||
public TlsFailureException(WebRequest request, WebException innerException)
|
||||
: base("Failed to establish secure https connection to '" + request.RequestUri + "', libcurl fallback might be unavailable.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response)
|
||||
: base("Failed to establish secure https connection to '" + request.RequestUri + "'.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ExternalModules\CurlSharp\CurlSharp\CurlSharp.csproj" />
|
||||
<ProjectReference Include="..\LogentriesNLog\LogentriesNLog.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -23,10 +23,9 @@ protected void UseRealHttp()
|
||||
|
||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||
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>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), 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<IHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class DotnetVersionCheckFixture : CoreTest<DotnetVersionCheck>
|
||||
{
|
||||
private void GivenOutput(string version)
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>()
|
||||
.SetupGet(s => s.Version)
|
||||
.Returns(new Version(version));
|
||||
}
|
||||
|
||||
[TestCase("4.7.2")]
|
||||
[TestCase("4.8")]
|
||||
public void should_return_ok(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[TestCase("4.6.2")]
|
||||
[TestCase("4.7")]
|
||||
[TestCase("4.7.1")]
|
||||
public void should_return_notice(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeNotice();
|
||||
}
|
||||
|
||||
public void should_return_warning(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[TestCase("4.5")]
|
||||
[TestCase("4.5.2")]
|
||||
[TestCase("4.6.1")]
|
||||
public void should_return_error(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,16 @@ public static void ShouldBeOk(this Core.HealthCheck.HealthCheck result)
|
||||
result.Type.Should().Be(HealthCheckResult.Ok);
|
||||
}
|
||||
|
||||
public static void ShouldBeNotice(this Core.HealthCheck.HealthCheck result, string message = null)
|
||||
{
|
||||
result.Type.Should().Be(HealthCheckResult.Notice);
|
||||
|
||||
if (message.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
result.Message.Should().Contain(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShouldBeWarning(this Core.HealthCheck.HealthCheck result, string message = null)
|
||||
{
|
||||
result.Type.Should().Be(HealthCheckResult.Warning);
|
||||
|
@ -18,14 +18,8 @@ private void GivenOutput(string version)
|
||||
.Returns(new Version(version));
|
||||
}
|
||||
|
||||
|
||||
[TestCase("4.6")]
|
||||
[TestCase("4.4.2")]
|
||||
[TestCase("4.6")]
|
||||
[TestCase("4.8")]
|
||||
[TestCase("5.0")]
|
||||
[TestCase("5.2")]
|
||||
[TestCase("5.4")]
|
||||
[TestCase("5.18")]
|
||||
[TestCase("5.20")]
|
||||
public void should_return_ok(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
@ -33,6 +27,23 @@ public void should_return_ok(string version)
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[TestCase("5.16")]
|
||||
public void should_return_notice(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeNotice();
|
||||
}
|
||||
|
||||
[TestCase("5.4")]
|
||||
[TestCase("5.8")]
|
||||
public void should_return_warning(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[TestCase("2.10.2")]
|
||||
[TestCase("2.10.8.1")]
|
||||
[TestCase("3.0.0.1")]
|
||||
@ -44,14 +55,6 @@ public void should_return_ok(string version)
|
||||
[TestCase("3.10")]
|
||||
[TestCase("4.0.0.0")]
|
||||
[TestCase("4.2")]
|
||||
public void should_return_warning(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
|
||||
[TestCase("4.4.0")]
|
||||
[TestCase("4.4.1")]
|
||||
public void should_return_error(string version)
|
||||
|
52
src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs
Normal file
52
src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class DotnetVersionCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DotnetVersionCheck(IPlatformInfo platformInfo, Logger logger)
|
||||
{
|
||||
_platformInfo = platformInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (!PlatformInfo.IsDotNet)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var dotnetVersion = _platformInfo.Version;
|
||||
|
||||
// Target .Net version, which would allow us to increase our target framework
|
||||
var targetVersion = new Version("4.7.2");
|
||||
if (dotnetVersion >= targetVersion)
|
||||
{
|
||||
_logger.Debug("Dotnet version is {0} or better: {1}", targetVersion, dotnetVersion);
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
// Supported .net version but below our desired target
|
||||
var stableVersion = new Version("4.6.2");
|
||||
if (dotnetVersion >= stableVersion)
|
||||
{
|
||||
_logger.Debug("Dotnet version is {0} or better: {1}", stableVersion, dotnetVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is supported but we recommend upgrading to at least {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-supported-but-upgrading-is-recommended");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is old and unsupported. Please upgrade the .Net Framework to at least {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
@ -26,11 +28,14 @@ public override HealthCheck Check()
|
||||
|
||||
var monoVersion = _platformInfo.Version;
|
||||
|
||||
if (monoVersion >= new Version("5.0.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy")
|
||||
if (monoVersion >= new Version("5.8.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy")
|
||||
{
|
||||
// Mono 5.0 still has issues in combination with libmediainfo, so disabling this check for now.
|
||||
//_logger.Debug("Mono version 5.0.0 or higher and legacy TLS provider is selected, recommending user to switch to btls.");
|
||||
//return new HealthCheck(GetType(), HealthCheckResult.Warning, "Sonarr now supports Mono 5.x with btls enabled, consider removing MONO_TLS_PROVIDER=legacy option");
|
||||
_logger.Debug()
|
||||
.Message("Mono version {0} and legacy TLS provider is selected, recommending user to switch to btls.", monoVersion)
|
||||
.WriteSentryDebug("LegacyTlsProvider", monoVersion.ToString())
|
||||
.Write();
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Sonarr Mono 4.x tls workaround still enabled, consider removing MONO_TLS_PROVIDER=legacy environment option");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
@ -24,19 +24,47 @@ public override HealthCheck Check()
|
||||
|
||||
var monoVersion = _platformInfo.Version;
|
||||
|
||||
// Known buggy Mono versions
|
||||
if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1"))
|
||||
{
|
||||
_logger.Debug("Mono version {0}", monoVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Your Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version");
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version",
|
||||
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
if (monoVersion >= new Version("4.4.2"))
|
||||
// Currently best stable Mono version (5.18 gets us .net 4.7.2 support)
|
||||
var bestVersion = new Version("5.20");
|
||||
var targetVersion = new Version("5.18");
|
||||
if (monoVersion >= targetVersion)
|
||||
{
|
||||
_logger.Debug("Mono version is 4.4.2 or better: {0}", monoVersion);
|
||||
_logger.Debug("Mono version is {0} or better: {1}", targetVersion, monoVersion);
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "You are running an old and unsupported version of Mono. Please upgrade Mono for improved stability.");
|
||||
// Stable Mono versions
|
||||
var stableVersion = new Version("5.16");
|
||||
if (monoVersion >= stableVersion)
|
||||
{
|
||||
_logger.Debug("Mono version is {0} or better: {1}", stableVersion, monoVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||
$"Currently installed Mono version {monoVersion} is supported but upgrading to {bestVersion} is recommended.",
|
||||
"#currently-installed-mono-version-is-supported-but-upgrading-is-recommended");
|
||||
}
|
||||
|
||||
// Old but supported Mono versions, there are known bugs
|
||||
var supportedVersion = new Version("5.4");
|
||||
if (monoVersion >= supportedVersion)
|
||||
{
|
||||
_logger.Debug("Mono version is {0} or better: {1}", supportedVersion, monoVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning,
|
||||
$"Currently installed Mono version {monoVersion} is supported but has some known issues. Please upgrade Mono to version {bestVersion}.",
|
||||
"#currently-installed-mono-version-is-supported-but-upgrading-is-recommended");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed Mono version {monoVersion} is old and unsupported. Please upgrade Mono to version {bestVersion}.",
|
||||
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
|
@ -46,7 +46,8 @@ private static HttpUri MakeWikiUrl(string fragment)
|
||||
public enum HealthCheckResult
|
||||
{
|
||||
Ok = 0,
|
||||
Warning = 1,
|
||||
Error = 2
|
||||
Notice = 1,
|
||||
Warning = 2,
|
||||
Error = 3
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ private MainAppContainerBuilder(StartupContext args, List<string> assemblies)
|
||||
AutoRegisterImplementations<NzbDronePersistentConnection>();
|
||||
|
||||
Container.Register<INancyBootstrapper, SonarrBootstrapper>();
|
||||
Container.Register<IHttpDispatcher, FallbackHttpDispatcher>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class UpdateContainerBuilder : ContainerBuilderBase
|
||||
private UpdateContainerBuilder(IStartupContext startupContext, List<string> assemblies)
|
||||
: base(startupContext, assemblies)
|
||||
{
|
||||
Container.Register<IHttpDispatcher, FallbackHttpDispatcher>();
|
||||
|
||||
}
|
||||
|
||||
public static IContainer Build(IStartupContext startupContext)
|
||||
|
@ -91,8 +91,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogentriesNLog", "Logentrie
|
||||
{90D6E9FC-7B88-4E1B-B018-8FA742274558} = {90D6E9FC-7B88-4E1B-B018-8FA742274558}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CurlSharp", "ExternalModules\CurlSharp\CurlSharp\CurlSharp.csproj", "{74420A79-CC16-442C-8B1E-7C1B913844F0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Api.V3", "Sonarr.Api.V3\Sonarr.Api.V3.csproj", "{7140FF1F-79BE-492F-9188-B21A050BF708}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Http", "Sonarr.Http\Sonarr.Http.csproj", "{5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}"
|
||||
@ -268,12 +266,6 @@ Global
|
||||
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB}.Mono|x86.Build.0 = Release|x86
|
||||
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB}.Release|x86.ActiveCfg = Release|x86
|
||||
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB}.Release|x86.Build.0 = Release|x86
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Mono|x86.ActiveCfg = Release|Any CPU
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Mono|x86.Build.0 = Release|Any CPU
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7140FF1F-79BE-492F-9188-B21A050BF708}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{7140FF1F-79BE-492F-9188-B21A050BF708}.Debug|x86.Build.0 = Debug|x86
|
||||
{7140FF1F-79BE-492F-9188-B21A050BF708}.Mono|x86.ActiveCfg = Release|x86
|
||||
@ -322,7 +314,6 @@ Global
|
||||
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||
{90D6E9FC-7B88-4E1B-B018-8FA742274558} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||
{74420A79-CC16-442C-8B1E-7C1B913844F0} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
Loading…
Reference in New Issue
Block a user