[#28] Client: Apply code optimizations #35
57 changed files with 845 additions and 1116 deletions
|
@ -214,7 +214,7 @@ dotnet_diagnostic.CA1700.severity = warning
|
||||||
dotnet_diagnostic.CA1707.severity = warning
|
dotnet_diagnostic.CA1707.severity = warning
|
||||||
|
|
||||||
# CA1708: Identifiers should differ by more than case
|
# CA1708: Identifiers should differ by more than case
|
||||||
dotnet_diagnostic.CA1708.severity = warning
|
dotnet_diagnostic.CA1708.severity = none
|
||||||
|
|
||||||
# CA1710: Identifiers should have correct suffix
|
# CA1710: Identifiers should have correct suffix
|
||||||
dotnet_diagnostic.CA1710.severity = warning
|
dotnet_diagnostic.CA1710.severity = warning
|
||||||
|
@ -232,7 +232,7 @@ dotnet_diagnostic.CA1713.severity = warning
|
||||||
dotnet_diagnostic.CA1715.severity = warning
|
dotnet_diagnostic.CA1715.severity = warning
|
||||||
|
|
||||||
# CA1716: Identifiers should not match keywords
|
# CA1716: Identifiers should not match keywords
|
||||||
dotnet_diagnostic.CA1716.severity = warning
|
dotnet_diagnostic.CA1716.severity = none
|
||||||
|
|
||||||
# CA1720: Identifier contains type name
|
# CA1720: Identifier contains type name
|
||||||
dotnet_diagnostic.CA1720.severity = warning
|
dotnet_diagnostic.CA1720.severity = warning
|
||||||
|
|
|
@ -11,4 +11,8 @@ public class ClientKey(ECDsa key)
|
||||||
internal ECDsa ECDsaKey { get; } = key;
|
internal ECDsa ECDsaKey { get; } = key;
|
||||||
|
|
||||||
internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey());
|
internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey());
|
||||||
|
|
||||||
|
internal string PublicKey { get; } = key.PublicKey().ToString();
|
||||||
|
|
||||||
|
internal FrostFsOwner Owner { get; } = new FrostFsOwner(key.PublicKey().PublicKeyToAddress());
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
|
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
|
||||||
|
|
|
@ -4,12 +4,7 @@ using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Frostfs.V2.Ape;
|
using Frostfs.V2.Ape;
|
||||||
using Frostfs.V2.Apemanager;
|
|
||||||
|
|
||||||
using FrostFS.Accounting;
|
|
||||||
using FrostFS.Container;
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
using FrostFS.Object;
|
|
||||||
using FrostFS.SDK.Client.Interfaces;
|
using FrostFS.SDK.Client.Interfaces;
|
||||||
using FrostFS.SDK.Client.Services;
|
using FrostFS.SDK.Client.Services;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
@ -21,32 +16,40 @@ using Grpc.Net.Client;
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
using static Frostfs.V2.Apemanager.APEManagerService;
|
||||||
|
using static FrostFS.Accounting.AccountingService;
|
||||||
|
using static FrostFS.Container.ContainerService;
|
||||||
|
using static FrostFS.Netmap.NetmapService;
|
||||||
|
using static FrostFS.Object.ObjectService;
|
||||||
|
using static FrostFS.Session.SessionService;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class FrostFSClient : IFrostFSClient
|
public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
private bool isDisposed;
|
private bool isDisposed;
|
||||||
|
|
||||||
internal ContainerService.ContainerServiceClient? ContainerServiceClient { get; set; }
|
internal ContainerServiceClient? ContainerServiceClient { get; set; }
|
||||||
|
internal ContainerServiceProvider? ContainerServiceProvider { get; set; }
|
||||||
|
|
||||||
internal NetmapService.NetmapServiceClient? NetmapServiceClient { get; set; }
|
internal NetmapServiceClient? NetmapServiceClient { get; set; }
|
||||||
|
internal NetmapServiceProvider? NetmapServiceProvider { get; set; }
|
||||||
|
|
||||||
internal APEManagerService.APEManagerServiceClient? ApeManagerServiceClient { get; set; }
|
internal APEManagerServiceClient? ApeManagerServiceClient { get; set; }
|
||||||
|
internal ApeManagerServiceProvider? ApeManagerServiceProvider { get; set; }
|
||||||
|
|
||||||
internal SessionService.SessionServiceClient? SessionServiceClient { get; set; }
|
internal SessionServiceClient? SessionServiceClient { get; set; }
|
||||||
|
internal SessionServiceProvider? SessionServiceProvider { get; set; }
|
||||||
|
|
||||||
internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; }
|
internal ObjectServiceClient? ObjectServiceClient { get; set; }
|
||||||
|
internal ObjectServiceProvider? ObjectServiceProvider { get; set; }
|
||||||
|
|
||||||
internal AccountingService.AccountingServiceClient? AccountingServiceClient { get; set; }
|
internal AccountingServiceClient? AccountingServiceClient { get; set; }
|
||||||
|
internal AccountingServiceProvider? AccountingServiceProvider { get; set; }
|
||||||
|
|
||||||
internal ClientContext ClientCtx { get; set; }
|
internal ClientContext ClientCtx { get; set; }
|
||||||
|
|
||||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
public static IFrostFSClient GetSingleOwnerInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
||||||
{
|
|
||||||
return new FrostFSClient(clientOptions, channelOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFrostFSClient GetSingleOwnerInstance(IOptions<SingleOwnerClientSettings> clientOptions, GrpcChannelOptions? channelOptions = null)
|
|
||||||
{
|
{
|
||||||
return new FrostFSClient(clientOptions, channelOptions);
|
return new FrostFSClient(clientOptions, channelOptions);
|
||||||
}
|
}
|
||||||
|
@ -62,28 +65,28 @@ public class FrostFSClient : IFrostFSClient
|
||||||
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
|
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IFrostFSClient GetTestInstance(
|
public static IFrostFSClient GetTestInstance(
|
||||||
IOptions<SingleOwnerClientSettings> clientOptions,
|
IOptions<ClientSettings> settings,
|
||||||
GrpcChannelOptions? channelOptions,
|
GrpcChannelOptions? channelOptions,
|
||||||
NetmapService.NetmapServiceClient netmapService,
|
NetmapServiceClient netmapService,
|
||||||
SessionService.SessionServiceClient sessionService,
|
SessionServiceClient sessionService,
|
||||||
ContainerService.ContainerServiceClient containerService,
|
ContainerServiceClient containerService,
|
||||||
ObjectService.ObjectServiceClient objectService)
|
ObjectServiceClient objectService)
|
||||||
{
|
{
|
||||||
if (clientOptions is null)
|
if (settings is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(clientOptions));
|
throw new ArgumentNullException(nameof(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FrostFSClient(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService);
|
return new FrostFSClient(settings, channelOptions, containerService, netmapService, sessionService, objectService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FrostFSClient(
|
private FrostFSClient(
|
||||||
IOptions<SingleOwnerClientSettings> settings,
|
IOptions<ClientSettings> settings,
|
||||||
GrpcChannelOptions? channelOptions,
|
GrpcChannelOptions? channelOptions,
|
||||||
ContainerService.ContainerServiceClient containerService,
|
ContainerServiceClient containerService,
|
||||||
NetmapService.NetmapServiceClient netmapService,
|
NetmapServiceClient netmapService,
|
||||||
SessionService.SessionServiceClient sessionService,
|
SessionServiceClient sessionService,
|
||||||
ObjectService.ObjectServiceClient objectService)
|
ObjectServiceClient objectService)
|
||||||
{
|
{
|
||||||
if (settings is null)
|
if (settings is null)
|
||||||
{
|
{
|
||||||
|
@ -91,14 +94,18 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
|
|
||||||
var ecdsaKey = settings.Value.Key.LoadWif();
|
var ecdsaKey = settings.Value.Key.LoadWif();
|
||||||
FrostFsOwner.FromKey(ecdsaKey);
|
|
||||||
|
|
||||||
ClientCtx = new ClientContext(
|
ClientCtx = new ClientContext(
|
||||||
client: this,
|
client: this,
|
||||||
key: ecdsaKey,
|
key: new ClientKey(ecdsaKey),
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
owner: FrostFsOwner.FromKey(ecdsaKey),
|
||||||
channel: InitGrpcChannel(settings.Value.Host, channelOptions),
|
channel: InitGrpcChannel(settings.Value.Host, channelOptions),
|
||||||
version: new FrostFsVersion(2, 13));
|
version: new FrostFsVersion(2, 13))
|
||||||
|
{
|
||||||
|
SessionCache = new SessionCache(0),
|
||||||
|
Callback = settings.Value.Callback,
|
||||||
|
Interceptors = settings.Value.Interceptors
|
||||||
|
};
|
||||||
|
|
||||||
ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService));
|
ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService));
|
||||||
NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService));
|
NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService));
|
||||||
|
@ -106,28 +113,9 @@ public class FrostFSClient : IFrostFSClient
|
||||||
ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService));
|
ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService));
|
||||||
}
|
}
|
||||||
|
|
||||||
private FrostFSClient(IOptions<ClientSettings> options, GrpcChannelOptions? channelOptions)
|
private FrostFSClient(IOptions<ClientSettings> settings, GrpcChannelOptions? channelOptions)
|
||||||
{
|
{
|
||||||
var clientSettings = (options?.Value) ?? throw new ArgumentNullException(nameof(options), "Options value must be initialized");
|
var clientSettings = (settings?.Value) ?? throw new ArgumentNullException(nameof(settings), "Options value must be initialized");
|
||||||
|
|
||||||
clientSettings.Validate();
|
|
||||||
|
|
||||||
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
|
||||||
|
|
||||||
ClientCtx = new ClientContext(
|
|
||||||
this,
|
|
||||||
key: null,
|
|
||||||
owner: null,
|
|
||||||
channel: channel,
|
|
||||||
version: new FrostFsVersion(2, 13));
|
|
||||||
|
|
||||||
// TODO: define timeout logic
|
|
||||||
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
|
||||||
}
|
|
||||||
|
|
||||||
private FrostFSClient(IOptions<SingleOwnerClientSettings> options, GrpcChannelOptions? channelOptions)
|
|
||||||
{
|
|
||||||
var clientSettings = (options?.Value) ?? throw new ArgumentNullException(nameof(options), "Options value must be initialized");
|
|
||||||
|
|
||||||
clientSettings.Validate();
|
clientSettings.Validate();
|
||||||
|
|
||||||
|
@ -137,10 +125,15 @@ public class FrostFSClient : IFrostFSClient
|
||||||
|
|
||||||
ClientCtx = new ClientContext(
|
ClientCtx = new ClientContext(
|
||||||
this,
|
this,
|
||||||
key: ecdsaKey,
|
key: new ClientKey(ecdsaKey),
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
owner: FrostFsOwner.FromKey(ecdsaKey),
|
||||||
channel: channel,
|
channel: channel,
|
||||||
version: new FrostFsVersion(2, 13));
|
version: new FrostFsVersion(2, 13))
|
||||||
|
{
|
||||||
|
SessionCache = new SessionCache(0),
|
||||||
|
Callback = settings.Value.Callback,
|
||||||
|
Interceptors = settings.Value.Interceptors
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: define timeout logic
|
// TODO: define timeout logic
|
||||||
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
||||||
|
@ -150,12 +143,14 @@ public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
ClientCtx = new ClientContext(
|
ClientCtx = new ClientContext(
|
||||||
client: this,
|
client: this,
|
||||||
key: prm.Key,
|
key: new ClientKey(prm.Key),
|
||||||
owner: FrostFsOwner.FromKey(prm.Key!),
|
owner: FrostFsOwner.FromKey(prm.Key!),
|
||||||
channel: InitGrpcChannel(prm.Address, null), //prm.GrpcChannelOptions),
|
channel: InitGrpcChannel(prm.Address, null), //prm.GrpcChannelOptions),
|
||||||
version: new FrostFsVersion(2, 13))
|
version: new FrostFsVersion(2, 13))
|
||||||
{
|
{
|
||||||
SessionCache = cache
|
SessionCache = cache,
|
||||||
|
Interceptors = prm.Interceptors,
|
||||||
|
Callback = prm.Callback
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,14 +170,14 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
|
|
||||||
#region ApeManagerImplementation
|
#region ApeManagerImplementation
|
||||||
public Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
public Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
var service = GetApeManagerService(args);
|
var service = GetApeManagerService();
|
||||||
return service.AddChainAsync(args);
|
return service.AddChainAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +188,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
var service = GetApeManagerService(args);
|
var service = GetApeManagerService();
|
||||||
return service.RemoveChainAsync(args);
|
return service.RemoveChainAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +197,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetApeManagerService(args);
|
var service = GetApeManagerService();
|
||||||
return service.ListChainAsync(args);
|
return service.ListChainAsync(args);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -213,14 +208,14 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetContainerService(args);
|
var service = GetContainerService();
|
||||||
return service.GetContainerAsync(args);
|
return service.GetContainerAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll? args = null)
|
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll? args = null)
|
||||||
{
|
{
|
||||||
args ??= new PrmContainerGetAll();
|
args ??= new PrmContainerGetAll();
|
||||||
var service = GetContainerService(args);
|
var service = GetContainerService();
|
||||||
return service.ListContainersAsync(args);
|
return service.ListContainersAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +224,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetContainerService(args);
|
var service = GetContainerService();
|
||||||
return service.CreateContainerAsync(args);
|
return service.CreateContainerAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +233,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetContainerService(args);
|
var service = GetContainerService();
|
||||||
return service.DeleteContainerAsync(args);
|
return service.DeleteContainerAsync(args);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -247,21 +242,21 @@ public class FrostFSClient : IFrostFSClient
|
||||||
public Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(PrmNetmapSnapshot? args)
|
public Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(PrmNetmapSnapshot? args)
|
||||||
{
|
{
|
||||||
args ??= new PrmNetmapSnapshot();
|
args ??= new PrmNetmapSnapshot();
|
||||||
var service = GetNetmapService(args);
|
var service = GetNetmapService();
|
||||||
return service.GetNetmapSnapshotAsync(args);
|
return service.GetNetmapSnapshotAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<FrostFsNodeInfo> GetNodeInfoAsync(PrmNodeInfo? args)
|
public Task<FrostFsNodeInfo> GetNodeInfoAsync(PrmNodeInfo? args)
|
||||||
{
|
{
|
||||||
args ??= new PrmNodeInfo();
|
args ??= new PrmNodeInfo();
|
||||||
var service = GetNetmapService(args);
|
var service = GetNetmapService();
|
||||||
return service.GetLocalNodeInfoAsync(args);
|
return service.GetLocalNodeInfoAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<NetworkSettings> GetNetworkSettingsAsync(PrmNetworkSettings? args)
|
public Task<NetworkSettings> GetNetworkSettingsAsync(PrmNetworkSettings? args)
|
||||||
{
|
{
|
||||||
args ??= new PrmNetworkSettings();
|
args ??= new PrmNetworkSettings();
|
||||||
var service = GetNetmapService(args);
|
var service = GetNetmapService();
|
||||||
return service.GetNetworkSettingsAsync(args.Context!);
|
return service.GetNetworkSettingsAsync(args.Context!);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -272,7 +267,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.GetObjectHeadAsync(args);
|
return service.GetObjectHeadAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +276,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.GetObjectAsync(args);
|
return service.GetObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +285,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.GetRangeAsync(args);
|
return service.GetRangeAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,17 +294,16 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.GetRangeHashAsync(args);
|
return service.GetRangeHashAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
public Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.PutObjectAsync(args);
|
return service.PutObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +312,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.PutSingleObjectAsync(args);
|
return service.PutSingleObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +323,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.PatchObjectAsync(args);
|
return service.PatchObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +332,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.DeleteObjectAsync(args);
|
return service.DeleteObjectAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,7 +341,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetObjectService(args);
|
var service = GetObjectService();
|
||||||
return service.SearchObjectsAsync(args);
|
return service.SearchObjectsAsync(args);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -358,10 +352,9 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var session = await CreateSessionInternalAsync(args).ConfigureAwait(false);
|
var token = await CreateSessionInternalAsync(args).ConfigureAwait(false);
|
||||||
|
|
||||||
var token = session.Serialize();
|
return new FrostFsSessionToken(token);
|
||||||
return new FrostFsSessionToken(token, session.Body.Id.ToUuid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args)
|
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args)
|
||||||
|
@ -369,7 +362,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
if (args is null)
|
if (args is null)
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
|
||||||
var service = GetSessionService(args);
|
var service = GetSessionService();
|
||||||
return service.CreateSessionAsync(args);
|
return service.CreateSessionAsync(args);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -379,18 +372,18 @@ public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
args ??= new PrmBalance();
|
args ??= new PrmBalance();
|
||||||
|
|
||||||
var service = GetAccouningService(args);
|
var service = GetAccouningService();
|
||||||
return await service.GetBallance(args).ConfigureAwait(false);
|
return await service.GetBallance(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ToolsImplementation
|
#region ToolsImplementation
|
||||||
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx)
|
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header)
|
||||||
{
|
{
|
||||||
if (header == null)
|
if (header == null)
|
||||||
throw new ArgumentNullException(nameof(header));
|
throw new ArgumentNullException(nameof(header));
|
||||||
|
|
||||||
return ObjectTools.CalculateObjectId(header, ctx);
|
return ObjectTools.CalculateObjectId(header, this.ClientCtx);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -398,59 +391,34 @@ public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
var args = new PrmNodeInfo(ctx);
|
var args = new PrmNodeInfo(ctx);
|
||||||
|
|
||||||
if (ctx?.Version == null)
|
var service = GetNetmapService();
|
||||||
throw new ArgumentNullException(nameof(ctx), "Version must be initialized");
|
|
||||||
|
|
||||||
var service = GetNetmapService(args);
|
|
||||||
var localNodeInfo = await service.GetLocalNodeInfoAsync(args).ConfigureAwait(false);
|
var localNodeInfo = await service.GetLocalNodeInfoAsync(args).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!localNodeInfo.Version.IsSupported(ctx.Version))
|
if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
|
||||||
{
|
{
|
||||||
var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
|
var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
|
||||||
throw new FrostFsException(msg);
|
throw new FrostFsException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallInvoker? SetupClientContext(IContext ctx)
|
private CallInvoker? CreateInvoker()
|
||||||
{
|
{
|
||||||
if (isDisposed)
|
if (isDisposed)
|
||||||
throw new FrostFsInvalidObjectException("Client is disposed.");
|
throw new FrostFsInvalidObjectException("Client is disposed.");
|
||||||
|
|
||||||
if (ctx.Context!.Key == null)
|
|
||||||
{
|
|
||||||
if (ClientCtx.Key == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(ctx), "Key is not initialized.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Context.Key = ClientCtx.Key.ECDsaKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.Context.OwnerId == null)
|
|
||||||
{
|
|
||||||
ctx.Context.OwnerId = ClientCtx.Owner ?? FrostFsOwner.FromKey(ctx.Context.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.Context.Version == null)
|
|
||||||
{
|
|
||||||
if (ClientCtx.Version == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(ctx), "Version is not initialized.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Context.Version = ClientCtx.Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
CallInvoker? callInvoker = null;
|
CallInvoker? callInvoker = null;
|
||||||
|
|
||||||
foreach (var interceptor in ctx.Context.Interceptors)
|
if (ClientCtx.Interceptors != null)
|
||||||
|
{
|
||||||
|
foreach (var interceptor in ClientCtx.Interceptors)
|
||||||
callInvoker = AddInvoker(callInvoker, interceptor);
|
callInvoker = AddInvoker(callInvoker, interceptor);
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx.Context.Callback != null)
|
if (ClientCtx.Callback != null)
|
||||||
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback));
|
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ClientCtx.Callback));
|
||||||
|
|
||||||
if (ctx.Context.PoolErrorHandler != null)
|
if (ClientCtx.PoolErrorHandler != null)
|
||||||
callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ctx.Context.PoolErrorHandler));
|
callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ClientCtx.PoolErrorHandler));
|
||||||
|
|
||||||
return callInvoker;
|
return callInvoker;
|
||||||
|
|
||||||
|
@ -465,76 +433,110 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NetmapServiceProvider GetNetmapService(IContext ctx)
|
private NetmapServiceProvider GetNetmapService()
|
||||||
{
|
{
|
||||||
var callInvoker = SetupClientContext(ctx);
|
if (NetmapServiceProvider == null)
|
||||||
var client = NetmapServiceClient ?? (callInvoker != null
|
{
|
||||||
? new NetmapService.NetmapServiceClient(callInvoker)
|
var invoker = CreateInvoker();
|
||||||
: new NetmapService.NetmapServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new NetmapServiceProvider(client, ClientCtx);
|
NetmapServiceClient = NetmapServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new NetmapServiceClient(invoker)
|
||||||
|
: new NetmapServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
NetmapServiceProvider = new NetmapServiceProvider(NetmapServiceClient, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionServiceProvider GetSessionService(IContext ctx)
|
return NetmapServiceProvider;
|
||||||
{
|
|
||||||
var callInvoker = SetupClientContext(ctx);
|
|
||||||
var client = SessionServiceClient ?? (callInvoker != null
|
|
||||||
? new SessionService.SessionServiceClient(callInvoker)
|
|
||||||
: new SessionService.SessionServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new SessionServiceProvider(client, ClientCtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApeManagerServiceProvider GetApeManagerService(IContext ctx)
|
private SessionServiceProvider GetSessionService()
|
||||||
{
|
{
|
||||||
var callInvoker = SetupClientContext(ctx);
|
if (SessionServiceProvider == null)
|
||||||
var client = ApeManagerServiceClient ?? (callInvoker != null
|
{
|
||||||
? new APEManagerService.APEManagerServiceClient(callInvoker)
|
var invoker = CreateInvoker();
|
||||||
: new APEManagerService.APEManagerServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new ApeManagerServiceProvider(client, ClientCtx);
|
SessionServiceClient = SessionServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new SessionServiceClient(invoker)
|
||||||
|
: new SessionServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
SessionServiceProvider = new SessionServiceProvider(SessionServiceClient, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountingServiceProvider GetAccouningService(IContext ctx)
|
return SessionServiceProvider;
|
||||||
{
|
|
||||||
var callInvoker = SetupClientContext(ctx);
|
|
||||||
var client = AccountingServiceClient ?? (callInvoker != null
|
|
||||||
? new AccountingService.AccountingServiceClient(callInvoker)
|
|
||||||
: new AccountingService.AccountingServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new AccountingServiceProvider(client, ClientCtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContainerServiceProvider GetContainerService(IContext ctx)
|
private ApeManagerServiceProvider GetApeManagerService()
|
||||||
{
|
{
|
||||||
var callInvoker = SetupClientContext(ctx);
|
if (ApeManagerServiceProvider == null)
|
||||||
var client = ContainerServiceClient ?? (callInvoker != null
|
{
|
||||||
? new ContainerService.ContainerServiceClient(callInvoker)
|
var invoker = CreateInvoker();
|
||||||
: new ContainerService.ContainerServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new ContainerServiceProvider(client, ClientCtx);
|
ApeManagerServiceClient = ApeManagerServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new APEManagerServiceClient(invoker)
|
||||||
|
: new APEManagerServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
ApeManagerServiceProvider = new ApeManagerServiceProvider(ApeManagerServiceClient, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectServiceProvider GetObjectService(IContext ctx)
|
return ApeManagerServiceProvider;
|
||||||
{
|
|
||||||
var callInvoker = SetupClientContext(ctx);
|
|
||||||
var client = ObjectServiceClient ?? (callInvoker != null
|
|
||||||
? new ObjectService.ObjectServiceClient(callInvoker)
|
|
||||||
: new ObjectService.ObjectServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new ObjectServiceProvider(client, ClientCtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountingServiceProvider GetAccountService(IContext ctx)
|
private AccountingServiceProvider GetAccouningService()
|
||||||
{
|
{
|
||||||
var callInvoker = SetupClientContext(ctx);
|
if (this.AccountingServiceProvider == null)
|
||||||
var client = AccountingServiceClient ?? (callInvoker != null
|
{
|
||||||
? new AccountingService.AccountingServiceClient(callInvoker)
|
var invoker = CreateInvoker();
|
||||||
: new AccountingService.AccountingServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
return new AccountingServiceProvider(client, ClientCtx);
|
AccountingServiceClient = AccountingServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new AccountingServiceClient(invoker)
|
||||||
|
: new AccountingServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
AccountingServiceProvider = new AccountingServiceProvider(AccountingServiceClient, ClientCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return AccountingServiceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContainerServiceProvider GetContainerService()
|
||||||
|
{
|
||||||
|
if (this.ContainerServiceProvider == null)
|
||||||
|
{
|
||||||
|
var invoker = CreateInvoker();
|
||||||
|
|
||||||
|
ContainerServiceClient = ContainerServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new ContainerServiceClient(invoker)
|
||||||
|
: new ContainerServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
ContainerServiceProvider = new ContainerServiceProvider(ContainerServiceClient, ClientCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContainerServiceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectServiceProvider GetObjectService()
|
||||||
|
{
|
||||||
|
if (this.ObjectServiceProvider == null)
|
||||||
|
{
|
||||||
|
var invoker = CreateInvoker();
|
||||||
|
|
||||||
|
ObjectServiceClient = ObjectServiceClient ?? (
|
||||||
|
invoker != null
|
||||||
|
? new ObjectServiceClient(invoker)
|
||||||
|
: new ObjectServiceClient(ClientCtx.Channel));
|
||||||
|
|
||||||
|
ObjectServiceProvider = new ObjectServiceProvider(ObjectServiceClient, ClientCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjectServiceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -559,7 +561,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
{
|
{
|
||||||
var prm = new PrmBalance(ctx);
|
var prm = new PrmBalance(ctx);
|
||||||
|
|
||||||
var service = GetAccouningService(prm);
|
var service = GetAccouningService();
|
||||||
_ = await service.GetBallance(prm).ConfigureAwait(false);
|
_ = await service.GetBallance(prm).ConfigureAwait(false);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -21,7 +21,7 @@ public interface IFrostFSClient : IDisposable
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ApeManager
|
#region ApeManager
|
||||||
Task<byte[]> AddChainAsync(PrmApeChainAdd args);
|
Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args);
|
||||||
|
|
||||||
Task RemoveChainAsync(PrmApeChainRemove args);
|
Task RemoveChainAsync(PrmApeChainRemove args);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public interface IFrostFSClient : IDisposable
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Tools
|
#region Tools
|
||||||
FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx);
|
FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public Task<string?> Dial(CallContext ctx);
|
public Task<string?> Dial(CallContext ctx);
|
||||||
|
|
|
@ -43,6 +43,6 @@ public static class ContainerIdMapper
|
||||||
throw new ArgumentNullException(nameof(message));
|
throw new ArgumentNullException(nameof(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FrostFsContainerId(Base58.Encode(message.Value.ToByteArray()));
|
return new FrostFsContainerId(Base58.Encode(message.Value.Span));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,7 +39,7 @@ public static class NodeInfoMapper
|
||||||
state: state,
|
state: state,
|
||||||
addresses: [.. nodeInfo.Addresses],
|
addresses: [.. nodeInfo.Addresses],
|
||||||
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
||||||
publicKey: nodeInfo.PublicKey.ToByteArray()
|
publicKey: nodeInfo.PublicKey.Memory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ internal static class ObjectMapper
|
||||||
{
|
{
|
||||||
return new FrostFsObject(obj.Header.ToModel())
|
return new FrostFsObject(obj.Header.ToModel())
|
||||||
{
|
{
|
||||||
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray())
|
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public static class ObjectHeaderMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = new FrostFsObjectHeader(
|
var model = new FrostFsObjectHeader(
|
||||||
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())),
|
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)),
|
||||||
objTypeName,
|
objTypeName,
|
||||||
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
||||||
split,
|
split,
|
||||||
|
|
|
@ -28,6 +28,6 @@ public static class ObjectIdMapper
|
||||||
throw new ArgumentNullException(nameof(objectId));
|
throw new ArgumentNullException(nameof(objectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(objectId.Value.ToByteArray());
|
return FrostFsObjectId.FromHash(objectId.Value.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,7 +44,7 @@ public static class OwnerIdMapper
|
||||||
|
|
||||||
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
||||||
{
|
{
|
||||||
model = new FrostFsOwner(Base58.Encode(message.Value.ToByteArray()));
|
model = new FrostFsOwner(Base58.Encode(message.Value.Span));
|
||||||
|
|
||||||
Caches.Owners.Set(message, model, _oneHourExpiration);
|
Caches.Owners.Set(message, model, _oneHourExpiration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ using System.Collections.ObjectModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
public class ClientSettings
|
public class ClientSettings
|
||||||
|
@ -11,60 +13,25 @@ public class ClientSettings
|
||||||
|
|
||||||
public string Host { get; set; } = string.Empty;
|
public string Host { get; set; } = string.Empty;
|
||||||
|
|
||||||
public virtual void Validate()
|
|
||||||
{
|
|
||||||
var errors = CheckFields();
|
|
||||||
if (errors != null)
|
|
||||||
ThrowSettingsException(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<string>? CheckFields()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(Host))
|
|
||||||
{
|
|
||||||
var error = string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host));
|
|
||||||
return new Collection<string>([error]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void ThrowSettingsException(Collection<string> errors)
|
|
||||||
{
|
|
||||||
if (errors is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(errors));
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder messages = new();
|
|
||||||
|
|
||||||
foreach (var error in errors)
|
|
||||||
{
|
|
||||||
messages.AppendLine(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException(messages.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SingleOwnerClientSettings : ClientSettings
|
|
||||||
{
|
|
||||||
public string Key { get; set; } = string.Empty;
|
public string Key { get; set; } = string.Empty;
|
||||||
|
|
||||||
public override void Validate()
|
public Action<CallStatistics>? Callback { get; set; }
|
||||||
{
|
|
||||||
var errors = CheckFields();
|
|
||||||
if (errors != null)
|
|
||||||
ThrowSettingsException(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected new Collection<string>? CheckFields()
|
public Collection<Interceptor> Interceptors { get; } = [];
|
||||||
|
|
||||||
|
public void Validate()
|
||||||
{
|
{
|
||||||
Collection<string>? errors = base.CheckFields();
|
StringBuilder? errors = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Host))
|
||||||
|
(errors = new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host)));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Key))
|
if (string.IsNullOrWhiteSpace(Key))
|
||||||
(errors ??= []).Add(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Key)));
|
(errors ??= new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Key)));
|
||||||
|
|
||||||
return errors;
|
if (errors != null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(errors.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,7 +27,7 @@ public class FrostFsContainerId
|
||||||
|
|
||||||
if (containerID != null)
|
if (containerID != null)
|
||||||
{
|
{
|
||||||
this.modelId = Base58.Encode(containerID.Value.ToByteArray());
|
this.modelId = Base58.Encode(containerID.Value.Span);
|
||||||
return this.modelId;
|
return this.modelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class FrostFsContainerInfo
|
||||||
PlacementPolicy = PlacementPolicy.Value.GetPolicy(),
|
PlacementPolicy = PlacementPolicy.Value.GetPolicy(),
|
||||||
Nonce = ByteString.CopyFrom(Nonce.ToBytes()),
|
Nonce = ByteString.CopyFrom(Nonce.ToBytes()),
|
||||||
OwnerId = Owner?.OwnerID,
|
OwnerId = Owner?.OwnerID,
|
||||||
Version = Version?.Version
|
Version = Version?.VersionID
|
||||||
};
|
};
|
||||||
|
|
||||||
var attribs = GetGrpsAttributes();
|
var attribs = GetGrpsAttributes();
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class FrostFsVersion(int major, int minor)
|
||||||
public int Major { get; set; } = major;
|
public int Major { get; set; } = major;
|
||||||
public int Minor { get; set; } = minor;
|
public int Minor { get; set; } = minor;
|
||||||
|
|
||||||
internal Version Version
|
internal Version VersionID
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,13 +8,8 @@ public class FrostFsObjectId(string id)
|
||||||
{
|
{
|
||||||
public string Value { get; } = id;
|
public string Value { get; } = id;
|
||||||
|
|
||||||
public static FrostFsObjectId FromHash(byte[] hash)
|
public static FrostFsObjectId FromHash(ReadOnlySpan<byte> hash)
|
||||||
{
|
{
|
||||||
if (hash is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash.Length != Constants.Sha256HashLength)
|
if (hash.Length != Constants.Sha256HashLength)
|
||||||
throw new FormatException("ObjectID must be a sha256 hash.");
|
throw new FormatException("ObjectID must be a sha256 hash.");
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using FrostFS.Refs;
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
public class FrostFsOwner(string id)
|
public class FrostFsOwner(string id)
|
||||||
|
|
|
@ -1,9 +1,117 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using FrostFS.Refs;
|
||||||
|
|
||||||
|
using FrostFS.SDK.Client;
|
||||||
|
using FrostFS.SDK.Cryptography;
|
||||||
|
using FrostFS.Session;
|
||||||
|
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
namespace FrostFS.SDK;
|
||||||
|
|
||||||
public class FrostFsSessionToken(byte[] token, Guid id)
|
public class FrostFsSessionToken
|
||||||
{
|
{
|
||||||
public Guid Id { get; private set; } = id;
|
private Guid _id;
|
||||||
public byte[] Token { get; private set; } = token;
|
private ReadOnlyMemory<byte> _sessionKey;
|
||||||
|
private readonly SessionToken.Types.Body _body;
|
||||||
|
|
||||||
|
private FrostFsSessionToken()
|
||||||
|
{
|
||||||
|
ProtoId = ByteString.Empty;
|
||||||
|
ProtoSessionKey = ByteString.Empty;
|
||||||
|
_body = new SessionToken.Types.Body();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FrostFsSessionToken(SessionToken token)
|
||||||
|
{
|
||||||
|
ProtoId = token.Body.Id;
|
||||||
|
ProtoSessionKey = token.Body.SessionKey;
|
||||||
|
|
||||||
|
_body = token.Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_id == Guid.Empty)
|
||||||
|
_id = ProtoId.ToUuid();
|
||||||
|
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyMemory<byte> SessionKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_sessionKey.IsEmpty)
|
||||||
|
_sessionKey = ProtoSessionKey.Memory;
|
||||||
|
|
||||||
|
return _sessionKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ByteString ProtoId { get; }
|
||||||
|
|
||||||
|
internal ByteString ProtoSessionKey { get; }
|
||||||
|
|
||||||
|
public SessionToken CreateContainerToken(ContainerID? containerId, ContainerSessionContext.Types.Verb verb, ClientKey key)
|
||||||
|
{
|
||||||
|
if (key is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionToken sessionToken = new() { Body = _body.Clone() };
|
||||||
|
|
||||||
|
sessionToken.Body.Container = new() { Verb = verb };
|
||||||
|
|
||||||
|
if (containerId != null)
|
||||||
|
sessionToken.Body.Container.ContainerId = containerId;
|
||||||
|
else
|
||||||
|
sessionToken.Body.Container.Wildcard = true;
|
||||||
|
|
||||||
|
sessionToken.Body.SessionKey = key.PublicKeyProto;
|
||||||
|
|
||||||
|
sessionToken.Signature = key.ECDsaKey.SignMessagePart(sessionToken.Body);
|
||||||
|
|
||||||
|
return sessionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionToken CreateObjectTokenContext(Address address, ObjectSessionContext.Types.Verb verb, ClientKey key)
|
||||||
|
{
|
||||||
|
if (address is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionToken sessionToken = new()
|
||||||
|
{
|
||||||
|
Body = _body.Clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectSessionContext.Types.Target target = new() { Container = address.ContainerId };
|
||||||
|
|
||||||
|
if (address.ObjectId != null)
|
||||||
|
target.Objects.Add(address.ObjectId);
|
||||||
|
|
||||||
|
sessionToken.Body.Object = new()
|
||||||
|
{
|
||||||
|
Target = target,
|
||||||
|
Verb = verb
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionToken.Body.SessionKey = key.PublicKeyProto;
|
||||||
|
|
||||||
|
sessionToken.Signature = key.ECDsaKey.SignMessagePart(sessionToken.Body);
|
||||||
|
|
||||||
|
return sessionToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class CallContext()
|
public class CallContext()
|
||||||
{
|
{
|
||||||
private ByteString? publicKeyCache;
|
|
||||||
|
|
||||||
internal Action<Exception>? PoolErrorHandler { get; set; }
|
// internal Action<Exception>? PoolErrorHandler { get; set; }
|
||||||
|
|
||||||
public ECDsa? Key { get; set; }
|
// public FrostFsOwner? OwnerId { get; set; }
|
||||||
|
|
||||||
public FrostFsOwner? OwnerId { get; set; }
|
// public FrostFsVersion? Version { get; set; }
|
||||||
|
|
||||||
public FrostFsVersion? Version { get; set; }
|
|
||||||
|
|
||||||
public CancellationToken CancellationToken { get; set; }
|
public CancellationToken CancellationToken { get; set; }
|
||||||
|
|
||||||
|
@ -29,17 +18,7 @@ public class CallContext()
|
||||||
|
|
||||||
public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null;
|
public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null;
|
||||||
|
|
||||||
public Action<CallStatistics>? Callback { get; set; }
|
// public Action<CallStatistics>? Callback { get; set; }
|
||||||
|
|
||||||
public Collection<Interceptor> Interceptors { get; } = [];
|
// public Collection<Interceptor> Interceptors { get; } = [];
|
||||||
|
|
||||||
public ByteString? GetPublicKeyCache()
|
|
||||||
{
|
|
||||||
if (publicKeyCache == null && Key != null)
|
|
||||||
{
|
|
||||||
publicKeyCache = ByteString.CopyFrom(Key.PublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
return publicKeyCache;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,8 @@ namespace FrostFS.SDK.Client;
|
||||||
public class ClientWrapper : ClientStatusMonitor
|
public class ClientWrapper : ClientStatusMonitor
|
||||||
{
|
{
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
private SessionCache sessionCache;
|
private SessionCache sessionCache;
|
||||||
|
|
||||||
|
|
||||||
internal ClientWrapper(WrapperPrm wrapperPrm, Pool pool) : base(wrapperPrm.Logger, wrapperPrm.Address)
|
internal ClientWrapper(WrapperPrm wrapperPrm, Pool pool) : base(wrapperPrm.Logger, wrapperPrm.Address)
|
||||||
{
|
{
|
||||||
WrapperPrm = wrapperPrm;
|
WrapperPrm = wrapperPrm;
|
||||||
|
@ -140,6 +138,7 @@ public class ClientWrapper : ClientStatusMonitor
|
||||||
catch (FrostFsException)
|
catch (FrostFsException)
|
||||||
{
|
{
|
||||||
SetUnhealthy();
|
SetUnhealthy();
|
||||||
|
|
||||||
return wasHealthy;
|
return wasHealthy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -33,4 +35,8 @@ public class InitParameters
|
||||||
public ulong GracefulCloseOnSwitchTimeout { get; set; }
|
public ulong GracefulCloseOnSwitchTimeout { get; set; }
|
||||||
|
|
||||||
public ILogger? Logger { get; set; }
|
public ILogger? Logger { get; set; }
|
||||||
|
|
||||||
|
public Action<CallStatistics>? Callback { get; set; }
|
||||||
|
|
||||||
|
public Collection<Interceptor> Interceptors { get; } = [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,18 +35,17 @@ public partial class Pool : IFrostFSClient
|
||||||
|
|
||||||
private InnerPool[]? InnerPools { get; set; }
|
private InnerPool[]? InnerPools { get; set; }
|
||||||
|
|
||||||
private ECDsa Key { get; set; }
|
private ClientKey Key { get; set; }
|
||||||
|
|
||||||
private string PublicKey { get; }
|
|
||||||
|
|
||||||
private OwnerID? _ownerId;
|
private OwnerID? _ownerId;
|
||||||
|
|
||||||
private FrostFsOwner? _owner;
|
private FrostFsOwner? _owner;
|
||||||
|
|
||||||
private FrostFsOwner Owner
|
private FrostFsOwner Owner
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
_owner ??= new FrostFsOwner(Key.PublicKey().PublicKeyToAddress());
|
_owner ??= new FrostFsOwner(Key.ECDsaKey.PublicKey().PublicKeyToAddress());
|
||||||
return _owner;
|
return _owner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +56,7 @@ public partial class Pool : IFrostFSClient
|
||||||
{
|
{
|
||||||
if (_ownerId == null)
|
if (_ownerId == null)
|
||||||
{
|
{
|
||||||
_owner = new FrostFsOwner(Key.PublicKey().PublicKeyToAddress());
|
_owner = Key.Owner;
|
||||||
_ownerId = _owner.ToMessage();
|
_ownerId = _owner.ToMessage();
|
||||||
}
|
}
|
||||||
return _ownerId;
|
return _ownerId;
|
||||||
|
@ -101,8 +100,7 @@ public partial class Pool : IFrostFSClient
|
||||||
|
|
||||||
FillDefaultInitParams(options, this);
|
FillDefaultInitParams(options, this);
|
||||||
|
|
||||||
Key = options.Key;
|
Key = new ClientKey(options.Key);
|
||||||
PublicKey = $"{Key.PublicKey()}";
|
|
||||||
|
|
||||||
SessionCache = cache;
|
SessionCache = cache;
|
||||||
logger = options.Logger;
|
logger = options.Logger;
|
||||||
|
@ -115,16 +113,9 @@ public partial class Pool : IFrostFSClient
|
||||||
options.SessionExpirationDuration);
|
options.SessionExpirationDuration);
|
||||||
|
|
||||||
ClientBuilder = options.ClientBuilder!;
|
ClientBuilder = options.ClientBuilder!;
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupContext(CallContext ctx)
|
// ClientContext.PoolErrorHandler = client.HandleError;
|
||||||
{
|
|
||||||
if (ctx == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Key ??= Key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial establishes a connection to the servers from the FrostFS network.
|
// Dial establishes a connection to the servers from the FrostFS network.
|
||||||
|
@ -137,8 +128,6 @@ public partial class Pool : IFrostFSClient
|
||||||
// See also InitParameters.SetClientRebalanceInterval.
|
// See also InitParameters.SetClientRebalanceInterval.
|
||||||
public async Task<string?> Dial(CallContext ctx)
|
public async Task<string?> Dial(CallContext ctx)
|
||||||
{
|
{
|
||||||
SetupContext(ctx);
|
|
||||||
|
|
||||||
var inner = new InnerPool[RebalanceParams.NodesParams.Length];
|
var inner = new InnerPool[RebalanceParams.NodesParams.Length];
|
||||||
|
|
||||||
bool atLeastOneHealthy = false;
|
bool atLeastOneHealthy = false;
|
||||||
|
@ -158,11 +147,11 @@ public partial class Pool : IFrostFSClient
|
||||||
await client.Dial(ctx).ConfigureAwait(false);
|
await client.Dial(ctx).ConfigureAwait(false);
|
||||||
dialed = true;
|
dialed = true;
|
||||||
|
|
||||||
var token = await InitSessionForDuration(ctx, client, RebalanceParams.SessionExpirationDuration, Key, false)
|
var token = await InitSessionForDuration(ctx, client, RebalanceParams.SessionExpirationDuration, Key.ECDsaKey, false)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
var key = FormCacheKey(nodeParams.Addresses[j], Key.PrivateKey().ToString());
|
var key = FormCacheKey(nodeParams.Addresses[j], Key.PublicKey);
|
||||||
_ = SessionCache.Cache[key] = token;
|
SessionCache.SetValue(key, token);
|
||||||
|
|
||||||
atLeastOneHealthy = true;
|
atLeastOneHealthy = true;
|
||||||
}
|
}
|
||||||
|
@ -291,12 +280,14 @@ public partial class Pool : IFrostFSClient
|
||||||
var wrapperPrm = new WrapperPrm
|
var wrapperPrm = new WrapperPrm
|
||||||
{
|
{
|
||||||
Address = address,
|
Address = address,
|
||||||
Key = parameters.Key,
|
Key = parameters.Key!,
|
||||||
Logger = parameters.Logger,
|
Logger = parameters.Logger,
|
||||||
DialTimeout = parameters.NodeDialTimeout,
|
DialTimeout = parameters.NodeDialTimeout,
|
||||||
StreamTimeout = parameters.NodeStreamTimeout,
|
StreamTimeout = parameters.NodeStreamTimeout,
|
||||||
ErrorThreshold = parameters.ErrorThreshold,
|
ErrorThreshold = parameters.ErrorThreshold,
|
||||||
GracefulCloseOnSwitchTimeout = parameters.GracefulCloseOnSwitchTimeout
|
GracefulCloseOnSwitchTimeout = parameters.GracefulCloseOnSwitchTimeout,
|
||||||
|
Callback = parameters.Callback,
|
||||||
|
Interceptors = parameters.Interceptors
|
||||||
};
|
};
|
||||||
|
|
||||||
return new ClientWrapper(wrapperPrm, pool);
|
return new ClientWrapper(wrapperPrm, pool);
|
||||||
|
@ -318,7 +309,7 @@ public partial class Pool : IFrostFSClient
|
||||||
throw new FrostFsException("Cannot find alive client");
|
throw new FrostFsException("Cannot find alive client");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<FrostFsSessionToken?> InitSessionForDuration(CallContext ctx, ClientWrapper cw, ulong duration, ECDsa key, bool clientCut)
|
private static async Task<FrostFsSessionToken> InitSessionForDuration(CallContext ctx, ClientWrapper cw, ulong duration, ECDsa key, bool clientCut)
|
||||||
{
|
{
|
||||||
var client = cw.Client;
|
var client = cw.Client;
|
||||||
var networkInfo = await client!.GetNetworkSettingsAsync(new PrmNetworkSettings(ctx)).ConfigureAwait(false);
|
var networkInfo = await client!.GetNetworkSettingsAsync(new PrmNetworkSettings(ctx)).ConfigureAwait(false);
|
||||||
|
@ -521,7 +512,6 @@ public partial class Pool : IFrostFSClient
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args ??= new();
|
args ??= new();
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetNetmapSnapshotAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetNetmapSnapshotAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -529,278 +519,126 @@ public partial class Pool : IFrostFSClient
|
||||||
public async Task<FrostFsNodeInfo> GetNodeInfoAsync(PrmNodeInfo? args = null)
|
public async Task<FrostFsNodeInfo> GetNodeInfoAsync(PrmNodeInfo? args = null)
|
||||||
{
|
{
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args ??= new();
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetNodeInfoAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetNodeInfoAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<NetworkSettings> GetNetworkSettingsAsync(PrmNetworkSettings? args = null)
|
public async Task<NetworkSettings> GetNetworkSettingsAsync(PrmNetworkSettings? args = null)
|
||||||
{
|
{
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args ??= new();
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetNetworkSettingsAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetNetworkSettingsAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args)
|
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false);
|
return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
public async Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.AddChainAsync(args).ConfigureAwait(false);
|
return await client.Client!.AddChainAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveChainAsync(PrmApeChainRemove args)
|
public async Task RemoveChainAsync(PrmApeChainRemove args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
await client.Client!.RemoveChainAsync(args).ConfigureAwait(false);
|
await client.Client!.RemoveChainAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Chain[]> ListChainAsync(PrmApeChainList args)
|
public async Task<Chain[]> ListChainAsync(PrmApeChainList args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.ListChainAsync(args).ConfigureAwait(false);
|
return await client.Client!.ListChainAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args)
|
public async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetContainerAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetContainerAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll? args = null)
|
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll? args = null)
|
||||||
{
|
{
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args ??= new();
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return client.Client!.ListContainersAsync(args);
|
return client.Client!.ListContainersAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args)
|
public async Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.CreateContainerAsync(args).ConfigureAwait(false);
|
return await client.Client!.CreateContainerAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteContainerAsync(PrmContainerDelete args)
|
public async Task DeleteContainerAsync(PrmContainerDelete args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
await client.Client!.DeleteContainerAsync(args).ConfigureAwait(false);
|
await client.Client!.DeleteContainerAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args)
|
public async Task<FrostFsObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetObjectHeadAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetObjectHeadAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObject> GetObjectAsync(PrmObjectGet args)
|
public async Task<FrostFsObject> GetObjectAsync(PrmObjectGet args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetObjectAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
public async Task<FrostFsObjectId> PutObjectAsync(PrmObjectPut args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.PutObjectAsync(args).ConfigureAwait(false);
|
return await client.Client!.PutObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args)
|
public async Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.PutSingleObjectAsync(args).ConfigureAwait(false);
|
return await client.Client!.PutSingleObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
public async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false);
|
return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RangeReader> GetRangeAsync(PrmRangeGet args)
|
public async Task<RangeReader> GetRangeAsync(PrmRangeGet args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetRangeAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetRangeAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ReadOnlyMemory<byte>[]> GetRangeHashAsync(PrmRangeHashGet args)
|
public async Task<ReadOnlyMemory<byte>[]> GetRangeHashAsync(PrmRangeHashGet args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetRangeHashAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetRangeHashAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrostFsObjectId> PatchAsync(PrmObjectPatch args)
|
public async Task<FrostFsObjectId> PatchAsync(PrmObjectPatch args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false);
|
return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteObjectAsync(PrmObjectDelete args)
|
public async Task DeleteObjectAsync(PrmObjectDelete args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
await client.Client!.DeleteObjectAsync(args).ConfigureAwait(false);
|
await client.Client!.DeleteObjectAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args)
|
public IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return client.Client!.SearchObjectsAsync(args);
|
return client.Client!.SearchObjectsAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Accounting.Decimal> GetBalanceAsync(PrmBalance? args)
|
public async Task<Accounting.Decimal> GetBalanceAsync(PrmBalance? args)
|
||||||
{
|
{
|
||||||
var client = Connection();
|
var client = Connection();
|
||||||
|
|
||||||
args ??= new();
|
|
||||||
args.Context.PoolErrorHandler = client.HandleError;
|
|
||||||
|
|
||||||
return await client.Client!.GetBalanceAsync(args).ConfigureAwait(false);
|
return await client.Client!.GetBalanceAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,7 +662,7 @@ public partial class Pool : IFrostFSClient
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx)
|
public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,49 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
internal struct SessionCache(ulong sessionExpirationDuration)
|
internal sealed class SessionCache(ulong sessionExpirationDuration)
|
||||||
{
|
{
|
||||||
internal Hashtable Cache { get; } = [];
|
private ConcurrentDictionary<string, FrostFsSessionToken> _cache { get; } = [];
|
||||||
|
|
||||||
internal ulong CurrentEpoch { get; set; }
|
internal ulong CurrentEpoch { get; set; }
|
||||||
|
|
||||||
internal ulong TokenDuration { get; set; } = sessionExpirationDuration;
|
internal ulong TokenDuration { get; set; } = sessionExpirationDuration;
|
||||||
|
|
||||||
|
internal bool Contains(string key)
|
||||||
|
{
|
||||||
|
return _cache.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool TryGetValue(string? key, out FrostFsSessionToken? value)
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok = _cache.TryGetValue(key, out value);
|
||||||
|
|
||||||
|
return ok && value != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetValue(string? key, FrostFsSessionToken value)
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
_cache[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void DeleteByPrefix(string prefix)
|
internal void DeleteByPrefix(string prefix)
|
||||||
{
|
{
|
||||||
foreach (var key in Cache.Keys)
|
foreach (var key in _cache.Keys)
|
||||||
{
|
{
|
||||||
if (((string)key).StartsWith(prefix, StringComparison.Ordinal))
|
if (key.StartsWith(prefix, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
Cache.Remove(key);
|
_cache.TryRemove(key, out var _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
// wrapperPrm is params to create clientWrapper.
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "<Pending>")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "<Pending>")]
|
||||||
public struct WrapperPrm
|
public struct WrapperPrm
|
||||||
{
|
{
|
||||||
|
@ -15,7 +16,7 @@ public struct WrapperPrm
|
||||||
|
|
||||||
internal string Address { get; set; }
|
internal string Address { get; set; }
|
||||||
|
|
||||||
internal ECDsa? Key { get; set; }
|
internal ECDsa Key { get; set; }
|
||||||
|
|
||||||
internal ulong DialTimeout { get; set; }
|
internal ulong DialTimeout { get; set; }
|
||||||
|
|
||||||
|
@ -30,5 +31,9 @@ public struct WrapperPrm
|
||||||
internal GrpcChannelOptions GrpcChannelOptions { get; set; }
|
internal GrpcChannelOptions GrpcChannelOptions { get; set; }
|
||||||
|
|
||||||
internal ulong GracefulCloseOnSwitchTimeout { get; set; }
|
internal ulong GracefulCloseOnSwitchTimeout { get; set; }
|
||||||
|
|
||||||
|
internal Action<CallStatistics>? Callback { get; set; }
|
||||||
|
|
||||||
|
internal Collection<Interceptor>? Interceptors { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,12 @@ internal sealed class AccountingServiceProvider : ContextAccessor
|
||||||
{
|
{
|
||||||
Body = new()
|
Body = new()
|
||||||
{
|
{
|
||||||
OwnerId = ctx.OwnerId!.OwnerID
|
OwnerId = ClientContext.Owner.OwnerID
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key!);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await _accountingServiceClient!.BalanceAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await _accountingServiceClient!.BalanceAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,9 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
_apeManagerServiceClient = apeManagerServiceClient;
|
_apeManagerServiceClient = apeManagerServiceClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
internal async Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
AddChainRequest request = new()
|
AddChainRequest request = new()
|
||||||
{
|
{
|
||||||
|
@ -34,22 +30,18 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await _apeManagerServiceClient!.AddChainAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await _apeManagerServiceClient!.AddChainAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return response.Body.ChainId.ToByteArray();
|
return response.Body.ChainId.Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RemoveChainAsync(PrmApeChainRemove args)
|
internal async Task RemoveChainAsync(PrmApeChainRemove args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
RemoveChainRequest request = new()
|
RemoveChainRequest request = new()
|
||||||
{
|
{
|
||||||
|
@ -61,7 +53,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await _apeManagerServiceClient!.RemoveChainAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await _apeManagerServiceClient!.RemoveChainAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -71,10 +63,6 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
internal async Task<Chain[]> ListChainAsync(PrmApeChainList args)
|
internal async Task<Chain[]> ListChainAsync(PrmApeChainList args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
ListChainsRequest request = new()
|
ListChainsRequest request = new()
|
||||||
{
|
{
|
||||||
|
@ -85,7 +73,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await _apeManagerServiceClient!.ListChainsAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await _apeManagerServiceClient!.ListChainsAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using FrostFS.Container;
|
using FrostFS.Container;
|
||||||
|
@ -12,26 +13,34 @@ using FrostFS.Session;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientContext clientCtx) : ContextAccessor(clientCtx), ISessionProvider
|
internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientContext clientCtx) : ContextAccessor(clientCtx)
|
||||||
{
|
{
|
||||||
private SessionProvider? sessions;
|
private SessionProvider? sessions;
|
||||||
|
|
||||||
public async ValueTask<SessionToken> GetOrCreateSession(ISessionToken args, CallContext ctx)
|
public async ValueTask<FrostFsSessionToken> GetDefaultSession(ISessionToken args, CallContext ctx)
|
||||||
{
|
{
|
||||||
sessions ??= new(ClientContext);
|
sessions ??= new(ClientContext);
|
||||||
|
|
||||||
if (ClientContext.SessionCache.Cache != null &&
|
if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token))
|
||||||
ClientContext.SessionCache.Cache.ContainsKey(ClientContext.SessionCacheKey))
|
|
||||||
{
|
{
|
||||||
return (SessionToken)ClientContext.SessionCache.Cache[ClientContext.SessionCacheKey];
|
var protoToken = await sessions.GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
|
token = new FrostFsSessionToken(protoToken);
|
||||||
|
|
||||||
|
ClientContext.SessionCache.SetValue(ClientContext.SessionCacheKey, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await sessions.GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
if (token == null)
|
||||||
|
{
|
||||||
|
throw new FrostFsException("Cannot create session");
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args)
|
internal async Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args)
|
||||||
{
|
{
|
||||||
GetRequest request = GetContainerRequest(args.Container.ContainerID, args.XHeaders, args.Context);
|
GetRequest request = GetContainerRequest(args.Container.ContainerID, args.XHeaders, ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await service.GetAsync(request, null, args.Context.Deadline, args.Context.CancellationToken);
|
var response = await service.GetAsync(request, null, args.Context.Deadline, args.Context.CancellationToken);
|
||||||
|
|
||||||
|
@ -43,24 +52,17 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
internal async IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args)
|
internal async IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.OwnerId ??= ClientContext.Owner;
|
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
if (ctx.OwnerId == null)
|
|
||||||
throw new ArgumentException(nameof(ctx.OwnerId));
|
|
||||||
|
|
||||||
var request = new ListRequest
|
var request = new ListRequest
|
||||||
{
|
{
|
||||||
Body = new()
|
Body = new()
|
||||||
{
|
{
|
||||||
OwnerId = ctx.OwnerId.ToMessage()
|
OwnerId = ClientContext.Owner.OwnerID
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -68,7 +70,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
|
|
||||||
foreach (var cid in response.Body.ContainerIds)
|
foreach (var cid in response.Body.ContainerIds)
|
||||||
{
|
{
|
||||||
yield return new FrostFsContainerId(Base58.Encode(cid.Value.ToByteArray()));
|
yield return new FrostFsContainerId(Base58.Encode(cid.Value.Span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,36 +80,28 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
|
|
||||||
var grpcContainer = args.Container.GetContainer();
|
var grpcContainer = args.Container.GetContainer();
|
||||||
|
|
||||||
grpcContainer.OwnerId ??= ctx.OwnerId?.ToMessage();
|
grpcContainer.OwnerId ??= ClientContext.Owner.OwnerID;
|
||||||
grpcContainer.Version ??= ctx.Version?.ToMessage();
|
grpcContainer.Version ??= ClientContext.Version.VersionID;
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
if (grpcContainer.OwnerId == null)
|
|
||||||
throw new ArgumentException(nameof(grpcContainer.OwnerId));
|
|
||||||
if (grpcContainer.Version == null)
|
|
||||||
throw new ArgumentException(nameof(grpcContainer.Version));
|
|
||||||
|
|
||||||
var request = new PutRequest
|
var request = new PutRequest
|
||||||
{
|
{
|
||||||
Body = new PutRequest.Types.Body
|
Body = new PutRequest.Types.Body
|
||||||
{
|
{
|
||||||
Container = grpcContainer,
|
Container = grpcContainer,
|
||||||
Signature = ctx.Key.SignRFC6979(grpcContainer)
|
Signature = ClientContext.Key.ECDsaKey.SignRFC6979(grpcContainer)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = (args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false)) ?? throw new FrostFsException("Cannot create session token");
|
||||||
|
|
||||||
sessionToken.CreateContainerTokenContext(
|
var protoToken = sessionToken.CreateContainerToken(
|
||||||
null,
|
null,
|
||||||
ContainerSessionContext.Types.Verb.Put,
|
ContainerSessionContext.Types.Verb.Put,
|
||||||
ctx.Key,
|
ClientContext.Key);
|
||||||
ctx.GetPublicKeyCache()!);
|
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -121,29 +115,26 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
internal async Task DeleteContainerAsync(PrmContainerDelete args)
|
internal async Task DeleteContainerAsync(PrmContainerDelete args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new DeleteRequest
|
var request = new DeleteRequest
|
||||||
{
|
{
|
||||||
Body = new DeleteRequest.Types.Body
|
Body = new DeleteRequest.Types.Body
|
||||||
{
|
{
|
||||||
ContainerId = args.ContainerId.ToMessage(),
|
ContainerId = args.ContainerId.ToMessage(),
|
||||||
Signature = ctx.Key.SignRFC6979(args.ContainerId.ToMessage().Value)
|
Signature = ClientContext.Key.ECDsaKey.SignRFC6979(args.ContainerId.ToMessage().Value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateContainerTokenContext(
|
var protoToken = sessionToken.CreateContainerToken(
|
||||||
request.Body.ContainerId,
|
request.Body.ContainerId,
|
||||||
ContainerSessionContext.Types.Verb.Delete,
|
ContainerSessionContext.Types.Verb.Delete,
|
||||||
ctx.Key,
|
ClientContext.Key);
|
||||||
ctx.GetPublicKeyCache()!);
|
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await service.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await service.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -155,11 +146,8 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders, CallContext ctx)
|
private static GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders, ECDsa key)
|
||||||
{
|
{
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(ctx), "Key is null");
|
|
||||||
|
|
||||||
var request = new GetRequest
|
var request = new GetRequest
|
||||||
{
|
{
|
||||||
Body = new GetRequest.Types.Body
|
Body = new GetRequest.Types.Body
|
||||||
|
@ -169,7 +157,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(xHeaders);
|
request.AddMetaHeader(xHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(key);
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +170,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
|
|
||||||
private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait? waitParams, CallContext ctx)
|
private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait? waitParams, CallContext ctx)
|
||||||
{
|
{
|
||||||
var request = GetContainerRequest(id, null, ctx);
|
var request = GetContainerRequest(id, null, ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
async Task action()
|
async Task action()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
|
@ -47,10 +45,6 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
internal async Task<FrostFsNodeInfo> GetLocalNodeInfoAsync(PrmNodeInfo args)
|
internal async Task<FrostFsNodeInfo> GetLocalNodeInfoAsync(PrmNodeInfo args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new LocalNodeInfoRequest
|
var request = new LocalNodeInfoRequest
|
||||||
{
|
{
|
||||||
|
@ -58,7 +52,7 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -69,15 +63,10 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
|
|
||||||
internal async Task<NetworkInfoResponse> GetNetworkInfoAsync(CallContext ctx)
|
internal async Task<NetworkInfoResponse> GetNetworkInfoAsync(CallContext ctx)
|
||||||
{
|
{
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(ctx), "Key is null");
|
|
||||||
|
|
||||||
var request = new NetworkInfoRequest();
|
var request = new NetworkInfoRequest();
|
||||||
|
|
||||||
request.AddMetaHeader(null);
|
request.AddMetaHeader(null);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken)
|
var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@ -90,15 +79,11 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
internal async Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(PrmNetmapSnapshot args)
|
internal async Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(PrmNetmapSnapshot args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new NetmapSnapshotRequest();
|
var request = new NetmapSnapshotRequest();
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -107,12 +92,16 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
return response.ToModel();
|
return response.ToModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetBoolValue(byte[] bytes)
|
private static bool GetBoolValue(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
return bytes.Any(b => b != 0);
|
for (int i = bytes.Length - 1; i >= 0; i--)
|
||||||
|
if (bytes[i] != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong GetLongValue(byte[] bytes)
|
private static ulong GetLongValue(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
ulong val = 0;
|
ulong val = 0;
|
||||||
for (var i = bytes.Length - 1; i >= 0; i--)
|
for (var i = bytes.Length - 1; i >= 0; i--)
|
||||||
|
@ -123,24 +112,50 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
|
|
||||||
private static void SetNetworksParam(Parameter param, NetworkSettings settings)
|
private static void SetNetworksParam(Parameter param, NetworkSettings settings)
|
||||||
{
|
{
|
||||||
var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
|
var key = param.Key.ToStringUtf8();
|
||||||
|
|
||||||
var valueBytes = param.Value.ToByteArray();
|
var valueBytes = param.Value.Span;
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case "AuditFee": settings.AuditFee = GetLongValue(valueBytes); break;
|
case "AuditFee":
|
||||||
case "BasicIncomeRate": settings.BasicIncomeRate = GetLongValue(valueBytes); break;
|
settings.AuditFee = GetLongValue(valueBytes);
|
||||||
case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "ContainerAliasFee": settings.ContainerAliasFee = GetLongValue(valueBytes); break;
|
case "BasicIncomeRate":
|
||||||
case "EpochDuration": settings.EpochDuration = GetLongValue(valueBytes); break;
|
settings.BasicIncomeRate = GetLongValue(valueBytes);
|
||||||
case "InnerRingCandidateFee": settings.InnerRingCandidateFee = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "MaxECDataCount": settings.MaxECDataCount = GetLongValue(valueBytes); break;
|
case "ContainerFee":
|
||||||
case "MaxECParityCount": settings.MaxECParityCount = GetLongValue(valueBytes); break;
|
settings.ContainerFee = GetLongValue(valueBytes);
|
||||||
case "MaxObjectSize": settings.MaxObjectSize = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "WithdrawFee": settings.WithdrawFee = GetLongValue(valueBytes); break;
|
case "ContainerAliasFee":
|
||||||
case "HomomorphicHashingDisabled": settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); break;
|
settings.ContainerAliasFee = GetLongValue(valueBytes);
|
||||||
case "MaintenanceModeAllowed": settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); break;
|
break;
|
||||||
default: settings.UnnamedSettings.Add(key, valueBytes); break;
|
case "EpochDuration":
|
||||||
|
settings.EpochDuration = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "InnerRingCandidateFee":
|
||||||
|
settings.InnerRingCandidateFee = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxECDataCount":
|
||||||
|
settings.MaxECDataCount = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxECParityCount":
|
||||||
|
settings.MaxECParityCount = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxObjectSize":
|
||||||
|
settings.MaxObjectSize = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "WithdrawFee":
|
||||||
|
settings.WithdrawFee = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "HomomorphicHashingDisabled":
|
||||||
|
settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaintenanceModeAllowed":
|
||||||
|
settings.MaintenanceModeAllowed = GetBoolValue(valueBytes);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
settings.UnnamedSettings.Add(key, valueBytes.ToArray());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,31 +17,35 @@ using Google.Protobuf;
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientContext clientCtx)
|
internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientContext clientCtx)
|
||||||
: ContextAccessor(clientCtx), ISessionProvider
|
: ContextAccessor(clientCtx)
|
||||||
{
|
{
|
||||||
private SessionProvider? sessions;
|
private SessionProvider? sessions;
|
||||||
private readonly ObjectService.ObjectServiceClient client = client;
|
private readonly ObjectService.ObjectServiceClient client = client;
|
||||||
|
|
||||||
public async ValueTask<SessionToken> GetOrCreateSession(ISessionToken args, CallContext ctx)
|
public async ValueTask<FrostFsSessionToken> GetDefaultSession(ISessionToken args, CallContext ctx)
|
||||||
{
|
{
|
||||||
sessions ??= new(ClientContext);
|
sessions ??= new(ClientContext);
|
||||||
|
|
||||||
if (ClientContext.SessionCache.Cache != null &&
|
if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token))
|
||||||
ClientContext.SessionCache.Cache.ContainsKey(ClientContext.SessionCacheKey))
|
|
||||||
{
|
{
|
||||||
return (SessionToken)ClientContext.SessionCache.Cache[ClientContext.SessionCacheKey];
|
var protoToken = await sessions.GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
|
token = new FrostFsSessionToken(protoToken);
|
||||||
|
|
||||||
|
ClientContext.SessionCache.SetValue(ClientContext.SessionCacheKey, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await sessions.GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
if (token == null)
|
||||||
|
{
|
||||||
|
throw new FrostFsException("Cannot create session");
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<FrostFsObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args)
|
internal async Task<FrostFsObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new HeadRequest
|
var request = new HeadRequest
|
||||||
{
|
{
|
||||||
|
@ -55,16 +59,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
request.Body.Address,
|
request.Body.Address,
|
||||||
ObjectSessionContext.Types.Verb.Head,
|
ObjectSessionContext.Types.Verb.Head,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false);
|
var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -77,11 +81,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new GetRequest
|
var request = new GetRequest
|
||||||
{
|
{
|
||||||
Body = new GetRequest.Types.Body
|
Body = new GetRequest.Types.Body
|
||||||
|
@ -94,16 +93,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
request.Body.Address,
|
request.Body.Address,
|
||||||
ObjectSessionContext.Types.Verb.Get,
|
ObjectSessionContext.Types.Verb.Get,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
return await GetObject(request, ctx).ConfigureAwait(false);
|
return await GetObject(request, ctx).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -112,11 +111,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new GetRangeRequest
|
var request = new GetRangeRequest
|
||||||
{
|
{
|
||||||
Body = new GetRangeRequest.Types.Body
|
Body = new GetRangeRequest.Types.Body
|
||||||
|
@ -135,16 +129,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
request.Body.Address,
|
request.Body.Address,
|
||||||
ObjectSessionContext.Types.Verb.Range,
|
ObjectSessionContext.Types.Verb.Range,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var call = client.GetRange(request, null, ctx.Deadline, ctx.CancellationToken);
|
var call = client.GetRange(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
return new RangeReader(call);
|
return new RangeReader(call);
|
||||||
|
@ -154,11 +148,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new GetRangeHashRequest
|
var request = new GetRangeHashRequest
|
||||||
{
|
{
|
||||||
Body = new GetRangeHashRequest.Types.Body
|
Body = new GetRangeHashRequest.Types.Body
|
||||||
|
@ -182,16 +171,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
request.Body.Address,
|
request.Body.Address,
|
||||||
ObjectSessionContext.Types.Verb.Rangehash,
|
ObjectSessionContext.Types.Verb.Rangehash,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await client.GetRangeHashAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await client.GetRangeHashAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -206,10 +195,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
internal async Task DeleteObjectAsync(PrmObjectDelete args)
|
internal async Task DeleteObjectAsync(PrmObjectDelete args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new DeleteRequest
|
var request = new DeleteRequest
|
||||||
{
|
{
|
||||||
|
@ -223,15 +208,15 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
request.Body.Address,
|
request.Body.Address,
|
||||||
ObjectSessionContext.Types.Verb.Delete,
|
ObjectSessionContext.Types.Verb.Delete,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await client.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
var response = await client.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
|
||||||
|
|
||||||
|
@ -242,9 +227,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var request = new SearchRequest
|
var request = new SearchRequest
|
||||||
{
|
{
|
||||||
Body = new SearchRequest.Types.Body
|
Body = new SearchRequest.Types.Body
|
||||||
|
@ -256,22 +238,30 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
|
|
||||||
request.Body.Filters.AddRange(args.Filters.Select(f => f.ToMessage()));
|
request.Body.Filters.AddRange(args.Filters.Select(f => f.ToMessage()));
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
new Address { ContainerId = request.Body.ContainerId },
|
new Address { ContainerId = request.Body.ContainerId },
|
||||||
ObjectSessionContext.Types.Verb.Search,
|
ObjectSessionContext.Types.Verb.Search,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var objectsIds = SearchObjects(request, ctx);
|
using var stream = GetSearchReader(request, ctx);
|
||||||
|
|
||||||
await foreach (var oid in objectsIds)
|
while (true)
|
||||||
{
|
{
|
||||||
yield return FrostFsObjectId.FromHash(oid.Value.ToByteArray());
|
var ids = await stream.Read(ctx.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (ids == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var oid in ids)
|
||||||
|
{
|
||||||
|
yield return FrostFsObjectId.FromHash(oid.Value.Span);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,6 +286,8 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
args.FullLength = args.Header.PayloadLength;
|
args.FullLength = args.Header.PayloadLength;
|
||||||
else if (args.Payload.CanSeek)
|
else if (args.Payload.CanSeek)
|
||||||
args.FullLength = (ulong)args.Payload.Length;
|
args.FullLength = (ulong)args.Payload.Length;
|
||||||
|
else
|
||||||
|
throw new ArgumentException("The stream does not have a length and payload length is not defined");
|
||||||
|
|
||||||
var response = await PutStreamObject(args).ConfigureAwait(false);
|
var response = await PutStreamObject(args).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -307,39 +299,34 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
if (ctx.Key == null)
|
var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ClientContext);
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ctx);
|
|
||||||
|
|
||||||
var request = new PutSingleRequest
|
var request = new PutSingleRequest
|
||||||
{
|
{
|
||||||
Body = new() { Object = grpcObject }
|
Body = new() { Object = grpcObject }
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId },
|
new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId },
|
||||||
ObjectSessionContext.Types.Verb.Put,
|
ObjectSessionContext.Types.Verb.Put,
|
||||||
ctx.Key);
|
ClientContext.Key);
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
var response = await client.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false);
|
var response = await client.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray());
|
return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.Span);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
internal async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var chunkSize = args.MaxPayloadPatchChunkLength;
|
var chunkSize = args.MaxPayloadPatchChunkLength;
|
||||||
Stream payload = args.Payload ?? throw new ArgumentNullException(nameof(args), "Stream parameter is null");
|
Stream payload = args.Payload ?? throw new ArgumentNullException(nameof(args), "Stream parameter is null");
|
||||||
|
@ -350,7 +337,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// common
|
// common
|
||||||
chunkBuffer = ClientContext.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize);
|
chunkBuffer = ArrayPool<byte>.Shared.Rent(chunkSize);
|
||||||
|
|
||||||
var address = new Address
|
var address = new Address
|
||||||
{
|
{
|
||||||
|
@ -358,13 +345,12 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
ContainerId = args.Address.ContainerId
|
ContainerId = args.Address.ContainerId
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
address,
|
address,
|
||||||
ObjectSessionContext.Types.Verb.Patch,
|
ObjectSessionContext.Types.Verb.Patch,
|
||||||
ctx.Key
|
ClientContext.Key);
|
||||||
);
|
|
||||||
|
|
||||||
var request = new PatchRequest()
|
var request = new PatchRequest()
|
||||||
{
|
{
|
||||||
|
@ -403,9 +389,9 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
|
|
||||||
currentPos += (ulong)bytesCount;
|
currentPos += (ulong)bytesCount;
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders, sessionToken);
|
request.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
request.Sign(ctx.Key);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
await call.RequestStream.WriteAsync(request).ConfigureAwait(false);
|
await call.RequestStream.WriteAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -433,20 +419,17 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
var tokenRaw = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
args.SessionToken ??= await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
var token = new FrostFsSessionToken(tokenRaw.Serialize(), tokenRaw.Body.Id.ToUuid());
|
|
||||||
|
|
||||||
args.SessionToken = token;
|
|
||||||
|
|
||||||
var payloadStream = args.Payload!;
|
var payloadStream = args.Payload!;
|
||||||
var header = args.Header!;
|
var header = args.Header!;
|
||||||
|
|
||||||
var fullLength = header.PayloadLength;
|
if (header.PayloadLength > 0)
|
||||||
|
args.FullLength = header.PayloadLength;
|
||||||
if (payloadStream.CanSeek && fullLength == 0)
|
else if (payloadStream.CanSeek)
|
||||||
fullLength = (ulong)payloadStream.Length;
|
args.FullLength = (ulong)payloadStream.Length;
|
||||||
|
else
|
||||||
args.FullLength = fullLength;
|
throw new ArgumentException("The stream does not have a length and payload length is not defined");
|
||||||
|
|
||||||
if (args.MaxObjectSizeCache == 0)
|
if (args.MaxObjectSizeCache == 0)
|
||||||
{
|
{
|
||||||
|
@ -456,12 +439,12 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
args.MaxObjectSizeCache = (int)networkSettings.MaxObjectSize;
|
args.MaxObjectSizeCache = (int)networkSettings.MaxObjectSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var restBytes = fullLength - args.CurrentStreamPosition;
|
var restBytes = args.FullLength - args.CurrentStreamPosition;
|
||||||
var objectSize = restBytes > 0 ? (int)Math.Min((ulong)args.MaxObjectSizeCache, restBytes) : args.MaxObjectSizeCache;
|
var objectSize = restBytes > 0 ? (int)Math.Min((ulong)args.MaxObjectSizeCache, restBytes) : args.MaxObjectSizeCache;
|
||||||
|
|
||||||
//define collection capacity
|
//define collection capacity
|
||||||
var restPart = (restBytes % (ulong)objectSize) > 0 ? 1 : 0;
|
var restPart = (restBytes % (ulong)objectSize) > 0 ? 1 : 0;
|
||||||
var objectsCount = fullLength > 0 ? (int)(restBytes / (ulong)objectSize) + restPart : 0;
|
var objectsCount = args.FullLength > 0 ? (int)(restBytes / (ulong)objectSize) + restPart : 0;
|
||||||
|
|
||||||
List<FrostFsObjectId> sentObjectIds = new(objectsCount);
|
List<FrostFsObjectId> sentObjectIds = new(objectsCount);
|
||||||
|
|
||||||
|
@ -491,7 +474,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId, FrostFsObjectType.Regular, [.. attributes])
|
var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId, FrostFsObjectType.Regular, [.. attributes])
|
||||||
{
|
{
|
||||||
PayloadLength = fullLength,
|
PayloadLength = args.FullLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
args.Header.Split!.ParentHeader = largeObjectHeader;
|
args.Header.Split!.ParentHeader = largeObjectHeader;
|
||||||
|
@ -526,8 +509,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
private async Task<PutObjectResult> PutStreamObject(PrmObjectPut args)
|
private async Task<PutObjectResult> PutStreamObject(PrmObjectPut args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
if (ctx.Key == null)
|
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
|
||||||
|
|
||||||
var payload = args.Payload!;
|
var payload = args.Payload!;
|
||||||
|
|
||||||
|
@ -542,21 +523,26 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 0 means no limit from client, so server side cut is performed
|
||||||
|
var objectLimitSize = args.ClientCut ? args.MaxObjectSizeCache : 0;
|
||||||
|
|
||||||
if (args.CustomBuffer != null)
|
if (args.CustomBuffer != null)
|
||||||
{
|
{
|
||||||
|
if (args.CustomBuffer.Length < chunkSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Buffer size is too small. At least {chunkSize} required");
|
||||||
|
}
|
||||||
|
|
||||||
chunkBuffer = args.CustomBuffer;
|
chunkBuffer = args.CustomBuffer;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
chunkBuffer = ClientContext.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize);
|
chunkBuffer = ArrayPool<byte>.Shared.Rent(chunkSize);
|
||||||
isRentBuffer = true;
|
isRentBuffer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sentBytes = 0;
|
var sentBytes = 0;
|
||||||
|
|
||||||
// 0 means no limit from client, so server side cut is performed
|
|
||||||
var objectLimitSize = args.ClientCut ? args.MaxObjectSizeCache : 0;
|
|
||||||
|
|
||||||
using var stream = await GetUploadStream(args, ctx).ConfigureAwait(false);
|
using var stream = await GetUploadStream(args, ctx).ConfigureAwait(false);
|
||||||
|
|
||||||
while (objectLimitSize == 0 || sentBytes < objectLimitSize)
|
while (objectLimitSize == 0 || sentBytes < objectLimitSize)
|
||||||
|
@ -581,7 +567,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chunkRequest.Sign(ctx.Key);
|
chunkRequest.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
await stream.Write(chunkRequest).ConfigureAwait(false);
|
await stream.Write(chunkRequest).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -589,7 +575,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
var response = await stream.Close().ConfigureAwait(false);
|
var response = await stream.Close().ConfigureAwait(false);
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes);
|
return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.Span), sentBytes);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -604,17 +590,14 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
{
|
{
|
||||||
var header = args.Header!;
|
var header = args.Header!;
|
||||||
|
|
||||||
if (ctx.Key == null)
|
header.OwnerId ??= ClientContext.Owner;
|
||||||
throw new ArgumentNullException(nameof(args), "Key is null");
|
header.Version ??= ClientContext.Version;
|
||||||
|
|
||||||
header.OwnerId ??= ctx.OwnerId;
|
|
||||||
header.Version ??= ctx.Version;
|
|
||||||
|
|
||||||
var grpcHeader = header.GetHeader();
|
var grpcHeader = header.GetHeader();
|
||||||
|
|
||||||
if (header.Split != null)
|
if (header.Split != null)
|
||||||
{
|
{
|
||||||
ObjectTools.SetSplitValues(grpcHeader, header.Split, ctx);
|
ObjectTools.SetSplitValues(grpcHeader, header.Split, ClientContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
var oid = new ObjectID { Value = grpcHeader.Sha256() };
|
var oid = new ObjectID { Value = grpcHeader.Sha256() };
|
||||||
|
@ -630,17 +613,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sessionToken = await GetOrCreateSession(args, ctx).ConfigureAwait(false);
|
var sessionToken = (args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false));
|
||||||
|
|
||||||
sessionToken.CreateObjectTokenContext(
|
var protoToken = sessionToken.CreateObjectTokenContext(
|
||||||
new Address { ContainerId = grpcHeader.ContainerId, ObjectId = oid },
|
new Address { ContainerId = grpcHeader.ContainerId, ObjectId = oid },
|
||||||
ObjectSessionContext.Types.Verb.Put,
|
ObjectSessionContext.Types.Verb.Put,
|
||||||
ctx.Key
|
ClientContext.Key);
|
||||||
);
|
|
||||||
|
|
||||||
initRequest.AddMetaHeader(args.XHeaders, sessionToken);
|
initRequest.AddMetaHeader(args.XHeaders, protoToken);
|
||||||
|
|
||||||
initRequest.Sign(ctx.Key);
|
initRequest.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
return await PutObjectInit(initRequest, ctx).ConfigureAwait(false);
|
return await PutObjectInit(initRequest, ctx).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -681,24 +663,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
return new ObjectReader(call);
|
return new ObjectReader(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async IAsyncEnumerable<ObjectID> SearchObjects(SearchRequest request, CallContext ctx)
|
|
||||||
{
|
|
||||||
using var stream = GetSearchReader(request, ctx);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var ids = await stream.Read(ctx.CancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (ids == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
foreach (var oid in ids)
|
|
||||||
{
|
|
||||||
yield return oid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchReader GetSearchReader(SearchRequest initRequest, CallContext ctx)
|
private SearchReader GetSearchReader(SearchRequest initRequest, CallContext ctx)
|
||||||
{
|
{
|
||||||
if (initRequest is null)
|
if (initRequest is null)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
@ -20,19 +18,17 @@ internal sealed class SessionServiceProvider : ContextAccessor
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
|
|
||||||
ctx.OwnerId ??= ClientContext.Owner;
|
|
||||||
|
|
||||||
var request = new CreateRequest
|
var request = new CreateRequest
|
||||||
{
|
{
|
||||||
Body = new CreateRequest.Types.Body
|
Body = new CreateRequest.Types.Body
|
||||||
{
|
{
|
||||||
OwnerId = ctx.OwnerId!.ToMessage(),
|
OwnerId = ClientContext.Owner.OwnerID,
|
||||||
Expiration = args.Expiration
|
Expiration = args.Expiration
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddMetaHeader(args.XHeaders);
|
request.AddMetaHeader(args.XHeaders);
|
||||||
request.Sign(ctx.Key!);
|
request.Sign(ClientContext.Key.ECDsaKey);
|
||||||
|
|
||||||
return await CreateSession(request, args.Context!).ConfigureAwait(false);
|
return await CreateSession(request, args.Context!).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,15 @@ internal interface ISessionProvider
|
||||||
|
|
||||||
internal sealed class SessionProvider(ClientContext envCtx)
|
internal sealed class SessionProvider(ClientContext envCtx)
|
||||||
{
|
{
|
||||||
public async ValueTask<Session.SessionToken> GetOrCreateSession(ISessionToken args, CallContext ctx)
|
public async Task<FrostFsSessionToken> CreateSession(ISessionToken args, CallContext ctx)
|
||||||
{
|
{
|
||||||
if (args.SessionToken is null)
|
var token = await GetDefaultSession(args, ctx).ConfigureAwait(false);
|
||||||
{
|
|
||||||
return await envCtx.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx))
|
return new FrostFsSessionToken(token);
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Session.SessionToken().Deserialize(args.SessionToken.Token);
|
internal async Task<Session.SessionToken> GetDefaultSession(ISessionToken args, CallContext ctx)
|
||||||
|
{
|
||||||
|
return await envCtx.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Collections.ObjectModel;
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
|
using Grpc.Core.Interceptors;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public class ClientContext(FrostFSClient client, ECDsa? key, FrostFsOwner? owner, GrpcChannel channel, FrostFsVersion version) : IDisposable
|
public class ClientContext(FrostFSClient client, ClientKey key, FrostFsOwner owner, GrpcChannel channel, FrostFsVersion version) : IDisposable
|
||||||
{
|
{
|
||||||
private ArrayPool<byte>? _arrayPool;
|
|
||||||
private string? sessionKey;
|
private string? sessionKey;
|
||||||
|
|
||||||
internal FrostFsOwner? Owner { get; } = owner;
|
internal FrostFsOwner Owner { get; } = owner;
|
||||||
|
|
||||||
internal string? Address { get; } = channel.Target;
|
internal string? Address { get; } = channel.Target;
|
||||||
|
|
||||||
|
@ -25,9 +22,16 @@ public class ClientContext(FrostFSClient client, ECDsa? key, FrostFsOwner? owner
|
||||||
|
|
||||||
internal FrostFSClient Client { get; } = client;
|
internal FrostFSClient Client { get; } = client;
|
||||||
|
|
||||||
internal ClientKey? Key { get; } = key != null ? new ClientKey(key) : null;
|
internal ClientKey Key { get; } = key;
|
||||||
|
|
||||||
|
internal SessionCache? SessionCache { get; set; }
|
||||||
|
|
||||||
|
internal Action<CallStatistics>? Callback { get; set; }
|
||||||
|
|
||||||
|
internal Collection<Interceptor>? Interceptors { get; set; }
|
||||||
|
|
||||||
|
internal Action<Exception>? PoolErrorHandler { get; set; }
|
||||||
|
|
||||||
internal SessionCache SessionCache { get; set; }
|
|
||||||
|
|
||||||
internal string? SessionCacheKey
|
internal string? SessionCacheKey
|
||||||
{
|
{
|
||||||
|
@ -35,23 +39,13 @@ public class ClientContext(FrostFSClient client, ECDsa? key, FrostFsOwner? owner
|
||||||
{
|
{
|
||||||
if (sessionKey == null && Key != null && Address != null)
|
if (sessionKey == null && Key != null && Address != null)
|
||||||
{
|
{
|
||||||
sessionKey = Pool.FormCacheKey(Address, Key.ECDsaKey.PrivateKey().ToString());
|
sessionKey = Pool.FormCacheKey(Address, Key.PublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionKey;
|
return sessionKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom pool is used for predefined sizes of buffers like grpc chunk
|
|
||||||
/// </summary>
|
|
||||||
internal ArrayPool<byte> GetArrayPool(int size)
|
|
||||||
{
|
|
||||||
_arrayPool ??= ArrayPool<byte>.Create(size, 256);
|
|
||||||
|
|
||||||
return _arrayPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
internal static class ObjectTools
|
internal static class ObjectTools
|
||||||
{
|
{
|
||||||
internal static FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx)
|
internal static FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, ClientContext ctx)
|
||||||
{
|
{
|
||||||
var grpcHeader = CreateHeader(header, [], ctx);
|
var grpcHeader = CreateHeader(header, [], ctx);
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ internal static class ObjectTools
|
||||||
return new ObjectID { Value = grpcHeader.Sha256() }.ToModel();
|
return new ObjectID { Value = grpcHeader.Sha256() }.ToModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Object.Object CreateObject(FrostFsObject @object, CallContext ctx)
|
internal static Object.Object CreateObject(FrostFsObject @object, ClientContext ctx)
|
||||||
{
|
{
|
||||||
@object.Header.OwnerId ??= ctx.OwnerId;
|
@object.Header.OwnerId ??= ctx.Owner;
|
||||||
@object.Header.Version ??= ctx.Version;
|
@object.Header.Version ??= ctx.Version;
|
||||||
|
|
||||||
var grpcHeader = @object.Header.GetHeader();
|
var grpcHeader = @object.Header.GetHeader();
|
||||||
|
@ -46,14 +46,14 @@ internal static class ObjectTools
|
||||||
|
|
||||||
obj.Signature = new Signature
|
obj.Signature = new Signature
|
||||||
{
|
{
|
||||||
Key = ctx.GetPublicKeyCache(),
|
Key = ctx.Key.PublicKeyProto,
|
||||||
Sign = ByteString.CopyFrom(ctx.Key!.SignData(obj.ObjectId.ToByteArray())),
|
Sign = ctx.Key.ECDsaKey.SignData(obj.ObjectId.ToByteArray()),
|
||||||
};
|
};
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void SetSplitValues(Header grpcHeader, FrostFsSplit split, CallContext ctx)
|
internal static void SetSplitValues(Header grpcHeader, FrostFsSplit split, ClientContext ctx)
|
||||||
{
|
{
|
||||||
if (split == null)
|
if (split == null)
|
||||||
return;
|
return;
|
||||||
|
@ -77,17 +77,17 @@ internal static class ObjectTools
|
||||||
grpcHeader.Split.ParentHeader = grpcParentHeader;
|
grpcHeader.Split.ParentHeader = grpcParentHeader;
|
||||||
grpcHeader.Split.ParentSignature = new Signature
|
grpcHeader.Split.ParentSignature = new Signature
|
||||||
{
|
{
|
||||||
Key = ctx.GetPublicKeyCache(),
|
Key = ctx.Key.PublicKeyProto,
|
||||||
Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())),
|
Sign = ctx.Key.ECDsaKey.SignData(grpcHeader.Split.Parent.ToByteArray()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
grpcHeader.Split.Previous = split.Previous?.ToMessage();
|
grpcHeader.Split.Previous = split.Previous?.ToMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Header CreateHeader(FrostFsObjectHeader header, byte[]? payload, CallContext ctx)
|
internal static Header CreateHeader(FrostFsObjectHeader header, byte[]? payload, ClientContext ctx)
|
||||||
{
|
{
|
||||||
header.OwnerId ??= ctx.OwnerId;
|
header.OwnerId ??= ctx.Owner;
|
||||||
header.Version ??= ctx.Version;
|
header.Version ??= ctx.Version;
|
||||||
|
|
||||||
var grpcHeader = header.GetHeader();
|
var grpcHeader = header.GetHeader();
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
using FrostFS.SDK.Proto.Interfaces;
|
using FrostFS.SDK.Proto.Interfaces;
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
|
@ -33,59 +30,4 @@ public static class RequestConstructor
|
||||||
xHeaders.Cast<string>().SelectMany(key => xHeaders.GetValues(key),
|
xHeaders.Cast<string>().SelectMany(key => xHeaders.GetValues(key),
|
||||||
(k, v) => new XHeader { Key = k, Value = v }));
|
(k, v) => new XHeader { Key = k, Value = v }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateObjectTokenContext(this SessionToken sessionToken,
|
|
||||||
Address address,
|
|
||||||
ObjectSessionContext.Types.Verb verb,
|
|
||||||
ECDsa key)
|
|
||||||
{
|
|
||||||
if (sessionToken is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(sessionToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionToken.Body.Object?.Target != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ObjectSessionContext.Types.Target target = new() { Container = address.ContainerId };
|
|
||||||
|
|
||||||
if (address.ObjectId != null)
|
|
||||||
target.Objects.Add(address.ObjectId);
|
|
||||||
|
|
||||||
sessionToken.Body.Object = new()
|
|
||||||
{
|
|
||||||
Target = target,
|
|
||||||
Verb = verb
|
|
||||||
};
|
|
||||||
|
|
||||||
sessionToken.Body.SessionKey = Google.Protobuf.ByteString.CopyFrom(key.PublicKey());
|
|
||||||
|
|
||||||
sessionToken.Signature = key.SignMessagePart(sessionToken.Body);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void CreateContainerTokenContext(this SessionToken sessionToken,
|
|
||||||
ContainerID? containerId,
|
|
||||||
ContainerSessionContext.Types.Verb verb,
|
|
||||||
ECDsa key,
|
|
||||||
Google.Protobuf.ByteString publicKey)
|
|
||||||
{
|
|
||||||
if (sessionToken.Body.Container?.ContainerId != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
sessionToken.Body.Container = new() { Verb = verb };
|
|
||||||
|
|
||||||
if (containerId != null)
|
|
||||||
sessionToken.Body.Container.ContainerId = containerId;
|
|
||||||
else
|
|
||||||
sessionToken.Body.Container.Wildcard = true;
|
|
||||||
|
|
||||||
sessionToken.Body.SessionKey = publicKey;
|
|
||||||
|
|
||||||
sessionToken.Signature = key.SignMessagePart(sessionToken.Body);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,21 @@ using Org.BouncyCastle.Crypto.Parameters;
|
||||||
using Org.BouncyCastle.Crypto.Signers;
|
using Org.BouncyCastle.Crypto.Signers;
|
||||||
using Org.BouncyCastle.Math;
|
using Org.BouncyCastle.Math;
|
||||||
|
|
||||||
|
using Signature = FrostFS.Refs.Signature;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
public static class RequestSigner
|
public static class RequestSigner
|
||||||
{
|
{
|
||||||
internal const int RFC6979SignatureSize = 64;
|
internal const int RFC6979SignatureSize = 64;
|
||||||
|
|
||||||
internal static byte[] SignRFC6979(this ECDsa key, byte[] data)
|
internal static ByteString SignRFC6979(this ECDsa key, byte[] data)
|
||||||
{
|
{
|
||||||
if (key is null)
|
if (key is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
var digest = new Sha256Digest();
|
var digest = new Sha256Digest();
|
||||||
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
||||||
|
@ -44,24 +41,27 @@ public static class RequestSigner
|
||||||
signer.Init(true, privateKey);
|
signer.Init(true, privateKey);
|
||||||
|
|
||||||
var rs = signer.GenerateSignature(hash);
|
var rs = signer.GenerateSignature(hash);
|
||||||
var signature = new byte[RFC6979SignatureSize];
|
|
||||||
|
Span<byte> signature = stackalloc byte[RFC6979SignatureSize];
|
||||||
|
|
||||||
var rbytes = rs[0].ToByteArrayUnsigned();
|
var rbytes = rs[0].ToByteArrayUnsigned();
|
||||||
var sbytes = rs[1].ToByteArrayUnsigned();
|
var sbytes = rs[1].ToByteArrayUnsigned();
|
||||||
var index = RFC6979SignatureSize / 2 - rbytes.Length;
|
var index = RFC6979SignatureSize / 2 - rbytes.Length;
|
||||||
|
|
||||||
rbytes.CopyTo(signature, index);
|
rbytes.AsSpan().CopyTo(signature[index..]);
|
||||||
index = RFC6979SignatureSize - sbytes.Length;
|
index = RFC6979SignatureSize - sbytes.Length;
|
||||||
sbytes.CopyTo(signature, index);
|
sbytes.AsSpan().CopyTo(signature[index..]);
|
||||||
|
|
||||||
return signature;
|
return ByteString.CopyFrom(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message)
|
internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message)
|
||||||
{
|
{
|
||||||
return new SignatureRFC6979
|
return new SignatureRFC6979
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignRFC6979(message.ToByteArray())),
|
Sign = key.SignRFC6979(message.ToByteArray()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,23 +70,26 @@ public static class RequestSigner
|
||||||
return new SignatureRFC6979
|
return new SignatureRFC6979
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignRFC6979(data.ToByteArray())),
|
Sign = key.SignRFC6979(data.ToByteArray()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] SignData(this ECDsa key, byte[] data)
|
public static ByteString SignData(this ECDsa key, byte[] data)
|
||||||
{
|
{
|
||||||
if (key is null)
|
if (key is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = new byte[65];
|
Span<byte> result = stackalloc byte[65];
|
||||||
hash[0] = 0x04;
|
result[0] = 0x04;
|
||||||
|
|
||||||
key.SignHash(data.Sha512()).CopyTo(hash, 1);
|
//var hash = new byte[65];
|
||||||
|
//hash[0] = 0x04;
|
||||||
|
|
||||||
return hash;
|
key.SignHash(data.Sha512()).AsSpan().CopyTo(result[1..]);
|
||||||
|
|
||||||
|
return ByteString.CopyFrom(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
internal static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
||||||
|
@ -95,7 +98,7 @@ public static class RequestSigner
|
||||||
var sig = new Signature
|
var sig = new Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignData(data2Sign)),
|
Sign = key.SignData(data2Sign),
|
||||||
};
|
};
|
||||||
|
|
||||||
return sig;
|
return sig;
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
|
|
||||||
|
using Google.Protobuf.Collections;
|
||||||
|
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
namespace FrostFS.SDK.Client;
|
||||||
|
|
||||||
internal sealed class SearchReader(AsyncServerStreamingCall<SearchResponse> call) : IDisposable
|
internal sealed class SearchReader(AsyncServerStreamingCall<SearchResponse> call) : IDisposable
|
||||||
{
|
{
|
||||||
public AsyncServerStreamingCall<SearchResponse> Call { get; private set; } = call;
|
internal AsyncServerStreamingCall<SearchResponse> Call { get; private set; } = call;
|
||||||
|
|
||||||
public async Task<List<ObjectID>?> Read(CancellationToken cancellationToken)
|
internal async Task<RepeatedField<ObjectID>?> Read(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false))
|
if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false))
|
||||||
return null;
|
return null;
|
||||||
|
@ -24,7 +24,7 @@ internal sealed class SearchReader(AsyncServerStreamingCall<SearchResponse> call
|
||||||
|
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return response.Body?.IdList.ToList();
|
return response.Body?.IdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -88,6 +88,7 @@ public static class Verifier
|
||||||
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
|
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
||||||
{
|
{
|
||||||
if (!verification.MetaSignature.VerifyMessagePart(meta))
|
if (!verification.MetaSignature.VerifyMessagePart(meta))
|
||||||
|
|
|
@ -11,9 +11,7 @@ public static class UUIDExtension
|
||||||
if (id == null)
|
if (id == null)
|
||||||
throw new ArgumentNullException(nameof(id));
|
throw new ArgumentNullException(nameof(id));
|
||||||
|
|
||||||
var bytes = id.ToByteArray();
|
var orderedBytes = GetGuidBytesDirectOrder(id.Span);
|
||||||
|
|
||||||
var orderedBytes = GetGuidBytesDirectOrder(bytes);
|
|
||||||
|
|
||||||
return new Guid(orderedBytes);
|
return new Guid(orderedBytes);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +30,7 @@ public static class UUIDExtension
|
||||||
return orderedBytes;
|
return orderedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] GetGuidBytesDirectOrder(byte[] source)
|
private static byte[] GetGuidBytesDirectOrder(ReadOnlySpan<byte> source)
|
||||||
{
|
{
|
||||||
if (source.Length != 16)
|
if (source.Length != 16)
|
||||||
throw new ArgumentException("Wrong uuid binary format");
|
throw new ArgumentException("Wrong uuid binary format");
|
||||||
|
|
|
@ -8,12 +8,12 @@ public abstract class ContainerTestsBase
|
||||||
{
|
{
|
||||||
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||||
|
|
||||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
protected IOptions<ClientSettings> Settings { get; set; }
|
||||||
protected ContainerMocker Mocker { get; set; }
|
protected ContainerMocker Mocker { get; set; }
|
||||||
|
|
||||||
protected ContainerTestsBase()
|
protected ContainerTestsBase()
|
||||||
{
|
{
|
||||||
Settings = Options.Create(new SingleOwnerClientSettings
|
Settings = Options.Create(new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = "http://localhost:8080"
|
Host = "http://localhost:8080"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="TestData\cat.jpeg" />
|
<None Remove="cat.jpeg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="TestData\cat.jpg">
|
<None Update="cat.jpg">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader)
|
||||||
Signature = new Refs.Signature
|
Signature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(header.ToByteArray())),
|
Sign = Key.SignData(header.ToByteArray()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,19 +46,19 @@ public abstract class ServiceBase(string key)
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray()))
|
Sign = Key.SignData(response.MetaHeader.ToByteArray())
|
||||||
},
|
},
|
||||||
BodySignature = new Refs.Signature
|
BodySignature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray()))
|
Sign = Key.SignData(response.GetBody().ToByteArray())
|
||||||
},
|
},
|
||||||
OriginSignature = new Refs.Signature
|
OriginSignature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData([]))
|
Sign = Key.SignData([])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ using FrostFS.Object;
|
||||||
using FrostFS.SDK.Client;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
using FrostFS.Session;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
headResponse.Body.Header.Signature = new Refs.Signature
|
headResponse.Body.Header.Signature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(headResponse.Body.Header.ToByteArray())),
|
Sign = Key.SignData(headResponse.Body.Header.ToByteArray()),
|
||||||
};
|
};
|
||||||
|
|
||||||
headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse);
|
headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse);
|
||||||
|
@ -281,7 +280,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
{
|
{
|
||||||
Body = new PatchResponse.Types.Body
|
Body = new PatchResponse.Types.Body
|
||||||
{
|
{
|
||||||
ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1,2,3])) },
|
ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1, 2, 3])) },
|
||||||
},
|
},
|
||||||
MetaHeader = ResponseMetaHeader
|
MetaHeader = ResponseMetaHeader
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class SessionMocker(string key) : ServiceBase(key)
|
||||||
|
|
||||||
if (SessionKey == null)
|
if (SessionKey == null)
|
||||||
{
|
{
|
||||||
SessionKey = new byte[32];
|
SessionKey = new byte[33];
|
||||||
rand.NextBytes(SessionKey);
|
rand.NextBytes(SessionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,16 +32,13 @@ public class NetworkTest : NetworkTestsBase
|
||||||
new PrmNetworkSettings(new CallContext
|
new PrmNetworkSettings(new CallContext
|
||||||
{
|
{
|
||||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20)
|
||||||
OwnerId = OwnerId,
|
|
||||||
Key = ECDsaKey,
|
|
||||||
Version = Version
|
|
||||||
})
|
})
|
||||||
: new PrmNetworkSettings();
|
: new PrmNetworkSettings();
|
||||||
|
|
||||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
var result = await GetClient().GetNetworkSettingsAsync(param);
|
var result = await GetClient(DefaultSettings).GetNetworkSettingsAsync(param);
|
||||||
|
|
||||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
|
@ -113,17 +110,14 @@ public class NetworkTest : NetworkTestsBase
|
||||||
|
|
||||||
Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body };
|
Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body };
|
||||||
|
|
||||||
PrmNetmapSnapshot param;
|
PrmNetmapSnapshot param = new();
|
||||||
|
|
||||||
if (useContext)
|
if (useContext)
|
||||||
{
|
{
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20)
|
||||||
OwnerId = OwnerId,
|
|
||||||
Key = ECDsaKey,
|
|
||||||
Version = Version
|
|
||||||
};
|
};
|
||||||
|
|
||||||
param = new(ctx);
|
param = new(ctx);
|
||||||
|
@ -137,7 +131,7 @@ public class NetworkTest : NetworkTestsBase
|
||||||
|
|
||||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
var result = await GetClient().GetNetmapSnapshotAsync(param);
|
var result = await GetClient(DefaultSettings).GetNetmapSnapshotAsync(param);
|
||||||
|
|
||||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
|
@ -219,10 +213,7 @@ public class NetworkTest : NetworkTestsBase
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20)
|
||||||
OwnerId = OwnerId,
|
|
||||||
Key = ECDsaKey,
|
|
||||||
Version = Version
|
|
||||||
};
|
};
|
||||||
|
|
||||||
param = new(ctx);
|
param = new(ctx);
|
||||||
|
@ -236,7 +227,7 @@ public class NetworkTest : NetworkTestsBase
|
||||||
|
|
||||||
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
var result = await GetClient().GetNodeInfoAsync(param);
|
var result = await GetClient(DefaultSettings).GetNodeInfoAsync(param);
|
||||||
|
|
||||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client.Interfaces;
|
using FrostFS.SDK.Client.Interfaces;
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
@ -13,32 +11,27 @@ public abstract class NetworkTestsBase
|
||||||
{
|
{
|
||||||
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||||
|
|
||||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
// protected FrostFsVersion Version { get; set; } = new FrostFsVersion(2, 13);
|
||||||
|
|
||||||
protected FrostFsVersion Version { get; set; } = new FrostFsVersion(2, 13);
|
|
||||||
|
|
||||||
protected ECDsa ECDsaKey { get; set; }
|
|
||||||
protected FrostFsOwner OwnerId { get; set; }
|
|
||||||
protected NetworkMocker Mocker { get; set; }
|
protected NetworkMocker Mocker { get; set; }
|
||||||
|
|
||||||
|
protected ClientSettings DefaultSettings { get; }
|
||||||
|
|
||||||
protected NetworkTestsBase()
|
protected NetworkTestsBase()
|
||||||
{
|
{
|
||||||
Settings = Options.Create(new SingleOwnerClientSettings
|
DefaultSettings = new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = "http://localhost:8080"
|
Host = "http://localhost:8080",
|
||||||
});
|
};
|
||||||
|
|
||||||
ECDsaKey = key.LoadWif();
|
|
||||||
OwnerId = FrostFsOwner.FromKey(ECDsaKey);
|
|
||||||
|
|
||||||
Mocker = new NetworkMocker(this.key);
|
Mocker = new NetworkMocker(this.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IFrostFSClient GetClient()
|
protected IFrostFSClient GetClient(ClientSettings settings)
|
||||||
{
|
{
|
||||||
return Client.FrostFSClient.GetTestInstance(
|
return Client.FrostFSClient.GetTestInstance(
|
||||||
Settings,
|
Options.Create(settings),
|
||||||
null,
|
null,
|
||||||
Mocker.GetMock().Object,
|
Mocker.GetMock().Object,
|
||||||
new SessionMocker(this.key).GetMock().Object,
|
new SessionMocker(this.key).GetMock().Object,
|
||||||
|
|
|
@ -6,12 +6,9 @@ using System.Text;
|
||||||
using FrostFS.Refs;
|
using FrostFS.Refs;
|
||||||
using FrostFS.SDK.Client;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
using static FrostFS.Object.ECInfo.Types;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Tests;
|
namespace FrostFS.SDK.Tests;
|
||||||
|
|
||||||
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
||||||
|
@ -23,18 +20,9 @@ public class ObjectTest : ObjectTestsBase
|
||||||
{
|
{
|
||||||
var client = GetClient();
|
var client = GetClient();
|
||||||
|
|
||||||
var ecdsaKey = key.LoadWif();
|
var objectId = client.CalculateObjectId(Mocker.ObjectHeader!);
|
||||||
|
|
||||||
var ctx = new CallContext
|
var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId));
|
||||||
{
|
|
||||||
Key = ecdsaKey,
|
|
||||||
OwnerId = FrostFsOwner.FromKey(ecdsaKey),
|
|
||||||
Version = new FrostFsVersion(2, 13)
|
|
||||||
};
|
|
||||||
|
|
||||||
var objectId = client.CalculateObjectId(Mocker.ObjectHeader!, ctx);
|
|
||||||
|
|
||||||
var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId, ctx));
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
|
|
||||||
|
@ -90,7 +78,7 @@ public class ObjectTest : ObjectTestsBase
|
||||||
NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]);
|
NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]);
|
||||||
|
|
||||||
var blockSize = 2560;
|
var blockSize = 2560;
|
||||||
byte[] bytes = File.ReadAllBytes(@".\..\..\..\TestData\cat.jpg");
|
byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg");
|
||||||
var fileLength = bytes.Length;
|
var fileLength = bytes.Length;
|
||||||
|
|
||||||
var param = new PrmObjectPut
|
var param = new PrmObjectPut
|
||||||
|
|
|
@ -10,7 +10,7 @@ public abstract class ObjectTestsBase
|
||||||
{
|
{
|
||||||
protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||||
|
|
||||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
protected IOptions<ClientSettings> Settings { get; set; }
|
||||||
protected FrostFsContainerId ContainerId { get; set; }
|
protected FrostFsContainerId ContainerId { get; set; }
|
||||||
|
|
||||||
protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key);
|
protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key);
|
||||||
|
@ -22,7 +22,7 @@ public abstract class ObjectTestsBase
|
||||||
{
|
{
|
||||||
var ecdsaKey = key.LoadWif();
|
var ecdsaKey = key.LoadWif();
|
||||||
|
|
||||||
Settings = Options.Create(new SingleOwnerClientSettings
|
Settings = Options.Create(new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = "http://localhost:8080"
|
Host = "http://localhost:8080"
|
||||||
|
|
|
@ -6,8 +6,6 @@ using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
using static FrostFS.Session.SessionToken.Types.Body;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.SmokeTests;
|
namespace FrostFS.SDK.SmokeTests;
|
||||||
|
|
||||||
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
||||||
|
@ -78,6 +76,8 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void NodeInfoStatisticsTwoNodesTest()
|
public async void NodeInfoStatisticsTwoNodesTest()
|
||||||
{
|
{
|
||||||
|
var callbackText = string.Empty;
|
||||||
|
|
||||||
var options = new InitParameters
|
var options = new InitParameters
|
||||||
{
|
{
|
||||||
Key = keyString.LoadWif(),
|
Key = keyString.LoadWif(),
|
||||||
|
@ -87,16 +87,14 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
],
|
],
|
||||||
ClientBuilder = null,
|
ClientBuilder = null,
|
||||||
GracefulCloseOnSwitchTimeout = 30_000_000,
|
GracefulCloseOnSwitchTimeout = 30_000_000,
|
||||||
Logger = null
|
Logger = null,
|
||||||
|
Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"
|
||||||
};
|
};
|
||||||
|
|
||||||
using var pool = new Pool(options);
|
using var pool = new Pool(options);
|
||||||
|
|
||||||
var callbackText = string.Empty;
|
|
||||||
|
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var error = await pool.Dial(ctx).ConfigureAwait(true);
|
var error = await pool.Dial(ctx).ConfigureAwait(true);
|
||||||
|
@ -118,14 +116,12 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
{
|
{
|
||||||
var options = GetDefaultParams();
|
var options = GetDefaultParams();
|
||||||
|
|
||||||
|
var callbackText = string.Empty;
|
||||||
|
options.Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds";
|
||||||
|
|
||||||
using var pool = new Pool(options);
|
using var pool = new Pool(options);
|
||||||
|
|
||||||
var callbackText = string.Empty;
|
var ctx = new CallContext();
|
||||||
|
|
||||||
var ctx = new CallContext
|
|
||||||
{
|
|
||||||
Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"
|
|
||||||
};
|
|
||||||
|
|
||||||
var error = await pool.Dial(ctx).ConfigureAwait(true);
|
var error = await pool.Dial(ctx).ConfigureAwait(true);
|
||||||
|
|
||||||
|
@ -154,18 +150,11 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
|
|
||||||
var token = await pool.CreateSessionAsync(prm).ConfigureAwait(true);
|
var token = await pool.CreateSessionAsync(prm).ConfigureAwait(true);
|
||||||
|
|
||||||
var session = new Session.SessionToken().Deserialize(token.Token);
|
|
||||||
|
|
||||||
var ownerHash = Base58.Decode(OwnerId!.Value);
|
var ownerHash = Base58.Decode(OwnerId!.Value);
|
||||||
|
|
||||||
Assert.NotNull(session);
|
Assert.NotNull(token);
|
||||||
Assert.Null(session.Body.Container);
|
Assert.NotEqual(Guid.Empty, token.Id);
|
||||||
Assert.Null(session.Body.Object);
|
Assert.Equal(33, token.SessionKey.Length);
|
||||||
Assert.Equal(16, session.Body.Id.Length);
|
|
||||||
Assert.Equal(100ul, session.Body.Lifetime.Exp);
|
|
||||||
Assert.Equal(ownerHash, session.Body.OwnerId.Value);
|
|
||||||
Assert.Equal(33, session.Body.SessionKey.Length);
|
|
||||||
Assert.Equal(ContextOneofCase.None, session.Body.ContextCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -314,8 +303,16 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
[InlineData(6 * 1024 * 1024 + 100)]
|
[InlineData(6 * 1024 * 1024 + 100)]
|
||||||
public async void SimpleScenarioTest(int objectSize)
|
public async void SimpleScenarioTest(int objectSize)
|
||||||
{
|
{
|
||||||
|
bool callbackInvoked = false;
|
||||||
|
|
||||||
var options = GetDefaultParams();
|
var options = GetDefaultParams();
|
||||||
|
|
||||||
|
options.Callback = new((CallStatistics cs) =>
|
||||||
|
{
|
||||||
|
callbackInvoked = true;
|
||||||
|
Assert.True(cs.ElapsedMicroSeconds > 0);
|
||||||
|
});
|
||||||
|
|
||||||
using var pool = new Pool(options);
|
using var pool = new Pool(options);
|
||||||
|
|
||||||
var error = await pool.Dial(new CallContext()).ConfigureAwait(true);
|
var error = await pool.Dial(new CallContext()).ConfigureAwait(true);
|
||||||
|
@ -324,16 +321,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
|
|
||||||
await Cleanup(pool);
|
await Cleanup(pool);
|
||||||
|
|
||||||
bool callbackInvoked = false;
|
var ctx = new CallContext { };
|
||||||
var ctx = new CallContext
|
|
||||||
{
|
|
||||||
// Timeout = TimeSpan.FromSeconds(20),
|
|
||||||
Callback = new((CallStatistics cs) =>
|
|
||||||
{
|
|
||||||
callbackInvoked = true;
|
|
||||||
Assert.True(cs.ElapsedMicroSeconds > 0);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
var createContainerParam = new PrmContainerCreate(
|
||||||
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
||||||
|
@ -346,12 +334,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
|
|
||||||
var bytes = GetRandomBytes(objectSize);
|
var bytes = GetRandomBytes(objectSize);
|
||||||
|
|
||||||
var ctx1 = new CallContext
|
var param = new PrmObjectPut()
|
||||||
{
|
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
};
|
|
||||||
|
|
||||||
var param = new PrmObjectPut(ctx1)
|
|
||||||
{
|
{
|
||||||
Header = new FrostFsObjectHeader(
|
Header = new FrostFsObjectHeader(
|
||||||
containerId: createdContainer,
|
containerId: createdContainer,
|
||||||
|
@ -410,6 +393,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
var options = GetDefaultParams();
|
var options = GetDefaultParams();
|
||||||
|
|
||||||
using var pool = new Pool(options);
|
using var pool = new Pool(options);
|
||||||
|
options.Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0));
|
||||||
|
|
||||||
var error = await pool.Dial(new CallContext()).ConfigureAwait(true);
|
var error = await pool.Dial(new CallContext()).ConfigureAwait(true);
|
||||||
|
|
||||||
|
@ -421,8 +405,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
|
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20)
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
var createContainerParam = new PrmContainerCreate(
|
||||||
|
@ -435,7 +418,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
|
|
||||||
var bytes = GetRandomBytes(objectSize);
|
var bytes = GetRandomBytes(objectSize);
|
||||||
|
|
||||||
var param = new PrmObjectPut(new CallContext { Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) })
|
var param = new PrmObjectPut(new CallContext())
|
||||||
{
|
{
|
||||||
Header = new FrostFsObjectHeader(
|
Header = new FrostFsObjectHeader(
|
||||||
containerId: container,
|
containerId: container,
|
||||||
|
@ -517,7 +500,7 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
Timeout = TimeSpan.FromSeconds(10),
|
Timeout = TimeSpan.FromSeconds(10),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.Interceptors.Add(new CallbackInterceptor());
|
// ctx.Interceptors.Add(new CallbackInterceptor());
|
||||||
|
|
||||||
var container = await pool.GetContainerAsync(new PrmContainerGet(containerId, ctx));
|
var container = await pool.GetContainerAsync(new PrmContainerGet(containerId, ctx));
|
||||||
|
|
||||||
|
@ -585,9 +568,9 @@ public class PoolSmokeTests : SmokeTestsBase
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOptions<SingleOwnerClientSettings> GetSingleOwnerOptions(string key, string url)
|
private static IOptions<ClientSettings> GetSingleOwnerOptions(string key, string url)
|
||||||
{
|
{
|
||||||
return Options.Create(new SingleOwnerClientSettings
|
return Options.Create(new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = url
|
Host = url
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using FrostFS.SDK.Client;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
|
using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
namespace FrostFS.SDK.Tests;
|
namespace FrostFS.SDK.Tests;
|
||||||
|
|
||||||
|
@ -21,10 +22,7 @@ public class SessionTest : SessionTestsBase
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
CancellationToken = Mocker.CancellationTokenSource.Token,
|
CancellationToken = Mocker.CancellationTokenSource.Token,
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20)
|
||||||
OwnerId = OwnerId,
|
|
||||||
Key = ECDsaKey,
|
|
||||||
Version = Mocker.Version
|
|
||||||
};
|
};
|
||||||
|
|
||||||
param = new PrmSessionCreate(exp, ctx);
|
param = new PrmSessionCreate(exp, ctx);
|
||||||
|
@ -43,18 +41,16 @@ public class SessionTest : SessionTestsBase
|
||||||
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
var validTimeoutTo = DateTime.UtcNow.AddSeconds(20);
|
||||||
|
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.NotNull(result.Token);
|
Assert.NotEqual(Guid.Empty, result.Id);
|
||||||
|
|
||||||
var session = new Session.SessionToken().Deserialize(result.Token);
|
Assert.Equal(Mocker.SessionId, result.Id.ToBytes());
|
||||||
|
Assert.Equal(Mocker.SessionKey, result.SessionKey.ToArray());
|
||||||
|
|
||||||
Assert.Equal(Mocker.SessionId, session.Body.Id);
|
//Assert.Equal(OwnerId.ToMessage(), result.Token.Body.OwnerId);
|
||||||
Assert.Equal(Mocker.SessionKey, session.Body.SessionKey);
|
//Assert.Equal(exp, result.Token.Body.Lifetime.Exp);
|
||||||
|
//Assert.Equal(exp, result.Token.Body.Lifetime.Iat);
|
||||||
Assert.Equal(OwnerId.ToMessage(), session.Body.OwnerId);
|
//Assert.Equal(exp, result.Token.Body.Lifetime.Nbf);
|
||||||
Assert.Equal(exp, session.Body.Lifetime.Exp);
|
//Assert.Null(result.Token.Body.Container);
|
||||||
Assert.Equal(exp, session.Body.Lifetime.Iat);
|
|
||||||
Assert.Equal(exp, session.Body.Lifetime.Nbf);
|
|
||||||
Assert.Null(session.Body.Container);
|
|
||||||
|
|
||||||
Assert.NotNull(Mocker.CreateSessionRequest);
|
Assert.NotNull(Mocker.CreateSessionRequest);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ public abstract class SessionTestsBase
|
||||||
{
|
{
|
||||||
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
||||||
|
|
||||||
protected IOptions<SingleOwnerClientSettings> Settings { get; set; }
|
protected IOptions<ClientSettings> Settings { get; set; }
|
||||||
|
|
||||||
protected ECDsa ECDsaKey { get; set; }
|
protected ECDsa ECDsaKey { get; set; }
|
||||||
protected FrostFsOwner OwnerId { get; set; }
|
protected FrostFsOwner OwnerId { get; set; }
|
||||||
|
@ -19,7 +19,7 @@ public abstract class SessionTestsBase
|
||||||
|
|
||||||
protected SessionTestsBase()
|
protected SessionTestsBase()
|
||||||
{
|
{
|
||||||
Settings = Options.Create(new SingleOwnerClientSettings
|
Settings = Options.Create(new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = "http://localhost:8080"
|
Host = "http://localhost:8080"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
using FrostFS.SDK.Client;
|
using FrostFS.SDK.Client;
|
||||||
|
@ -7,8 +7,6 @@ using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
using static FrostFS.Session.SessionToken.Types.Body;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.SmokeTests;
|
namespace FrostFS.SDK.SmokeTests;
|
||||||
|
|
||||||
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
||||||
|
@ -17,29 +15,23 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
{
|
{
|
||||||
private static readonly PrmWait lightWait = new(100, 1);
|
private static readonly PrmWait lightWait = new(100, 1);
|
||||||
|
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData(false)]
|
public async void AccountTest()
|
||||||
[InlineData(true)]
|
|
||||||
public async void AccountTest(bool isSingleOnwerClient)
|
|
||||||
{
|
{
|
||||||
using var client = isSingleOnwerClient
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url))
|
|
||||||
: FrostFSClient.GetInstance(GetOptions(this.url));
|
|
||||||
|
|
||||||
PrmBalance? prm = isSingleOnwerClient ? default : new(Ctx);
|
var result = await client.GetBalanceAsync();
|
||||||
|
|
||||||
var result = await client.GetBalanceAsync(prm);
|
Assert.NotNull(result);
|
||||||
|
Assert.True(result.Value == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData(false)]
|
public async void NetworkMapTest()
|
||||||
[InlineData(true)]
|
|
||||||
public async void NetworkMapTest(bool isSingleOnwerClient)
|
|
||||||
{
|
{
|
||||||
using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new(Ctx);
|
var result = await client.GetNetmapSnapshotAsync();
|
||||||
var result = await client.GetNetmapSnapshotAsync(prm);
|
|
||||||
|
|
||||||
Assert.True(result.Epoch > 0);
|
Assert.True(result.Epoch > 0);
|
||||||
Assert.Single(result.NodeInfoCollection);
|
Assert.Single(result.NodeInfoCollection);
|
||||||
|
@ -54,18 +46,12 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData(false)]
|
public async void NodeInfoTest()
|
||||||
[InlineData(true)]
|
|
||||||
public async void NodeInfoTest(bool isSingleOnwerClient)
|
|
||||||
{
|
{
|
||||||
using var client = isSingleOnwerClient
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url))
|
|
||||||
: FrostFSClient.GetInstance(GetOptions(this.url));
|
|
||||||
|
|
||||||
PrmNodeInfo? prm = isSingleOnwerClient ? default : new(Ctx);
|
var result = await client.GetNodeInfoAsync();
|
||||||
|
|
||||||
var result = await client.GetNodeInfoAsync(prm);
|
|
||||||
|
|
||||||
Assert.Equal(2, result.Version.Major);
|
Assert.Equal(2, result.Version.Major);
|
||||||
Assert.Equal(13, result.Version.Minor);
|
Assert.Equal(13, result.Version.Minor);
|
||||||
|
@ -78,45 +64,38 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void NodeInfoStatisticsTest()
|
public async void NodeInfoStatisticsTest()
|
||||||
{
|
{
|
||||||
var ctx = new CallContext
|
var options = GetClientOptions(this.keyString, this.url);
|
||||||
{
|
|
||||||
Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds")
|
|
||||||
};
|
|
||||||
|
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
var callbackContent = string.Empty;
|
||||||
|
options.Value.Callback = (cs) => callbackContent = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds";
|
||||||
|
|
||||||
|
using var client = FrostFSClient.GetSingleOwnerInstance(options);
|
||||||
|
|
||||||
var result = await client.GetNodeInfoAsync();
|
var result = await client.GetNodeInfoAsync();
|
||||||
|
|
||||||
|
Assert.NotEmpty(callbackContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData(false)]
|
public async void GetSessionTest()
|
||||||
[InlineData(true)]
|
|
||||||
public async void GetSessionTest(bool isSingleOnwerClient)
|
|
||||||
{
|
{
|
||||||
using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
PrmSessionCreate? prm = isSingleOnwerClient ? new PrmSessionCreate(100) : new PrmSessionCreate(100, Ctx);
|
PrmSessionCreate? prm = new(100);
|
||||||
|
|
||||||
var token = await client.CreateSessionAsync(prm);
|
var token = await client.CreateSessionAsync(prm);
|
||||||
|
|
||||||
var session = new Session.SessionToken().Deserialize(token.Token);
|
|
||||||
|
|
||||||
var ownerHash = Base58.Decode(OwnerId!.Value);
|
var ownerHash = Base58.Decode(OwnerId!.Value);
|
||||||
|
|
||||||
Assert.NotNull(session);
|
Assert.NotNull(token);
|
||||||
Assert.Null(session.Body.Container);
|
Assert.NotEqual(Guid.Empty, token.Id);
|
||||||
Assert.Null(session.Body.Object);
|
Assert.Equal(33, token.SessionKey.Length);
|
||||||
Assert.Equal(16, session.Body.Id.Length);
|
|
||||||
Assert.Equal(100ul, session.Body.Lifetime.Exp);
|
|
||||||
Assert.Equal(ownerHash, session.Body.OwnerId.Value);
|
|
||||||
Assert.Equal(33, session.Body.SessionKey.Length);
|
|
||||||
Assert.Equal(ContextOneofCase.None, session.Body.ContextCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void CreateObjectWithSessionToken()
|
public async void CreateObjectWithSessionToken()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -164,7 +143,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void FilterTest()
|
public async void FilterTest()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -248,36 +227,34 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[InlineData(6 * 1024 * 1024 + 100)]
|
[InlineData(6 * 1024 * 1024 + 100)]
|
||||||
public async void SimpleScenarioTest(int objectSize)
|
public async void SimpleScenarioTest(int objectSize)
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
|
||||||
|
|
||||||
await Cleanup(client);
|
|
||||||
|
|
||||||
bool callbackInvoked = false;
|
bool callbackInvoked = false;
|
||||||
var ctx = new CallContext
|
|
||||||
{
|
var options = GetClientOptions(this.keyString, this.url);
|
||||||
// Timeout = TimeSpan.FromSeconds(20),
|
|
||||||
Callback = new((CallStatistics cs) =>
|
options.Value.Callback = new((CallStatistics cs) =>
|
||||||
{
|
{
|
||||||
callbackInvoked = true;
|
callbackInvoked = true;
|
||||||
Assert.True(cs.ElapsedMicroSeconds > 0);
|
Assert.True(cs.ElapsedMicroSeconds > 0);
|
||||||
})
|
});
|
||||||
};
|
|
||||||
|
using var client = FrostFSClient.GetSingleOwnerInstance(options);
|
||||||
|
|
||||||
|
await Cleanup(client);
|
||||||
|
|
||||||
|
var ctx = new CallContext { Timeout = TimeSpan.FromSeconds(20) };
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
var createContainerParam = new PrmContainerCreate(
|
||||||
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
||||||
|
|
||||||
var createdContainer = await client.CreateContainerAsync(createContainerParam);
|
var createdContainer = await client.CreateContainerAsync(createContainerParam);
|
||||||
|
|
||||||
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx));
|
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer));
|
||||||
Assert.NotNull(container);
|
Assert.NotNull(container);
|
||||||
Assert.True(callbackInvoked);
|
Assert.True(callbackInvoked);
|
||||||
|
|
||||||
var bytes = GetRandomBytes(objectSize);
|
var bytes = GetRandomBytes(objectSize);
|
||||||
|
|
||||||
var param = new PrmObjectPut(new CallContext
|
var param = new PrmObjectPut
|
||||||
{
|
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
Header = new FrostFsObjectHeader(
|
Header = new FrostFsObjectHeader(
|
||||||
containerId: createdContainer,
|
containerId: createdContainer,
|
||||||
|
@ -330,7 +307,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void PatchTest()
|
public async void PatchTest()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -388,7 +365,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
ms.Write(chunk.Value.Span);
|
ms.Write(chunk.Value.Span);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < (int)range.Offset; i++)
|
for (int i = 0; i < (int)range.Offset; i++)
|
||||||
Assert.Equal(downloadedBytes[i], bytes[i]);
|
Assert.Equal(downloadedBytes[i], bytes[i]);
|
||||||
|
|
||||||
var rangeEnd = range.Offset + range.Length;
|
var rangeEnd = range.Offset + range.Length;
|
||||||
|
@ -410,7 +387,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void RangeTest()
|
public async void RangeTest()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -465,7 +442,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void RangeHashTest()
|
public async void RangeHashTest()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -494,13 +471,13 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
|
|
||||||
var objectId = await client.PutObjectAsync(param);
|
var objectId = await client.PutObjectAsync(param);
|
||||||
|
|
||||||
var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [ new FrostFsRange(100, 64)], bytes);
|
var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [new FrostFsRange(100, 64)], bytes);
|
||||||
|
|
||||||
var hashes = await client.GetRangeHashAsync(rangeParam);
|
var hashes = await client.GetRangeHashAsync(rangeParam);
|
||||||
|
|
||||||
foreach (var hash in hashes)
|
foreach (var hash in hashes)
|
||||||
{
|
{
|
||||||
var x = hash.Slice(0, 32).ToArray();
|
var x = hash[..32].ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
@ -517,8 +494,8 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[InlineData(6 * 1024 * 1024 + 100)]
|
[InlineData(6 * 1024 * 1024 + 100)]
|
||||||
public async void SimpleScenarioWithSessionTest(int objectSize)
|
public async void SimpleScenarioWithSessionTest(int objectSize)
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
//Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
||||||
var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue));
|
var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
@ -526,7 +503,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
{
|
{
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
Timeout = TimeSpan.FromSeconds(20),
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
var createContainerParam = new PrmContainerCreate(
|
||||||
|
@ -539,10 +516,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
|
|
||||||
var bytes = GetRandomBytes(objectSize);
|
var bytes = GetRandomBytes(objectSize);
|
||||||
|
|
||||||
var param = new PrmObjectPut(new CallContext
|
var param = new PrmObjectPut
|
||||||
{
|
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
Header = new FrostFsObjectHeader(
|
Header = new FrostFsObjectHeader(
|
||||||
containerId: container,
|
containerId: container,
|
||||||
|
@ -602,7 +576,11 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[InlineData(200)]
|
[InlineData(200)]
|
||||||
public async void ClientCutScenarioTest(int objectSize)
|
public async void ClientCutScenarioTest(int objectSize)
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
|
||||||
|
var options = GetClientOptions(this.keyString, this.url);
|
||||||
|
options.Value.Interceptors.Add(new CallbackInterceptor());
|
||||||
|
|
||||||
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetClientOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
|
@ -613,12 +591,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
|
|
||||||
var containerId = await client.CreateContainerAsync(createContainerParam);
|
var containerId = await client.CreateContainerAsync(createContainerParam);
|
||||||
|
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext { Timeout = TimeSpan.FromSeconds(10) };
|
||||||
{
|
|
||||||
Timeout = TimeSpan.FromSeconds(10)
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.Interceptors.Add(new CallbackInterceptor());
|
|
||||||
|
|
||||||
var container = await client.GetContainerAsync(new PrmContainerGet(containerId, ctx));
|
var container = await client.GetContainerAsync(new PrmContainerGet(containerId, ctx));
|
||||||
|
|
||||||
|
@ -692,23 +665,21 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void NodeInfoCallbackAndInterceptorTest()
|
public async void NodeInfoCallbackAndInterceptorTest()
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
|
||||||
|
|
||||||
bool callbackInvoked = false;
|
bool callbackInvoked = false;
|
||||||
bool intercepterInvoked = false;
|
bool intercepterInvoked = false;
|
||||||
|
|
||||||
var ctx = new CallContext
|
var options = GetClientOptions(this.keyString, this.url);
|
||||||
{
|
options.Value.Callback = (CallStatistics cs) =>
|
||||||
Callback = new((CallStatistics cs) =>
|
|
||||||
{
|
{
|
||||||
callbackInvoked = true;
|
callbackInvoked = true;
|
||||||
Assert.True(cs.ElapsedMicroSeconds > 0);
|
Assert.True(cs.ElapsedMicroSeconds > 0);
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.Interceptors.Add(new CallbackInterceptor(s => intercepterInvoked = true));
|
options.Value.Interceptors.Add(new CallbackInterceptor(s => intercepterInvoked = true));
|
||||||
|
|
||||||
var result = await client.GetNodeInfoAsync(new PrmNodeInfo(ctx));
|
using var client = FrostFSClient.GetSingleOwnerInstance(options);
|
||||||
|
|
||||||
|
var result = await client.GetNodeInfoAsync(new PrmNodeInfo());
|
||||||
|
|
||||||
Assert.True(callbackInvoked);
|
Assert.True(callbackInvoked);
|
||||||
Assert.True(intercepterInvoked);
|
Assert.True(intercepterInvoked);
|
||||||
|
@ -729,22 +700,15 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOptions<SingleOwnerClientSettings> GetSingleOwnerOptions(string key, string url)
|
private static IOptions<ClientSettings> GetClientOptions(string key, string url)
|
||||||
{
|
{
|
||||||
return Options.Create(new SingleOwnerClientSettings
|
return Options.Create(new ClientSettings
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Host = url
|
Host = url
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOptions<ClientSettings> GetOptions(string url)
|
|
||||||
{
|
|
||||||
return Options.Create(new ClientSettings
|
|
||||||
{
|
|
||||||
Host = url
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task Cleanup(IFrostFSClient client)
|
static async Task Cleanup(IFrostFSClient client)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,8 +7,6 @@ namespace FrostFS.SDK.SmokeTests;
|
||||||
|
|
||||||
public abstract class SmokeTestsBase
|
public abstract class SmokeTestsBase
|
||||||
{
|
{
|
||||||
// internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK";
|
|
||||||
|
|
||||||
internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK";
|
internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK";
|
||||||
|
|
||||||
internal readonly string url = "http://172.23.32.4:8080";
|
internal readonly string url = "http://172.23.32.4:8080";
|
||||||
|
@ -26,7 +24,5 @@ public abstract class SmokeTestsBase
|
||||||
Key = keyString.LoadWif();
|
Key = keyString.LoadWif();
|
||||||
OwnerId = FrostFsOwner.FromKey(Key);
|
OwnerId = FrostFsOwner.FromKey(Key);
|
||||||
Version = new FrostFsVersion(2, 13);
|
Version = new FrostFsVersion(2, 13);
|
||||||
|
|
||||||
Ctx = new CallContext { Key = Key, OwnerId = OwnerId, Version = Version };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Loading…
Reference in a new issue