Reworked Proxy Checker

This commit is contained in:
ruri
2019-12-21 01:47:02 +01:00
parent 779c7ca107
commit baba3405f7
3 changed files with 195 additions and 188 deletions
+131 -24
View File
@@ -1,5 +1,6 @@
using Extreme.Net;
using LiteDB;
using Newtonsoft.Json.Linq;
using OpenBullet.Repositories;
using RuriLib.Interfaces;
using RuriLib.Models;
@@ -9,12 +10,14 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace OpenBullet.ViewModels
{
public class ProxyManagerViewModel : ViewModelBase, IProxyManager
public class ProxyManagerViewModel : ViewModelBase, IProxyManager, IProxyChecker
{
private LiteDBRepository<CProxy> _repo;
@@ -34,6 +37,14 @@ namespace OpenBullet.ViewModels
public int Total => ProxiesCollection.Count;
public IEnumerable<CProxy> Proxies => ProxiesCollection;
public ProxyManagerViewModel()
{
_repo = new LiteDBRepository<CProxy>(Globals.dataBaseFile, "proxies");
ProxiesCollection = new ObservableCollection<CProxy>();
}
#region Statistics
public int Tested => ProxiesCollection.Count(x => x.Working != ProxyWorking.UNTESTED);
public int Http => ProxiesCollection.Count(x => x.Type == ProxyType.Http);
@@ -55,23 +66,12 @@ namespace OpenBullet.ViewModels
OnPropertyChanged(nameof(Chain));
OnPropertyChanged(nameof(Working));
OnPropertyChanged(nameof(NotWorking));
OnPropertyChanged(nameof(Progress));
}
public ProxyManagerStats Stats => new ProxyManagerStats(Total, Tested, Working, Http, Socks4, Socks4a, Socks5);
#endregion
#region Checking
public int Progress
{
get
{
var ret = 0;
try { ret = (Tested * 100) / Total; } catch { } // If Size is 0 this will throw an Exception
return ret;
}
}
#region Checker
private int botsAmount = 1;
public int BotsAmount { get { return botsAmount; } set { botsAmount = value; OnPropertyChanged(); } }
@@ -87,22 +87,129 @@ namespace OpenBullet.ViewModels
private int timeout = 2;
public int Timeout { get { return timeout; } set { timeout = value; OnPropertyChanged(); } }
public bool IsBusy => throw new NotImplementedException();
public static readonly int maximumBots = 200;
public void CheckAll(CancellationToken cancellationToken, IProgress<float> progress = null)
public async Task CheckAllAsync(IEnumerable<CProxy> proxies, CancellationToken cancellationToken, Action<CheckResult<ProxyResult>> onResult = null, IProgress<float> progress = null)
{
throw new NotImplementedException();
using (var ss = new SemaphoreSlim(BotsAmount, BotsAmount))
{
var total = proxies.Count();
var current = 0;
// Build the task list
var tasks = proxies.Select(async proxy =>
{
// Wait for the semaphore
await ss.WaitAsync();
CheckResult<ProxyResult> checkResult = default;
ProxyResult proxyResult = default;
proxyResult.proxy = proxy;
// Check the proxy
try
{
proxyResult = await CheckProxy(proxy);
checkResult = new CheckResult<ProxyResult>(true, proxyResult);
}
// Catch and log any errors
catch (Exception ex)
{
checkResult = new CheckResult<ProxyResult>(false, proxyResult, ex.Message);
}
// Report the progress and release the semaphore slot
finally
{
onResult?.Invoke(checkResult);
progress?.Report((float)++current / total);
ss.Release();
}
});
await Task.WhenAny(Task.WhenAll(tasks), AsTask(cancellationToken));
cancellationToken.ThrowIfCancellationRequested();
}
}
public async Task<ProxyResult> CheckAsync(CProxy proxy, CancellationToken cancellationToken)
{
var task = CheckProxy(proxy);
await Task.WhenAny(task, AsTask(cancellationToken));
cancellationToken.ThrowIfCancellationRequested();
return task.Result;
}
private async Task<ProxyResult> CheckProxy(CProxy proxy)
{
ProxyResult result = new ProxyResult();
result.proxy = proxy;
var sw = new Stopwatch();
sw.Start();
result.working = await CheckWorking(proxy);
sw.Stop();
result.ping = (int)sw.ElapsedMilliseconds;
// Try to check the country (it's not essential)
try
{
result.country = await CheckCountry(proxy);
}
catch { }
return result;
}
private async Task<bool> CheckWorking(CProxy proxy)
{
var timeout = Timeout * 1000;
using (var request = new HttpRequest())
{
request.Proxy = proxy.GetClient();
request.Proxy.ConnectTimeout = timeout;
request.Proxy.ReadWriteTimeout = timeout;
request.ConnectTimeout = timeout;
request.KeepAliveTimeout = timeout;
request.ReadWriteTimeout = timeout;
var response = await request.GetAsync(TestSite);
var source = response.ToString();
return source.Contains(SuccessKey);
}
}
private async Task<string> CheckCountry(CProxy proxy)
{
using (var request = new HttpRequest())
{
request.ConnectTimeout = Timeout;
var response = await request.GetAsync("http://ip-api.com/json/" + proxy.Host);
var json = JObject.Parse(response.ToString());
var status = json.Value<string>("status");
if (status == "success")
{
return json.Value<string>("country");
}
else
{
return "Unknown";
}
}
}
public static Task AsTask(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<object>();
cancellationToken.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false);
return tcs.Task;
}
#endregion
public IEnumerable<CProxy> Proxies => ProxiesCollection;
public ProxyManagerViewModel()
{
_repo = new LiteDBRepository<CProxy>(Globals.dataBaseFile, "proxies");
ProxiesCollection = new ObservableCollection<CProxy>();
}
#region CRUD Operations
// Create
public void Add(CProxy proxy)
+1 -1
View File
@@ -41,7 +41,7 @@
<Label Grid.Column="0" Grid.Row="0" VerticalAlignment="Center" Content="Progress:" />
<Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" Content="Test On:" />
<ProgressBar Grid.Column="1" Grid.Row="0" Value="{Binding Progress, Mode=OneWay}" Margin="5" />
<ProgressBar x:Name="progressBar" Grid.Column="1" Grid.Row="0" Minimum="0" Maximum="1" Margin="5" />
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" Text="{Binding TestSite}" VerticalContentAlignment="Center"/>
<DockPanel Grid.Column="2" Grid.Row="0" VerticalAlignment="Center">
<Label Content="Bots: " />
+63 -163
View File
@@ -1,5 +1,4 @@
using Extreme.Net;
using LiteDB;
using Microsoft.Win32;
using OpenBullet.ViewModels;
using RuriLib.Models;
@@ -36,180 +35,81 @@ namespace OpenBullet.Views.Main
InitializeComponent();
DataContext = vm;
botsSlider.Maximum = ProxyManagerViewModel.maximumBots;
vm.RefreshList();
vm.UpdateProperties();
}
#region Start Button
private void checkButton_Click(object sender, RoutedEventArgs e)
private async void checkButton_Click(object sender, RoutedEventArgs e)
{
switch (Status)
{
case WorkerStatus.Idle:
Globals.LogInfo(Components.ProxyManager, "Disabling the UI and starting the checker");
Globals.logger.LogInfo(Components.ProxyManager, "Disabling the UI and starting the checker");
checkButton.Content = "ABORT";
botsSlider.IsEnabled = false;
Status = WorkerStatus.Running;
#pragma warning disable CS4014 // Non è possibile attendere la chiamata, pertanto l'esecuzione del metodo corrente continuerà prima del completamento della chiamata
CheckProxiesAsync(vm.ProxiesCollection, vm.BotsAmount, 200);
#pragma warning restore CS4014 // Non è possibile attendere la chiamata, pertanto l'esecuzione del metodo corrente continuerà prima del completamento della chiamata
break;
case WorkerStatus.Running:
Globals.LogWarning(Components.ProxyManager, "Abort signal sent");
checkButton.Content = "HARD ABORT";
Status = WorkerStatus.Stopping;
cts.Cancel();
break;
var items = vm.OnlyUntested ? vm.Proxies : vm.Proxies.Where(p => p.Working == ProxyWorking.UNTESTED);
case WorkerStatus.Stopping:
Globals.LogWarning(Components.ProxyManager, "Hard abort signal sent");
checkButton.Content = "CHECK";
botsSlider.IsEnabled = true;
Status = WorkerStatus.Idle;
break;
}
}
#endregion
// Setup the progress bar
progressBar.Value = 0;
#region Check
public async Task CheckProxiesAsync(IEnumerable<CProxy> proxies, int threads, int step)
{
var proxiesToCheck = vm.OnlyUntested ? proxies.ToList() : proxies.Where(p => p.Working == ProxyWorking.UNTESTED).ToList();
// The semaphore will only allow {limit} elements at most running at the same time.
using (var semaphore = new SemaphoreSlim(threads, threads))
{
cts = new CancellationTokenSource();
for (int i = 0; i < proxiesToCheck.Count; i += step)
{
var tasks = proxiesToCheck
.Skip(i)
.Take(Math.Min(proxiesToCheck.Count - i, step))
.Select(p => CheckProxyAsync(p, semaphore, cts.Token))
.ToArray();
// Start checking
cts = new CancellationTokenSource();
try
{
await Task.WhenAny(Task.WhenAll(tasks), AsTask(cts.Token));
cts.Token.ThrowIfCancellationRequested();
await vm.CheckAllAsync(items, cts.Token,
new Action<CheckResult<ProxyResult>>(check =>
{
var result = check.result;
var proxy = result.proxy;
if (check.success)
{
// Set all the changed proxy fields
proxy.Working = result.working ? ProxyWorking.YES : ProxyWorking.NO;
proxy.Ping = result.ping;
proxy.Country = result.country;
var infoLog = $"[{DateTime.Now.ToLongTimeString()}] Check for proxy {proxy.Proxy} succeeded in {result.ping} milliseconds.";
Globals.logger.LogInfo(Components.ProxyManager, infoLog);
}
else
{
proxy.Working = ProxyWorking.NO;
proxy.Ping = 0;
var errorLog = $"[{DateTime.Now.ToLongTimeString()}] Check for proxy {proxy.Proxy} failed with error: {check.error}";
Globals.logger.LogError(Components.ProxyManager, errorLog);
}
// Update the proxy in the database
vm.Update(proxy);
}),
new Progress<float>(progress =>
{
progressBar.Value = progress;
vm.UpdateProperties();
}));
}
catch
{
break;
Globals.logger.LogWarning(Components.ProxyManager, "Abort signal received");
}
}
App.Current.Dispatcher.Invoke(() =>
{
Globals.LogInfo(Components.ProxyManager, "Check completed, re-enabling the UI");
checkButton.Content = "CHECK";
botsSlider.IsEnabled = true;
Status = WorkerStatus.Idle;
});
}
}
public static Task AsTask(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<object>();
cancellationToken.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false);
return tcs.Task;
}
public async Task CheckProxyAsync(CProxy proxy, SemaphoreSlim semaphore, CancellationToken token)
{
await semaphore.WaitAsync(token).ConfigureAwait(false);
try
{
token.ThrowIfCancellationRequested();
if (Status != WorkerStatus.Running) throw new OperationCanceledException();
// We do it like this, otherwise if we make the requests async the ping measurement won't work
await Task.Run(new Action(() =>
{
CheckCountry(proxy);
CheckProxy(proxy);
App.Current.Dispatcher.Invoke(new Action(() => vm.UpdateProperties()));
}));
}
catch (OperationCanceledException)
{
Globals.LogInfo(Components.ProxyManager, $"{proxy} - THROWING");
throw;
}
finally
{
semaphore.Release();
}
}
private void CheckCountry(CProxy proxy)
{
try
{
using (var request = new HttpRequest())
{
request.ConnectTimeout = (int)vm.Timeout;
var response = request.Get("http://ip-api.com/csv/" + proxy.Host);
var csv = response.ToString();
var split = csv.Split(',');
var country = "Unknown";
if (split[0] == "success")
country = split[1];
App.Current.Dispatcher.Invoke(new Action(() => proxy.Country = country.Replace("\"", "")));
using (var db = new LiteDatabase(Globals.dataBaseFile))
// Restore the GUI status
finally
{
db.GetCollection<CProxy>("proxies").Update(proxy);
checkButton.Content = "CHECK";
botsSlider.IsEnabled = true;
Status = WorkerStatus.Idle;
}
break;
Globals.LogInfo(Components.ProxyManager, "Checked country for proxy '" + proxy.Proxy + "' with result '" + proxy.Country + "'");
}
}
catch (Exception ex)
{
Globals.LogError(Components.ProxyManager, "Failted to check country for proxy '" + proxy.Proxy + $"' - {ex.Message}");
}
}
private void CheckProxy(CProxy proxy)
{
var before = DateTime.Now;
try
{
using (var request = new HttpRequest())
{
request.Proxy = proxy.GetClient();
request.Proxy.ConnectTimeout = (int)vm.Timeout * 1000;
request.Proxy.ReadWriteTimeout = (int)vm.Timeout * 1000;
request.ConnectTimeout = (int)vm.Timeout * 1000;
request.KeepAliveTimeout = (int)vm.Timeout * 1000;
request.ReadWriteTimeout = (int)vm.Timeout * 1000;
var response = request.Get(vm.TestSite);
var source = response.ToString();
App.Current.Dispatcher.Invoke(new Action(() => proxy.Ping = (int)(DateTime.Now - before).TotalMilliseconds));
App.Current.Dispatcher.Invoke(new Action(() => proxy.Working = source.Contains(vm.SuccessKey) ? ProxyWorking.YES : ProxyWorking.NO));
Globals.LogInfo(Components.ProxyManager, "Proxy '" + proxy.Proxy + $"' responded in {proxy.Ping} ms");
}
}
catch (Exception ex)
{
Globals.LogInfo(Components.ProxyManager, "Proxy '" + proxy.Proxy + $"' failed to respond - {ex.Message}");
App.Current.Dispatcher.Invoke(new Action(() => proxy.Working = ProxyWorking.NO));
}
using (var db = new LiteDatabase(Globals.dataBaseFile))
{
db.GetCollection<CProxy>("proxies").Update(proxy);
case WorkerStatus.Running:
cts.Cancel();
break;
}
}
#endregion
@@ -217,7 +117,7 @@ namespace OpenBullet.Views.Main
// TODO: Refactor this function, it shouldn't belong in a view!
public void AddProxies(IEnumerable<string> raw, ProxyType defaultType = ProxyType.Http, string defaultUsername = "", string defaultPassword = "")
{
Globals.LogInfo(Components.ProxyManager, $"Adding {raw.Count()} {defaultType} proxies to the database");
Globals.logger.LogInfo(Components.ProxyManager, $"Adding {raw.Count()} {defaultType} proxies to the database");
// Check if they're valid
var proxies = new List<CProxy>();
@@ -258,35 +158,35 @@ namespace OpenBullet.Views.Main
{
if (Selected.Count() > 0)
{
Globals.LogInfo(Components.ProxyManager, $"Exporting {proxiesListView.Items.Count} proxies");
Globals.logger.LogInfo(Components.ProxyManager, $"Exporting {proxiesListView.Items.Count} proxies");
Selected.SaveToFile(sfd.FileName, p => p.Proxy);
}
else
{
MessageBox.Show("No proxies selected!");
Globals.LogWarning(Components.ProxyManager, "No proxies selected");
Globals.logger.LogWarning(Components.ProxyManager, "No proxies selected");
}
}
}
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
Globals.LogInfo(Components.ProxyManager, $"Deleting {proxiesListView.SelectedItems.Count} proxies");
Globals.logger.LogInfo(Components.ProxyManager, $"Deleting {proxiesListView.SelectedItems.Count} proxies");
vm.Remove(Selected);
vm.UpdateProperties();
Globals.LogInfo(Components.ProxyManager, "Proxies deleted successfully");
Globals.logger.LogInfo(Components.ProxyManager, "Proxies deleted successfully");
}
private void deleteAllButton_Click(object sender, RoutedEventArgs e)
{
Globals.LogWarning(Components.ProxyManager, "Purging all proxies");
Globals.logger.LogWarning(Components.ProxyManager, "Purging all proxies");
vm.RemoveAll();
vm.UpdateProperties();
}
private void deleteNotWorkingButton_Click(object sender, RoutedEventArgs e)
{
Globals.LogInfo(Components.ProxyManager, "Deleting all non working proxies");
Globals.logger.LogInfo(Components.ProxyManager, "Deleting all non working proxies");
vm.RemoveNotWorking();
vm.UpdateProperties();
@@ -299,7 +199,7 @@ namespace OpenBullet.Views.Main
private void deleteDuplicatesButton_Click(object sender, RoutedEventArgs e)
{
Globals.LogInfo(Components.ProxyManager, "Deleting duplicate proxies");
Globals.logger.LogInfo(Components.ProxyManager, "Deleting duplicate proxies");
vm.RemoveDuplicates();
vm.UpdateProperties();
@@ -307,7 +207,7 @@ namespace OpenBullet.Views.Main
private void DeleteUntestedButton_Click(object sender, RoutedEventArgs e)
{
Globals.LogInfo(Components.ProxyManager, "Deleting all untested proxies");
Globals.logger.LogInfo(Components.ProxyManager, "Deleting all untested proxies");
vm.RemoveUntested();
vm.UpdateProperties();
@@ -344,13 +244,13 @@ namespace OpenBullet.Views.Main
}
else
{
Globals.LogError(Components.ProxyManager, "Failed to parse proxies type from file name, defaulting to HTTP");
Globals.logger.LogError(Components.ProxyManager, "Failed to parse proxies type from file name, defaulting to HTTP");
AddProxies(lines);
}
}
catch (Exception ex)
{
Globals.LogError(Components.ProxyManager, $"Failed to open file {file} - {ex.Message}");
Globals.logger.LogError(Components.ProxyManager, $"Failed to open file {file} - {ex.Message}");
}
}
}
@@ -361,11 +261,11 @@ namespace OpenBullet.Views.Main
try
{
Selected.CopyToClipboard(p => p.Proxy);
Globals.LogInfo(Components.ProxyManager, $"Copied {Selected.Count()} proxies");
Globals.logger.LogInfo(Components.ProxyManager, $"Copied {Selected.Count()} proxies");
}
catch (Exception ex)
{
Globals.LogError(Components.ProxyManager, $"Failed to copy proxies - {ex.Message}");
Globals.logger.LogError(Components.ProxyManager, $"Failed to copy proxies - {ex.Message}");
}
}