Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ name: Publish .NET Package

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
Expand All @@ -14,7 +12,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0.x' # Change this to the .NET version you are using
dotnet-version: '8.0.x' # Change this to the .NET version you are using
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand All @@ -23,8 +21,10 @@ jobs:
run: dotnet test --no-restore --verbosity normal
- name: Publish
run: dotnet pack --configuration Release --no-build --output ./artifacts
if: github.ref == 'refs/heads/master'
- name: Push to GitHub Packages
run: dotnet nuget push ./artifacts/*.nupkg -k ${{ secrets.GITHUB_TOKEN }} -s https://nuget.pkg.github.com/top-gg/index.json --skip-duplicate
if: github.ref == 'refs/heads/master'

env:
DOTNET_NOLOGO: true
Expand Down
6 changes: 6 additions & 0 deletions ProxyCheck.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.26403.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyCheckUtil", "ProxyCheck\ProxyCheckUtil.csproj", "{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProxyCheckUtil.Tests", "ProxyCheckUtil.Tests\ProxyCheckUtil.Tests.csproj", "{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +17,10 @@ Global
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Release|Any CPU.Build.0 = Release|Any CPU
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
31 changes: 31 additions & 0 deletions ProxyCheck/Http/DefaultHttpClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Net.Http;
using JetBrains.Annotations;

namespace ProxyCheckUtil
{
/// <summary>
/// Simple implementation of <see cref="IHttpClientFactory"/> that creates a single <see cref="System.Net.Http.HttpClient"/> instance.
/// Not recommended for production use, since it doesn't respect DNS changes.
///
/// Creating a new <see cref="System.Net.Http.HttpClient"/> instance for each request will lead to connection pool exhaustion.
/// Use the pooling from the 'Microsoft.Extensions.Http' package instead.
/// </summary>
internal class DefaultHttpClientFactory : IHttpClientFactory
{
private static readonly HttpClient HttpClient = new NonDisposableHttpClient();

public HttpClient CreateClient(string name)
{
return HttpClient;
}

private class NonDisposableHttpClient : HttpClient
{
protected override void Dispose(bool disposing)
{
// Do nothing, since we want to keep the HttpClient instance alive.
}
}
}
}
2 changes: 1 addition & 1 deletion ProxyCheck/IProxyCheckCacheProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace ProxyCheckUtil
{
public interface IProxyCheckCacheProvider
{
ProxyCheckResult.IpResult GetCacheRecord(IPAddress ip, ProxyCheckRequestOptions options);
ProxyCheckResult.IpResult? GetCacheRecord(IPAddress ip, ProxyCheckRequestOptions options);

IDictionary<IPAddress, ProxyCheckResult.IpResult> GetCacheRecords(IPAddress[] ipAddress, ProxyCheckRequestOptions options);

Expand Down
145 changes: 145 additions & 0 deletions ProxyCheck/Json/IpResultDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.Json;

namespace ProxyCheckUtil
{
/// <summary>
/// Dictionary that deserializes IP addresses keys as <see cref="IPAddress"/> and values as <see cref="ProxyCheckResult.IpResult"/>.
/// </summary>
internal class IpResultDictionary : IDictionary<string, JsonElement>
{
private readonly Dictionary<string, JsonElement> _extensionData = new();
private readonly Dictionary<IPAddress, ProxyCheckResult.IpResult> _results;

public IpResultDictionary(Dictionary<IPAddress, ProxyCheckResult.IpResult> results)
{
_results = results;
}

public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator()
{
foreach (var kvp in _results)
{
yield return new KeyValuePair<string, JsonElement>(kvp.Key.ToString(), JsonSerializer.SerializeToDocument(kvp.Value, ProxyJsonContext.Default.IpResult).RootElement);
}

foreach (var kvp in _extensionData)
{
yield return kvp;
}
}

public void Add(KeyValuePair<string, JsonElement> item)
{
Add(item.Key, item.Value);
}

public void Clear()
{
_results.Clear();
_extensionData.Clear();
}

public bool Contains(KeyValuePair<string, JsonElement> item)
{
return ContainsKey(item.Key);
}

public void CopyTo(KeyValuePair<string, JsonElement>[] array, int arrayIndex)
{
foreach (var kvp in _results)
{
array[arrayIndex++] = new KeyValuePair<string, JsonElement>(kvp.Key.ToString(), JsonSerializer.SerializeToDocument(kvp.Value, ProxyJsonContext.Default.IpResult).RootElement);
}

foreach (var kvp in _extensionData)
{
array[arrayIndex++] = kvp;
}
}

public bool Remove(KeyValuePair<string, JsonElement> item)
{
return Remove(item.Key);
}

public int Count => _results.Count + _extensionData.Count;

public bool IsReadOnly => false;

public void Add(string key, JsonElement value)
{
if (IPAddress.TryParse(key, out var ip))
{
_results.Add(ip, value.Deserialize(ProxyJsonContext.Default.IpResult)!);
}
else
{
_extensionData.Add(key, value);
}
}

public bool ContainsKey(string key)
{
return IPAddress.TryParse(key, out var ip)
? _results.ContainsKey(ip)
: _extensionData.ContainsKey(key);
}

public bool Remove(string key)
{
return IPAddress.TryParse(key, out var ip)
? _results.Remove(ip)
: _extensionData.Remove(key);
}

public bool TryGetValue(string key, out JsonElement value)
{
if (_results.TryGetValue(IPAddress.Parse(key), out var result))
{
value = JsonSerializer.SerializeToDocument(result, ProxyJsonContext.Default.IpResult).RootElement;
return true;
}

value = default;
return false;
}

public JsonElement this[string key]
{
get => IPAddress.TryParse(key, out var ip)
? JsonSerializer.SerializeToDocument(_results[ip], ProxyJsonContext.Default.IpResult).RootElement
: _extensionData[key];

set
{
if (IPAddress.TryParse(key, out var ip))
{
_results[ip] = value.Deserialize(ProxyJsonContext.Default.IpResult)!;
}
else
{
_extensionData[key] = value;
}
}
}

ICollection<string> IDictionary<string, JsonElement>.Keys => _results.Keys
.Select(ip => ip.ToString())
.Concat(_extensionData.Keys)
.ToList();

ICollection<JsonElement> IDictionary<string, JsonElement>.Values => _results.Values
.Select(result => JsonSerializer.SerializeToDocument(result, ProxyJsonContext.Default.IpResult).RootElement)
.Concat(_extensionData.Values)
.ToList();

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
18 changes: 18 additions & 0 deletions ProxyCheck/Json/ProxyJsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;

namespace ProxyCheckUtil
{
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
Converters = new []
{
typeof(JsonStringEnumConverter<StatusResult>),
typeof(JsonStringEnumConverter<RiskLevel>)
}
)]
[JsonSerializable(typeof(ProxyCheckResult))]
[JsonSerializable(typeof(ProxyCheckResult.IpResult))]
internal partial class ProxyJsonContext : JsonSerializerContext
{
}
}
19 changes: 19 additions & 0 deletions ProxyCheck/Json/YesNoJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ProxyCheckUtil
{
internal class YesNoJsonConverter : JsonConverter<bool>
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetString() == "yes";
}

public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
{
writer.WriteStringValue(value ? "yes" : "no");
}
}
}
Loading
Loading