[Draft] Update 0.8 (#25)

* get rid of file handling through grpc and signalr

* fix upload on controller

* adapt usersetpairpermissions

* send user perms

* server-side fixes

* rework file upload

* adjust log level to debug in docker standalone json

* update dependencies

---------

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2023-03-14 19:46:59 +01:00
committed by GitHub
parent 7a211aa236
commit 48323d68b0
31 changed files with 1010 additions and 634 deletions

View File

@@ -1,253 +0,0 @@
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using Google.Protobuf;
using Grpc.Core;
using MareSynchronos.API.Dto.Files;
using MareSynchronosServer.Utils;
using MareSynchronosShared.Models;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
namespace MareSynchronosServer.Hubs;
public partial class MareHub
{
private static readonly SemaphoreSlim _uploadSemaphore = new(20);
[Authorize(Policy = "Identified")]
public async Task FilesAbortUpload()
{
_logger.LogCallInfo();
var notUploadedFiles = await _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == UserUID).ToListAsync();
if (notUploadedFiles.Any())
{
_dbContext.RemoveRange(notUploadedFiles);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
[Authorize(Policy = "Identified")]
public async Task FilesDeleteAll()
{
_logger.LogCallInfo();
var ownFiles = await _dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == UserUID).ToListAsync().ConfigureAwait(false);
var request = new DeleteFilesRequest();
request.Hash.AddRange(ownFiles.Select(f => f.Hash));
Metadata headers = new Metadata()
{
{ "Authorization", "Bearer " + _generator.Token },
};
_ = await _fileServiceClient.DeleteFilesAsync(request, headers).ConfigureAwait(false);
}
[Authorize(Policy = "Identified")]
public async Task<List<DownloadFileDto>> FilesGetSizes(List<string> hashes)
{
_logger.LogCallInfo(MareHubLogger.Args(hashes.Count.ToString()));
var allFiles = await _dbContext.Files.Where(f => hashes.Contains(f.Hash)).ToListAsync().ConfigureAwait(false);
var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.
Where(f => hashes.Contains(f.Hash)).ToListAsync().ConfigureAwait(false);
List<DownloadFileDto> response = new();
var cacheFile = await _dbContext.Files.AsNoTracking().Where(f => hashes.Contains(f.Hash)).AsNoTracking().Select(k => new { k.Hash, k.Size }).AsNoTracking().ToListAsync().ConfigureAwait(false);
var shardConfig = new List<CdnShardConfiguration>(_configurationService.GetValueOrDefault(nameof(ServerConfiguration.CdnShardConfiguration), new List<CdnShardConfiguration>()));
foreach (var file in cacheFile)
{
var forbiddenFile = forbiddenFiles.SingleOrDefault(f => string.Equals(f.Hash, file.Hash, StringComparison.OrdinalIgnoreCase));
var matchedShardConfig = shardConfig.OrderBy(g => Guid.NewGuid()).FirstOrDefault(f => new Regex(f.FileMatch).IsMatch(file.Hash));
var baseUrl = matchedShardConfig?.CdnFullUrl ?? _mainCdnFullUrl;
response.Add(new DownloadFileDto
{
FileExists = file.Size > 0,
ForbiddenBy = forbiddenFile?.ForbiddenBy ?? string.Empty,
IsForbidden = forbiddenFile != null,
Hash = file.Hash,
Size = file.Size,
Url = baseUrl.ToString(),
});
}
return response;
}
[Authorize(Policy = "Identified")]
public async Task<bool> FilesIsUploadFinished()
{
_logger.LogCallInfo();
return await _dbContext.Files.AsNoTracking()
.AnyAsync(f => f.Uploader.UID == UserUID && !f.Uploaded).ConfigureAwait(false);
}
[Authorize(Policy = "Identified")]
public async Task<List<UploadFileDto>> FilesSend(List<string> fileListHashes)
{
var userSentHashes = new HashSet<string>(fileListHashes.Distinct(StringComparer.Ordinal).Select(s => string.Concat(s.Where(c => char.IsLetterOrDigit(c)))), StringComparer.Ordinal);
_logger.LogCallInfo(MareHubLogger.Args(userSentHashes.Count.ToString()));
var notCoveredFiles = new Dictionary<string, UploadFileDto>(StringComparer.Ordinal);
var forbiddenFiles = await _dbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false);
var existingFiles = await _dbContext.Files.AsNoTracking().Where(f => userSentHashes.Contains(f.Hash)).AsNoTracking().ToDictionaryAsync(f => f.Hash, f => f).ConfigureAwait(false);
var uploader = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
List<FileCache> fileCachesToUpload = new();
foreach (var hash in userSentHashes)
{
// Skip empty file hashes, duplicate file hashes, forbidden file hashes and existing file hashes
if (string.IsNullOrEmpty(hash)) { continue; }
if (notCoveredFiles.ContainsKey(hash)) { continue; }
if (forbiddenFiles.ContainsKey(hash))
{
notCoveredFiles[hash] = new UploadFileDto()
{
ForbiddenBy = forbiddenFiles[hash].ForbiddenBy,
Hash = hash,
IsForbidden = true,
};
continue;
}
if (existingFiles.TryGetValue(hash, out var file) && file.Uploaded) { continue; }
_logger.LogCallInfo(MareHubLogger.Args(hash, "Missing"));
if (file == null)
{
fileCachesToUpload.Add(new FileCache()
{
Hash = hash,
Uploaded = false,
Uploader = uploader,
UploadDate = DateTime.UtcNow,
});
}
notCoveredFiles[hash] = new UploadFileDto()
{
Hash = hash,
};
}
//Save bulk
await _dbContext.Files.AddRangeAsync(fileCachesToUpload).ConfigureAwait(false);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
return notCoveredFiles.Values.ToList();
}
[Authorize(Policy = "Identified")]
public async Task FilesUploadStreamAsync(string hash, IAsyncEnumerable<byte[]> fileContent)
{
_logger.LogCallInfo(MareHubLogger.Args(hash));
await _uploadSemaphore.WaitAsync(Context.ConnectionAborted).ConfigureAwait(false);
var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash && f.Uploader.UID == UserUID && !f.Uploaded);
if (relatedFile == null)
{
_uploadSemaphore.Release();
return;
}
var forbiddenFile = _dbContext.ForbiddenUploadEntries.SingleOrDefault(f => f.Hash == hash);
if (forbiddenFile != null)
{
_uploadSemaphore.Release();
return;
}
var tempFileName = Path.GetTempFileName();
using var fileStream = new FileStream(tempFileName, FileMode.OpenOrCreate);
long length = 0;
try
{
await foreach (var chunk in fileContent.ConfigureAwait(false))
{
length += chunk.Length;
await fileStream.WriteAsync(chunk).ConfigureAwait(false);
}
await fileStream.FlushAsync().ConfigureAwait(false);
await fileStream.DisposeAsync().ConfigureAwait(false);
}
catch
{
try
{
await fileStream.FlushAsync().ConfigureAwait(false);
await fileStream.DisposeAsync().ConfigureAwait(false);
_dbContext.Files.Remove(relatedFile);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
catch
{
// already removed
}
finally
{
File.Delete(tempFileName);
}
_uploadSemaphore.Release();
return;
}
_logger.LogCallInfo(MareHubLogger.Args(hash, "Uploaded"));
try
{
var decodedFile = LZ4.LZ4Codec.Unwrap(await File.ReadAllBytesAsync(tempFileName).ConfigureAwait(false));
using var sha1 = SHA1.Create();
using var ms = new MemoryStream(decodedFile);
var computedHash = await sha1.ComputeHashAsync(ms).ConfigureAwait(false);
var computedHashString = BitConverter.ToString(computedHash).Replace("-", "", StringComparison.Ordinal);
if (!string.Equals(hash, computedHashString, StringComparison.Ordinal))
{
_logger.LogCallWarning(MareHubLogger.Args(hash, "Invalid", computedHashString));
_dbContext.Remove(relatedFile);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
_uploadSemaphore.Release();
return;
}
Metadata headers = new Metadata()
{
{ "Authorization", "Bearer " + _generator.Token },
};
var streamingCall = _fileServiceClient.UploadFile(headers);
using var tempFileStream = new FileStream(tempFileName, FileMode.Open, FileAccess.Read);
int size = 1024 * 1024;
byte[] data = new byte[size];
int readBytes;
while ((readBytes = tempFileStream.Read(data, 0, size)) > 0)
{
await streamingCall.RequestStream.WriteAsync(new UploadFileRequest()
{
FileData = ByteString.CopyFrom(data, 0, readBytes),
Hash = computedHashString,
Uploader = UserUID,
}).ConfigureAwait(false);
}
await streamingCall.RequestStream.CompleteAsync().ConfigureAwait(false);
tempFileStream.Close();
await tempFileStream.DisposeAsync().ConfigureAwait(false);
_logger.LogCallInfo(MareHubLogger.Args(hash, "Pushed"));
}
catch (Exception ex)
{
_logger.LogCallWarning(MareHubLogger.Args("Failed", hash, ex.Message));
_dbContext.Remove(relatedFile);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
finally
{
_uploadSemaphore.Release();
File.Delete(tempFileName);
}
}
}

View File

@@ -30,7 +30,7 @@ public partial class MareHub
private async Task<Dictionary<string, string>> GetOnlineUsers(List<string> uids)
{
var result = await _redis.GetAllAsync<string>(uids.Select(u => "UID:" + u).ToArray()).ConfigureAwait(false);
var result = await _redis.GetAllAsync<string>(uids.Select(u => "UID:" + u).ToHashSet(StringComparer.Ordinal)).ConfigureAwait(false);
return uids.Where(u => result.TryGetValue("UID:" + u, out var ident) && !string.IsNullOrEmpty(ident)).ToDictionary(u => u, u => result["UID:" + u], StringComparer.Ordinal);
}

View File

@@ -68,6 +68,10 @@ public partial class MareHub
OtherIsPaused = otherEntry != null && otherEntry.IsPaused,
userToOther.OtherUserUID,
IsSynced = otherEntry != null,
DisableOwnAnimations = userToOther.DisableAnimations,
DisableOwnSounds = userToOther.DisableSounds,
DisableOtherAnimations = otherEntry == null ? false : otherEntry.DisableAnimations,
DisableOtherSounds = otherEntry == null ? false : otherEntry.DisableSounds
};
var results = await query.AsNoTracking().ToListAsync().ConfigureAwait(false);
@@ -76,9 +80,13 @@ public partial class MareHub
{
var ownPerm = UserPermissions.Paired;
ownPerm.SetPaused(c.IsPaused);
ownPerm.SetDisableAnimations(c.DisableOwnAnimations);
ownPerm.SetDisableSounds(c.DisableOwnSounds);
var otherPerm = UserPermissions.NoneSet;
otherPerm.SetPaired(c.IsSynced);
otherPerm.SetPaused(c.OtherIsPaused);
otherPerm.SetDisableAnimations(c.DisableOtherAnimations);
otherPerm.SetDisableSounds(c.DisableOtherSounds);
return new UserPairDto(new(c.OtherUserUID, c.Alias), ownPerm, otherPerm);
}).ToList();
}
@@ -215,7 +223,11 @@ public partial class MareHub
ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
if (pair == null) return;
var pauseChange = pair.IsPaused != dto.Permissions.IsPaused();
pair.IsPaused = dto.Permissions.IsPaused();
pair.DisableAnimations = dto.Permissions.IsDisableAnimations();
pair.DisableSounds = dto.Permissions.IsDisableSounds();
_dbContext.Update(pair);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
@@ -229,19 +241,22 @@ public partial class MareHub
{
await Clients.User(dto.User.UID).Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), dto.Permissions)).ConfigureAwait(false);
var otherCharaIdent = await GetUserIdent(pair.OtherUserUID).ConfigureAwait(false);
if (UserCharaIdent == null || otherCharaIdent == null || otherEntry.IsPaused) return;
if (dto.Permissions.IsPaused())
if (pauseChange)
{
await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false);
await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false);
}
else
{
await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherCharaIdent)).ConfigureAwait(false);
await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false);
var otherCharaIdent = await GetUserIdent(pair.OtherUserUID).ConfigureAwait(false);
if (UserCharaIdent == null || otherCharaIdent == null || otherEntry.IsPaused) return;
if (dto.Permissions.IsPaused())
{
await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false);
await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false);
}
else
{
await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherCharaIdent)).ConfigureAwait(false);
await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false);
}
}
}
}

View File

@@ -7,7 +7,6 @@ using MareSynchronosServer.Utils;
using MareSynchronosShared;
using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Services;
using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization;
@@ -20,37 +19,33 @@ namespace MareSynchronosServer.Hubs;
public partial class MareHub : Hub<IMareHub>, IMareHub
{
private readonly MareMetrics _mareMetrics;
private readonly FileService.FileServiceClient _fileServiceClient;
private readonly SystemInfoService _systemInfoService;
private readonly IHttpContextAccessor _contextAccessor;
private readonly MareHubLogger _logger;
private readonly MareDbContext _dbContext;
private readonly Uri _mainCdnFullUrl;
private readonly string _shardName;
private readonly int _maxExistingGroupsByUser;
private readonly int _maxJoinedGroupsByUser;
private readonly int _maxGroupUserCount;
private readonly IConfigurationService<ServerConfiguration> _configurationService;
private readonly IRedisDatabase _redis;
private readonly ServerTokenGenerator _generator;
private readonly Uri _fileServerAddress;
private readonly Version _expectedClientVersion;
public MareHub(MareMetrics mareMetrics, FileService.FileServiceClient fileServiceClient,
public MareHub(MareMetrics mareMetrics,
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService,
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
IRedisDatabase redisDb, ServerTokenGenerator generator)
IRedisDatabase redisDb)
{
_mareMetrics = mareMetrics;
_fileServiceClient = fileServiceClient;
_systemInfoService = systemInfoService;
_configurationService = configuration;
_mainCdnFullUrl = configuration.GetValue<Uri>(nameof(ServerConfiguration.CdnFullUrl));
_shardName = configuration.GetValue<string>(nameof(ServerConfiguration.ShardName));
_maxExistingGroupsByUser = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxExistingGroupsByUser), 3);
_maxJoinedGroupsByUser = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxJoinedGroupsByUser), 6);
_maxGroupUserCount = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxGroupUserCount), 100);
_fileServerAddress = configuration.GetValue<Uri>(nameof(ServerConfiguration.CdnFullUrl));
_expectedClientVersion = configuration.GetValueOrDefault(nameof(ServerConfiguration.ExpectedClientVersion), new Version(0, 0, 0));
_contextAccessor = contextAccessor;
_redis = redisDb;
_generator = generator;
_logger = new MareHubLogger(this, logger);
_dbContext = mareDbContext;
}
@@ -73,6 +68,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
return new ConnectionDto(new UserData(dbUser.UID, string.IsNullOrWhiteSpace(dbUser.Alias) ? null : dbUser.Alias))
{
CurrentClientVersion = _expectedClientVersion,
ServerVersion = IMareHub.ApiVersion,
IsAdmin = dbUser.IsAdmin,
IsModerator = dbUser.IsModerator,
@@ -82,6 +78,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
ShardName = _shardName,
MaxGroupsJoinedByUser = _maxJoinedGroupsByUser,
MaxGroupUserCount = _maxGroupUserCount,
FileServerAddress = _fileServerAddress
},
};
}

View File

@@ -21,29 +21,18 @@
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Bazinga.AspNetCore.Authentication.Basic" Version="2.0.1" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
<PackageReference Include="Grpc.AspNetCore" Version="2.51.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.51.0" />
<PackageReference Include="Karambolo.Extensions.Logging.File" Version="3.3.1" />
<PackageReference Include="lz4net" Version="1.0.15.93" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.14">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.19">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.26.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.6.90" />
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="8.0.5" />
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="8.0.5" />
<PackageReference Include="StackExchange.Redis.Extensions.System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.26.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.27.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="8.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Authorization;
namespace MareSynchronosServer.RequirementHandlers;
public class UserRequirement : IAuthorizationRequirement
{
public UserRequirement(UserRequirements requirements)
{
Requirements = requirements;
}
public UserRequirements Requirements { get; }
}

View File

@@ -1,52 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using MareSynchronosShared.Data;
using Microsoft.EntityFrameworkCore;
using MareSynchronosShared.Utils;
using StackExchange.Redis;
using StackExchange.Redis.Extensions.Core.Abstractions;
namespace MareSynchronosServer.RequirementHandlers;
public class UserRequirementHandler : AuthorizationHandler<UserRequirement, HubInvocationContext>
{
private readonly MareDbContext _dbContext;
private readonly ILogger<UserRequirementHandler> _logger;
private readonly IRedisDatabase _redis;
public UserRequirementHandler(MareDbContext dbContext, ILogger<UserRequirementHandler> logger, IRedisDatabase redisDb)
{
_dbContext = dbContext;
_logger = logger;
_redis = redisDb;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRequirement requirement, HubInvocationContext resource)
{
var uid = context.User.Claims.SingleOrDefault(g => string.Equals(g.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value;
if (uid == null) context.Fail();
if ((requirement.Requirements & UserRequirements.Identified) is UserRequirements.Identified)
{
var ident = await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
if (ident == RedisValue.EmptyString) context.Fail();
}
if ((requirement.Requirements & UserRequirements.Administrator) is UserRequirements.Administrator)
{
var user = await _dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
if (user == null || !user.IsAdmin) context.Fail();
_logger.LogInformation("Admin {uid} authenticated", uid);
}
if ((requirement.Requirements & UserRequirements.Moderator) is UserRequirements.Moderator)
{
var user = await _dbContext.Users.AsNoTracking().SingleOrDefaultAsync(b => b.UID == uid).ConfigureAwait(false);
if (user == null || !user.IsAdmin && !user.IsModerator) context.Fail();
_logger.LogInformation("Admin/Moderator {uid} authenticated", uid);
}
context.Succeed(requirement);
}
}

View File

@@ -1,8 +0,0 @@
namespace MareSynchronosServer.RequirementHandlers;
public enum UserRequirements
{
Identified = 0b00000001,
Moderator = 0b00000010,
Administrator = 0b00000100,
}

View File

@@ -9,7 +9,6 @@ using MareSynchronosShared.Protos;
using Grpc.Net.Client.Configuration;
using MareSynchronosShared.Metrics;
using MareSynchronosServer.Services;
using MareSynchronosServer.RequirementHandlers;
using MareSynchronosShared.Utils;
using MareSynchronosShared.Services;
using Prometheus;
@@ -26,6 +25,7 @@ using MessagePack;
using MessagePack.Resolvers;
using Microsoft.AspNetCore.Mvc.Controllers;
using MareSynchronosServer.Controllers;
using MareSynchronosShared.RequirementHandlers;
namespace MareSynchronosServer;
@@ -52,9 +52,6 @@ public class Startup
// configure metrics
ConfigureMetrics(services);
// configure file service grpc connection
ConfigureFileServiceGrpcClient(services);
// configure database
ConfigureDatabase(services, mareConfig);
@@ -294,30 +291,6 @@ public class Startup
}
}
private static void ConfigureFileServiceGrpcClient(IServiceCollection services)
{
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
MaxAttempts = 1000,
InitialBackoff = TimeSpan.FromSeconds(1),
MaxBackoff = TimeSpan.FromSeconds(5),
BackoffMultiplier = 1.5,
RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable },
},
};
services.AddGrpcClient<FileService.FileServiceClient>((serviceProvider, c) =>
{
c.Address = serviceProvider.GetRequiredService<IConfigurationService<ServerConfiguration>>()
.GetValue<Uri>(nameof(ServerConfiguration.StaticFileServiceAddress));
}).ConfigureChannel(c =>
{
c.ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } };
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
logger.LogInformation("Running Configure");