mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-17 17:34:09 +02:00
Feature/netcore preparation (#2072)
* Use platform detection that works on mono 4.6+ * Move to use package reference for restoring nuget packages. * DateTimeRoutines does not have Nuget packages that support .NET Standard (and therefore .NET Core). We will have to include them for now until we can get rid of this dependency. * Start spliting some interfaces into their own files - this will help by allowing us to split them out in the future into a seperate project so the actual implementations can stay within their respective architectures when required * Move out common libraries * Few more tidy up tasks to get things working with .NET Standard * Restructure the solution layout * Encoding work to reduce rework later on platforms without Windows codepages (or require compliance with RFC1345) * Move folder structure around to have more natural layout of the solutions * DI server configuration to get rid of "temporary" hack and dependency circle for serverservice * Make all encoding consistent to match the expected encoding casing for earlier versions of mono.
This commit is contained in:

committed by
flightlevel

parent
47a2ffa313
commit
571c52a0f2
173
src/Jackett.Common/Utils/Clients/UnixLibCurlWebClient.cs
Normal file
173
src/Jackett.Common/Utils/Clients/UnixLibCurlWebClient.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using AutoMapper;
|
||||
using CurlSharp;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CloudFlareUtilities;
|
||||
using Jackett.Services.Interfaces;
|
||||
using Jacket.Common;
|
||||
using Jackett.Models.Config;
|
||||
|
||||
namespace Jackett.Utils.Clients
|
||||
{
|
||||
public class UnixLibCurlWebClient : WebClient
|
||||
{
|
||||
public UnixLibCurlWebClient(IProcessService p, Logger l, IConfigurationService c, ServerConfig sc)
|
||||
: base(p: p,
|
||||
l: l,
|
||||
c: c,
|
||||
sc: sc)
|
||||
{
|
||||
}
|
||||
|
||||
private string CloudFlareChallengeSolverSolve(string challengePageContent, Uri uri)
|
||||
{
|
||||
var solution = ChallengeSolver.Solve(challengePageContent, uri.Host);
|
||||
string clearanceUri = uri.Scheme + Uri.SchemeDelimiter + uri.Host + ":" + uri.Port + solution.ClearanceQuery;
|
||||
return clearanceUri;
|
||||
}
|
||||
|
||||
override public void Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Info("LibCurl init " + Curl.GlobalInit(CurlInitFlag.All).ToString());
|
||||
CurlHelper.OnErrorMessage += (msg) =>
|
||||
{
|
||||
logger.Error(msg);
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Warn("Libcurl failed to initalize. Did you install it?");
|
||||
logger.Warn("Debian: apt-get install libcurl4-openssl-dev");
|
||||
logger.Warn("Redhat: yum install libcurl-devel");
|
||||
throw e;
|
||||
}
|
||||
|
||||
var version = Curl.Version;
|
||||
logger.Info("LibCurl version " + version);
|
||||
|
||||
if (!JackettStartup.DoSSLFix.HasValue && version.IndexOf("NSS") > -1)
|
||||
{
|
||||
logger.Info("NSS Detected SSL ECC workaround enabled.");
|
||||
JackettStartup.DoSSLFix = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for Run which takes care of CloudFlare challenges, calls RunCurl
|
||||
override protected async Task<WebClientByteResult> Run(WebRequest request)
|
||||
{
|
||||
WebClientByteResult result = await RunCurl(request);
|
||||
|
||||
// check if we've received a CloudFlare challenge
|
||||
string[] server;
|
||||
if (result.Status == HttpStatusCode.ServiceUnavailable && result.Headers.TryGetValue("server", out server) && server[0] == "cloudflare-nginx")
|
||||
{
|
||||
logger.Info("UnixLibCurlWebClient: Received a new CloudFlare challenge");
|
||||
|
||||
// solve the challenge
|
||||
string pageContent = Encoding.UTF8.GetString(result.Content);
|
||||
Uri uri = new Uri(request.Url);
|
||||
string clearanceUri = CloudFlareChallengeSolverSolve(pageContent, uri);
|
||||
logger.Info(string.Format("UnixLibCurlWebClient: CloudFlare clearanceUri: {0}", clearanceUri));
|
||||
|
||||
// wait...
|
||||
await Task.Delay(5000);
|
||||
|
||||
// request clearanceUri to get cf_clearance cookie
|
||||
var response = await CurlHelper.GetAsync(clearanceUri, request.Cookies, request.Referer);
|
||||
logger.Info(string.Format("UnixLibCurlWebClient: received CloudFlare clearance cookie: {0}", response.Cookies));
|
||||
|
||||
// add new cf_clearance cookies to the original request
|
||||
request.Cookies = response.Cookies + request.Cookies;
|
||||
|
||||
// re-run the original request with updated cf_clearance cookie
|
||||
result = await RunCurl(request);
|
||||
|
||||
// add cf_clearance cookie to the final result so we update the config for the next request
|
||||
result.Cookies = response.Cookies + " " + result.Cookies;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<WebClientByteResult> RunCurl(WebRequest request)
|
||||
{
|
||||
Jackett.CurlHelper.CurlResponse response;
|
||||
if (request.Type == RequestType.GET)
|
||||
{
|
||||
response = await CurlHelper.GetAsync(request.Url, request.Cookies, request.Referer, request.Headers);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(request.RawBody))
|
||||
{
|
||||
logger.Debug("UnixLibCurlWebClient: Posting " + request.RawBody);
|
||||
}
|
||||
else if (request.PostData != null && request.PostData.Count() > 0)
|
||||
{
|
||||
logger.Debug("UnixLibCurlWebClient: Posting " + StringUtil.PostDataFromDict(request.PostData));
|
||||
}
|
||||
|
||||
response = await CurlHelper.PostAsync(request.Url, request.PostData, request.Cookies, request.Referer, request.Headers, request.RawBody);
|
||||
}
|
||||
|
||||
var result = new WebClientByteResult()
|
||||
{
|
||||
Content = response.Content,
|
||||
Cookies = response.Cookies,
|
||||
Status = response.Status
|
||||
};
|
||||
|
||||
if (response.HeaderList != null)
|
||||
{
|
||||
foreach (var header in response.HeaderList)
|
||||
{
|
||||
var key = header[0].ToLowerInvariant();
|
||||
|
||||
result.Headers[key] = new string[] { header[1] }; // doesn't support multiple identical headers?
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case "location":
|
||||
result.RedirectingTo = header[1];
|
||||
break;
|
||||
case "refresh":
|
||||
if (response.Status == System.Net.HttpStatusCode.ServiceUnavailable)
|
||||
{
|
||||
//"Refresh: 8;URL=/cdn-cgi/l/chk_jschl?pass=1451000679.092-1vJFUJLb9R"
|
||||
var redirval = "";
|
||||
var value = header[1];
|
||||
var start = value.IndexOf("=");
|
||||
var end = value.IndexOf(";");
|
||||
var len = value.Length;
|
||||
if (start > -1)
|
||||
{
|
||||
redirval = value.Substring(start + 1);
|
||||
result.RedirectingTo = redirval;
|
||||
// normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature
|
||||
// of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally
|
||||
// it shoudln't include service unavailable..only if we have this redirect header.
|
||||
result.Status = System.Net.HttpStatusCode.Redirect;
|
||||
var redirtime = Int32.Parse(value.Substring(0, end));
|
||||
System.Threading.Thread.Sleep(redirtime * 1000);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerUtil.ResureRedirectIsFullyQualified(request, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user