Skip to content

Commit bdc638a

Browse files
committed
Rename test symbols
1 parent 0c2d0e3 commit bdc638a

File tree

331 files changed

+15043
-441
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

331 files changed

+15043
-441
lines changed

.github/workflows/base.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
strategy:
2626
fail-fast: false
2727
matrix:
28-
framework: [ net8.0 ]
28+
framework: [ net9.0 ]
2929
os: [ ubuntu-latest ]
3030
configuration: [ release ]
3131
runs-on: ${{ matrix.os }}
@@ -53,7 +53,7 @@ jobs:
5353
uses: actions/setup-dotnet@v3
5454
with:
5555
dotnet-version: |
56-
8.0.x
56+
9.0.x
5757
- name: Run Tests
5858
shell: bash
5959
env:
@@ -64,7 +64,7 @@ jobs:
6464
dotnet test --configuration ${{ matrix.configuration }} --blame \
6565
--logger:"GitHubActions;report-warnings=false" --logger:"console;verbosity=normal" \
6666
--framework ${{ matrix.framework }} \
67-
test/EventStore.Client.Tests
67+
test/Kurrent.Client.Tests
6868
6969
# run: |
7070
# sudo ./gencert.sh

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ jobs:
1414
strategy:
1515
fail-fast: false
1616
matrix:
17-
docker-tag: [ ci, lts, previous-lts ]
17+
# docker-tag: [ ci, lts, previous-lts ]
18+
docker-tag: [ ci ]
1819
# test: [ Streams, PersistentSubscriptions, Operations, UserManagement, ProjectionManagement ]
1920
name: Test CE (${{ matrix.docker-tag }})
2021
with:

EventStore.Client.sln renamed to Kurrent.Client.sln

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client", "src\Ev
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C51F2C69-45A9-4D0D-A708-4FC319D5D340}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.Tests", "test\EventStore.Client.Tests\EventStore.Client.Tests.csproj", "{FC829F1B-43AD-4C96-9002-23D04BBA3AF3}"
13-
EndProject
14-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.Tests.Common", "test\EventStore.Client.Tests.Common\EventStore.Client.Tests.Common.csproj", "{E326832D-DE52-4DE4-9E54-C800908B75F3}"
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kurrent.Client.Tests", "test\Kurrent.Client.Tests\Kurrent.Client.Tests.csproj", "{FC829F1B-43AD-4C96-9002-23D04BBA3AF3}"
1513
EndProject
1614
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.Extensions.OpenTelemetry", "src\EventStore.Client.Extensions.OpenTelemetry\EventStore.Client.Extensions.OpenTelemetry.csproj", "{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E}"
1715
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kurrent.Client", "src\Kurrent.Client\Kurrent.Client.csproj", "{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kurrent.Client.Tests.Common", "test\Kurrent.Client.Tests.Common\Kurrent.Client.Tests.Common.csproj", "{47BF715B-A0BF-4044-B335-717E56422550}"
19+
EndProject
1820
Global
1921
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2022
Debug|x64 = Debug|x64
@@ -32,19 +34,24 @@ Global
3234
{FC829F1B-43AD-4C96-9002-23D04BBA3AF3}.Debug|x64.Build.0 = Debug|Any CPU
3335
{FC829F1B-43AD-4C96-9002-23D04BBA3AF3}.Release|x64.ActiveCfg = Release|Any CPU
3436
{FC829F1B-43AD-4C96-9002-23D04BBA3AF3}.Release|x64.Build.0 = Release|Any CPU
35-
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Debug|x64.ActiveCfg = Debug|Any CPU
36-
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Debug|x64.Build.0 = Debug|Any CPU
37-
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Release|x64.ActiveCfg = Release|Any CPU
38-
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Release|x64.Build.0 = Release|Any CPU
3937
{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E}.Debug|x64.ActiveCfg = Debug|Any CPU
4038
{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E}.Debug|x64.Build.0 = Debug|Any CPU
4139
{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E}.Release|x64.ActiveCfg = Release|Any CPU
4240
{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E}.Release|x64.Build.0 = Release|Any CPU
41+
{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0}.Debug|x64.ActiveCfg = Debug|Any CPU
42+
{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0}.Debug|x64.Build.0 = Debug|Any CPU
43+
{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0}.Release|x64.ActiveCfg = Release|Any CPU
44+
{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0}.Release|x64.Build.0 = Release|Any CPU
45+
{47BF715B-A0BF-4044-B335-717E56422550}.Debug|x64.ActiveCfg = Debug|Any CPU
46+
{47BF715B-A0BF-4044-B335-717E56422550}.Debug|x64.Build.0 = Debug|Any CPU
47+
{47BF715B-A0BF-4044-B335-717E56422550}.Release|x64.ActiveCfg = Release|Any CPU
48+
{47BF715B-A0BF-4044-B335-717E56422550}.Release|x64.Build.0 = Release|Any CPU
4349
EndGlobalSection
4450
GlobalSection(NestedProjects) = preSolution
4551
{8853D875-4A8E-450B-A1BE-9CEF8BEDABC3} = {EA59C1CB-16DA-4F68-AF8A-642A969B4CF8}
4652
{FC829F1B-43AD-4C96-9002-23D04BBA3AF3} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
47-
{E326832D-DE52-4DE4-9E54-C800908B75F3} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
4853
{F6A7B391-36F1-4838-AD08-E0EE0F2FE57E} = {EA59C1CB-16DA-4F68-AF8A-642A969B4CF8}
54+
{762EECAA-122E-4B0C-BC50-5AA4F72CA4E0} = {EA59C1CB-16DA-4F68-AF8A-642A969B4CF8}
55+
{47BF715B-A0BF-4044-B335-717E56422550} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
4956
EndGlobalSection
5057
EndGlobal
File renamed without changes.

src/Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33

44
<PropertyGroup>
55
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
6-
<RootNamespace>EventStore.Client</RootNamespace>
7-
<PackageId>EventStore.Client</PackageId>
6+
<RootNamespace>Kurrent.Client</RootNamespace>
7+
<PackageId>Kurrent.Client</PackageId>
88
</PropertyGroup>
99

1010
<PropertyGroup>
1111
<PackageIcon>ouro.png</PackageIcon>
1212
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
1313
<PackageProjectUrl>https://kurrent.io</PackageProjectUrl>
1414
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
15-
<PackageReleaseNotes>https://eventstore.com/blog/</PackageReleaseNotes>
16-
<PackageTags>kurrent eventstore client grpc</PackageTags>
15+
<PackageReleaseNotes>https://kurrent.io/blog/</PackageReleaseNotes>
16+
<PackageTags>kurrent client grpc</PackageTags>
1717
<Authors>Kurrent Ltd</Authors>
1818
<Copyright>Copyright 2012-$([System.DateTime]::Today.Year.ToString()) Kurrent Ltd</Copyright>
1919
<MinVerTagPrefix>v</MinVerTagPrefix>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
2+
3+
using System.Security.Cryptography;
4+
using System.Security.Cryptography.X509Certificates;
5+
6+
#if NET48
7+
using Org.BouncyCastle.Crypto;
8+
using Org.BouncyCastle.Crypto.Parameters;
9+
using Org.BouncyCastle.OpenSsl;
10+
using Org.BouncyCastle.Security;
11+
#endif
12+
13+
namespace EventStore.Client;
14+
15+
static class X509Certificates {
16+
// TODO SS: Use .NET 8 X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath) once the Windows32Exception issue is resolved
17+
public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath) {
18+
try {
19+
#if NET9_0_OR_GREATER
20+
using var publicCert = X509CertificateLoader.LoadCertificateFromFile(certPemFilePath);
21+
#else
22+
using var publicCert = new X509Certificate2(certPemFilePath);
23+
#endif
24+
using var privateKey = RSA.Create().ImportPrivateKeyFromFile(keyPemFilePath);
25+
using var certificate = publicCert.CopyWithPrivateKey(privateKey);
26+
27+
#if NET48
28+
return new(certificate.Export(X509ContentType.Pfx));
29+
#else
30+
return X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath);
31+
#endif
32+
} catch (Exception ex) {
33+
throw new CryptographicException($"Failed to load private key: {ex.Message}");
34+
}
35+
36+
// Notes:
37+
// using X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath) would be the ideal choice here,
38+
// but it's currently causing a Win32Exception specifically on Windows. Alternative implementation is used until the issue is resolved.
39+
//
40+
// Error: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed because the platform
41+
// does not support ephemeral keys. Win32Exception: No credentials are available in the security package
42+
//
43+
// public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath) =>
44+
// X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath);
45+
}
46+
}
47+
48+
public static class RsaExtensions {
49+
#if NET48
50+
public static RSA ImportPrivateKeyFromFile(this RSA rsa, string privateKeyPath) {
51+
var (content, label) = LoadPemKeyFile(privateKeyPath);
52+
53+
using var reader = new PemReader(new StringReader(string.Join(Environment.NewLine, content)));
54+
55+
var keyParameters = reader.ReadObject() switch {
56+
RsaPrivateCrtKeyParameters parameters => parameters,
57+
AsymmetricCipherKeyPair keyPair => keyPair.Private as RsaPrivateCrtKeyParameters,
58+
_ => throw new NotSupportedException($"Invalid private key format: {label}")
59+
};
60+
61+
rsa.ImportParameters(DotNetUtilities.ToRSAParameters(keyParameters));
62+
63+
return rsa;
64+
}
65+
#else
66+
public static RSA ImportPrivateKeyFromFile(this RSA rsa, string privateKeyPath) {
67+
var (content, label) = LoadPemKeyFile(privateKeyPath);
68+
69+
var privateKey = string.Join(string.Empty, content[1..^1]);
70+
var privateKeyBytes = Convert.FromBase64String(privateKey);
71+
72+
if (label == RsaPemLabels.Pkcs8PrivateKey)
73+
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
74+
else if (label == RsaPemLabels.RSAPrivateKey)
75+
rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
76+
77+
return rsa;
78+
}
79+
#endif
80+
81+
static (string[] Content, string Label) LoadPemKeyFile(string privateKeyPath) {
82+
var content = File.ReadAllLines(privateKeyPath);
83+
var label = RsaPemLabels.ParseKeyLabel(content[0]);
84+
85+
if (RsaPemLabels.IsEncryptedPrivateKey(label))
86+
throw new NotSupportedException("Encrypted private keys are not supported");
87+
88+
return (content, label);
89+
}
90+
}
91+
92+
static class RsaPemLabels {
93+
public const string RSAPrivateKey = "RSA PRIVATE KEY";
94+
public const string Pkcs8PrivateKey = "PRIVATE KEY";
95+
public const string EncryptedPkcs8PrivateKey = "ENCRYPTED PRIVATE KEY";
96+
97+
public static readonly string[] PrivateKeyLabels = [RSAPrivateKey, Pkcs8PrivateKey, EncryptedPkcs8PrivateKey];
98+
99+
public static bool IsPrivateKey(string label) => Array.IndexOf(PrivateKeyLabels, label) != -1;
100+
101+
public static bool IsEncryptedPrivateKey(string label) => label == EncryptedPkcs8PrivateKey;
102+
103+
const string LabelPrefix = "-----BEGIN ";
104+
const string LabelSuffix = "-----";
105+
106+
public static string ParseKeyLabel(string pemFileHeader) {
107+
var label = pemFileHeader.Replace(LabelPrefix, string.Empty).Replace(LabelSuffix, string.Empty);
108+
109+
if (!IsPrivateKey(label))
110+
throw new CryptographicException($"Unknown private key label: {label}");
111+
112+
return label;
113+
}
114+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Grpc.Core;
2+
3+
namespace EventStore.Client;
4+
5+
static class ChannelBaseExtensions {
6+
public static async ValueTask DisposeAsync(this ChannelBase channel) {
7+
await channel.ShutdownAsync().ConfigureAwait(false);
8+
(channel as IDisposable)?.Dispose();
9+
}
10+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System.Net;
2+
using TChannel = Grpc.Net.Client.GrpcChannel;
3+
4+
namespace EventStore.Client {
5+
// Maintains Channels keyed by DnsEndPoint so the channels can be reused.
6+
// Deals with the disposal difference between grpc.net and grpc.core
7+
// Thread safe.
8+
internal class ChannelCache :
9+
IAsyncDisposable {
10+
11+
private readonly KurrentClientSettings _settings;
12+
private readonly Random _random;
13+
private readonly Dictionary<DnsEndPoint, TChannel> _channels;
14+
private readonly object _lock = new();
15+
private bool _disposed;
16+
17+
public ChannelCache(KurrentClientSettings settings) {
18+
_settings = settings;
19+
_random = new Random(0);
20+
_channels = new Dictionary<DnsEndPoint, TChannel>(
21+
DnsEndPointEqualityComparer.Instance);
22+
}
23+
24+
public TChannel GetChannelInfo(DnsEndPoint endPoint) {
25+
lock (_lock) {
26+
ThrowIfDisposed();
27+
28+
if (!_channels.TryGetValue(endPoint, out var channel)) {
29+
channel = ChannelFactory.CreateChannel(
30+
settings: _settings,
31+
endPoint: endPoint);
32+
_channels[endPoint] = channel;
33+
}
34+
35+
return channel;
36+
}
37+
}
38+
39+
public KeyValuePair<DnsEndPoint, TChannel>[] GetRandomOrderSnapshot() {
40+
lock (_lock) {
41+
ThrowIfDisposed();
42+
43+
return _channels
44+
.OrderBy(_ => _random.Next())
45+
.ToArray();
46+
}
47+
}
48+
49+
// Update the cache to contain channels for exactly these endpoints
50+
public void UpdateCache(IEnumerable<DnsEndPoint> endPoints) {
51+
lock (_lock) {
52+
ThrowIfDisposed();
53+
54+
// remove
55+
var endPointsToDiscard = _channels.Keys
56+
.Except(endPoints, DnsEndPointEqualityComparer.Instance)
57+
.ToArray();
58+
59+
var channelsToDispose = new List<TChannel>(endPointsToDiscard.Length);
60+
61+
foreach (var endPoint in endPointsToDiscard) {
62+
if (!_channels.TryGetValue(endPoint, out var channel))
63+
continue;
64+
65+
_channels.Remove(endPoint);
66+
channelsToDispose.Add(channel);
67+
}
68+
69+
_ = DisposeChannelsAsync(channelsToDispose);
70+
71+
// add
72+
foreach (var endPoint in endPoints) {
73+
GetChannelInfo(endPoint);
74+
}
75+
}
76+
}
77+
78+
public void Dispose() {
79+
lock (_lock) {
80+
if (_disposed)
81+
return;
82+
83+
_disposed = true;
84+
85+
foreach (var channel in _channels.Values) {
86+
channel.Dispose();
87+
}
88+
89+
_channels.Clear();
90+
}
91+
}
92+
93+
public async ValueTask DisposeAsync() {
94+
var channelsToDispose = Array.Empty<TChannel>();
95+
96+
lock (_lock) {
97+
if (_disposed)
98+
return;
99+
_disposed = true;
100+
101+
channelsToDispose = _channels.Values.ToArray();
102+
_channels.Clear();
103+
}
104+
105+
await DisposeChannelsAsync(channelsToDispose).ConfigureAwait(false);
106+
}
107+
108+
private void ThrowIfDisposed() {
109+
lock (_lock) {
110+
if (_disposed) {
111+
throw new ObjectDisposedException(GetType().ToString());
112+
}
113+
}
114+
}
115+
116+
private static async Task DisposeChannelsAsync(IEnumerable<TChannel> channels) {
117+
foreach (var channel in channels)
118+
await channel.DisposeAsync().ConfigureAwait(false);
119+
}
120+
121+
private class DnsEndPointEqualityComparer : IEqualityComparer<DnsEndPoint> {
122+
public static readonly DnsEndPointEqualityComparer Instance = new();
123+
124+
public bool Equals(DnsEndPoint? x, DnsEndPoint? y) {
125+
if (ReferenceEquals(x, y))
126+
return true;
127+
if (x is null)
128+
return false;
129+
if (y is null)
130+
return false;
131+
if (x.GetType() != y.GetType())
132+
return false;
133+
return
134+
string.Equals(x.Host, y.Host, StringComparison.OrdinalIgnoreCase) &&
135+
x.Port == y.Port;
136+
}
137+
138+
public int GetHashCode(DnsEndPoint obj) {
139+
unchecked {
140+
return (StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Host) * 397) ^
141+
obj.Port;
142+
}
143+
}
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)