Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 58 additions & 17 deletions src/FlareSolverrSharp/ClearanceHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class ClearanceHandler : DelegatingHandler
{
private readonly HttpClient _client;
private readonly string _flareSolverrApiUrl;
private readonly IFlaresolverrResponseStorage _responseStorage;
private FlareSolverr _flareSolverr;
private string _userAgent;

Expand All @@ -42,6 +43,17 @@ public class ClearanceHandler : DelegatingHandler
/// <param name="flareSolverrApiUrl">FlareSolverr API URL. If null or empty it will detect the challenges, but
/// they will not be solved. Example: "http://localhost:8191/"</param>
public ClearanceHandler(string flareSolverrApiUrl)
: this(flareSolverrApiUrl, new DefaultFlaresolverrResponseStorage())
{
}

/// <summary>
/// Creates a new instance of the <see cref="ClearanceHandler"/>.
/// </summary>
/// <param name="flareSolverrApiUrl">FlareSolverr API URL. If null or empty it will detect the challenges, but
/// they will not be solved. Example: "http://localhost:8191/"</param>
/// <param name="responseStorage">Storage to persist challenge responses.</param>
public ClearanceHandler(string flareSolverrApiUrl, IFlaresolverrResponseStorage responseStorage)
: base(new HttpClientHandler())
{
// Validate URI
Expand All @@ -50,6 +62,7 @@ public ClearanceHandler(string flareSolverrApiUrl)
throw new FlareSolverrException("FlareSolverr URL is malformed: " + flareSolverrApiUrl);

_flareSolverrApiUrl = flareSolverrApiUrl;
_responseStorage = responseStorage;

_client = new HttpClient(new HttpClientHandler
{
Expand Down Expand Up @@ -84,36 +97,64 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

// Detect if there is a challenge in the response
if (ChallengeDetector.IsClearanceRequired(response))
if (!ChallengeDetector.IsClearanceRequired(response))
{
if (_flareSolverr == null)
throw new FlareSolverrException("Challenge detected but FlareSolverr is not configured");
return response;
}

// Resolve the challenge using FlareSolverr API
var flareSolverrResponse = await _flareSolverr.Solve(request);
if (_flareSolverr == null)
throw new FlareSolverrException("Challenge detected but FlareSolverr is not configured");

// Check if saved response exists.
var flareSolverrResponse = await _responseStorage.LoadAsync();

// Save the FlareSolverr User-Agent for the following requests
var flareSolverUserAgent = flareSolverrResponse.Solution.UserAgent;
if (flareSolverUserAgent != null && !flareSolverUserAgent.Equals(request.Headers.UserAgent.ToString()))
if (flareSolverrResponse != null)
{
// Set user agent
if (flareSolverrResponse.Solution.UserAgent != null &&
!flareSolverrResponse.Solution.UserAgent.Equals(request.Headers.UserAgent.ToString()))
{
_userAgent = flareSolverUserAgent;

// Set the User-Agent if required
_userAgent = flareSolverrResponse.Solution.UserAgent;
SetUserAgentHeader(request);
}

// Change the cookies in the original request with the cookies provided by FlareSolverr
// Retry request with saved response
InjectCookies(request, flareSolverrResponse);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

// Detect if there is a challenge in the response
if (ChallengeDetector.IsClearanceRequired(response))
throw new FlareSolverrException("The cookies provided by FlareSolverr are not valid");
if (!ChallengeDetector.IsClearanceRequired(response))
{
// Success with saved response.
InjectSetCookieHeader(response, flareSolverrResponse);
return response;
}
}

// Resolve the challenge using FlareSolverr API
flareSolverrResponse = await _flareSolverr.Solve(request);

// Save the FlareSolverr User-Agent for the following requests
var flareSolverUserAgent = flareSolverrResponse.Solution.UserAgent;
if (flareSolverUserAgent != null && !flareSolverUserAgent.Equals(request.Headers.UserAgent.ToString()))
{
_userAgent = flareSolverUserAgent;

// Add the "Set-Cookie" header in the response with the cookies provided by FlareSolverr
InjectSetCookieHeader(response, flareSolverrResponse);
// Set the User-Agent if required
SetUserAgentHeader(request);
}

// Change the cookies in the original request with the cookies provided by FlareSolverr
InjectCookies(request, flareSolverrResponse);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

// Detect if there is a challenge in the response
if (ChallengeDetector.IsClearanceRequired(response))
throw new FlareSolverrException("The cookies provided by FlareSolverr are not valid");

// Add the "Set-Cookie" header in the response with the cookies provided by FlareSolverr
InjectSetCookieHeader(response, flareSolverrResponse);
await _responseStorage.SaveAsync(flareSolverrResponse);

return response;
}

Expand Down
6 changes: 4 additions & 2 deletions src/FlareSolverrSharp/FlareSolverrSharp.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<PackageId>FlareSolverrSharp</PackageId>
<RootNamespace>FlareSolverrSharp</RootNamespace>
<Version>3.0.5</Version>
Expand All @@ -16,7 +18,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions src/FlareSolverrSharp/FlaresolverrResponseStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Threading.Tasks;
using FlareSolverrSharp.Types;

namespace FlareSolverrSharp
{
public interface IFlaresolverrResponseStorage
{
Task SaveAsync(FlareSolverrResponse result);
Task<FlareSolverrResponse?> LoadAsync();
}

public class DefaultFlaresolverrResponseStorage : IFlaresolverrResponseStorage
{
public Task<FlareSolverrResponse?> LoadAsync()
{
return Task.FromResult<FlareSolverrResponse?>(null);
}

public Task SaveAsync(FlareSolverrResponse result)
{
return Task.CompletedTask;
}
}
}
13 changes: 7 additions & 6 deletions src/FlareSolverrSharp/Solvers/FlareSolverr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using FlareSolverrSharp.Exceptions;
using FlareSolverrSharp.Types;
using FlareSolverrSharp.Utilities;
using Newtonsoft.Json;

namespace FlareSolverrSharp.Solvers
{
Expand Down Expand Up @@ -102,7 +102,9 @@ await Locker.LockAsync(async () =>
var resContent = await response.Content.ReadAsStringAsync();
try
{
result = JsonConvert.DeserializeObject<FlareSolverrResponse>(resContent);
result = JsonSerializer.Deserialize(
resContent,
FlareSolverrContext.Default.FlareSolverrResponse);
}
catch (Exception)
{
Expand Down Expand Up @@ -160,10 +162,9 @@ private FlareSolverrRequestProxy GetProxy()

private HttpContent GetSolverRequestContent(FlareSolverrRequest request)
{
var payload = JsonConvert.SerializeObject(request, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
var payload = JsonSerializer.Serialize(
request,
FlareSolverrContext.Default.FlareSolverrRequest);
HttpContent content = new StringContent(payload, Encoding.UTF8, "application/json");
return content;
}
Expand Down
12 changes: 12 additions & 0 deletions src/FlareSolverrSharp/Solvers/FlareSolverrContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
using FlareSolverrSharp.Types;

namespace FlareSolverrSharp.Solvers
{
[JsonSerializable(typeof(FlareSolverrRequest))]
[JsonSerializable(typeof(FlareSolverrResponse))]
[JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
public partial class FlareSolverrContext : JsonSerializerContext
{
}
}
20 changes: 11 additions & 9 deletions src/FlareSolverrSharp/Types/FlareSolverrRequest.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace FlareSolverrSharp.Types
{
[JsonDerivedType(typeof(FlareSolverrRequestGet))]
[JsonDerivedType(typeof(FlareSolverrRequestPost))]
public class FlareSolverrRequest
{
[JsonProperty("cmd")]
public string Cmd;
[JsonPropertyName("cmd")]
public string Cmd { get; set; }

[JsonProperty("url")]
public string Url;
[JsonPropertyName("url")]
public string Url { get; set; }

[JsonProperty("session")]
public string Session;
[JsonPropertyName("session")]
public string Session { get; set; }

[JsonProperty("proxy")]
public FlareSolverrRequestProxy Proxy;
[JsonPropertyName("proxy")]
public FlareSolverrRequestProxy Proxy { get; set; }
}
}
6 changes: 3 additions & 3 deletions src/FlareSolverrSharp/Types/FlareSolverrRequestGet.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace FlareSolverrSharp.Types
{
public class FlareSolverrRequestGet : FlareSolverrRequest
{
[JsonProperty("maxTimeout")]
public int MaxTimeout;
[JsonPropertyName("maxTimeout")]
public int MaxTimeout { get; set; }
}
}
10 changes: 5 additions & 5 deletions src/FlareSolverrSharp/Types/FlareSolverrRequestPost.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace FlareSolverrSharp.Types
{
public class FlareSolverrRequestPost : FlareSolverrRequest
{
[JsonProperty("postData")]
public string PostData;
[JsonPropertyName("postData")]
public string PostData { get; set; }

[JsonProperty("maxTimeout")]
public int MaxTimeout;
[JsonPropertyName("maxTimeout")]
public int MaxTimeout { get; set; }
}
}
6 changes: 3 additions & 3 deletions src/FlareSolverrSharp/Types/FlareSolverrRequestProxy.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace FlareSolverrSharp.Types
{
public class FlareSolverrRequestProxy
{
[JsonProperty("url")]
public string Url;
[JsonPropertyName("url")]
public string Url { get; set; }
}
}
Loading