diff --git a/.editorconfig b/.editorconfig index 7a29e50..482baac 100644 --- a/.editorconfig +++ b/.editorconfig @@ -214,7 +214,7 @@ dotnet_diagnostic.CA1700.severity = warning dotnet_diagnostic.CA1707.severity = warning # CA1708: Identifiers should differ by more than case -dotnet_diagnostic.CA1708.severity = warning +dotnet_diagnostic.CA1708.severity = none # CA1710: Identifiers should have correct suffix dotnet_diagnostic.CA1710.severity = warning @@ -232,7 +232,7 @@ dotnet_diagnostic.CA1713.severity = warning dotnet_diagnostic.CA1715.severity = warning # CA1716: Identifiers should not match keywords -dotnet_diagnostic.CA1716.severity = warning +dotnet_diagnostic.CA1716.severity = none # CA1720: Identifier contains type name dotnet_diagnostic.CA1720.severity = warning diff --git a/src/FrostFS.SDK.Client/CllientKey.cs b/src/FrostFS.SDK.Client/CllientKey.cs index cdafb3f..da02b02 100644 --- a/src/FrostFS.SDK.Client/CllientKey.cs +++ b/src/FrostFS.SDK.Client/CllientKey.cs @@ -11,4 +11,8 @@ public class ClientKey(ECDsa key) internal ECDsa ECDsaKey { get; } = key; internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey()); + + internal string PublicKey { get; } = key.PublicKey().ToString(); + + internal FrostFsOwner Owner { get; } = new FrostFsOwner(key.PublicKey().PublicKeyToAddress()); } diff --git a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj b/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj index e175d13..7f4df50 100644 --- a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj +++ b/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj @@ -14,6 +14,10 @@ true + + + true + diff --git a/src/FrostFS.SDK.Client/FrostFSClient.cs b/src/FrostFS.SDK.Client/FrostFSClient.cs index 4112a5b..e73ba49 100644 --- a/src/FrostFS.SDK.Client/FrostFSClient.cs +++ b/src/FrostFS.SDK.Client/FrostFSClient.cs @@ -4,12 +4,7 @@ using System.Net.Http; using System.Threading.Tasks; 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.Services; using FrostFS.SDK.Cryptography; @@ -21,32 +16,40 @@ using Grpc.Net.Client; 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; public class FrostFSClient : IFrostFSClient { 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; } - public static IFrostFSClient GetInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) - { - return new FrostFSClient(clientOptions, channelOptions); - } - - public static IFrostFSClient GetSingleOwnerInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) + public static IFrostFSClient GetSingleOwnerInstance(IOptions clientOptions, GrpcChannelOptions? channelOptions = null) { return new FrostFSClient(clientOptions, channelOptions); } @@ -62,28 +65,28 @@ public class FrostFSClient : IFrostFSClient /// Object.ObjectService.ObjectServiceClient implementation /// public static IFrostFSClient GetTestInstance( - IOptions clientOptions, + IOptions settings, GrpcChannelOptions? channelOptions, - NetmapService.NetmapServiceClient netmapService, - SessionService.SessionServiceClient sessionService, - ContainerService.ContainerServiceClient containerService, - ObjectService.ObjectServiceClient objectService) + NetmapServiceClient netmapService, + SessionServiceClient sessionService, + ContainerServiceClient containerService, + 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( - IOptions settings, + IOptions settings, GrpcChannelOptions? channelOptions, - ContainerService.ContainerServiceClient containerService, - NetmapService.NetmapServiceClient netmapService, - SessionService.SessionServiceClient sessionService, - ObjectService.ObjectServiceClient objectService) + ContainerServiceClient containerService, + NetmapServiceClient netmapService, + SessionServiceClient sessionService, + ObjectServiceClient objectService) { if (settings is null) { @@ -91,14 +94,18 @@ public class FrostFSClient : IFrostFSClient } var ecdsaKey = settings.Value.Key.LoadWif(); - FrostFsOwner.FromKey(ecdsaKey); ClientCtx = new ClientContext( client: this, - key: ecdsaKey, + key: new ClientKey(ecdsaKey), owner: FrostFsOwner.FromKey(ecdsaKey), 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)); NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService)); @@ -106,28 +113,9 @@ public class FrostFSClient : IFrostFSClient ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService)); } - private FrostFSClient(IOptions options, GrpcChannelOptions? channelOptions) + private FrostFSClient(IOptions settings, GrpcChannelOptions? channelOptions) { - var clientSettings = (options?.Value) ?? throw new ArgumentNullException(nameof(options), "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 options, 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(); @@ -137,10 +125,15 @@ public class FrostFSClient : IFrostFSClient ClientCtx = new ClientContext( this, - key: ecdsaKey, + key: new ClientKey(ecdsaKey), owner: FrostFsOwner.FromKey(ecdsaKey), 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 // CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) }); @@ -150,12 +143,14 @@ public class FrostFSClient : IFrostFSClient { ClientCtx = new ClientContext( client: this, - key: prm.Key, + key: new ClientKey(prm.Key), owner: FrostFsOwner.FromKey(prm.Key!), channel: InitGrpcChannel(prm.Address, null), //prm.GrpcChannelOptions), 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 - public Task AddChainAsync(PrmApeChainAdd args) + public Task> AddChainAsync(PrmApeChainAdd args) { if (args is null) { throw new ArgumentNullException(nameof(args)); } - var service = GetApeManagerService(args); + var service = GetApeManagerService(); return service.AddChainAsync(args); } @@ -193,7 +188,7 @@ public class FrostFSClient : IFrostFSClient throw new ArgumentNullException(nameof(args)); } - var service = GetApeManagerService(args); + var service = GetApeManagerService(); return service.RemoveChainAsync(args); } @@ -202,7 +197,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetApeManagerService(args); + var service = GetApeManagerService(); return service.ListChainAsync(args); } #endregion @@ -213,14 +208,14 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetContainerService(args); + var service = GetContainerService(); return service.GetContainerAsync(args); } public IAsyncEnumerable ListContainersAsync(PrmContainerGetAll? args = null) { args ??= new PrmContainerGetAll(); - var service = GetContainerService(args); + var service = GetContainerService(); return service.ListContainersAsync(args); } @@ -229,7 +224,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetContainerService(args); + var service = GetContainerService(); return service.CreateContainerAsync(args); } @@ -238,7 +233,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetContainerService(args); + var service = GetContainerService(); return service.DeleteContainerAsync(args); } #endregion @@ -247,21 +242,21 @@ public class FrostFSClient : IFrostFSClient public Task GetNetmapSnapshotAsync(PrmNetmapSnapshot? args) { args ??= new PrmNetmapSnapshot(); - var service = GetNetmapService(args); + var service = GetNetmapService(); return service.GetNetmapSnapshotAsync(args); } public Task GetNodeInfoAsync(PrmNodeInfo? args) { args ??= new PrmNodeInfo(); - var service = GetNetmapService(args); + var service = GetNetmapService(); return service.GetLocalNodeInfoAsync(args); } public Task GetNetworkSettingsAsync(PrmNetworkSettings? args) { args ??= new PrmNetworkSettings(); - var service = GetNetmapService(args); + var service = GetNetmapService(); return service.GetNetworkSettingsAsync(args.Context!); } #endregion @@ -272,7 +267,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.GetObjectHeadAsync(args); } @@ -281,7 +276,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.GetObjectAsync(args); } @@ -290,7 +285,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.GetRangeAsync(args); } @@ -299,17 +294,16 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.GetRangeHashAsync(args); } - public Task PutObjectAsync(PrmObjectPut args) { if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.PutObjectAsync(args); } @@ -318,7 +312,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.PutSingleObjectAsync(args); } @@ -329,7 +323,7 @@ public class FrostFSClient : IFrostFSClient throw new ArgumentNullException(nameof(args)); } - var service = GetObjectService(args); + var service = GetObjectService(); return service.PatchObjectAsync(args); } @@ -338,7 +332,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.DeleteObjectAsync(args); } @@ -347,7 +341,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetObjectService(args); + var service = GetObjectService(); return service.SearchObjectsAsync(args); } #endregion @@ -358,10 +352,9 @@ public class FrostFSClient : IFrostFSClient if (args is null) 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, session.Body.Id.ToUuid()); + return new FrostFsSessionToken(token); } internal Task CreateSessionInternalAsync(PrmSessionCreate args) @@ -369,7 +362,7 @@ public class FrostFSClient : IFrostFSClient if (args is null) throw new ArgumentNullException(nameof(args)); - var service = GetSessionService(args); + var service = GetSessionService(); return service.CreateSessionAsync(args); } #endregion @@ -379,18 +372,18 @@ public class FrostFSClient : IFrostFSClient { args ??= new PrmBalance(); - var service = GetAccouningService(args); + var service = GetAccouningService(); return await service.GetBallance(args).ConfigureAwait(false); } #endregion #region ToolsImplementation - public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx) + public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header) { if (header == null) throw new ArgumentNullException(nameof(header)); - return ObjectTools.CalculateObjectId(header, ctx); + return ObjectTools.CalculateObjectId(header, this.ClientCtx); } #endregion @@ -398,59 +391,34 @@ public class FrostFSClient : IFrostFSClient { var args = new PrmNodeInfo(ctx); - if (ctx?.Version == null) - throw new ArgumentNullException(nameof(ctx), "Version must be initialized"); - - var service = GetNetmapService(args); + var service = GetNetmapService(); 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."; throw new FrostFsException(msg); } } - private CallInvoker? SetupClientContext(IContext ctx) + private CallInvoker? CreateInvoker() { if (isDisposed) 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; - foreach (var interceptor in ctx.Context.Interceptors) - callInvoker = AddInvoker(callInvoker, interceptor); + if (ClientCtx.Interceptors != null) + { + foreach (var interceptor in ClientCtx.Interceptors) + callInvoker = AddInvoker(callInvoker, interceptor); + } - if (ctx.Context.Callback != null) - callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback)); + if (ClientCtx.Callback != null) + callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ClientCtx.Callback)); - if (ctx.Context.PoolErrorHandler != null) - callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ctx.Context.PoolErrorHandler)); + if (ClientCtx.PoolErrorHandler != null) + callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ClientCtx.PoolErrorHandler)); return callInvoker; @@ -465,75 +433,109 @@ public class FrostFSClient : IFrostFSClient } } - private NetmapServiceProvider GetNetmapService(IContext ctx) + private NetmapServiceProvider GetNetmapService() { - var callInvoker = SetupClientContext(ctx); - var client = NetmapServiceClient ?? (callInvoker != null - ? new NetmapService.NetmapServiceClient(callInvoker) - : new NetmapService.NetmapServiceClient(ClientCtx.Channel)); + if (NetmapServiceProvider == null) + { + var invoker = CreateInvoker(); - return new NetmapServiceProvider(client, ClientCtx); + NetmapServiceClient = NetmapServiceClient ?? ( + invoker != null + ? new NetmapServiceClient(invoker) + : new NetmapServiceClient(ClientCtx.Channel)); + + NetmapServiceProvider = new NetmapServiceProvider(NetmapServiceClient, ClientCtx); + } + + return NetmapServiceProvider; } - private SessionServiceProvider GetSessionService(IContext ctx) + private SessionServiceProvider GetSessionService() { - var callInvoker = SetupClientContext(ctx); - var client = SessionServiceClient ?? (callInvoker != null - ? new SessionService.SessionServiceClient(callInvoker) - : new SessionService.SessionServiceClient(ClientCtx.Channel)); + if (SessionServiceProvider == null) + { + var invoker = CreateInvoker(); + + SessionServiceClient = SessionServiceClient ?? ( + invoker != null + ? new SessionServiceClient(invoker) + : new SessionServiceClient(ClientCtx.Channel)); + + SessionServiceProvider = new SessionServiceProvider(SessionServiceClient, ClientCtx); + } + + return SessionServiceProvider; - return new SessionServiceProvider(client, ClientCtx); } - private ApeManagerServiceProvider GetApeManagerService(IContext ctx) + private ApeManagerServiceProvider GetApeManagerService() { - var callInvoker = SetupClientContext(ctx); - var client = ApeManagerServiceClient ?? (callInvoker != null - ? new APEManagerService.APEManagerServiceClient(callInvoker) - : new APEManagerService.APEManagerServiceClient(ClientCtx.Channel)); + if (ApeManagerServiceProvider == null) + { + var invoker = CreateInvoker(); - return new ApeManagerServiceProvider(client, ClientCtx); + ApeManagerServiceClient = ApeManagerServiceClient ?? ( + invoker != null + ? new APEManagerServiceClient(invoker) + : new APEManagerServiceClient(ClientCtx.Channel)); + + ApeManagerServiceProvider = new ApeManagerServiceProvider(ApeManagerServiceClient, ClientCtx); + } + + return ApeManagerServiceProvider; } - private AccountingServiceProvider GetAccouningService(IContext ctx) + private AccountingServiceProvider GetAccouningService() { - var callInvoker = SetupClientContext(ctx); - var client = AccountingServiceClient ?? (callInvoker != null - ? new AccountingService.AccountingServiceClient(callInvoker) - : new AccountingService.AccountingServiceClient(ClientCtx.Channel)); + if (this.AccountingServiceProvider == null) + { + var invoker = CreateInvoker(); - 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(IContext ctx) + private ContainerServiceProvider GetContainerService() { - var callInvoker = SetupClientContext(ctx); - var client = ContainerServiceClient ?? (callInvoker != null - ? new ContainerService.ContainerServiceClient(callInvoker) - : new ContainerService.ContainerServiceClient(ClientCtx.Channel)); + if (this.ContainerServiceProvider == null) + { + var invoker = CreateInvoker(); - return new ContainerServiceProvider(client, ClientCtx); + ContainerServiceClient = ContainerServiceClient ?? ( + invoker != null + ? new ContainerServiceClient(invoker) + : new ContainerServiceClient(ClientCtx.Channel)); + + ContainerServiceProvider = new ContainerServiceProvider(ContainerServiceClient, ClientCtx); + } + + return ContainerServiceProvider; } - private ObjectServiceProvider GetObjectService(IContext ctx) + private ObjectServiceProvider GetObjectService() { - var callInvoker = SetupClientContext(ctx); - var client = ObjectServiceClient ?? (callInvoker != null - ? new ObjectService.ObjectServiceClient(callInvoker) - : new ObjectService.ObjectServiceClient(ClientCtx.Channel)); + if (this.ObjectServiceProvider == null) + { + var invoker = CreateInvoker(); - return new ObjectServiceProvider(client, ClientCtx); + ObjectServiceClient = ObjectServiceClient ?? ( + invoker != null + ? new ObjectServiceClient(invoker) + : new ObjectServiceClient(ClientCtx.Channel)); + + ObjectServiceProvider = new ObjectServiceProvider(ObjectServiceClient, ClientCtx); + } + + return ObjectServiceProvider; } - private AccountingServiceProvider GetAccountService(IContext ctx) - { - var callInvoker = SetupClientContext(ctx); - var client = AccountingServiceClient ?? (callInvoker != null - ? new AccountingService.AccountingServiceClient(callInvoker) - : new AccountingService.AccountingServiceClient(ClientCtx.Channel)); - - return new AccountingServiceProvider(client, ClientCtx); - } private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions) { @@ -559,7 +561,7 @@ public class FrostFSClient : IFrostFSClient { var prm = new PrmBalance(ctx); - var service = GetAccouningService(prm); + var service = GetAccouningService(); _ = await service.GetBallance(prm).ConfigureAwait(false); return null; diff --git a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs index 456962f..8520528 100644 --- a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs @@ -21,7 +21,7 @@ public interface IFrostFSClient : IDisposable #endregion #region ApeManager - Task AddChainAsync(PrmApeChainAdd args); + Task> AddChainAsync(PrmApeChainAdd args); Task RemoveChainAsync(PrmApeChainRemove args); @@ -63,7 +63,7 @@ public interface IFrostFSClient : IDisposable #endregion #region Tools - FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx); + FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header); #endregion public Task Dial(CallContext ctx); diff --git a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs b/src/FrostFS.SDK.Client/Mappers/ContainerId.cs index fe8632f..df27320 100644 --- a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs +++ b/src/FrostFS.SDK.Client/Mappers/ContainerId.cs @@ -43,6 +43,6 @@ public static class ContainerIdMapper throw new ArgumentNullException(nameof(message)); } - return new FrostFsContainerId(Base58.Encode(message.Value.ToByteArray())); + return new FrostFsContainerId(Base58.Encode(message.Value.Span)); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs index 747468b..d7340eb 100644 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs +++ b/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs @@ -39,7 +39,7 @@ public static class NodeInfoMapper state: state, addresses: [.. nodeInfo.Addresses], attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value), - publicKey: nodeInfo.PublicKey.ToByteArray() + publicKey: nodeInfo.PublicKey.Memory ); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs b/src/FrostFS.SDK.Client/Mappers/Object/Object.cs index b9d1520..5f85fc5 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/Object.cs @@ -6,7 +6,7 @@ internal static class ObjectMapper { return new FrostFsObject(obj.Header.ToModel()) { - ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray()) + ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span) }; } } diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs index 1ee29a6..317b31c 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs @@ -40,7 +40,7 @@ public static class ObjectHeaderMapper } var model = new FrostFsObjectHeader( - new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())), + new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)), objTypeName, header.Attributes.Select(attribute => attribute.ToModel()).ToArray(), split, diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs index 373edfb..343e3de 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs @@ -28,6 +28,6 @@ public static class ObjectIdMapper throw new ArgumentNullException(nameof(objectId)); } - return FrostFsObjectId.FromHash(objectId.Value.ToByteArray()); + return FrostFsObjectId.FromHash(objectId.Value.Span); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs b/src/FrostFS.SDK.Client/Mappers/OwnerId.cs index 297178a..6739a0b 100644 --- a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs +++ b/src/FrostFS.SDK.Client/Mappers/OwnerId.cs @@ -44,7 +44,7 @@ public static class OwnerIdMapper 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); } diff --git a/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs b/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs index 6ebec1f..4aa00f9 100644 --- a/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs +++ b/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs @@ -3,6 +3,8 @@ using System.Collections.ObjectModel; using System.Globalization; using System.Text; +using Grpc.Core.Interceptors; + namespace FrostFS.SDK; public class ClientSettings @@ -11,60 +13,25 @@ public class ClientSettings public string Host { get; set; } = string.Empty; - public virtual void Validate() - { - var errors = CheckFields(); - if (errors != null) - ThrowSettingsException(errors); - } - - protected Collection? CheckFields() - { - if (string.IsNullOrWhiteSpace(Host)) - { - var error = string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host)); - return new Collection([error]); - } - - return null; - } - - protected static void ThrowSettingsException(Collection 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 override void Validate() - { - var errors = CheckFields(); - if (errors != null) - ThrowSettingsException(errors); - } + public Action? Callback { get; set; } - protected new Collection? CheckFields() + public Collection Interceptors { get; } = []; + + public void Validate() { - Collection? 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)) - (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()); + } } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs index f0b3fdf..9f081cd 100644 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs +++ b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs @@ -27,7 +27,7 @@ public class FrostFsContainerId if (containerID != null) { - this.modelId = Base58.Encode(containerID.Value.ToByteArray()); + this.modelId = Base58.Encode(containerID.Value.Span); return this.modelId; } diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs index 6223a7f..af4ffdc 100644 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs +++ b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs @@ -96,7 +96,7 @@ public class FrostFsContainerInfo PlacementPolicy = PlacementPolicy.Value.GetPolicy(), Nonce = ByteString.CopyFrom(Nonce.ToBytes()), OwnerId = Owner?.OwnerID, - Version = Version?.Version + Version = Version?.VersionID }; var attribs = GetGrpsAttributes(); diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs index 6fd0afc..81e642d 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs @@ -10,7 +10,7 @@ public class FrostFsVersion(int major, int minor) public int Major { get; set; } = major; public int Minor { get; set; } = minor; - internal Version Version + internal Version VersionID { get { diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs index 49fde5a..57300a3 100644 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs +++ b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs @@ -8,13 +8,8 @@ public class FrostFsObjectId(string id) { public string Value { get; } = id; - public static FrostFsObjectId FromHash(byte[] hash) + public static FrostFsObjectId FromHash(ReadOnlySpan hash) { - if (hash is null) - { - throw new ArgumentNullException(nameof(hash)); - } - if (hash.Length != Constants.Sha256HashLength) throw new FormatException("ObjectID must be a sha256 hash."); diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs index 1bd6754..228edf2 100644 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs +++ b/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs @@ -4,6 +4,7 @@ using FrostFS.Refs; using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.SDK.Cryptography; + namespace FrostFS.SDK; public class FrostFsOwner(string id) diff --git a/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs b/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs index 672754d..54f0615 100644 --- a/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs +++ b/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs @@ -1,9 +1,117 @@ using System; +using FrostFS.Refs; + +using FrostFS.SDK.Client; +using FrostFS.SDK.Cryptography; +using FrostFS.Session; + +using Google.Protobuf; + namespace FrostFS.SDK; -public class FrostFsSessionToken(byte[] token, Guid id) +public class FrostFsSessionToken { - public Guid Id { get; private set; } = id; - public byte[] Token { get; private set; } = token; + private Guid _id; + private ReadOnlyMemory _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 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; + } } diff --git a/src/FrostFS.SDK.Client/Parameters/CallContext.cs b/src/FrostFS.SDK.Client/Parameters/CallContext.cs index 323a990..05e061c 100644 --- a/src/FrostFS.SDK.Client/Parameters/CallContext.cs +++ b/src/FrostFS.SDK.Client/Parameters/CallContext.cs @@ -1,27 +1,16 @@ using System; -using System.Collections.ObjectModel; -using System.Security.Cryptography; using System.Threading; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -using Grpc.Core.Interceptors; - namespace FrostFS.SDK.Client; public class CallContext() { - private ByteString? publicKeyCache; - internal Action? PoolErrorHandler { get; set; } + // internal Action? 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; } @@ -29,17 +18,7 @@ public class CallContext() public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; - public Action? Callback { get; set; } + // public Action? Callback { get; set; } - public Collection Interceptors { get; } = []; - - public ByteString? GetPublicKeyCache() - { - if (publicKeyCache == null && Key != null) - { - publicKeyCache = ByteString.CopyFrom(Key.PublicKey()); - } - - return publicKeyCache; - } + // public Collection Interceptors { get; } = []; } diff --git a/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs b/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs index 7e50337..de984d6 100644 --- a/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs +++ b/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs @@ -9,10 +9,8 @@ namespace FrostFS.SDK.Client; public class ClientWrapper : ClientStatusMonitor { private readonly object _lock = new(); - private SessionCache sessionCache; - internal ClientWrapper(WrapperPrm wrapperPrm, Pool pool) : base(wrapperPrm.Logger, wrapperPrm.Address) { WrapperPrm = wrapperPrm; @@ -140,6 +138,7 @@ public class ClientWrapper : ClientStatusMonitor catch (FrostFsException) { SetUnhealthy(); + return wasHealthy; } diff --git a/src/FrostFS.SDK.Client/Pool/InitParameters.cs b/src/FrostFS.SDK.Client/Pool/InitParameters.cs index 480820e..3f4d14e 100644 --- a/src/FrostFS.SDK.Client/Pool/InitParameters.cs +++ b/src/FrostFS.SDK.Client/Pool/InitParameters.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.ObjectModel; using System.Security.Cryptography; +using Grpc.Core.Interceptors; using Grpc.Net.Client; using Microsoft.Extensions.Logging; @@ -33,4 +35,8 @@ public class InitParameters public ulong GracefulCloseOnSwitchTimeout { get; set; } public ILogger? Logger { get; set; } + + public Action? Callback { get; set; } + + public Collection Interceptors { get; } = []; } diff --git a/src/FrostFS.SDK.Client/Pool/Pool.cs b/src/FrostFS.SDK.Client/Pool/Pool.cs index 105978d..acb60b9 100644 --- a/src/FrostFS.SDK.Client/Pool/Pool.cs +++ b/src/FrostFS.SDK.Client/Pool/Pool.cs @@ -35,18 +35,17 @@ public partial class Pool : IFrostFSClient private InnerPool[]? InnerPools { get; set; } - private ECDsa Key { get; set; } - - private string PublicKey { get; } + private ClientKey Key { get; set; } private OwnerID? _ownerId; + private FrostFsOwner? _owner; private FrostFsOwner Owner { get { - _owner ??= new FrostFsOwner(Key.PublicKey().PublicKeyToAddress()); + _owner ??= new FrostFsOwner(Key.ECDsaKey.PublicKey().PublicKeyToAddress()); return _owner; } } @@ -57,7 +56,7 @@ public partial class Pool : IFrostFSClient { if (_ownerId == null) { - _owner = new FrostFsOwner(Key.PublicKey().PublicKeyToAddress()); + _owner = Key.Owner; _ownerId = _owner.ToMessage(); } return _ownerId; @@ -101,8 +100,7 @@ public partial class Pool : IFrostFSClient FillDefaultInitParams(options, this); - Key = options.Key; - PublicKey = $"{Key.PublicKey()}"; + Key = new ClientKey(options.Key); SessionCache = cache; logger = options.Logger; @@ -115,16 +113,9 @@ public partial class Pool : IFrostFSClient options.SessionExpirationDuration); ClientBuilder = options.ClientBuilder!; - } - private void SetupContext(CallContext ctx) - { - if (ctx == null) - { - throw new ArgumentNullException(nameof(ctx)); - } + // ClientContext.PoolErrorHandler = client.HandleError; - ctx.Key ??= Key; } // Dial establishes a connection to the servers from the FrostFS network. @@ -137,8 +128,6 @@ public partial class Pool : IFrostFSClient // See also InitParameters.SetClientRebalanceInterval. public async Task Dial(CallContext ctx) { - SetupContext(ctx); - var inner = new InnerPool[RebalanceParams.NodesParams.Length]; bool atLeastOneHealthy = false; @@ -158,11 +147,11 @@ public partial class Pool : IFrostFSClient await client.Dial(ctx).ConfigureAwait(false); 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); - var key = FormCacheKey(nodeParams.Addresses[j], Key.PrivateKey().ToString()); - _ = SessionCache.Cache[key] = token; + var key = FormCacheKey(nodeParams.Addresses[j], Key.PublicKey); + SessionCache.SetValue(key, token); atLeastOneHealthy = true; } @@ -291,12 +280,14 @@ public partial class Pool : IFrostFSClient var wrapperPrm = new WrapperPrm { Address = address, - Key = parameters.Key, + Key = parameters.Key!, Logger = parameters.Logger, DialTimeout = parameters.NodeDialTimeout, StreamTimeout = parameters.NodeStreamTimeout, ErrorThreshold = parameters.ErrorThreshold, - GracefulCloseOnSwitchTimeout = parameters.GracefulCloseOnSwitchTimeout + GracefulCloseOnSwitchTimeout = parameters.GracefulCloseOnSwitchTimeout, + Callback = parameters.Callback, + Interceptors = parameters.Interceptors }; return new ClientWrapper(wrapperPrm, pool); @@ -318,7 +309,7 @@ public partial class Pool : IFrostFSClient throw new FrostFsException("Cannot find alive client"); } - private static async Task InitSessionForDuration(CallContext ctx, ClientWrapper cw, ulong duration, ECDsa key, bool clientCut) + private static async Task InitSessionForDuration(CallContext ctx, ClientWrapper cw, ulong duration, ECDsa key, bool clientCut) { var client = cw.Client; var networkInfo = await client!.GetNetworkSettingsAsync(new PrmNetworkSettings(ctx)).ConfigureAwait(false); @@ -521,7 +512,6 @@ public partial class Pool : IFrostFSClient var client = Connection(); args ??= new(); - args.Context.PoolErrorHandler = client.HandleError; return await client.Client!.GetNetmapSnapshotAsync(args).ConfigureAwait(false); } @@ -529,278 +519,126 @@ public partial class Pool : IFrostFSClient public async Task GetNodeInfoAsync(PrmNodeInfo? args = null) { var client = Connection(); - - args ??= new(); - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetNodeInfoAsync(args).ConfigureAwait(false); } public async Task GetNetworkSettingsAsync(PrmNetworkSettings? args = null) { var client = Connection(); - - args ??= new(); - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetNetworkSettingsAsync(args).ConfigureAwait(false); } public async Task CreateSessionAsync(PrmSessionCreate args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false); } - public async Task AddChainAsync(PrmApeChainAdd args) + public async Task> AddChainAsync(PrmApeChainAdd args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.AddChainAsync(args).ConfigureAwait(false); } public async Task RemoveChainAsync(PrmApeChainRemove args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - await client.Client!.RemoveChainAsync(args).ConfigureAwait(false); } public async Task ListChainAsync(PrmApeChainList args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.ListChainAsync(args).ConfigureAwait(false); } public async Task GetContainerAsync(PrmContainerGet args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetContainerAsync(args).ConfigureAwait(false); } public IAsyncEnumerable ListContainersAsync(PrmContainerGetAll? args = null) { var client = Connection(); - - args ??= new(); - args.Context.PoolErrorHandler = client.HandleError; - return client.Client!.ListContainersAsync(args); } public async Task CreateContainerAsync(PrmContainerCreate args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.CreateContainerAsync(args).ConfigureAwait(false); } public async Task DeleteContainerAsync(PrmContainerDelete args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - await client.Client!.DeleteContainerAsync(args).ConfigureAwait(false); } public async Task GetObjectHeadAsync(PrmObjectHeadGet args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetObjectHeadAsync(args).ConfigureAwait(false); } public async Task GetObjectAsync(PrmObjectGet args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetObjectAsync(args).ConfigureAwait(false); } public async Task PutObjectAsync(PrmObjectPut args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.PutObjectAsync(args).ConfigureAwait(false); } public async Task PutSingleObjectAsync(PrmSingleObjectPut args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.PutSingleObjectAsync(args).ConfigureAwait(false); } public async Task PatchObjectAsync(PrmObjectPatch args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false); } public async Task GetRangeAsync(PrmRangeGet args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetRangeAsync(args).ConfigureAwait(false); } public async Task[]> GetRangeHashAsync(PrmRangeHashGet args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetRangeHashAsync(args).ConfigureAwait(false); } public async Task PatchAsync(PrmObjectPatch args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.PatchObjectAsync(args).ConfigureAwait(false); } public async Task DeleteObjectAsync(PrmObjectDelete args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - await client.Client!.DeleteObjectAsync(args).ConfigureAwait(false); } public IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args) { - if (args is null) - { - throw new ArgumentNullException(nameof(args)); - } - var client = Connection(); - - args.Context.PoolErrorHandler = client.HandleError; - return client.Client!.SearchObjectsAsync(args); } public async Task GetBalanceAsync(PrmBalance? args) { var client = Connection(); - - args ??= new(); - args.Context.PoolErrorHandler = client.HandleError; - return await client.Client!.GetBalanceAsync(args).ConfigureAwait(false); } @@ -824,7 +662,7 @@ public partial class Pool : IFrostFSClient GC.SuppressFinalize(this); } - public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header, CallContext ctx) + public FrostFsObjectId CalculateObjectId(FrostFsObjectHeader header) { throw new NotImplementedException(); } diff --git a/src/FrostFS.SDK.Client/Pool/SessionCache.cs b/src/FrostFS.SDK.Client/Pool/SessionCache.cs index 829b1c9..fae3de1 100644 --- a/src/FrostFS.SDK.Client/Pool/SessionCache.cs +++ b/src/FrostFS.SDK.Client/Pool/SessionCache.cs @@ -1,23 +1,49 @@ using System; -using System.Collections; +using System.Collections.Concurrent; namespace FrostFS.SDK.Client; -internal struct SessionCache(ulong sessionExpirationDuration) +internal sealed class SessionCache(ulong sessionExpirationDuration) { - internal Hashtable Cache { get; } = []; + private ConcurrentDictionary _cache { get; } = []; internal ulong CurrentEpoch { get; set; } 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) { - 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 _); } } } diff --git a/src/FrostFS.SDK.Client/Pool/WrapperPrm.cs b/src/FrostFS.SDK.Client/Pool/WrapperPrm.cs index 3c23505..b68b6ee 100644 --- a/src/FrostFS.SDK.Client/Pool/WrapperPrm.cs +++ b/src/FrostFS.SDK.Client/Pool/WrapperPrm.cs @@ -1,13 +1,14 @@ using System; +using System.Collections.ObjectModel; using System.Security.Cryptography; +using Grpc.Core.Interceptors; using Grpc.Net.Client; using Microsoft.Extensions.Logging; 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 = "")] public struct WrapperPrm { @@ -15,7 +16,7 @@ public struct WrapperPrm internal string Address { get; set; } - internal ECDsa? Key { get; set; } + internal ECDsa Key { get; set; } internal ulong DialTimeout { get; set; } @@ -30,5 +31,9 @@ public struct WrapperPrm internal GrpcChannelOptions GrpcChannelOptions { get; set; } internal ulong GracefulCloseOnSwitchTimeout { get; set; } + + internal Action? Callback { get; set; } + + internal Collection? Interceptors { get; set; } } diff --git a/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs b/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs index ba8ba2a..bdb8e61 100644 --- a/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs @@ -24,12 +24,12 @@ internal sealed class AccountingServiceProvider : ContextAccessor { Body = new() { - OwnerId = ctx.OwnerId!.OwnerID + OwnerId = ClientContext.Owner.OwnerID } }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key!); + request.Sign(ClientContext.Key.ECDsaKey); var response = await _accountingServiceClient!.BalanceAsync(request, null, ctx.Deadline, ctx.CancellationToken); diff --git a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs index 840223d..1c22cdc 100644 --- a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs @@ -16,13 +16,9 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor _apeManagerServiceClient = apeManagerServiceClient; } - internal async Task AddChainAsync(PrmApeChainAdd args) + internal async Task> AddChainAsync(PrmApeChainAdd args) { var ctx = args.Context!; - ctx.Key ??= ClientContext.Key?.ECDsaKey; - - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); AddChainRequest request = new() { @@ -34,22 +30,18 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await _apeManagerServiceClient!.AddChainAsync(request, null, ctx.Deadline, ctx.CancellationToken); Verifier.CheckResponse(response); - return response.Body.ChainId.ToByteArray(); + return response.Body.ChainId.Memory; } internal async Task RemoveChainAsync(PrmApeChainRemove args) { var ctx = args.Context!; - ctx.Key ??= ClientContext.Key?.ECDsaKey; - - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); RemoveChainRequest request = new() { @@ -61,7 +53,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await _apeManagerServiceClient!.RemoveChainAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -71,10 +63,6 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor internal async Task ListChainAsync(PrmApeChainList args) { var ctx = args.Context!; - ctx.Key ??= ClientContext.Key?.ECDsaKey; - - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); ListChainsRequest request = new() { @@ -85,7 +73,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await _apeManagerServiceClient!.ListChainsAsync(request, null, ctx.Deadline, ctx.CancellationToken); diff --git a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs index 3a0379e..fcdc0e5 100644 --- a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Security.Cryptography; using System.Threading.Tasks; using FrostFS.Container; @@ -12,26 +13,34 @@ using FrostFS.Session; 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; - public async ValueTask GetOrCreateSession(ISessionToken args, CallContext ctx) + public async ValueTask GetDefaultSession(ISessionToken args, CallContext ctx) { sessions ??= new(ClientContext); - if (ClientContext.SessionCache.Cache != null && - ClientContext.SessionCache.Cache.ContainsKey(ClientContext.SessionCacheKey)) + if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token)) { - 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 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); @@ -43,24 +52,17 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService internal async IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args) { 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 { Body = new() { - OwnerId = ctx.OwnerId.ToMessage() + OwnerId = ClientContext.Owner.OwnerID } }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); 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) { - 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(); - grpcContainer.OwnerId ??= ctx.OwnerId?.ToMessage(); - grpcContainer.Version ??= ctx.Version?.ToMessage(); - - 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)); + grpcContainer.OwnerId ??= ClientContext.Owner.OwnerID; + grpcContainer.Version ??= ClientContext.Version.VersionID; var request = new PutRequest { Body = new PutRequest.Types.Body { 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, ContainerSessionContext.Types.Verb.Put, - ctx.Key, - ctx.GetPublicKeyCache()!); + ClientContext.Key); - 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); @@ -121,29 +115,26 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService internal async Task DeleteContainerAsync(PrmContainerDelete args) { var ctx = args.Context!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); var request = new DeleteRequest { Body = new DeleteRequest.Types.Body { 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, ContainerSessionContext.Types.Verb.Delete, - ctx.Key, - ctx.GetPublicKeyCache()!); + ClientContext.Key); - 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); @@ -155,11 +146,8 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService 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 { Body = new GetRequest.Types.Body @@ -169,7 +157,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService }; request.AddMetaHeader(xHeaders); - request.Sign(ctx.Key); + request.Sign(key); return request; } @@ -182,7 +170,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService 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() { diff --git a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs index 18649d8..570bd24 100644 --- a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Text; using System.Threading.Tasks; using FrostFS.Netmap; @@ -47,10 +45,6 @@ internal sealed class NetmapServiceProvider : ContextAccessor internal async Task GetLocalNodeInfoAsync(PrmNodeInfo args) { 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 { @@ -58,7 +52,7 @@ internal sealed class NetmapServiceProvider : ContextAccessor }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -69,15 +63,10 @@ internal sealed class NetmapServiceProvider : ContextAccessor internal async Task GetNetworkInfoAsync(CallContext ctx) { - ctx.Key ??= ClientContext.Key?.ECDsaKey; - - if (ctx.Key == null) - throw new ArgumentNullException(nameof(ctx), "Key is null"); - var request = new NetworkInfoRequest(); request.AddMetaHeader(null); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken) .ConfigureAwait(false); @@ -90,15 +79,11 @@ internal sealed class NetmapServiceProvider : ContextAccessor internal async Task GetNetmapSnapshotAsync(PrmNetmapSnapshot args) { 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(); request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key); + request.Sign(ClientContext.Key.ECDsaKey); var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken); @@ -107,12 +92,16 @@ internal sealed class NetmapServiceProvider : ContextAccessor return response.ToModel(); } - private static bool GetBoolValue(byte[] bytes) + private static bool GetBoolValue(ReadOnlySpan 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 bytes) { ulong val = 0; 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) { - 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) { - case "AuditFee": settings.AuditFee = GetLongValue(valueBytes); break; - case "BasicIncomeRate": settings.BasicIncomeRate = GetLongValue(valueBytes); break; - case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break; - case "ContainerAliasFee": settings.ContainerAliasFee = GetLongValue(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); break; + case "AuditFee": + settings.AuditFee = GetLongValue(valueBytes); + break; + case "BasicIncomeRate": + settings.BasicIncomeRate = GetLongValue(valueBytes); + break; + case "ContainerFee": + settings.ContainerFee = GetLongValue(valueBytes); + break; + case "ContainerAliasFee": + settings.ContainerAliasFee = GetLongValue(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; } } } diff --git a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs index 7737c62..d3c4347 100644 --- a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs @@ -17,31 +17,35 @@ using Google.Protobuf; namespace FrostFS.SDK.Client; internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientContext clientCtx) - : ContextAccessor(clientCtx), ISessionProvider + : ContextAccessor(clientCtx) { private SessionProvider? sessions; private readonly ObjectService.ObjectServiceClient client = client; - public async ValueTask GetOrCreateSession(ISessionToken args, CallContext ctx) + public async ValueTask GetDefaultSession(ISessionToken args, CallContext ctx) { sessions ??= new(ClientContext); - if (ClientContext.SessionCache.Cache != null && - ClientContext.SessionCache.Cache.ContainsKey(ClientContext.SessionCacheKey)) + if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token)) { - 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 GetObjectHeadAsync(PrmObjectHeadGet args) { 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 { @@ -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, 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); @@ -77,11 +81,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { 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 { 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, 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); } @@ -112,11 +111,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { 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 { 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, 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); return new RangeReader(call); @@ -154,11 +148,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { 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 { 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, 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); @@ -206,10 +195,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl internal async Task DeleteObjectAsync(PrmObjectDelete args) { 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 { @@ -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, ObjectSessionContext.Types.Verb.Delete, - ctx.Key); + ClientContext.Key); - request.AddMetaHeader(args.XHeaders, sessionToken); - request.Sign(ctx.Key); + request.AddMetaHeader(args.XHeaders, protoToken); + request.Sign(ClientContext.Key.ECDsaKey); 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!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); - var request = new SearchRequest { 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())); - 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 }, 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; else if (args.Payload.CanSeek) 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); @@ -307,39 +299,34 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { var ctx = args.Context!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); - - var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ctx); + var grpcObject = ObjectTools.CreateObject(args.FrostFsObject, ClientContext); var request = new PutSingleRequest { 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 }, 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); Verifier.CheckResponse(response); - return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray()); + return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.Span); } internal async Task PatchObjectAsync(PrmObjectPatch args) { var ctx = args.Context!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); var chunkSize = args.MaxPayloadPatchChunkLength; 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 { // common - chunkBuffer = ClientContext.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize); + chunkBuffer = ArrayPool.Shared.Rent(chunkSize); var address = new Address { @@ -358,13 +345,12 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl 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, ObjectSessionContext.Types.Verb.Patch, - ctx.Key - ); + ClientContext.Key); var request = new PatchRequest() { @@ -403,9 +389,9 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl 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); @@ -433,20 +419,17 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { var ctx = args.Context!; - var tokenRaw = await GetOrCreateSession(args, ctx).ConfigureAwait(false); - var token = new FrostFsSessionToken(tokenRaw.Serialize(), tokenRaw.Body.Id.ToUuid()); - - args.SessionToken = token; + args.SessionToken ??= await GetDefaultSession(args, ctx).ConfigureAwait(false); var payloadStream = args.Payload!; var header = args.Header!; - var fullLength = header.PayloadLength; - - if (payloadStream.CanSeek && fullLength == 0) - fullLength = (ulong)payloadStream.Length; - - args.FullLength = fullLength; + if (header.PayloadLength > 0) + args.FullLength = header.PayloadLength; + else if (payloadStream.CanSeek) + args.FullLength = (ulong)payloadStream.Length; + else + throw new ArgumentException("The stream does not have a length and payload length is not defined"); if (args.MaxObjectSizeCache == 0) { @@ -456,12 +439,12 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl 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; //define collection capacity 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 sentObjectIds = new(objectsCount); @@ -491,7 +474,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { var largeObjectHeader = new FrostFsObjectHeader(header.ContainerId, FrostFsObjectType.Regular, [.. attributes]) { - PayloadLength = fullLength, + PayloadLength = args.FullLength, }; args.Header.Split!.ParentHeader = largeObjectHeader; @@ -526,8 +509,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl private async Task PutStreamObject(PrmObjectPut args) { var ctx = args.Context!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); var payload = args.Payload!; @@ -542,21 +523,26 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl 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.Length < chunkSize) + { + throw new ArgumentException($"Buffer size is too small. At least {chunkSize} required"); + } + chunkBuffer = args.CustomBuffer; } else { - chunkBuffer = ClientContext.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize); + chunkBuffer = ArrayPool.Shared.Rent(chunkSize); isRentBuffer = true; } 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); 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); } @@ -589,7 +575,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl var response = await stream.Close().ConfigureAwait(false); 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 { @@ -604,17 +590,14 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { var header = args.Header!; - if (ctx.Key == null) - throw new ArgumentNullException(nameof(args), "Key is null"); - - header.OwnerId ??= ctx.OwnerId; - header.Version ??= ctx.Version; + header.OwnerId ??= ClientContext.Owner; + header.Version ??= ClientContext.Version; var grpcHeader = header.GetHeader(); if (header.Split != null) { - ObjectTools.SetSplitValues(grpcHeader, header.Split, ctx); + ObjectTools.SetSplitValues(grpcHeader, header.Split, ClientContext); } 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 }, 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); } @@ -681,24 +663,6 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl return new ObjectReader(call); } - private async IAsyncEnumerable 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) { if (initRequest is null) diff --git a/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs b/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs index e9b5963..859d836 100644 --- a/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs @@ -1,7 +1,5 @@ using System.Threading.Tasks; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.Session; namespace FrostFS.SDK.Client; @@ -20,19 +18,17 @@ internal sealed class SessionServiceProvider : ContextAccessor { var ctx = args.Context!; - ctx.OwnerId ??= ClientContext.Owner; - var request = new CreateRequest { Body = new CreateRequest.Types.Body { - OwnerId = ctx.OwnerId!.ToMessage(), + OwnerId = ClientContext.Owner.OwnerID, Expiration = args.Expiration } }; request.AddMetaHeader(args.XHeaders); - request.Sign(ctx.Key!); + request.Sign(ClientContext.Key.ECDsaKey); return await CreateSession(request, args.Context!).ConfigureAwait(false); } diff --git a/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs b/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs index a8d80ac..e8ff793 100644 --- a/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs +++ b/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs @@ -9,14 +9,15 @@ internal interface ISessionProvider internal sealed class SessionProvider(ClientContext envCtx) { - public async ValueTask GetOrCreateSession(ISessionToken args, CallContext ctx) + public async Task CreateSession(ISessionToken args, CallContext ctx) { - if (args.SessionToken is null) - { - return await envCtx.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx)) - .ConfigureAwait(false); - } + var token = await GetDefaultSession(args, ctx).ConfigureAwait(false); - return new Session.SessionToken().Deserialize(args.SessionToken.Token); + return new FrostFsSessionToken(token); + } + + internal async Task GetDefaultSession(ISessionToken args, CallContext ctx) + { + return await envCtx.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx)).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Tools/ClientContext.cs b/src/FrostFS.SDK.Client/Tools/ClientContext.cs index 5f7c72e..133ad3f 100644 --- a/src/FrostFS.SDK.Client/Tools/ClientContext.cs +++ b/src/FrostFS.SDK.Client/Tools/ClientContext.cs @@ -1,19 +1,16 @@ using System; -using System.Buffers; -using System.Security.Cryptography; - -using FrostFS.SDK.Cryptography; +using System.Collections.ObjectModel; +using Grpc.Core.Interceptors; using Grpc.Net.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? _arrayPool; private string? sessionKey; - internal FrostFsOwner? Owner { get; } = owner; + internal FrostFsOwner Owner { get; } = owner; 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 ClientKey? Key { get; } = key != null ? new ClientKey(key) : null; + internal ClientKey Key { get; } = key; + + internal SessionCache? SessionCache { get; set; } + + internal Action? Callback { get; set; } + + internal Collection? Interceptors { get; set; } + + internal Action? PoolErrorHandler { get; set; } - internal SessionCache SessionCache { get; set; } internal string? SessionCacheKey { @@ -35,23 +39,13 @@ public class ClientContext(FrostFSClient client, ECDsa? key, FrostFsOwner? owner { if (sessionKey == null && Key != null && Address != null) { - sessionKey = Pool.FormCacheKey(Address, Key.ECDsaKey.PrivateKey().ToString()); + sessionKey = Pool.FormCacheKey(Address, Key.PublicKey); } return sessionKey; } } - /// - /// Custom pool is used for predefined sizes of buffers like grpc chunk - /// - internal ArrayPool GetArrayPool(int size) - { - _arrayPool ??= ArrayPool.Create(size, 256); - - return _arrayPool; - } - public void Dispose() { Dispose(true); diff --git a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs b/src/FrostFS.SDK.Client/Tools/ObjectTools.cs index f20cacd..eeb06c0 100644 --- a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs +++ b/src/FrostFS.SDK.Client/Tools/ObjectTools.cs @@ -11,7 +11,7 @@ namespace FrostFS.SDK.Client; 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); @@ -21,9 +21,9 @@ internal static class ObjectTools 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; var grpcHeader = @object.Header.GetHeader(); @@ -46,14 +46,14 @@ internal static class ObjectTools obj.Signature = new Signature { - Key = ctx.GetPublicKeyCache(), - Sign = ByteString.CopyFrom(ctx.Key!.SignData(obj.ObjectId.ToByteArray())), + Key = ctx.Key.PublicKeyProto, + Sign = ctx.Key.ECDsaKey.SignData(obj.ObjectId.ToByteArray()), }; 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) return; @@ -77,17 +77,17 @@ internal static class ObjectTools grpcHeader.Split.ParentHeader = grpcParentHeader; grpcHeader.Split.ParentSignature = new Signature { - Key = ctx.GetPublicKeyCache(), - Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())), + Key = ctx.Key.PublicKeyProto, + Sign = ctx.Key.ECDsaKey.SignData(grpcHeader.Split.Parent.ToByteArray()), }; } 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; var grpcHeader = header.GetHeader(); diff --git a/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs b/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs index 65d9e35..64aecc6 100644 --- a/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs +++ b/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Specialized; using System.Linq; -using System.Security.Cryptography; -using FrostFS.Refs; using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; using FrostFS.SDK.Proto.Interfaces; using FrostFS.Session; @@ -33,59 +30,4 @@ public static class RequestConstructor xHeaders.Cast().SelectMany(key => xHeaders.GetValues(key), (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); - } } diff --git a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs b/src/FrostFS.SDK.Client/Tools/RequestSigner.cs index a45c569..f747b3f 100644 --- a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs +++ b/src/FrostFS.SDK.Client/Tools/RequestSigner.cs @@ -14,24 +14,21 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; +using Signature = FrostFS.Refs.Signature; + namespace FrostFS.SDK.Client; public static class RequestSigner { 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) { throw new ArgumentNullException(nameof(key)); } - if (data is null) - { - throw new ArgumentNullException(nameof(data)); - } - var digest = new Sha256Digest(); var secp256R1 = SecNamedCurves.GetByName("secp256r1"); var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); @@ -44,24 +41,27 @@ public static class RequestSigner signer.Init(true, privateKey); var rs = signer.GenerateSignature(hash); - var signature = new byte[RFC6979SignatureSize]; + + Span signature = stackalloc byte[RFC6979SignatureSize]; + var rbytes = rs[0].ToByteArrayUnsigned(); var sbytes = rs[1].ToByteArrayUnsigned(); var index = RFC6979SignatureSize / 2 - rbytes.Length; - rbytes.CopyTo(signature, index); + rbytes.AsSpan().CopyTo(signature[index..]); 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) { return new SignatureRFC6979 { 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 { 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) { throw new ArgumentNullException(nameof(key)); } - var hash = new byte[65]; - hash[0] = 0x04; + Span result = stackalloc byte[65]; + 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) @@ -95,7 +98,7 @@ public static class RequestSigner var sig = new Signature { Key = ByteString.CopyFrom(key.PublicKey()), - Sign = ByteString.CopyFrom(key.SignData(data2Sign)), + Sign = key.SignData(data2Sign), }; return sig; diff --git a/src/FrostFS.SDK.Client/Tools/SearchReader.cs b/src/FrostFS.SDK.Client/Tools/SearchReader.cs index c2e7607..ba9b1d1 100644 --- a/src/FrostFS.SDK.Client/Tools/SearchReader.cs +++ b/src/FrostFS.SDK.Client/Tools/SearchReader.cs @@ -1,21 +1,21 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using FrostFS.Object; using FrostFS.Refs; +using Google.Protobuf.Collections; + using Grpc.Core; namespace FrostFS.SDK.Client; internal sealed class SearchReader(AsyncServerStreamingCall call) : IDisposable { - public AsyncServerStreamingCall Call { get; private set; } = call; + internal AsyncServerStreamingCall Call { get; private set; } = call; - public async Task?> Read(CancellationToken cancellationToken) + internal async Task?> Read(CancellationToken cancellationToken) { if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) return null; @@ -24,7 +24,7 @@ internal sealed class SearchReader(AsyncServerStreamingCall call Verifier.CheckResponse(response); - return response.Body?.IdList.ToList(); + return response.Body?.IdList; } public void Dispose() diff --git a/src/FrostFS.SDK.Client/Tools/Verifier.cs b/src/FrostFS.SDK.Client/Tools/Verifier.cs index bc8dd31..ce215ba 100644 --- a/src/FrostFS.SDK.Client/Tools/Verifier.cs +++ b/src/FrostFS.SDK.Client/Tools/Verifier.cs @@ -88,6 +88,7 @@ public static class Verifier return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); } + internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) { if (!verification.MetaSignature.VerifyMessagePart(meta)) diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index 148ae78..604fe7c 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -7,12 +7,12 @@ - true + true - + - true - + true + diff --git a/src/FrostFS.SDK.Cryptography/UUID.cs b/src/FrostFS.SDK.Cryptography/UUID.cs index c92a874..94fba3f 100644 --- a/src/FrostFS.SDK.Cryptography/UUID.cs +++ b/src/FrostFS.SDK.Cryptography/UUID.cs @@ -11,9 +11,7 @@ public static class UUIDExtension if (id == null) throw new ArgumentNullException(nameof(id)); - var bytes = id.ToByteArray(); - - var orderedBytes = GetGuidBytesDirectOrder(bytes); + var orderedBytes = GetGuidBytesDirectOrder(id.Span); return new Guid(orderedBytes); } @@ -32,7 +30,7 @@ public static class UUIDExtension return orderedBytes; } - private static byte[] GetGuidBytesDirectOrder(byte[] source) + private static byte[] GetGuidBytesDirectOrder(ReadOnlySpan source) { if (source.Length != 16) throw new ArgumentException("Wrong uuid binary format"); diff --git a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj b/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj index 0bf48d5..8f69fe0 100644 --- a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj +++ b/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj @@ -7,11 +7,11 @@ - true + true - + - true + true diff --git a/src/FrostFS.SDK.Tests/ContainerTestsBase.cs b/src/FrostFS.SDK.Tests/ContainerTestsBase.cs index 55f548d..3a0d396 100644 --- a/src/FrostFS.SDK.Tests/ContainerTestsBase.cs +++ b/src/FrostFS.SDK.Tests/ContainerTestsBase.cs @@ -8,12 +8,12 @@ public abstract class ContainerTestsBase { internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions Settings { get; set; } + protected IOptions Settings { get; set; } protected ContainerMocker Mocker { get; set; } protected ContainerTestsBase() { - Settings = Options.Create(new SingleOwnerClientSettings + Settings = Options.Create(new ClientSettings { Key = key, Host = "http://localhost:8080" diff --git a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj index d5cb78f..30c0564 100644 --- a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj +++ b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -10,15 +10,15 @@ - true + true - + - true + true - + - + @@ -38,7 +38,7 @@ - + PreserveNewest diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs index d1366c7..bd9d858 100644 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs @@ -1,9 +1,4 @@ -using System.Security.Cryptography; - using FrostFS.Object; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; using FrostFS.Session; using Google.Protobuf; diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs index c09df83..9e9a18d 100644 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs @@ -43,7 +43,7 @@ public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader) Signature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), - Sign = ByteString.CopyFrom(Key.SignData(header.ToByteArray())), + Sign = Key.SignData(header.ToByteArray()), } } }, diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs index ca1c670..8d4230c 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs @@ -46,19 +46,19 @@ public abstract class ServiceBase(string key) { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray())) + Sign = Key.SignData(response.MetaHeader.ToByteArray()) }, BodySignature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray())) + Sign = Key.SignData(response.GetBody().ToByteArray()) }, OriginSignature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData([])) + Sign = Key.SignData([]) } }; diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs index 4b2b5c1..4f07e41 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs @@ -5,7 +5,6 @@ using FrostFS.Object; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.SDK.Cryptography; -using FrostFS.Session; using Google.Protobuf; @@ -41,7 +40,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) public GetRangeHashRequest? GetRangeHashRequest { get; set; } - public Collection RangeHashResponses { get; } = []; + public Collection RangeHashResponses { get; } = []; public override Mock GetMock() { @@ -94,7 +93,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) headResponse.Body.Header.Signature = new Refs.Signature { 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); @@ -259,7 +258,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) response.Body.HashList.Add(hash); } } - + response.VerifyHeader = GetResponseVerificationHeader(response); return new AsyncUnaryCall( @@ -281,7 +280,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) { 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 }; diff --git a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs index 88ed60d..ebea821 100644 --- a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs @@ -31,7 +31,7 @@ public class SessionMocker(string key) : ServiceBase(key) if (SessionKey == null) { - SessionKey = new byte[32]; + SessionKey = new byte[33]; rand.NextBytes(SessionKey); } diff --git a/src/FrostFS.SDK.Tests/NetworkTest.cs b/src/FrostFS.SDK.Tests/NetworkTest.cs index 2123460..9e83bb0 100644 --- a/src/FrostFS.SDK.Tests/NetworkTest.cs +++ b/src/FrostFS.SDK.Tests/NetworkTest.cs @@ -32,16 +32,13 @@ public class NetworkTest : NetworkTestsBase new PrmNetworkSettings(new CallContext { CancellationToken = Mocker.CancellationTokenSource.Token, - Timeout = TimeSpan.FromSeconds(20), - OwnerId = OwnerId, - Key = ECDsaKey, - Version = Version + Timeout = TimeSpan.FromSeconds(20) }) : new PrmNetworkSettings(); 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); @@ -113,17 +110,14 @@ public class NetworkTest : NetworkTestsBase Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body }; - PrmNetmapSnapshot param; + PrmNetmapSnapshot param = new(); if (useContext) { var ctx = new CallContext { CancellationToken = Mocker.CancellationTokenSource.Token, - Timeout = TimeSpan.FromSeconds(20), - OwnerId = OwnerId, - Key = ECDsaKey, - Version = Version + Timeout = TimeSpan.FromSeconds(20) }; param = new(ctx); @@ -137,7 +131,7 @@ public class NetworkTest : NetworkTestsBase 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); @@ -219,10 +213,7 @@ public class NetworkTest : NetworkTestsBase var ctx = new CallContext { CancellationToken = Mocker.CancellationTokenSource.Token, - Timeout = TimeSpan.FromSeconds(20), - OwnerId = OwnerId, - Key = ECDsaKey, - Version = Version + Timeout = TimeSpan.FromSeconds(20) }; param = new(ctx); @@ -236,7 +227,7 @@ public class NetworkTest : NetworkTestsBase 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); diff --git a/src/FrostFS.SDK.Tests/NetworkTestsBase.cs b/src/FrostFS.SDK.Tests/NetworkTestsBase.cs index 8090ecd..0cabab5 100644 --- a/src/FrostFS.SDK.Tests/NetworkTestsBase.cs +++ b/src/FrostFS.SDK.Tests/NetworkTestsBase.cs @@ -1,8 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; using Microsoft.Extensions.Options; @@ -13,32 +11,27 @@ public abstract class NetworkTestsBase { internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions 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 ClientSettings DefaultSettings { get; } + protected NetworkTestsBase() { - Settings = Options.Create(new SingleOwnerClientSettings + DefaultSettings = new ClientSettings { Key = key, - Host = "http://localhost:8080" - }); - - ECDsaKey = key.LoadWif(); - OwnerId = FrostFsOwner.FromKey(ECDsaKey); + Host = "http://localhost:8080", + }; Mocker = new NetworkMocker(this.key); } - protected IFrostFSClient GetClient() + protected IFrostFSClient GetClient(ClientSettings settings) { return Client.FrostFSClient.GetTestInstance( - Settings, + Options.Create(settings), null, Mocker.GetMock().Object, new SessionMocker(this.key).GetMock().Object, diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs index dabbbc8..5914605 100644 --- a/src/FrostFS.SDK.Tests/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/ObjectTest.cs @@ -6,12 +6,9 @@ using System.Text; using FrostFS.Refs; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; using Google.Protobuf; -using static FrostFS.Object.ECInfo.Types; - namespace FrostFS.SDK.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 ecdsaKey = key.LoadWif(); + var objectId = client.CalculateObjectId(Mocker.ObjectHeader!); - var ctx = new CallContext - { - 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)); + var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId)); Assert.NotNull(result); @@ -90,7 +78,7 @@ public class ObjectTest : ObjectTestsBase NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); var blockSize = 2560; - byte[] bytes = File.ReadAllBytes(@".\..\..\..\TestData\cat.jpg"); + byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); var fileLength = bytes.Length; var param = new PrmObjectPut @@ -295,7 +283,7 @@ public class ObjectTest : ObjectTestsBase Assert.Equal(SHA256.HashData(hash), SHA256.HashData(result.First().ToArray())); } - + [Fact] public async void PatchTest() { @@ -337,9 +325,9 @@ public class ObjectTest : ObjectTestsBase Assert.Equal(address.ContainerId, body.Address.ContainerId); Assert.Equal(address.ObjectId, body.Address.ObjectId); - + Assert.Equal(32, body.Patch.Chunk.Length); - + Assert.Equal(SHA256.HashData(patch), SHA256.HashData(body.Patch.Chunk.ToArray())); } } diff --git a/src/FrostFS.SDK.Tests/ObjectTestsBase.cs b/src/FrostFS.SDK.Tests/ObjectTestsBase.cs index ba68e6f..21289e4 100644 --- a/src/FrostFS.SDK.Tests/ObjectTestsBase.cs +++ b/src/FrostFS.SDK.Tests/ObjectTestsBase.cs @@ -10,7 +10,7 @@ public abstract class ObjectTestsBase { protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions Settings { get; set; } + protected IOptions Settings { get; set; } protected FrostFsContainerId ContainerId { get; set; } protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key); @@ -22,7 +22,7 @@ public abstract class ObjectTestsBase { var ecdsaKey = key.LoadWif(); - Settings = Options.Create(new SingleOwnerClientSettings + Settings = Options.Create(new ClientSettings { Key = key, Host = "http://localhost:8080" diff --git a/src/FrostFS.SDK.Tests/PoolSmokeTests.cs b/src/FrostFS.SDK.Tests/PoolSmokeTests.cs index efc4802..0a6f2b3 100644 --- a/src/FrostFS.SDK.Tests/PoolSmokeTests.cs +++ b/src/FrostFS.SDK.Tests/PoolSmokeTests.cs @@ -6,8 +6,6 @@ using FrostFS.SDK.Cryptography; using Microsoft.Extensions.Options; -using static FrostFS.Session.SessionToken.Types.Body; - namespace FrostFS.SDK.SmokeTests; [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] public async void NodeInfoStatisticsTwoNodesTest() { + var callbackText = string.Empty; + var options = new InitParameters { Key = keyString.LoadWif(), @@ -87,16 +87,14 @@ public class PoolSmokeTests : SmokeTestsBase ], ClientBuilder = null, GracefulCloseOnSwitchTimeout = 30_000_000, - Logger = null + Logger = null, + Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds" }; using var pool = new Pool(options); - var callbackText = string.Empty; - var ctx = new CallContext { - Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds" }; var error = await pool.Dial(ctx).ConfigureAwait(true); @@ -118,14 +116,12 @@ public class PoolSmokeTests : SmokeTestsBase { var options = GetDefaultParams(); + var callbackText = string.Empty; + options.Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"; + using var pool = new Pool(options); - var callbackText = string.Empty; - - var ctx = new CallContext - { - Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds" - }; + var ctx = new CallContext(); 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 session = new Session.SessionToken().Deserialize(token.Token); - var ownerHash = Base58.Decode(OwnerId!.Value); - Assert.NotNull(session); - Assert.Null(session.Body.Container); - Assert.Null(session.Body.Object); - 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); + Assert.NotNull(token); + Assert.NotEqual(Guid.Empty, token.Id); + Assert.Equal(33, token.SessionKey.Length); } [Fact] @@ -314,8 +303,16 @@ public class PoolSmokeTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioTest(int objectSize) { + bool callbackInvoked = false; + var options = GetDefaultParams(); + options.Callback = new((CallStatistics cs) => + { + callbackInvoked = true; + Assert.True(cs.ElapsedMicroSeconds > 0); + }); + using var pool = new Pool(options); var error = await pool.Dial(new CallContext()).ConfigureAwait(true); @@ -324,16 +321,7 @@ public class PoolSmokeTests : SmokeTestsBase await Cleanup(pool); - bool callbackInvoked = false; - var ctx = new CallContext - { - // Timeout = TimeSpan.FromSeconds(20), - Callback = new((CallStatistics cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }) - }; + var ctx = new CallContext { }; var createContainerParam = new PrmContainerCreate( 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 ctx1 = new CallContext - { - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) - }; - - var param = new PrmObjectPut(ctx1) + var param = new PrmObjectPut() { Header = new FrostFsObjectHeader( containerId: createdContainer, @@ -410,6 +393,7 @@ public class PoolSmokeTests : SmokeTestsBase var options = GetDefaultParams(); 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); @@ -421,8 +405,7 @@ public class PoolSmokeTests : SmokeTestsBase var ctx = new CallContext { - Timeout = TimeSpan.FromSeconds(20), - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) + Timeout = TimeSpan.FromSeconds(20) }; var createContainerParam = new PrmContainerCreate( @@ -435,7 +418,7 @@ public class PoolSmokeTests : SmokeTestsBase 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( containerId: container, @@ -517,7 +500,7 @@ public class PoolSmokeTests : SmokeTestsBase Timeout = TimeSpan.FromSeconds(10), }; - ctx.Interceptors.Add(new CallbackInterceptor()); + // ctx.Interceptors.Add(new CallbackInterceptor()); var container = await pool.GetContainerAsync(new PrmContainerGet(containerId, ctx)); @@ -585,9 +568,9 @@ public class PoolSmokeTests : SmokeTestsBase return bytes; } - private static IOptions GetSingleOwnerOptions(string key, string url) + private static IOptions GetSingleOwnerOptions(string key, string url) { - return Options.Create(new SingleOwnerClientSettings + return Options.Create(new ClientSettings { Key = key, Host = url diff --git a/src/FrostFS.SDK.Tests/SessionTests.cs b/src/FrostFS.SDK.Tests/SessionTests.cs index 4cf03ba..9e79b29 100644 --- a/src/FrostFS.SDK.Tests/SessionTests.cs +++ b/src/FrostFS.SDK.Tests/SessionTests.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; +using FrostFS.SDK.Cryptography; namespace FrostFS.SDK.Tests; @@ -21,10 +22,7 @@ public class SessionTest : SessionTestsBase var ctx = new CallContext { CancellationToken = Mocker.CancellationTokenSource.Token, - Timeout = TimeSpan.FromSeconds(20), - OwnerId = OwnerId, - Key = ECDsaKey, - Version = Mocker.Version + Timeout = TimeSpan.FromSeconds(20) }; param = new PrmSessionCreate(exp, ctx); @@ -43,18 +41,16 @@ public class SessionTest : SessionTestsBase var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); 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(Mocker.SessionKey, session.Body.SessionKey); - - Assert.Equal(OwnerId.ToMessage(), session.Body.OwnerId); - Assert.Equal(exp, session.Body.Lifetime.Exp); - Assert.Equal(exp, session.Body.Lifetime.Iat); - Assert.Equal(exp, session.Body.Lifetime.Nbf); - Assert.Null(session.Body.Container); + //Assert.Equal(OwnerId.ToMessage(), result.Token.Body.OwnerId); + //Assert.Equal(exp, result.Token.Body.Lifetime.Exp); + //Assert.Equal(exp, result.Token.Body.Lifetime.Iat); + //Assert.Equal(exp, result.Token.Body.Lifetime.Nbf); + //Assert.Null(result.Token.Body.Container); Assert.NotNull(Mocker.CreateSessionRequest); diff --git a/src/FrostFS.SDK.Tests/SessionTestsBase.cs b/src/FrostFS.SDK.Tests/SessionTestsBase.cs index 93bc140..bd958fc 100644 --- a/src/FrostFS.SDK.Tests/SessionTestsBase.cs +++ b/src/FrostFS.SDK.Tests/SessionTestsBase.cs @@ -11,7 +11,7 @@ public abstract class SessionTestsBase { internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - protected IOptions Settings { get; set; } + protected IOptions Settings { get; set; } protected ECDsa ECDsaKey { get; set; } protected FrostFsOwner OwnerId { get; set; } @@ -19,7 +19,7 @@ public abstract class SessionTestsBase protected SessionTestsBase() { - Settings = Options.Create(new SingleOwnerClientSettings + Settings = Options.Create(new ClientSettings { Key = key, Host = "http://localhost:8080" diff --git a/src/FrostFS.SDK.Tests/SmokeClientTests.cs b/src/FrostFS.SDK.Tests/SmokeClientTests.cs index ba822c7..aaf1869 100644 --- a/src/FrostFS.SDK.Tests/SmokeClientTests.cs +++ b/src/FrostFS.SDK.Tests/SmokeClientTests.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using FrostFS.SDK.Client; @@ -7,8 +7,6 @@ using FrostFS.SDK.Cryptography; using Microsoft.Extensions.Options; -using static FrostFS.Session.SessionToken.Types.Body; - namespace FrostFS.SDK.SmokeTests; [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); - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void AccountTest(bool isSingleOnwerClient) + [Fact] + public async void AccountTest() { - 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)); - 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] - [InlineData(false)] - [InlineData(true)] - public async void NetworkMapTest(bool isSingleOnwerClient) + [Fact] + public async void NetworkMapTest() { - 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(prm); + var result = await client.GetNetmapSnapshotAsync(); Assert.True(result.Epoch > 0); Assert.Single(result.NodeInfoCollection); @@ -54,18 +46,12 @@ public class SmokeClientTests : SmokeTestsBase } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void NodeInfoTest(bool isSingleOnwerClient) + [Fact] + public async void NodeInfoTest() { - 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)); - PrmNodeInfo? prm = isSingleOnwerClient ? default : new(Ctx); - - var result = await client.GetNodeInfoAsync(prm); + var result = await client.GetNodeInfoAsync(); Assert.Equal(2, result.Version.Major); Assert.Equal(13, result.Version.Minor); @@ -78,45 +64,38 @@ public class SmokeClientTests : SmokeTestsBase [Fact] public async void NodeInfoStatisticsTest() { - var ctx = new CallContext - { - Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds") - }; + var options = GetClientOptions(this.keyString, this.url); - 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(); + + Assert.NotEmpty(callbackContent); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void GetSessionTest(bool isSingleOnwerClient) + [Fact] + public async void GetSessionTest() { - 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 session = new Session.SessionToken().Deserialize(token.Token); - var ownerHash = Base58.Decode(OwnerId!.Value); - Assert.NotNull(session); - Assert.Null(session.Body.Container); - Assert.Null(session.Body.Object); - 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); + Assert.NotNull(token); + Assert.NotEqual(Guid.Empty, token.Id); + Assert.Equal(33, token.SessionKey.Length); } [Fact] 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); @@ -164,7 +143,7 @@ public class SmokeClientTests : SmokeTestsBase [Fact] 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); @@ -248,89 +227,87 @@ public class SmokeClientTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioTest(int objectSize) { - using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)); + bool callbackInvoked = false; - await Cleanup(client); + var options = GetClientOptions(this.keyString, this.url); - bool callbackInvoked = false; - var ctx = new CallContext - { - // Timeout = TimeSpan.FromSeconds(20), - Callback = new((CallStatistics cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }) - }; + options.Value.Callback = new((CallStatistics cs) => + { + callbackInvoked = true; + Assert.True(cs.ElapsedMicroSeconds > 0); + }); - var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx); + using var client = FrostFSClient.GetSingleOwnerInstance(options); - var createdContainer = await client.CreateContainerAsync(createContainerParam); + await Cleanup(client); - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx)); - Assert.NotNull(container); - Assert.True(callbackInvoked); + var ctx = new CallContext { Timeout = TimeSpan.FromSeconds(20) }; - var bytes = GetRandomBytes(objectSize); + var createContainerParam = new PrmContainerCreate( + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx); - var param = new PrmObjectPut(new CallContext - { - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) - }) - { - Header = new FrostFsObjectHeader( - containerId: createdContainer, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - Payload = new MemoryStream(bytes), - ClientCut = false - }; + var createdContainer = await client.CreateContainerAsync(createContainerParam); - var objectId = await client.PutObjectAsync(param); + var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer)); + Assert.NotNull(container); + Assert.True(callbackInvoked); - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); + var bytes = GetRandomBytes(objectSize); - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] })) - { - hasObject = true; + var param = new PrmObjectPut + { + Header = new FrostFsObjectHeader( + containerId: createdContainer, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")]), + Payload = new MemoryStream(bytes), + ClientCut = false + }; - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId)); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName", objHeader.Attributes.First().Key); - Assert.Equal("test", objHeader.Attributes.First().Value); - } + var objectId = await client.PutObjectAsync(param); - Assert.True(hasObject); + var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId)); + bool hasObject = false; + await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] })) + { + hasObject = true; - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); + var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId)); + Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); + Assert.NotNull(objHeader.Attributes); + Assert.Single(objHeader.Attributes); + Assert.Equal("fileName", objHeader.Attributes.First().Key); + Assert.Equal("test", objHeader.Attributes.First().Value); + } - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } + Assert.True(hasObject); - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); + var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId)); - await Cleanup(client); + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); - await foreach (var _ in client.ListContainersAsync()) - { - Assert.Fail("Containers exist"); - } + ReadOnlyMemory? chunk = null; + while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + { + ms.Write(chunk.Value.Span); + } + + Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); + + await Cleanup(client); + + await foreach (var _ in client.ListContainersAsync()) + { + Assert.Fail("Containers exist"); + } } [Fact] 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); @@ -388,7 +365,7 @@ public class SmokeClientTests : SmokeTestsBase 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]); var rangeEnd = range.Offset + range.Length; @@ -410,7 +387,7 @@ public class SmokeClientTests : SmokeTestsBase [Fact] 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); @@ -465,7 +442,7 @@ public class SmokeClientTests : SmokeTestsBase [Fact] 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); @@ -494,15 +471,15 @@ public class SmokeClientTests : SmokeTestsBase 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); foreach (var hash in hashes) { - var x = hash.Slice(0, 32).ToArray(); + var x = hash[..32].ToArray(); } - + await Cleanup(client); await foreach (var _ in client.ListContainersAsync()) @@ -517,8 +494,8 @@ public class SmokeClientTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] 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)); await Cleanup(client); @@ -526,7 +503,7 @@ public class SmokeClientTests : SmokeTestsBase var ctx = new CallContext { Timeout = TimeSpan.FromSeconds(20), - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) + }; var createContainerParam = new PrmContainerCreate( @@ -539,10 +516,7 @@ public class SmokeClientTests : SmokeTestsBase var bytes = GetRandomBytes(objectSize); - var param = new PrmObjectPut(new CallContext - { - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) - }) + var param = new PrmObjectPut { Header = new FrostFsObjectHeader( containerId: container, @@ -602,7 +576,11 @@ public class SmokeClientTests : SmokeTestsBase [InlineData(200)] 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); @@ -613,12 +591,7 @@ public class SmokeClientTests : SmokeTestsBase var containerId = await client.CreateContainerAsync(createContainerParam); - var ctx = new CallContext - { - Timeout = TimeSpan.FromSeconds(10) - }; - - ctx.Interceptors.Add(new CallbackInterceptor()); + var ctx = new CallContext { Timeout = TimeSpan.FromSeconds(10) }; var container = await client.GetContainerAsync(new PrmContainerGet(containerId, ctx)); @@ -692,23 +665,21 @@ public class SmokeClientTests : SmokeTestsBase [Fact] public async void NodeInfoCallbackAndInterceptorTest() { - using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)); - bool callbackInvoked = 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; - Assert.True(cs.ElapsedMicroSeconds > 0); - }) + callbackInvoked = true; + 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(intercepterInvoked); @@ -729,22 +700,15 @@ public class SmokeClientTests : SmokeTestsBase return bytes; } - private static IOptions GetSingleOwnerOptions(string key, string url) + private static IOptions GetClientOptions(string key, string url) { - return Options.Create(new SingleOwnerClientSettings + return Options.Create(new ClientSettings { Key = key, Host = url }); } - private static IOptions GetOptions(string url) - { - return Options.Create(new ClientSettings - { - Host = url - }); - } static async Task Cleanup(IFrostFSClient client) { diff --git a/src/FrostFS.SDK.Tests/SmokeTestsBase.cs b/src/FrostFS.SDK.Tests/SmokeTestsBase.cs index 5d67029..b96997c 100644 --- a/src/FrostFS.SDK.Tests/SmokeTestsBase.cs +++ b/src/FrostFS.SDK.Tests/SmokeTestsBase.cs @@ -7,8 +7,6 @@ namespace FrostFS.SDK.SmokeTests; public abstract class SmokeTestsBase { - // internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; - internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; internal readonly string url = "http://172.23.32.4:8080"; @@ -26,7 +24,5 @@ public abstract class SmokeTestsBase Key = keyString.LoadWif(); OwnerId = FrostFsOwner.FromKey(Key); Version = new FrostFsVersion(2, 13); - - Ctx = new CallContext { Key = Key, OwnerId = OwnerId, Version = Version }; } } diff --git a/src/FrostFS.SDK.Tests/TestData/cat.jpg b/src/FrostFS.SDK.Tests/cat.jpg similarity index 100% rename from src/FrostFS.SDK.Tests/TestData/cat.jpg rename to src/FrostFS.SDK.Tests/cat.jpg