Skip to content

Commit aadadaa

Browse files
authored
Merge pull request #2 from GerardSmit/feature/system-text-json
feat!: parse JSON with System.Text.Json
2 parents c01cbaf + 91754bd commit aadadaa

16 files changed

+538
-156
lines changed

.github/workflows/dotnet.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ name: Publish .NET Package
22

33
on:
44
push:
5-
branches: [ master ]
65
pull_request:
7-
branches: [ master ]
86

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

2929
env:
3030
DOTNET_NOLOGO: true

ProxyCheck.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.26403.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyCheckUtil", "ProxyCheck\ProxyCheckUtil.csproj", "{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}"
77
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProxyCheckUtil.Tests", "ProxyCheckUtil.Tests\ProxyCheckUtil.Tests.csproj", "{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}"
9+
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1012
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
1517
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
1618
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
1719
{234ED760-6E95-4C53-B4EE-D4612A9E5BAD}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{D768DB67-D857-4CA3-A8BA-88CA0F5E2306}.Release|Any CPU.Build.0 = Release|Any CPU
1824
EndGlobalSection
1925
GlobalSection(SolutionProperties) = preSolution
2026
HideSolutionNode = FALSE
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Net.Http;
3+
using JetBrains.Annotations;
4+
5+
namespace ProxyCheckUtil
6+
{
7+
/// <summary>
8+
/// Simple implementation of <see cref="IHttpClientFactory"/> that creates a single <see cref="System.Net.Http.HttpClient"/> instance.
9+
/// Not recommended for production use, since it doesn't respect DNS changes.
10+
///
11+
/// Creating a new <see cref="System.Net.Http.HttpClient"/> instance for each request will lead to connection pool exhaustion.
12+
/// Use the pooling from the 'Microsoft.Extensions.Http' package instead.
13+
/// </summary>
14+
internal class DefaultHttpClientFactory : IHttpClientFactory
15+
{
16+
private static readonly HttpClient HttpClient = new NonDisposableHttpClient();
17+
18+
public HttpClient CreateClient(string name)
19+
{
20+
return HttpClient;
21+
}
22+
23+
private class NonDisposableHttpClient : HttpClient
24+
{
25+
protected override void Dispose(bool disposing)
26+
{
27+
// Do nothing, since we want to keep the HttpClient instance alive.
28+
}
29+
}
30+
}
31+
}

ProxyCheck/IProxyCheckCacheProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace ProxyCheckUtil
55
{
66
public interface IProxyCheckCacheProvider
77
{
8-
ProxyCheckResult.IpResult GetCacheRecord(IPAddress ip, ProxyCheckRequestOptions options);
8+
ProxyCheckResult.IpResult? GetCacheRecord(IPAddress ip, ProxyCheckRequestOptions options);
99

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

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Text.Json;
6+
7+
namespace ProxyCheckUtil
8+
{
9+
/// <summary>
10+
/// Dictionary that deserializes IP addresses keys as <see cref="IPAddress"/> and values as <see cref="ProxyCheckResult.IpResult"/>.
11+
/// </summary>
12+
internal class IpResultDictionary : IDictionary<string, JsonElement>
13+
{
14+
private readonly Dictionary<string, JsonElement> _extensionData = new();
15+
private readonly Dictionary<IPAddress, ProxyCheckResult.IpResult> _results;
16+
17+
public IpResultDictionary(Dictionary<IPAddress, ProxyCheckResult.IpResult> results)
18+
{
19+
_results = results;
20+
}
21+
22+
public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator()
23+
{
24+
foreach (var kvp in _results)
25+
{
26+
yield return new KeyValuePair<string, JsonElement>(kvp.Key.ToString(), JsonSerializer.SerializeToDocument(kvp.Value, ProxyJsonContext.Default.IpResult).RootElement);
27+
}
28+
29+
foreach (var kvp in _extensionData)
30+
{
31+
yield return kvp;
32+
}
33+
}
34+
35+
public void Add(KeyValuePair<string, JsonElement> item)
36+
{
37+
Add(item.Key, item.Value);
38+
}
39+
40+
public void Clear()
41+
{
42+
_results.Clear();
43+
_extensionData.Clear();
44+
}
45+
46+
public bool Contains(KeyValuePair<string, JsonElement> item)
47+
{
48+
return ContainsKey(item.Key);
49+
}
50+
51+
public void CopyTo(KeyValuePair<string, JsonElement>[] array, int arrayIndex)
52+
{
53+
foreach (var kvp in _results)
54+
{
55+
array[arrayIndex++] = new KeyValuePair<string, JsonElement>(kvp.Key.ToString(), JsonSerializer.SerializeToDocument(kvp.Value, ProxyJsonContext.Default.IpResult).RootElement);
56+
}
57+
58+
foreach (var kvp in _extensionData)
59+
{
60+
array[arrayIndex++] = kvp;
61+
}
62+
}
63+
64+
public bool Remove(KeyValuePair<string, JsonElement> item)
65+
{
66+
return Remove(item.Key);
67+
}
68+
69+
public int Count => _results.Count + _extensionData.Count;
70+
71+
public bool IsReadOnly => false;
72+
73+
public void Add(string key, JsonElement value)
74+
{
75+
if (IPAddress.TryParse(key, out var ip))
76+
{
77+
_results.Add(ip, value.Deserialize(ProxyJsonContext.Default.IpResult)!);
78+
}
79+
else
80+
{
81+
_extensionData.Add(key, value);
82+
}
83+
}
84+
85+
public bool ContainsKey(string key)
86+
{
87+
return IPAddress.TryParse(key, out var ip)
88+
? _results.ContainsKey(ip)
89+
: _extensionData.ContainsKey(key);
90+
}
91+
92+
public bool Remove(string key)
93+
{
94+
return IPAddress.TryParse(key, out var ip)
95+
? _results.Remove(ip)
96+
: _extensionData.Remove(key);
97+
}
98+
99+
public bool TryGetValue(string key, out JsonElement value)
100+
{
101+
if (_results.TryGetValue(IPAddress.Parse(key), out var result))
102+
{
103+
value = JsonSerializer.SerializeToDocument(result, ProxyJsonContext.Default.IpResult).RootElement;
104+
return true;
105+
}
106+
107+
value = default;
108+
return false;
109+
}
110+
111+
public JsonElement this[string key]
112+
{
113+
get => IPAddress.TryParse(key, out var ip)
114+
? JsonSerializer.SerializeToDocument(_results[ip], ProxyJsonContext.Default.IpResult).RootElement
115+
: _extensionData[key];
116+
117+
set
118+
{
119+
if (IPAddress.TryParse(key, out var ip))
120+
{
121+
_results[ip] = value.Deserialize(ProxyJsonContext.Default.IpResult)!;
122+
}
123+
else
124+
{
125+
_extensionData[key] = value;
126+
}
127+
}
128+
}
129+
130+
ICollection<string> IDictionary<string, JsonElement>.Keys => _results.Keys
131+
.Select(ip => ip.ToString())
132+
.Concat(_extensionData.Keys)
133+
.ToList();
134+
135+
ICollection<JsonElement> IDictionary<string, JsonElement>.Values => _results.Values
136+
.Select(result => JsonSerializer.SerializeToDocument(result, ProxyJsonContext.Default.IpResult).RootElement)
137+
.Concat(_extensionData.Values)
138+
.ToList();
139+
140+
IEnumerator IEnumerable.GetEnumerator()
141+
{
142+
return GetEnumerator();
143+
}
144+
}
145+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ProxyCheckUtil
4+
{
5+
[JsonSourceGenerationOptions(
6+
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
7+
Converters = new []
8+
{
9+
typeof(JsonStringEnumConverter<StatusResult>),
10+
typeof(JsonStringEnumConverter<RiskLevel>)
11+
}
12+
)]
13+
[JsonSerializable(typeof(ProxyCheckResult))]
14+
[JsonSerializable(typeof(ProxyCheckResult.IpResult))]
15+
internal partial class ProxyJsonContext : JsonSerializerContext
16+
{
17+
}
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace ProxyCheckUtil
6+
{
7+
internal class YesNoJsonConverter : JsonConverter<bool>
8+
{
9+
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
10+
{
11+
return reader.GetString() == "yes";
12+
}
13+
14+
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
15+
{
16+
writer.WriteStringValue(value ? "yes" : "no");
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)